new crop-modal
This commit is contained in:
parent
19e7dab62c
commit
c979280d45
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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<Theme> = {
|
||||
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<Props> = ({
|
||||
isOpen,
|
||||
@ -80,11 +57,44 @@ export const CropModal: FC<Props> = ({
|
||||
const [percentCrop, setPercentCrop] = useState<PercentCrop | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const [darken, setDarken] = useState(0);
|
||||
const [imageWidth, setImageWidth] = useState<number | null>(null);
|
||||
const [imageHeight, setImageHeight] = useState<number | null>(null);
|
||||
const [modalStep, setModalStep] = useState<string>(stepsScreen[0]);
|
||||
const [lastStep, setLastStep] = useState<boolean>(null)
|
||||
const cropImageElementRef = useRef<HTMLImageElement>(null);
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(786));
|
||||
let stepIndex = stepsScreen.indexOf(modalStep)
|
||||
const modalTitle = {
|
||||
"desktop": {name: "Десктоп", icon: <DevaceDesktopIcon/>},
|
||||
"tablet": {name:"Планшет", icon: <DevaceTabletIcon/>},
|
||||
"mobile": {name:"Телефон", icon: <DevaceMobileIcon/>},
|
||||
"small": {name:"Самые узкие экраны", icon: <DevaceSmallIcon/>}
|
||||
}
|
||||
|
||||
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<Props> = ({
|
||||
transform: "translate(-50%, -50%)",
|
||||
bgcolor: "background.paper",
|
||||
boxShadow: 24,
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
width: isMobile ? "343px" : "620px",
|
||||
height: isMobile ? "80vh" : undefined,
|
||||
@ -208,131 +217,36 @@ export const CropModal: FC<Props> = ({
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
height: "320px",
|
||||
backgroundSize: "cover",
|
||||
backgroundRepeat: "no-repeat",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: isMobile ? "91px" : "70px",
|
||||
backgroundColor: "#F2F3F7",
|
||||
padding: "20px",
|
||||
borderRadius: "8px 8px 0px 0px",
|
||||
}}
|
||||
>
|
||||
{imageUrl && (
|
||||
<ReactCrop
|
||||
crop={percentCrop}
|
||||
onChange={(_, percentCrop) => setPercentCrop(percentCrop)}
|
||||
minWidth={5}
|
||||
minHeight={5}
|
||||
locked
|
||||
aspect={
|
||||
cropAspectRatio
|
||||
? cropAspectRatio.width / cropAspectRatio.height
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<img
|
||||
onLoad={(e) => {
|
||||
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",
|
||||
}}
|
||||
/>
|
||||
</ReactCrop>
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "40px",
|
||||
display: isMobile ? "block" : "flex",
|
||||
alignItems: "end",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<IconButton onClick={handleRotateClick}>
|
||||
<ResetIcon />
|
||||
</IconButton>
|
||||
<Box>
|
||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
||||
Размер
|
||||
<Typography sx={{ color: "#9A9AAF", fontSize: "18px" }}>
|
||||
Настройте вариант отображения картинки на разных девайсах
|
||||
</Typography>
|
||||
<Slider
|
||||
sx={[
|
||||
styleSlider,
|
||||
{
|
||||
width: isMobile ? undefined : "200px",
|
||||
},
|
||||
]}
|
||||
value={percentCrop?.width ?? 1}
|
||||
min={1}
|
||||
max={100}
|
||||
step={0.1}
|
||||
onChange={(_, newValue) => {
|
||||
if (typeof newValue === "number") handleSizeChange(newValue);
|
||||
}}
|
||||
</Box>
|
||||
<SwitchCaseCrop
|
||||
imageUrl={imageUrl}
|
||||
handleSizeChange={handleSizeChange}
|
||||
handleRotateClick={handleRotateClick}
|
||||
getInitialCrop={getInitialCrop}
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
onDeleteClick={onDeleteClick}
|
||||
onClose={onClose}
|
||||
modalProps={modalTitle}
|
||||
modalStep={modalStep}
|
||||
stepIndex={stepIndex}
|
||||
cropImageElementRef={cropImageElementRef}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
||||
Затемнение
|
||||
</Typography>
|
||||
<Slider
|
||||
sx={[
|
||||
styleSlider,
|
||||
{
|
||||
width: isMobile ? undefined : "200px",
|
||||
},
|
||||
]}
|
||||
value={darken}
|
||||
min={0}
|
||||
max={100}
|
||||
step={1}
|
||||
onChange={(_, newValue) => setDarken(newValue as number)}
|
||||
/>
|
||||
</Box>
|
||||
{onDeleteClick && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onDeleteClick?.();
|
||||
onClose();
|
||||
}}
|
||||
sx={{
|
||||
height: "48px",
|
||||
width: "48px",
|
||||
p: 0,
|
||||
color: theme.palette.orange.main,
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: "40px",
|
||||
padding: "0 20px 20px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
gap: "5px",
|
||||
flexWrap: isMobile ? "wrap" : undefined,
|
||||
}}
|
||||
>
|
||||
@ -345,26 +259,60 @@ export const CropModal: FC<Props> = ({
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #7E2AEA",
|
||||
px: "20px",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
}}
|
||||
>
|
||||
Сохранить оригинал
|
||||
</Button>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "5px",
|
||||
ml: "auto",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={handleSaveModifiedImage}
|
||||
onClick={()=> handlePrevStep()}
|
||||
variant="contained"
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
padding: "10px 15px",
|
||||
color: "#FFFFFF",
|
||||
border: "1px solid #9A9AAF",
|
||||
background: "#FFFFFF",
|
||||
"&:hover": {
|
||||
color: "#FFFFFF",
|
||||
border: `1px solid ${theme.palette.primary.dark}`,
|
||||
},
|
||||
}}
|
||||
|
||||
>
|
||||
<BackArrowIcon color={"#7E2AEA"}/>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleNextStep()
|
||||
// handleSaveModifiedImage()
|
||||
}
|
||||
}
|
||||
disableRipple
|
||||
variant="contained"
|
||||
sx={{
|
||||
height: "48px",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #7E2AEA",
|
||||
marginRight: "10px",
|
||||
px: "20px",
|
||||
ml: "auto",
|
||||
p: "10px 15px",
|
||||
width: isMobile ? "100%" : undefined,
|
||||
// ml: "auto",
|
||||
}}
|
||||
>
|
||||
Сохранить редактированное
|
||||
{lastStep ?
|
||||
"Сохранить редактированное" : "Далее"
|
||||
}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
@ -0,0 +1,24 @@
|
||||
import {Box} from "@mui/material";
|
||||
|
||||
export default function DevaceDesktopIcon() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
height: "36px",
|
||||
width: "36px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "6px",
|
||||
backgroundColor: "#EEE4FC"
|
||||
}}
|
||||
>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="2" y="17" width="20" height="3" rx="1" stroke="#7E2AEA" strokeWidth="1.5"/>
|
||||
<rect x="3" y="5" width="18" height="12" rx="1" stroke="#7E2AEA" strokeWidth="1.5"/>
|
||||
<path d="M14 5.5L10 5.5" stroke="#7E2AEA" strokeWidth="2.5" strokeLinecap="round"/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import {Box} from "@mui/material";
|
||||
|
||||
export default function DevaceMobileIcon() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
height: "36px",
|
||||
width: "36px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "6px",
|
||||
backgroundColor: "#EEE4FC"
|
||||
}}
|
||||
>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="6" y="2" width="12" height="20" rx="3" stroke="#7E2AEA" strokeWidth="1.5"/>
|
||||
<path d="M14 2.5L10 2.5" stroke="#7E2AEA" strokeWidth="2.5" strokeLinecap="round"/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
23
src/ui_kit/Modal/CropModal/IconCropModal/DevaceSmallIcon.tsx
Normal file
23
src/ui_kit/Modal/CropModal/IconCropModal/DevaceSmallIcon.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import {Box} from "@mui/material";
|
||||
|
||||
export default function DevaceSmallIcon() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
height: "36px",
|
||||
width: "36px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "6px",
|
||||
backgroundColor: "#EEE4FC"
|
||||
}}
|
||||
>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="7.5" y="2" width="9" height="20" rx="3" stroke="#7E2AEA" strokeWidth="1.5"/>
|
||||
<path d="M13.5 2.5L10.5 2.5" stroke="#7E2AEA" strokeWidth="2.5" strokeLinecap="round"/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import {Box} from "@mui/material";
|
||||
|
||||
export default function DevaceTabletIcon() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
height: "36px",
|
||||
width: "36px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "6px",
|
||||
backgroundColor: "#EEE4FC"
|
||||
}}
|
||||
>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.5 19.5V4.5C19.5 3.67157 18.8284 3 18 3L6 3C5.17157 3 4.5 3.67157 4.5 4.5L4.5 19.5C4.5 20.3284 5.17157 21 6 21H18C18.8284 21 19.5 20.3284 19.5 19.5Z" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M14 4L10 4" stroke="#7E2AEA" strokeWidth="2.5" strokeLinecap="round"/>
|
||||
<path d="M4.5 18H19.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
116
src/ui_kit/Modal/CropModal/SwitchCaseCrop.tsx
Normal file
116
src/ui_kit/Modal/CropModal/SwitchCaseCrop.tsx
Normal file
@ -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: <DevaceDesktopIcon/>},
|
||||
"tablet": {name:"Планшет", icon: <DevaceTabletIcon/>},
|
||||
"mobile": {name:"Телефон", icon: <DevaceMobileIcon/>},
|
||||
"small": {name:"Самые узкие экраны", icon: <DevaceSmallIcon/>}
|
||||
}
|
||||
|
||||
interface Props{
|
||||
imageUrl: null | string;
|
||||
handleSizeChange: (a: number)=> void;
|
||||
handleRotateClick: Promise<void>;
|
||||
getInitialCrop: (imageWidth: number, imageHeight: number, aspectRatio: number)=> PercentCrop;
|
||||
modalProps: {string:{}};
|
||||
modalStep: string;
|
||||
stepIndex: number;
|
||||
onClose: () => void;
|
||||
cropImageElementRef: MutableRefObject<HTMLImageElement>;
|
||||
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(
|
||||
<>
|
||||
<CropGeneral
|
||||
imageUrl={imageUrl}
|
||||
handleSizeChange={handleSizeChange}
|
||||
handleRotateClick={handleRotateClick}
|
||||
getInitialCrop={getInitialCrop}
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
onDeleteClick={onDeleteClick}
|
||||
onClose={onClose}
|
||||
modalProps={modalProps.desktop}
|
||||
modalStep={stepIndex}
|
||||
cropImageElementRef={cropImageElementRef}
|
||||
/>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
case "tablet": {
|
||||
return(
|
||||
<>
|
||||
<CropGeneral
|
||||
imageUrl={imageUrl}
|
||||
handleSizeChange={handleSizeChange}
|
||||
handleRotateClick={handleRotateClick}
|
||||
getInitialCrop={getInitialCrop}
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
onDeleteClick={onDeleteClick}
|
||||
onClose={onClose}
|
||||
modalProps={modalProps.tablet}
|
||||
modalStep={stepIndex}
|
||||
cropImageElementRef={cropImageElementRef}
|
||||
/>
|
||||
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
case "mobile": {
|
||||
return(
|
||||
<CropGeneral
|
||||
imageUrl={imageUrl}
|
||||
handleSizeChange={handleSizeChange}
|
||||
handleRotateClick={handleRotateClick}
|
||||
getInitialCrop={getInitialCrop}
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
onDeleteClick={onDeleteClick}
|
||||
onClose={onClose}
|
||||
modalProps={modalProps.mobile}
|
||||
modalStep={stepIndex}
|
||||
cropImageElementRef={cropImageElementRef}
|
||||
/>
|
||||
)
|
||||
}
|
||||
case "small": {
|
||||
return(
|
||||
<CropGeneral
|
||||
imageUrl={imageUrl}
|
||||
handleSizeChange={handleSizeChange}
|
||||
handleRotateClick={handleRotateClick}
|
||||
getInitialCrop={getInitialCrop}
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
onDeleteClick={onDeleteClick}
|
||||
onClose={onClose}
|
||||
modalProps={modalProps.small}
|
||||
modalStep={stepIndex}
|
||||
cropImageElementRef={cropImageElementRef}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
217
src/ui_kit/Modal/CropModal/cropGeneral.tsx
Normal file
217
src/ui_kit/Modal/CropModal/cropGeneral.tsx
Normal file
@ -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<void>;
|
||||
getInitialCrop: (imageWidth: number, imageHeight: number, aspectRatio: number)=> PercentCrop;
|
||||
modalProps: any;
|
||||
modalStep: number;
|
||||
onClose: () => void;
|
||||
cropImageElementRef: MutableRefObject<HTMLImageElement>;
|
||||
onDeleteClick?: () => void;
|
||||
cropAspectRatio?: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
const styleSlider: SxProps<Theme> = {
|
||||
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<PercentCrop | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [imageWidth, setImageWidth] = useState<number | null>(null);
|
||||
const [imageHeight, setImageHeight] = useState<number | null>(null);
|
||||
const [darken, setDarken] = useState(0);
|
||||
const step = modalStep + 1
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
padding: "20px 20px 0",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "12px"
|
||||
}}
|
||||
>
|
||||
<Typography>{modalProps.name}</Typography>
|
||||
{modalProps.icon}
|
||||
</Box>
|
||||
{onDeleteClick && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
onDeleteClick?.();
|
||||
onClose();
|
||||
}}
|
||||
sx={{
|
||||
height: "48px",
|
||||
width: "48px",
|
||||
p: 0,
|
||||
color: theme.palette.orange.main,
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
<Typography sx={{p: "0 20px 20px", fontSize: "14px", color: "#9A9AAF"}}>
|
||||
{step + " шаг"}</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
height: "320px",
|
||||
backgroundSize: "cover",
|
||||
backgroundRepeat: "no-repeat",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "0 20px"
|
||||
}}
|
||||
>
|
||||
{imageUrl && (
|
||||
<ReactCrop
|
||||
crop={percentCrop}
|
||||
onChange={(_, percentCrop) => setPercentCrop(percentCrop)}
|
||||
minWidth={5}
|
||||
minHeight={5}
|
||||
locked
|
||||
aspect={
|
||||
cropAspectRatio
|
||||
? cropAspectRatio.width / cropAspectRatio.height
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<img
|
||||
onLoad={(e) => {
|
||||
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",
|
||||
}}
|
||||
/>
|
||||
</ReactCrop>
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "40px",
|
||||
display: isMobile ? "block" : "flex",
|
||||
alignItems: "end",
|
||||
justifyContent: "space-between",
|
||||
padding: "0 20px"
|
||||
}}
|
||||
>
|
||||
<IconButton onClick={handleRotateClick}>
|
||||
<ResetIcon />
|
||||
</IconButton>
|
||||
<Box>
|
||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
||||
Размер
|
||||
</Typography>
|
||||
<Slider
|
||||
sx={[
|
||||
styleSlider,
|
||||
{
|
||||
width: isMobile ? undefined : "200px",
|
||||
},
|
||||
]}
|
||||
value={percentCrop?.width ?? 1}
|
||||
min={1}
|
||||
max={100}
|
||||
step={0.1}
|
||||
onChange={(_, newValue) => {
|
||||
if (typeof newValue === "number") handleSizeChange(newValue);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography sx={{ color: "#9A9AAF", fontSize: "16px" }}>
|
||||
Затемнение
|
||||
</Typography>
|
||||
<Slider
|
||||
sx={[
|
||||
styleSlider,
|
||||
{
|
||||
width: isMobile ? undefined : "200px",
|
||||
},
|
||||
]}
|
||||
value={darken}
|
||||
min={0}
|
||||
max={100}
|
||||
step={1}
|
||||
onChange={(_, newValue) => setDarken(newValue as number)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user