From 3fef7795681c405322aed6e1c876948ebc2cc932 Mon Sep 17 00:00:00 2001 From: eug-vs Date: Thu, 29 Oct 2020 23:53:13 +0300 Subject: refactor: separate ImageCropAreaSelect component --- src/components/AvatarCrop/AvatarCrop.tsx | 74 ---------------------- src/components/AvatarCrop/canvasUtils.js | 54 ---------------- .../ImageCropAreaSelect/ImageCropAreaSelect.tsx | 55 ++++++++++++++++ src/components/ModalScreen/ModalScreen.tsx | 7 +- 4 files changed, 57 insertions(+), 133 deletions(-) delete mode 100644 src/components/AvatarCrop/AvatarCrop.tsx delete mode 100644 src/components/AvatarCrop/canvasUtils.js create mode 100644 src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx (limited to 'src/components') diff --git a/src/components/AvatarCrop/AvatarCrop.tsx b/src/components/AvatarCrop/AvatarCrop.tsx deleted file mode 100644 index ef6d9c8..0000000 --- a/src/components/AvatarCrop/AvatarCrop.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, {useCallback, useContext, useState} from 'react'; -import Cropper from 'react-easy-crop'; -import {makeStyles} from '@material-ui/core/styles'; -import SendIcon from "@material-ui/icons/Send"; -import ModalScreen from "../ModalScreen/ModalScreen"; -import { getCroppedImg } from './canvasUtils' - -interface PropTypes { - avatarToCrop: string; - setAvatarToCrop: (src: string) => void; - callback: (file: File) => void; -} - -const useStyles = makeStyles(theme => ({ - cropContainer: { - position: 'relative', - width: '100%', - height: '100vh', - background: '#333', - [theme.breakpoints.up('sm')]: { - height: 400, - }, - } -})); - -const AvatarCrop: React.FC = ({ avatarToCrop, setAvatarToCrop, callback }) => { - const classes = useStyles(); - const [crop, setCrop] = useState({x: 0, y: 0}); - const [zoom, setZoom] = useState(1); - const [croppedAreaPixels, setCroppedAreaPixels] = useState(null); - - const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => { - setCroppedAreaPixels(croppedAreaPixels) - }, []); - - const handleLoadCroppedImage = useCallback(async () => { - try { - const croppedImage = await getCroppedImg(avatarToCrop, croppedAreaPixels); - callback(croppedImage); - } catch (e) { - console.error(e) - } - }, [avatarToCrop, croppedAreaPixels]); - - const handleCloseModal = useCallback( () => { - setAvatarToCrop(''); - },[]); - - return ( - } - handleAction={handleLoadCroppedImage} - isActionDisabled={false} - handleCloseModal={handleCloseModal} - > -
- -
-
- ) -}; - -export default AvatarCrop; diff --git a/src/components/AvatarCrop/canvasUtils.js b/src/components/AvatarCrop/canvasUtils.js deleted file mode 100644 index b01aadc..0000000 --- a/src/components/AvatarCrop/canvasUtils.js +++ /dev/null @@ -1,54 +0,0 @@ -const createImage = url => - new Promise((resolve, reject) => { - const image = new Image(); - image.addEventListener('load', () => resolve(image)); - image.addEventListener('error', error => reject(error)); - image.setAttribute('crossOrigin', 'anonymous') ;// needed to avoid cross-origin issues on CodeSandbox - image.src = url - }); - -export async function getCroppedImg(imageSrc, pixelCrop) { - const image = await createImage(imageSrc); - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - - const maxSize = Math.max(image.width, image.height); - const safeArea = 2 * ((maxSize / 2) * Math.sqrt(2)); - - // set each dimensions to double largest dimension to allow for a safe area for the - // image to rotate in without being clipped by canvas context - canvas.width = safeArea; - canvas.height = safeArea; - - // translate canvas context to a central location on image to allow rotating around the center. - ctx.translate(safeArea / 2, safeArea / 2); - ctx.translate(-safeArea / 2, -safeArea / 2); - - // draw rotated image and store data. - ctx.drawImage( - image, - safeArea / 2 - image.width * 0.5, - safeArea / 2 - image.height * 0.5 - ); - const data = ctx.getImageData(0, 0, safeArea, safeArea); - - // set canvas width to final desired crop size - this will clear existing context - canvas.width = pixelCrop.width; - canvas.height = pixelCrop.height; - - // paste generated rotate image with correct offsets for x,y crop values. - ctx.putImageData( - data, - Math.round(0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x), - Math.round(0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y) - ); - - // As Base64 string - // return canvas.toDataURL('image/jpeg'); - - // As a blob - - return new Promise(resolve => { - canvas.toBlob(file => resolve(file)) - }) -} diff --git a/src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx b/src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx new file mode 100644 index 0000000..015dd47 --- /dev/null +++ b/src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx @@ -0,0 +1,55 @@ +import React, { useCallback, useState } from 'react'; +import Cropper from 'react-easy-crop'; +import { makeStyles } from '@material-ui/core/styles'; + +interface Area { + x: number; + y: number; + width: number; + height: number; +} + +interface PropTypes { + image: string; + setArea: (area: Area) => void; +} + +const useStyles = makeStyles(theme => ({ + root: { + position: 'relative', + width: '100%', + height: '100vh', + background: '#333', + [theme.breakpoints.up('sm')]: { + height: 400, + } + } +})); + +const ImageCropAreaSelect: React.FC = ({ image, setArea }) => { + const classes = useStyles(); + const [crop, setCrop] = useState({ x: 0, y: 0 }); + const [zoom, setZoom] = useState(1); + + const onCropComplete = useCallback((areaPercentage: Area, areaPixels: Area): void => { + setArea(areaPixels); + }, [setArea]); + + return ( +
+ +
+ ) +}; + +export default ImageCropAreaSelect; diff --git a/src/components/ModalScreen/ModalScreen.tsx b/src/components/ModalScreen/ModalScreen.tsx index 61cf44a..b71c2c8 100644 --- a/src/components/ModalScreen/ModalScreen.tsx +++ b/src/components/ModalScreen/ModalScreen.tsx @@ -1,5 +1,4 @@ import React, { useState, useCallback } from 'react'; -import { useHistory } from 'react-router-dom'; import { AppBar, Dialog, @@ -20,7 +19,7 @@ interface PropTypes { actionIcon?: JSX.Element; handleAction?: () => void; isActionDisabled?: boolean; - handleCloseModal: ()=> void; + handleCloseModal?: ()=> void; } const useStyles = makeStyles(theme => ({ @@ -48,11 +47,9 @@ const ModalScreen: React.FC = ({ title, actionIcon, handleAction, isA const classes = useStyles(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - const history = useHistory(); - const handleClose = useCallback(() => setIsOpen(false), [setIsOpen]); - const onExited = useCallback(handleCloseModal, [history, handleAction]); + const onExited = useCallback(() => handleCloseModal && handleCloseModal(), [handleCloseModal]); const handleClickAction = useCallback(async () => { if (handleAction) await handleAction(); -- cgit v1.2.3