diff --git a/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx b/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx index 651b4859..ec6e1884 100644 --- a/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx +++ b/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx @@ -5,7 +5,7 @@ import { uploadQuestionImage, } from "@root/questions/actions"; import { useCurrentQuiz } from "@root/quizes/hooks"; -import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal"; +import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal/CropModal"; import { useEffect, useState } from "react"; import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon"; import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg"; diff --git a/src/pages/Questions/OptionsPicture/OptionsPicture.tsx b/src/pages/Questions/OptionsPicture/OptionsPicture.tsx index 1c148795..1396beed 100644 --- a/src/pages/Questions/OptionsPicture/OptionsPicture.tsx +++ b/src/pages/Questions/OptionsPicture/OptionsPicture.tsx @@ -4,7 +4,7 @@ import { uploadQuestionImage, } from "@root/questions/actions"; import { useCurrentQuiz } from "@root/quizes/hooks"; -import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal"; +import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal/CropModal"; import { useState } from "react"; import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon"; import type { QuizQuestionImages } from "../../../model/questionTypes/images"; diff --git a/src/pages/startPage/dropZone.tsx b/src/pages/startPage/dropZone.tsx index 74b894c7..486e69c2 100644 --- a/src/pages/startPage/dropZone.tsx +++ b/src/pages/startPage/dropZone.tsx @@ -10,7 +10,7 @@ import { useTheme, } from "@mui/material"; import { useCurrentQuiz } from "@root/quizes/hooks"; -import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal"; +import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal/CropModal"; import { enqueueSnackbar } from "notistack"; import { useState } from "react"; import { UploadImageModal } from "../../pages/Questions/UploadImage/UploadImageModal"; diff --git a/src/ui_kit/MediaSelectionAndDisplay.tsx b/src/ui_kit/MediaSelectionAndDisplay.tsx index bd2d6665..686f0868 100644 --- a/src/ui_kit/MediaSelectionAndDisplay.tsx +++ b/src/ui_kit/MediaSelectionAndDisplay.tsx @@ -9,7 +9,7 @@ import { useTheme, } from "@mui/material"; import { updateQuestion, uploadQuestionImage } from "@root/questions/actions"; -import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal"; +import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal/CropModal"; import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton"; import { UploadImageModal } from "../pages/Questions/UploadImage/UploadImageModal"; diff --git a/src/ui_kit/Modal/CropModal.tsx b/src/ui_kit/Modal/CropModal/CropModal.tsx similarity index 58% rename from src/ui_kit/Modal/CropModal.tsx rename to src/ui_kit/Modal/CropModal/CropModal.tsx index 377865db..34e8b401 100644 --- a/src/ui_kit/Modal/CropModal.tsx +++ b/src/ui_kit/Modal/CropModal/CropModal.tsx @@ -1,14 +1,8 @@ import { devlog } from "@frontend/kitui"; -import { ResetIcon } from "@icons/ResetIcon"; -import DeleteIcon from "@mui/icons-material/Delete"; import { Box, Button, - IconButton, Modal, - Slider, - SxProps, - Theme, Typography, useMediaQuery, useTheme, @@ -22,35 +16,17 @@ import ReactCrop, { makeAspectCrop, } from "react-image-crop"; import "react-image-crop/dist/ReactCrop.css"; -import { isImageBlobAGifFile } from "../../utils/isImageBlobAGifFile"; +import { isImageBlobAGifFile } from "@utils/isImageBlobAGifFile"; import { getModifiedImageBlob, getRotatedImageBlob, -} from "./utils/imageManipulation"; - -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`, - }, - }, -}; +} from "../utils/imageManipulation"; +import DevaceMobileIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceMobileIcon"; +import BackArrowIcon from "@icons/BackArrowIcon"; +import DevaceDesktopIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceDesktopIcon"; +import DevaceTabletIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceTabletIcon"; +import DevaceSmallIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceSmallIcon"; +import SwitchCaseCrop from "@ui_kit/Modal/CropModal/SwitchCaseCrop"; interface Props { isOpen: boolean; @@ -65,6 +41,7 @@ interface Props { height: number; }; } +const stepsScreen: string[] = ["desktop", "tablet", "mobile", "small"] export const CropModal: FC = ({ isOpen, @@ -80,11 +57,44 @@ export const CropModal: FC = ({ const [percentCrop, setPercentCrop] = useState( undefined, ); + const [darken, setDarken] = useState(0); const [imageWidth, setImageWidth] = useState(null); const [imageHeight, setImageHeight] = useState(null); + const [modalStep, setModalStep] = useState(stepsScreen[0]); + const [lastStep, setLastStep] = useState(null) const cropImageElementRef = useRef(null); const isMobile = useMediaQuery(theme.breakpoints.down(786)); + let stepIndex = stepsScreen.indexOf(modalStep) + const modalTitle = { + "desktop": {name: "Десктоп", icon: }, + "tablet": {name:"Планшет", icon: }, + "mobile": {name:"Телефон", icon: }, + "small": {name:"Самые узкие экраны", icon: } + } + + const handleNextStep = () => { + if (stepIndex === stepsScreen.length - 1) { + onClose() + return + } + if (stepIndex === stepsScreen.length - 2) { + setLastStep(true) + } + + let nextStepIndex = stepIndex + 1 + setModalStep(stepsScreen[nextStepIndex]); + }; + const handlePrevStep = () => { + + if (stepIndex === 0) return + if (stepIndex === stepsScreen.length - 1) { + setLastStep(false) + } + let nextStepIndex = stepIndex - 1 + + setModalStep(stepsScreen[nextStepIndex]); + }; const imageUrl = useMemo( () => imageBlob && URL.createObjectURL(imageBlob), @@ -196,7 +206,6 @@ export const CropModal: FC = ({ transform: "translate(-50%, -50%)", bgcolor: "background.paper", boxShadow: 24, - padding: "20px", borderRadius: "8px", width: isMobile ? "343px" : "620px", height: isMobile ? "80vh" : undefined, @@ -207,132 +216,37 @@ export const CropModal: FC = ({ }} > - {imageUrl && ( - setPercentCrop(percentCrop)} - minWidth={5} - minHeight={5} - locked - aspect={ - cropAspectRatio - ? cropAspectRatio.width / cropAspectRatio.height - : undefined - } - > - { - setImageWidth(e.currentTarget.naturalWidth); - setImageHeight(e.currentTarget.naturalHeight); - - if (cropImageElementRef.current) { - setPercentCrop( - getInitialCrop( - cropImageElementRef.current.width, - cropImageElementRef.current.height, - cropAspectRatio - ? cropAspectRatio.width / cropAspectRatio.height - : 1, - ), - ); - } - }} - ref={cropImageElementRef} - alt="Crop me" - src={imageUrl} - style={{ - filter: `brightness(${100 - darken}%)`, - maxWidth: "100%", - maxHeight: "320px", - display: "block", - objectFit: "contain", - }} - /> - - )} - - - - - - - - Размер + + Настройте вариант отображения картинки на разных девайсах - { - if (typeof newValue === "number") handleSizeChange(newValue); - }} - /> - - - - Затемнение - - setDarken(newValue as number)} - /> - - {onDeleteClick && ( - { - onDeleteClick?.(); - onClose(); - }} - sx={{ - height: "48px", - width: "48px", - p: 0, - color: theme.palette.orange.main, - borderRadius: "50%", - }} - > - - - )} + @@ -345,25 +259,59 @@ export const CropModal: FC = ({ borderRadius: "8px", border: "1px solid #7E2AEA", px: "20px", + width: isMobile ? "100%" : undefined, }} > Сохранить оригинал - + + + + + diff --git a/src/ui_kit/Modal/CropModal/IconCropModal/DevaceDesktopIcon.tsx b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceDesktopIcon.tsx new file mode 100644 index 00000000..840b0a98 --- /dev/null +++ b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceDesktopIcon.tsx @@ -0,0 +1,24 @@ +import {Box} from "@mui/material"; + +export default function DevaceDesktopIcon() { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/ui_kit/Modal/CropModal/IconCropModal/DevaceMobileIcon.tsx b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceMobileIcon.tsx new file mode 100644 index 00000000..e8041fda --- /dev/null +++ b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceMobileIcon.tsx @@ -0,0 +1,23 @@ +import {Box} from "@mui/material"; + +export default function DevaceMobileIcon() { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/ui_kit/Modal/CropModal/IconCropModal/DevaceSmallIcon.tsx b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceSmallIcon.tsx new file mode 100644 index 00000000..6a4d4073 --- /dev/null +++ b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceSmallIcon.tsx @@ -0,0 +1,23 @@ +import {Box} from "@mui/material"; + +export default function DevaceSmallIcon() { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/ui_kit/Modal/CropModal/IconCropModal/DevaceTabletIcon.tsx b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceTabletIcon.tsx new file mode 100644 index 00000000..2d1eb797 --- /dev/null +++ b/src/ui_kit/Modal/CropModal/IconCropModal/DevaceTabletIcon.tsx @@ -0,0 +1,24 @@ +import {Box} from "@mui/material"; + +export default function DevaceTabletIcon() { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/ui_kit/Modal/CropModal/SwitchCaseCrop.tsx b/src/ui_kit/Modal/CropModal/SwitchCaseCrop.tsx new file mode 100644 index 00000000..97614424 --- /dev/null +++ b/src/ui_kit/Modal/CropModal/SwitchCaseCrop.tsx @@ -0,0 +1,116 @@ +import CropGeneral from "@ui_kit/Modal/CropModal/cropGeneral"; +import DevaceDesktopIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceDesktopIcon"; +import DevaceTabletIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceTabletIcon"; +import DevaceMobileIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceMobileIcon"; +import DevaceSmallIcon from "@ui_kit/Modal/CropModal/IconCropModal/DevaceSmallIcon"; +import {PercentCrop} from "react-image-crop"; +import {MutableRefObject} from "react"; + +const modalProps = { + "desktop": {name: "Десктоп", icon: }, + "tablet": {name:"Планшет", icon: }, + "mobile": {name:"Телефон", icon: }, + "small": {name:"Самые узкие экраны", icon: } +} + +interface Props{ + imageUrl: null | string; + handleSizeChange: (a: number)=> void; + handleRotateClick: Promise; + getInitialCrop: (imageWidth: number, imageHeight: number, aspectRatio: number)=> PercentCrop; + modalProps: {string:{}}; + modalStep: string; + stepIndex: number; + onClose: () => void; + cropImageElementRef: MutableRefObject; + onDeleteClick?: () => void; + cropAspectRatio?: { + width: number; + height: number; + }; +} + +export default function SwitchCaseCrop ({imageUrl, + cropAspectRatio, + handleSizeChange, + handleRotateClick, + getInitialCrop, + onDeleteClick, + onClose, + modalStep, + stepIndex, + cropImageElementRef}: Props) { + switch (modalStep) { + case "desktop": { + return( + <> + + + + ) + } + case "tablet": { + return( + <> + + + + + ) + } + case "mobile": { + return( + + ) + } + case "small": { + return( + + ) + } + } +} \ No newline at end of file diff --git a/src/ui_kit/Modal/CropModal/cropGeneral.tsx b/src/ui_kit/Modal/CropModal/cropGeneral.tsx new file mode 100644 index 00000000..80657e5e --- /dev/null +++ b/src/ui_kit/Modal/CropModal/cropGeneral.tsx @@ -0,0 +1,217 @@ +import {Box, IconButton, Slider, SxProps, Theme, Typography, useMediaQuery, useTheme} from "@mui/material"; +import ReactCrop, {PercentCrop} from "react-image-crop"; +import {ResetIcon} from "@icons/ResetIcon"; +import DeleteIcon from "@mui/icons-material/Delete"; +import {MutableRefObject, useRef, useState} from "react"; + +interface Props{ + imageUrl: null | string; + handleSizeChange: (a: number)=> void; + handleRotateClick: Promise; + getInitialCrop: (imageWidth: number, imageHeight: number, aspectRatio: number)=> PercentCrop; + modalProps: any; + modalStep: number; + onClose: () => void; + cropImageElementRef: MutableRefObject; + onDeleteClick?: () => void; + cropAspectRatio?: { + width: number; + height: number; + }; +} + +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`, + }, + }, +}; + +export default function CropGeneral({imageUrl, + cropAspectRatio, + handleSizeChange, + handleRotateClick, + getInitialCrop, + modalProps, + onDeleteClick, + onClose, + modalStep, + cropImageElementRef}: Props) { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(786)); + + const [percentCrop, setPercentCrop] = useState( + undefined, + ); + const [imageWidth, setImageWidth] = useState(null); + const [imageHeight, setImageHeight] = useState(null); + const [darken, setDarken] = useState(0); + const step = modalStep + 1 + + return ( + <> + + + {modalProps.name} + {modalProps.icon} + + {onDeleteClick && ( + { + onDeleteClick?.(); + onClose(); + }} + sx={{ + height: "48px", + width: "48px", + p: 0, + color: theme.palette.orange.main, + borderRadius: "50%", + }} + > + + + )} + + + {step + " шаг"} + + {imageUrl && ( + setPercentCrop(percentCrop)} + minWidth={5} + minHeight={5} + locked + aspect={ + cropAspectRatio + ? cropAspectRatio.width / cropAspectRatio.height + : undefined + } + > + { + setImageWidth(e.currentTarget.naturalWidth); + setImageHeight(e.currentTarget.naturalHeight); + + if (cropImageElementRef.current) { + setPercentCrop( + getInitialCrop( + cropImageElementRef.current.width, + cropImageElementRef.current.height, + cropAspectRatio + ? cropAspectRatio.width / cropAspectRatio.height + : 1, + ), + ); + } + }} + ref={cropImageElementRef} + alt="Crop me" + src={imageUrl} + style={{ + filter: `brightness(${100 - darken}%)`, + maxWidth: "100%", + maxHeight: "320px", + display: "block", + objectFit: "contain", + }} + /> + + )} + + + + + + + + Размер + + { + if (typeof newValue === "number") handleSizeChange(newValue); + }} + /> + + + + Затемнение + + setDarken(newValue as number)} + /> + + + + + ) +} \ No newline at end of file