| @@ -4912,6 +4912,11 @@ | |||
| } | |||
| } | |||
| }, | |||
| "classnames": { | |||
| "version": "2.3.1", | |||
| "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", | |||
| "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" | |||
| }, | |||
| "clean-css": { | |||
| "version": "4.2.3", | |||
| "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", | |||
| @@ -5744,6 +5749,11 @@ | |||
| "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", | |||
| "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==" | |||
| }, | |||
| "debounce": { | |||
| "version": "1.2.1", | |||
| "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", | |||
| "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" | |||
| }, | |||
| "debug": { | |||
| "version": "4.3.1", | |||
| "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", | |||
| @@ -6252,6 +6262,11 @@ | |||
| } | |||
| } | |||
| }, | |||
| "easy-bem": { | |||
| "version": "1.1.1", | |||
| "resolved": "https://registry.npmjs.org/easy-bem/-/easy-bem-1.1.1.tgz", | |||
| "integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A==" | |||
| }, | |||
| "ecdsa-sig-formatter": { | |||
| "version": "1.0.11", | |||
| "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", | |||
| @@ -14219,6 +14234,16 @@ | |||
| } | |||
| } | |||
| }, | |||
| "react-indiana-drag-scroll": { | |||
| "version": "2.2.0", | |||
| "resolved": "https://registry.npmjs.org/react-indiana-drag-scroll/-/react-indiana-drag-scroll-2.2.0.tgz", | |||
| "integrity": "sha512-+W/3B2OQV0FrbdnsoIo4dww/xpH0MUQJz6ziQb7H+oBko3OCbXuzDFYnho6v6yhGrYDNWYPuFUewb89IONEl/A==", | |||
| "requires": { | |||
| "classnames": "^2.2.6", | |||
| "debounce": "^1.2.0", | |||
| "easy-bem": "^1.1.1" | |||
| } | |||
| }, | |||
| "react-input-autosize": { | |||
| "version": "3.0.0", | |||
| "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-3.0.0.tgz", | |||
| @@ -29,6 +29,7 @@ | |||
| "react-dom": "^17.0.2", | |||
| "react-helmet-async": "^1.0.9", | |||
| "react-i18next": "^11.10.0", | |||
| "react-indiana-drag-scroll": "^2.2.0", | |||
| "react-redux": "^7.2.4", | |||
| "react-router-dom": "^5.2.0", | |||
| "react-scripts": "4.0.3", | |||
| @@ -0,0 +1,3 @@ | |||
| <svg width="72" height="82" viewBox="0 0 72 82" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <path d="M55.9821 20.9284C55.9821 32.1726 46.8691 41.2856 35.625 41.2856C24.3809 41.2856 15.2679 32.1726 15.2679 20.9284C15.2679 9.68588 24.3809 0.571289 35.625 0.571289C46.8691 0.571289 55.9821 9.68588 55.9821 20.9284ZM33.2553 57.6985L27.9911 48.9195H43.2589L37.9947 57.6985L43.2907 77.4036L49.5728 51.7663C61.8507 53.6748 71.25 64.2987 71.25 77.1173C71.25 79.8051 69.0552 81.9999 66.3675 81.9999H4.88571C2.1868 81.9999 0 79.8051 0 77.1173C0 64.2987 9.39769 53.6748 21.6772 51.7663L27.9593 77.4036L33.2553 57.6985Z" fill="#D4D4D4"/> | |||
| </svg> | |||
| @@ -0,0 +1,4 @@ | |||
| <svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <path d="M36 15V57" stroke="#5A3984" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M15 36H57" stroke="#5A3984" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| </svg> | |||
| @@ -0,0 +1,102 @@ | |||
| import React, { useEffect, useRef, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| AddFile, | |||
| AddIcon, | |||
| ImageOverlay, | |||
| ImagePickerContainer, | |||
| ImageUploaded, | |||
| Tools, | |||
| } from "./ImagePicker.styled"; | |||
| import { IconButton } from "../Buttons/IconButton/IconButton"; | |||
| import { ReactComponent as EditIcon } from "../../assets/images/svg/edit.svg"; | |||
| import { ReactComponent as TrashIcon } from "../../assets/images/svg/trash.svg"; | |||
| // import { Input } from "@mui/material"; | |||
| const ImagePicker = (props) => { | |||
| const fileInputRef = useRef(null); | |||
| const imageRef = useRef(null); | |||
| const [image, setImage] = useState(""); | |||
| const [isEditing, setIsEditing] = useState(false); | |||
| let listener; | |||
| useEffect(() => { | |||
| listener = (event) => { | |||
| if (imageRef.current) { | |||
| if (imageRef.current.contains(event.target)) { | |||
| setIsEditing(true); | |||
| } else { | |||
| setIsEditing(false); | |||
| } | |||
| } | |||
| }; | |||
| window.addEventListener("click", listener); | |||
| return () => window.removeEventListener("click", listener); | |||
| }, [imageRef]); | |||
| const handleChange = () => { | |||
| fileInputRef.current.value = ""; | |||
| fileInputRef.current.click(); | |||
| }; | |||
| const handleImage = (event) => { | |||
| let reader = new FileReader(); | |||
| reader.readAsDataURL(event.target.files[0]); | |||
| reader.onload = () => { | |||
| if (props.setImage) props.setImage(reader.result); | |||
| setImage(reader.result); | |||
| }; | |||
| reader.onerror = (error) => { | |||
| console.log(error); | |||
| }; | |||
| }; | |||
| const handleDelete = () => { | |||
| if (props.deleteImage) props.deleteImage(); | |||
| setImage(""); | |||
| setIsEditing(false); | |||
| }; | |||
| return ( | |||
| <ImagePickerContainer | |||
| className={props.className} | |||
| onClick={!image ? handleChange : () => {}} | |||
| hasImage={props.image} | |||
| > | |||
| <AddFile type="file" ref={fileInputRef} onInput={handleImage} /> | |||
| {image ? ( | |||
| <React.Fragment> | |||
| <ImageUploaded src={image} draggable={false} ref={imageRef} /> | |||
| {isEditing && ( | |||
| <React.Fragment> | |||
| <ImageOverlay /> | |||
| <Tools showDeleteIcon={props.showDeleteIcon}> | |||
| <IconButton onClick={handleChange}> | |||
| <EditIcon /> | |||
| </IconButton> | |||
| {props.showDeleteIcon && ( | |||
| <IconButton onClick={handleDelete}> | |||
| <TrashIcon /> | |||
| </IconButton> | |||
| )} | |||
| </Tools> | |||
| </React.Fragment> | |||
| )} | |||
| </React.Fragment> | |||
| ) : ( | |||
| <AddIcon /> | |||
| )} | |||
| {props.children} | |||
| </ImagePickerContainer> | |||
| ); | |||
| }; | |||
| ImagePicker.propTypes = { | |||
| children: PropTypes.node, | |||
| className: PropTypes.string, | |||
| setImage: PropTypes.func, | |||
| image: PropTypes.func, | |||
| deleteImage: PropTypes.func, | |||
| showDeleteIcon: PropTypes.bool, | |||
| }; | |||
| export default ImagePicker; | |||
| @@ -0,0 +1,111 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../themes"; | |||
| import { ReactComponent as Plus } from "../../assets/images/svg/plus.svg"; | |||
| import { ReactComponent as TrashIcon } from "../../assets/images/svg/trash.svg"; | |||
| export const ImagePickerContainer = styled(Box)` | |||
| flex: 1; | |||
| display: flex; | |||
| flex-basis: 144px; | |||
| flex-grow: 0; | |||
| flex-shrink: 0; | |||
| height: 144px; | |||
| width: 144px; | |||
| margin: 0 9px; | |||
| border-radius: 4px; | |||
| position: relative; | |||
| cursor: pointer; | |||
| background-color: ${selectedTheme.imagePickerBackground}; | |||
| background-image: linear-gradient( | |||
| to right, | |||
| ${selectedTheme.primaryPurple} 50%, | |||
| rgba(255, 255, 255, 0) 0% | |||
| ), | |||
| linear-gradient( | |||
| ${selectedTheme.primaryPurple} 50%, | |||
| rgba(255, 255, 255, 0) 0% | |||
| ), | |||
| linear-gradient( | |||
| to right, | |||
| ${selectedTheme.primaryPurple} 50%, | |||
| rgba(255, 255, 255, 0) 0% | |||
| ), | |||
| linear-gradient( | |||
| ${selectedTheme.primaryPurple} 50%, | |||
| rgba(255, 255, 255, 0) 0% | |||
| ); | |||
| background-position: bottom, right, top, left; | |||
| background-size: 20px 1px, 1px 20px, 20px 1px, 1px 20px; | |||
| background-repeat: repeat-x, repeat-y, repeat-x, repeat-y; | |||
| &:first-of-type { | |||
| margin-left: 0; | |||
| } | |||
| &:last-of-type { | |||
| margin-right: 0; | |||
| } | |||
| ${(props) => | |||
| props.hasImage && | |||
| ` | |||
| background-image: none; | |||
| border: 1px solid ${selectedTheme.primaryPurple}; | |||
| `} | |||
| `; | |||
| export const AddIcon = styled(Plus)` | |||
| margin: auto; | |||
| z-index: 1; | |||
| width: 60px; | |||
| height: 60px; | |||
| `; | |||
| export const AddFile = styled.input` | |||
| display: none; | |||
| `; | |||
| export const ImageUploaded = styled.img` | |||
| width: 144px; | |||
| height: 144px; | |||
| border-radius: 100px; | |||
| object-fit: scale-down; | |||
| z-index: 1; | |||
| `; | |||
| export const ImageOverlay = styled(Box)` | |||
| position: absolute; | |||
| top: 0; | |||
| left: 0; | |||
| width: 100%; | |||
| height: 100%; | |||
| background-color: rgba(0,0,0,0.5); | |||
| z-index: 3; | |||
| overflow: hidden; | |||
| ` | |||
| export const Tooltip = styled(Box)` | |||
| background-color: rgba(255, 255, 255, 0.5); | |||
| width: 100px; | |||
| height: 100px; | |||
| position: absolute; | |||
| left: 0; | |||
| top: 0; | |||
| `; | |||
| export const Trash = styled(TrashIcon)` | |||
| cursor: pointer; | |||
| margin: auto; | |||
| width: 22px; | |||
| height: 22px; | |||
| & path { | |||
| stroke: white; | |||
| } | |||
| `; | |||
| export const Tools = styled(Box)` | |||
| position: absolute; | |||
| padding-top: 44px; | |||
| padding-left: ${props => props.showDeleteIcon ? "16px" : "45px"}; | |||
| z-index: 4; | |||
| flex-direction: row; | |||
| display: flex; | |||
| & div { | |||
| background-color: ${selectedTheme.primaryIconBackgroundColor}; | |||
| border-radius: 100px; | |||
| display: flex; | |||
| flex: 1; | |||
| margin: 10px; | |||
| } | |||
| `; | |||