import { CropIcon } from "@icons/CropIcon"; import { ResetIcon } from "@icons/ResetIcon"; import { Box, Button, IconButton, Modal, Slider, SxProps, Theme, Typography, useMediaQuery, useTheme, } from "@mui/material"; import { FC, useMemo, useRef, useState } from "react"; import ReactCrop, { Crop, PixelCrop } from "react-image-crop"; import "react-image-crop/dist/ReactCrop.css"; import { canvasPreview } from "./utils/canvasPreview"; const styleSlider: SxProps = { color: "#7E2AEA", height: "12px", "& .MuiSlider-track": { border: "none", }, "& .MuiSlider-rail": { backgroundColor: "#F2F3F7", border: `1px solid #9A9AAF`, }, "& .MuiSlider-thumb": { height: 26, width: 26, border: `6px solid #7E2AEA`, backgroundColor: "white", boxShadow: `0px 0px 0px 3px white, 0px 4px 4px 3px #C3C8DD`, "&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": { boxShadow: `0px 0px 0px 3px white, 0px 4px 4px 3px #C3C8DD`, }, }, }; interface Props { isOpen: boolean; imageBlob: Blob | null; originalImageUrl: string | null; setCropModalImageBlob: (imageBlob: Blob) => void; onClose: () => void; onSaveImageClick: (imageBlob: Blob) => void; } export const CropModal: FC = ({ isOpen, imageBlob, originalImageUrl, setCropModalImageBlob, onSaveImageClick, onClose }) => { const theme = useTheme(); const [crop, setCrop] = useState(); const [completedCrop, setCompletedCrop] = useState(); const [darken, setDarken] = useState(0); const [rotate, setRotate] = useState(0); const [width, setWidth] = useState(240); const cropImageElementRef = useRef(null); const isMobile = useMediaQuery(theme.breakpoints.down(786)); const imageUrl = useMemo(() => imageBlob && URL.createObjectURL(imageBlob), [imageBlob]); const handleCropClick = async () => { if (!completedCrop) throw new Error("No completed crop"); if (!cropImageElementRef.current) throw new Error("No image"); const canvasCopy = document.createElement("canvas"); const ctx = canvasCopy.getContext("2d"); if (!ctx) throw new Error("No 2d context"); canvasCopy.width = completedCrop.width; canvasCopy.height = completedCrop.height; ctx.filter = `brightness(${100 - darken}%)`; await canvasPreview(cropImageElementRef.current, canvasCopy, completedCrop, rotate); canvasCopy.toBlob((blob) => { if (!blob) throw new Error("Failed to create blob"); setCropModalImageBlob(blob); setCrop(undefined); setCompletedCrop(undefined); }); }; function handleSaveClick() { if (imageBlob) onSaveImageClick?.(imageBlob); setCrop(undefined); setCompletedCrop(undefined); onClose(); } async function handleLoadOriginalImage() { if (!originalImageUrl) return; const response = await fetch(originalImageUrl); const blob = await response.blob(); setCropModalImageBlob(blob); setCrop(undefined); setCompletedCrop(undefined); } const getImageSize = () => { if (cropImageElementRef.current) { const imageWidth = cropImageElementRef.current.naturalWidth; const imageHeight = cropImageElementRef.current.naturalHeight; const aspect = imageWidth / imageHeight; if (aspect <= 1.333) { setWidth(240); } if (aspect >= 1.5) { setWidth(580); } if (aspect >= 1.778) { setWidth(580); } } }; return ( {imageUrl && ( setCrop(percentCrop)} onComplete={(c) => setCompletedCrop(c)} maxWidth={500} minWidth={50} maxHeight={320} minHeight={50} > Crop me )} {crop?.width ? Math.round(crop.width) + "px" : ""} {crop?.height ? Math.round(crop.height) + "px" : ""} setRotate(r => (r + 90) % 360)}> Размер { setWidth(newValue as number); }} /> Затемнение setDarken(newValue as number)} /> ); }; export function useCropModalState(initialOpenState = false) { const [isCropModalOpen, setOpened] = useState(initialOpenState); const [imageBlob, setCropModalImageBlob] = useState(null); const [originalImageUrl, setOriginalImageUrl] = useState(null); const closeCropModal = () => { setOpened(false); setCropModalImageBlob(null); setOriginalImageUrl(null); }; async function openCropModal(image: Blob | string, originalImageUrl: string | null | undefined = null) { if (typeof image === "string") { const response = await fetch(image); image = await response.blob(); } setCropModalImageBlob(image); setOriginalImageUrl(originalImageUrl); setOpened(true); } return { isCropModalOpen, openCropModal, closeCropModal, imageBlob, setCropModalImageBlob, originalImageUrl, } as const; }