fix dropzone & image modals
This commit is contained in:
parent
7824fafc51
commit
0e1f9aab23
@ -14,11 +14,10 @@ import {
|
|||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme
|
useTheme
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { setCropModal } from "@root/cropModal";
|
|
||||||
import { addQuestionVariant, uploadQuestionImage } from "@root/questions/actions";
|
import { addQuestionVariant, uploadQuestionImage } from "@root/questions/actions";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||||||
import { CropModal } from "@ui_kit/Modal/CropModal";
|
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useDisclosure } from "../../../utils/useDisclosure";
|
import { useDisclosure } from "../../../utils/useDisclosure";
|
||||||
import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon";
|
import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon";
|
||||||
@ -40,17 +39,22 @@ export default function OptionsAndPicture({ question }: Props) {
|
|||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
const quizQid = useCurrentQuiz()?.qid;
|
const quizQid = useCurrentQuiz()?.qid;
|
||||||
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
|
|
||||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||||
|
const {
|
||||||
|
isCropModalOpen,
|
||||||
|
openCropModal,
|
||||||
|
closeCropModal,
|
||||||
|
imageBlob,
|
||||||
|
originalImageUrl,
|
||||||
|
setCropModalImageBlob,
|
||||||
|
} = useCropModalState();
|
||||||
|
|
||||||
const SSHC = (data: string) => {
|
const SSHC = (data: string) => {
|
||||||
setSwitchState(data);
|
setSwitchState(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImageUpload = async (files: FileList | null) => {
|
const handleImageUpload = async (file: File) => {
|
||||||
if (!files?.length || !selectedVariantId) return;
|
if (!selectedVariantId) return;
|
||||||
|
|
||||||
const [file] = Array.from(files);
|
|
||||||
|
|
||||||
const url = await uploadQuestionImage(question.id, quizQid, file, (question, url) => {
|
const url = await uploadQuestionImage(question.id, quizQid, file, (question, url) => {
|
||||||
if (!("variants" in question.content)) return;
|
if (!("variants" in question.content)) return;
|
||||||
@ -62,8 +66,7 @@ export default function OptionsAndPicture({ question }: Props) {
|
|||||||
variant.originalImageUrl = url;
|
variant.originalImageUrl = url;
|
||||||
});
|
});
|
||||||
closeImageUploadModal();
|
closeImageUploadModal();
|
||||||
setCropModal(file, url);
|
openCropModal(file, url);
|
||||||
openCropModal();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
function handleCropModalSaveClick(imageBlob: Blob) {
|
||||||
@ -92,13 +95,10 @@ export default function OptionsAndPicture({ question }: Props) {
|
|||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
setSelectedVariantId(variant.id);
|
setSelectedVariantId(variant.id);
|
||||||
if (variant.extendedText) {
|
if (variant.extendedText) {
|
||||||
openCropModal();
|
return openCropModal(
|
||||||
setCropModal(
|
|
||||||
variant.extendedText,
|
variant.extendedText,
|
||||||
variant.originalImageUrl
|
variant.originalImageUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openImageUploadModal();
|
openImageUploadModal();
|
||||||
@ -120,13 +120,10 @@ export default function OptionsAndPicture({ question }: Props) {
|
|||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
setSelectedVariantId(variant.id);
|
setSelectedVariantId(variant.id);
|
||||||
if (variant.extendedText) {
|
if (variant.extendedText) {
|
||||||
openCropModal();
|
return openCropModal(
|
||||||
setCropModal(
|
|
||||||
variant.extendedText,
|
variant.extendedText,
|
||||||
variant.originalImageUrl
|
variant.originalImageUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openImageUploadModal();
|
openImageUploadModal();
|
||||||
@ -141,8 +138,19 @@ export default function OptionsAndPicture({ question }: Props) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
|
<UploadImageModal
|
||||||
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
|
isOpen={isImageUploadOpen}
|
||||||
|
onClose={closeImageUploadModal}
|
||||||
|
handleImageChange={handleImageUpload}
|
||||||
|
/>
|
||||||
|
<CropModal
|
||||||
|
isOpen={isCropModalOpen}
|
||||||
|
imageBlob={imageBlob}
|
||||||
|
originalImageUrl={originalImageUrl}
|
||||||
|
setCropModalImageBlob={setCropModalImageBlob}
|
||||||
|
onClose={closeCropModal}
|
||||||
|
onSaveImageClick={handleCropModalSaveClick}
|
||||||
|
/>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
|||||||
@ -5,10 +5,9 @@ import {
|
|||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme
|
useTheme
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { setCropModal } from "@root/cropModal";
|
|
||||||
import { addQuestionVariant, uploadQuestionImage } from "@root/questions/actions";
|
import { addQuestionVariant, uploadQuestionImage } from "@root/questions/actions";
|
||||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||||||
import { CropModal } from "@ui_kit/Modal/CropModal";
|
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon";
|
import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon";
|
||||||
import type { QuizQuestionImages } from "../../../model/questionTypes/images";
|
import type { QuizQuestionImages } from "../../../model/questionTypes/images";
|
||||||
@ -30,17 +29,22 @@ export default function OptionsPicture({ question }: Props) {
|
|||||||
const [selectedVariantId, setSelectedVariantId] = useState<string | null>(null);
|
const [selectedVariantId, setSelectedVariantId] = useState<string | null>(null);
|
||||||
const [switchState, setSwitchState] = useState("setting");
|
const [switchState, setSwitchState] = useState("setting");
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
|
|
||||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||||
|
const {
|
||||||
|
isCropModalOpen,
|
||||||
|
openCropModal,
|
||||||
|
closeCropModal,
|
||||||
|
imageBlob,
|
||||||
|
originalImageUrl,
|
||||||
|
setCropModalImageBlob,
|
||||||
|
} = useCropModalState();
|
||||||
|
|
||||||
const SSHC = (data: string) => {
|
const SSHC = (data: string) => {
|
||||||
setSwitchState(data);
|
setSwitchState(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImageUpload = async (files: FileList | null) => {
|
const handleImageUpload = async (file: File) => {
|
||||||
if (!files?.length || !selectedVariantId) return;
|
if (!selectedVariantId) return;
|
||||||
|
|
||||||
const [file] = Array.from(files);
|
|
||||||
|
|
||||||
const url = await uploadQuestionImage(question.id, quizQid, file, (question, url) => {
|
const url = await uploadQuestionImage(question.id, quizQid, file, (question, url) => {
|
||||||
if (!("variants" in question.content)) return;
|
if (!("variants" in question.content)) return;
|
||||||
@ -52,8 +56,7 @@ export default function OptionsPicture({ question }: Props) {
|
|||||||
variant.originalImageUrl = url;
|
variant.originalImageUrl = url;
|
||||||
});
|
});
|
||||||
closeImageUploadModal();
|
closeImageUploadModal();
|
||||||
setCropModal(file, url);
|
openCropModal(file, url);
|
||||||
openCropModal();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
function handleCropModalSaveClick(imageBlob: Blob) {
|
||||||
@ -82,13 +85,10 @@ export default function OptionsPicture({ question }: Props) {
|
|||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
setSelectedVariantId(variant.id);
|
setSelectedVariantId(variant.id);
|
||||||
if (variant.extendedText) {
|
if (variant.extendedText) {
|
||||||
openCropModal();
|
return openCropModal(
|
||||||
setCropModal(
|
|
||||||
variant.extendedText,
|
variant.extendedText,
|
||||||
variant.originalImageUrl || ""
|
variant.originalImageUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openImageUploadModal();
|
openImageUploadModal();
|
||||||
@ -110,13 +110,10 @@ export default function OptionsPicture({ question }: Props) {
|
|||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
setSelectedVariantId(variant.id);
|
setSelectedVariantId(variant.id);
|
||||||
if (variant.extendedText) {
|
if (variant.extendedText) {
|
||||||
openCropModal();
|
return openCropModal(
|
||||||
setCropModal(
|
|
||||||
variant.extendedText,
|
variant.extendedText,
|
||||||
variant.originalImageUrl || ""
|
variant.originalImageUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openImageUploadModal();
|
openImageUploadModal();
|
||||||
@ -131,8 +128,19 @@ export default function OptionsPicture({ question }: Props) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
|
<UploadImageModal
|
||||||
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
|
isOpen={isImageUploadOpen}
|
||||||
|
onClose={closeImageUploadModal}
|
||||||
|
handleImageChange={handleImageUpload}
|
||||||
|
/>
|
||||||
|
<CropModal
|
||||||
|
isOpen={isCropModalOpen}
|
||||||
|
imageBlob={imageBlob}
|
||||||
|
originalImageUrl={originalImageUrl}
|
||||||
|
setCropModalImageBlob={setCropModalImageBlob}
|
||||||
|
onClose={closeCropModal}
|
||||||
|
onSaveImageClick={handleCropModalSaveClick}
|
||||||
|
/>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||||
<Link
|
<Link
|
||||||
component="button"
|
component="button"
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { VideofileIcon } from "@icons/questionsPage/VideofileIcon";
|
import { VideofileIcon } from "@icons/questionsPage/VideofileIcon";
|
||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { setCropModal } from "@root/cropModal";
|
|
||||||
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
|
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import { CropModal } from "@ui_kit/Modal/CropModal";
|
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
||||||
@ -29,7 +28,14 @@ export default function PageOptions({ disableInput, question }: Props) {
|
|||||||
const isFigmaTablet = useMediaQuery(theme.breakpoints.down(990));
|
const isFigmaTablet = useMediaQuery(theme.breakpoints.down(990));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(780));
|
const isMobile = useMediaQuery(theme.breakpoints.down(780));
|
||||||
const quizQid = useCurrentQuiz()?.qid;
|
const quizQid = useCurrentQuiz()?.qid;
|
||||||
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
|
const {
|
||||||
|
isCropModalOpen,
|
||||||
|
openCropModal,
|
||||||
|
closeCropModal,
|
||||||
|
imageBlob,
|
||||||
|
originalImageUrl,
|
||||||
|
setCropModalImageBlob,
|
||||||
|
} = useCropModalState();
|
||||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||||
|
|
||||||
const setText = useDebouncedCallback((value) => {
|
const setText = useDebouncedCallback((value) => {
|
||||||
@ -44,18 +50,15 @@ export default function PageOptions({ disableInput, question }: Props) {
|
|||||||
setSwitchState(data);
|
setSwitchState(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleImageUpload(fileList: FileList | null) {
|
async function handleImageUpload(file: File) {
|
||||||
if (!fileList?.length) return;
|
const url = await uploadQuestionImage(question.id, quizQid, file, (question, url) => {
|
||||||
|
|
||||||
const url = await uploadQuestionImage(question.id, quizQid, fileList[0], (question, url) => {
|
|
||||||
if (question.type !== "page") return;
|
if (question.type !== "page") return;
|
||||||
|
|
||||||
question.content.picture = url;
|
question.content.picture = url;
|
||||||
question.content.originalPicture = url;
|
question.content.originalPicture = url;
|
||||||
});
|
});
|
||||||
closeImageUploadModal();
|
closeImageUploadModal();
|
||||||
setCropModal(fileList[0], url);
|
openCropModal(file, url);
|
||||||
openCropModal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
function handleCropModalSaveClick(imageBlob: Blob) {
|
||||||
@ -108,7 +111,7 @@ export default function PageOptions({ disableInput, question }: Props) {
|
|||||||
imageSrc={question.content.picture}
|
imageSrc={question.content.picture}
|
||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
if (question.content.picture) {
|
if (question.content.picture) {
|
||||||
return setCropModal(
|
return openCropModal(
|
||||||
question.content.picture,
|
question.content.picture,
|
||||||
question.content.originalPicture
|
question.content.originalPicture
|
||||||
);
|
);
|
||||||
@ -133,8 +136,19 @@ export default function PageOptions({ disableInput, question }: Props) {
|
|||||||
Изображение
|
Изображение
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
|
<UploadImageModal
|
||||||
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
|
isOpen={isImageUploadOpen}
|
||||||
|
onClose={closeImageUploadModal}
|
||||||
|
handleImageChange={handleImageUpload}
|
||||||
|
/>
|
||||||
|
<CropModal
|
||||||
|
isOpen={isCropModalOpen}
|
||||||
|
imageBlob={imageBlob}
|
||||||
|
originalImageUrl={originalImageUrl}
|
||||||
|
setCropModalImageBlob={setCropModalImageBlob}
|
||||||
|
onClose={closeCropModal}
|
||||||
|
onSaveImageClick={handleCropModalSaveClick}
|
||||||
|
/>
|
||||||
<Typography> или</Typography>
|
<Typography> или</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@ -9,29 +9,26 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import UploadIcon from "../../../assets/icons/UploadIcon";
|
import UploadIcon from "../../../assets/icons/UploadIcon";
|
||||||
import SearchIcon from "../../../assets/icons/SearchIcon";
|
import SearchIcon from "../../../assets/icons/SearchIcon";
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
import UnsplashIcon from "../../../assets/icons/Unsplash.svg";
|
import UnsplashIcon from "../../../assets/icons/Unsplash.svg";
|
||||||
|
import { useRef, useState, type DragEvent } from "react";
|
||||||
|
|
||||||
import type { DragEvent } from "react";
|
|
||||||
|
|
||||||
interface ModalkaProps {
|
interface ModalkaProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
imgHC: (imgInp: FileList | null) => void;
|
handleImageChange: (file: File) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UploadImageModal: React.FC<ModalkaProps> = ({
|
export const UploadImageModal: React.FC<ModalkaProps> = ({
|
||||||
imgHC,
|
handleImageChange,
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const dropZone = useRef<HTMLDivElement>(null);
|
||||||
|
const [ready, setReady] = useState(false);
|
||||||
|
|
||||||
const dropZone = React.useRef<HTMLDivElement>(null);
|
const handleDragEnter = (event: DragEvent<HTMLDivElement>) => {
|
||||||
const [ready, setReady] = React.useState(false);
|
|
||||||
|
|
||||||
const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setReady(true);
|
setReady(true);
|
||||||
};
|
};
|
||||||
@ -40,7 +37,10 @@ export const UploadImageModal: React.FC<ModalkaProps> = ({
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
imgHC(event.dataTransfer.files);
|
const file = event.dataTransfer.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
handleImageChange(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -79,7 +79,7 @@ export const UploadImageModal: React.FC<ModalkaProps> = ({
|
|||||||
</Typography>
|
</Typography>
|
||||||
<ButtonBase component="label" sx={{ justifyContent: "flex-start" }}>
|
<ButtonBase component="label" sx={{ justifyContent: "flex-start" }}>
|
||||||
<input
|
<input
|
||||||
onChange={(event) => imgHC(event.target.files)}
|
onChange={(event) => event.target.files?.[0] && handleImageChange(event.target.files[0])}
|
||||||
hidden
|
hidden
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
multiple
|
multiple
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
import { Box, ButtonBase, Typography, useTheme } from "@mui/material";
|
import { Box, ButtonBase, Typography, useTheme } from "@mui/material";
|
||||||
import { uploadQuestionImage } from "@root/questions/actions";
|
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
|
||||||
import { CropModal } from "@ui_kit/Modal/CropModal";
|
|
||||||
import UploadBox from "@ui_kit/UploadBox";
|
|
||||||
import { type DragEvent } from "react";
|
|
||||||
import UploadIcon from "../../../assets/icons/UploadIcon";
|
|
||||||
import { UploadImageModal } from "./UploadImageModal";
|
|
||||||
import { setCropModal } from "@root/cropModal";
|
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||||
|
import { DropZone } from "../../../pages/startPage/dropZone";
|
||||||
import { useDisclosure } from "../../../utils/useDisclosure";
|
import { useDisclosure } from "../../../utils/useDisclosure";
|
||||||
|
import { UploadImageModal } from "./UploadImageModal";
|
||||||
|
|
||||||
|
|
||||||
type UploadImageProps = {
|
type UploadImageProps = {
|
||||||
@ -17,34 +14,9 @@ type UploadImageProps = {
|
|||||||
|
|
||||||
export default function UploadImage({ question }: UploadImageProps) {
|
export default function UploadImage({ question }: UploadImageProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quizQid = useCurrentQuiz()?.qid;
|
const quiz = useCurrentQuiz();
|
||||||
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
|
|
||||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
|
||||||
|
|
||||||
const handleImageUpload = async (files: FileList | null) => {
|
if (!quiz) return null;
|
||||||
if (!files?.length) return;
|
|
||||||
|
|
||||||
const url = await uploadQuestionImage(question.id, quizQid, files[0], (question, url) => {
|
|
||||||
question.content.back = url;
|
|
||||||
question.content.originalBack = url;
|
|
||||||
});
|
|
||||||
closeImageUploadModal();
|
|
||||||
setCropModal(files[0], url);
|
|
||||||
openCropModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
handleImageUpload(event.dataTransfer.files);
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
|
||||||
uploadQuestionImage(question.id, quizQid, imageBlob, (question, url) => {
|
|
||||||
question.content.back = url;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ padding: "20px" }}>
|
<Box sx={{ padding: "20px" }}>
|
||||||
@ -58,36 +30,29 @@ export default function UploadImage({ question }: UploadImageProps) {
|
|||||||
>
|
>
|
||||||
Загрузить изображение
|
Загрузить изображение
|
||||||
</Typography>
|
</Typography>
|
||||||
<ButtonBase
|
<DropZone
|
||||||
onClick={openImageUploadModal}
|
text={"5 MB максимум"}
|
||||||
sx={{
|
heightImg={"110px"}
|
||||||
width: "100%",
|
sx={{ maxWidth: "300px", width: "100%" }}
|
||||||
maxWidth: "260px",
|
imageUrl={question.content.back}
|
||||||
height: "120px",
|
originalImageUrl={question.content.originalBack}
|
||||||
|
onImageUploadClick={file => {
|
||||||
|
uploadQuestionImage(question.id, quiz.qid, file, (question, url) => {
|
||||||
|
question.content.back = url;
|
||||||
|
question.content.originalBack = url;
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
onDeleteClick={() => {
|
||||||
{question.content.back ?
|
updateQuestion(question.id, question => {
|
||||||
<img
|
question.content.back = null;
|
||||||
src={question.content.back}
|
});
|
||||||
alt="question background"
|
}}
|
||||||
style={{
|
onImageSaveClick={file => {
|
||||||
width: "100%",
|
uploadQuestionImage(question.id, quiz.qid, file, (question, url) => {
|
||||||
height: "100%",
|
question.content.back = url;
|
||||||
objectFit: "scale-down",
|
});
|
||||||
display: "block",
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
:
|
|
||||||
<UploadBox
|
|
||||||
handleDrop={handleDrop}
|
|
||||||
sx={{ maxWidth: "260px" }}
|
|
||||||
icon={<UploadIcon />}
|
|
||||||
text="5 MB максимум"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</ButtonBase>
|
|
||||||
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
|
|
||||||
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -307,7 +307,14 @@ export default function StartPageSettings() {
|
|||||||
heightImg={"110px"}
|
heightImg={"110px"}
|
||||||
sx={{ maxWidth: "300px" }}
|
sx={{ maxWidth: "300px" }}
|
||||||
imageUrl={quiz.config.startpage.background.desktop}
|
imageUrl={quiz.config.startpage.background.desktop}
|
||||||
onFileChange={file => {
|
originalImageUrl={quiz.config.startpage.background.originalDesktop}
|
||||||
|
onImageUploadClick={file => {
|
||||||
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
|
quiz.config.startpage.background.desktop = url;
|
||||||
|
quiz.config.startpage.background.originalDesktop = url;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onImageSaveClick={file => {
|
||||||
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
quiz.config.startpage.background.desktop = url;
|
quiz.config.startpage.background.desktop = url;
|
||||||
});
|
});
|
||||||
@ -368,7 +375,14 @@ export default function StartPageSettings() {
|
|||||||
text={"5 MB максимум"}
|
text={"5 MB максимум"}
|
||||||
heightImg={"110px"}
|
heightImg={"110px"}
|
||||||
imageUrl={quiz.config.startpage.background.mobile}
|
imageUrl={quiz.config.startpage.background.mobile}
|
||||||
onFileChange={file => {
|
originalImageUrl={quiz.config.startpage.background.originalMobile}
|
||||||
|
onImageUploadClick={file => {
|
||||||
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
|
quiz.config.startpage.background.mobile = url;
|
||||||
|
quiz.config.startpage.background.originalMobile = url;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onImageSaveClick={file => {
|
||||||
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
quiz.config.startpage.background.mobile = url;
|
quiz.config.startpage.background.mobile = url;
|
||||||
});
|
});
|
||||||
@ -464,7 +478,14 @@ export default function StartPageSettings() {
|
|||||||
text={"5 MB максимум"}
|
text={"5 MB максимум"}
|
||||||
heightImg={"110px"}
|
heightImg={"110px"}
|
||||||
imageUrl={quiz.config.startpage.background.mobile}
|
imageUrl={quiz.config.startpage.background.mobile}
|
||||||
onFileChange={file => {
|
originalImageUrl={quiz.config.startpage.background.originalMobile}
|
||||||
|
onImageUploadClick={file => {
|
||||||
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
|
quiz.config.startpage.background.mobile = url;
|
||||||
|
quiz.config.startpage.background.originalMobile = url;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onImageSaveClick={file => {
|
||||||
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
quiz.config.startpage.background.mobile = url;
|
quiz.config.startpage.background.mobile = url;
|
||||||
});
|
});
|
||||||
@ -539,7 +560,14 @@ export default function StartPageSettings() {
|
|||||||
heightImg={"110px"}
|
heightImg={"110px"}
|
||||||
sx={{ maxWidth: "300px" }}
|
sx={{ maxWidth: "300px" }}
|
||||||
imageUrl={quiz.config.logo}
|
imageUrl={quiz.config.logo}
|
||||||
onFileChange={file => {
|
originalImageUrl={quiz.config.originalLogo}
|
||||||
|
onImageUploadClick={file => {
|
||||||
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
|
quiz.config.logo = url;
|
||||||
|
quiz.config.originalLogo = url;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onImageSaveClick={file => {
|
||||||
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
quiz.config.logo = url;
|
quiz.config.logo = url;
|
||||||
});
|
});
|
||||||
@ -620,7 +648,14 @@ export default function StartPageSettings() {
|
|||||||
heightImg={"110px"}
|
heightImg={"110px"}
|
||||||
sx={{ maxWidth: "300px" }}
|
sx={{ maxWidth: "300px" }}
|
||||||
imageUrl={quiz.config.logo}
|
imageUrl={quiz.config.logo}
|
||||||
onFileChange={file => {
|
originalImageUrl={quiz.config.originalLogo}
|
||||||
|
onImageUploadClick={file => {
|
||||||
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
|
quiz.config.logo = url;
|
||||||
|
quiz.config.originalLogo = url;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onImageSaveClick={file => {
|
||||||
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||||
quiz.config.logo = url;
|
quiz.config.logo = url;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,7 +11,10 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { useState } from "react";
|
import { UploadImageModal } from "../../pages/Questions/UploadImage/UploadImageModal";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||||
|
import { useDisclosure } from "../../utils/useDisclosure";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -19,71 +22,69 @@ interface Props {
|
|||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
heightImg: string;
|
heightImg: string;
|
||||||
widthImg?: string;
|
widthImg?: string;
|
||||||
onFileChange?: (file: File) => void;
|
|
||||||
onDeleteClick?: () => void;
|
|
||||||
imageUrl: string | null;
|
imageUrl: string | null;
|
||||||
|
originalImageUrl: string | null;
|
||||||
|
onImageUploadClick: (image: Blob) => void;
|
||||||
|
onImageSaveClick: (image: Blob) => void;
|
||||||
|
onDeleteClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Научи функцию принимать данные для валидации
|
//Научи функцию принимать данные для валидации
|
||||||
export const DropZone = ({ text, sx, heightImg, widthImg, onFileChange, onDeleteClick, imageUrl }: Props) => {
|
export const DropZone = ({ text, sx, heightImg, widthImg, imageUrl, originalImageUrl, onImageUploadClick, onImageSaveClick, onDeleteClick }: Props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const [ready, setReady] = useState(false);
|
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||||
|
const {
|
||||||
|
isCropModalOpen,
|
||||||
|
openCropModal,
|
||||||
|
closeCropModal,
|
||||||
|
imageBlob,
|
||||||
|
setCropModalImageBlob,
|
||||||
|
} = useCropModalState();
|
||||||
|
|
||||||
if (!quiz) return null; // TODO throw and catch with error boundary
|
if (!quiz) return null; // TODO throw and catch with error boundary
|
||||||
|
|
||||||
const imgHC = async (imgInp: HTMLInputElement) => {
|
async function handleImageUpload(file: File) {
|
||||||
if (!quiz) return;
|
onImageUploadClick?.(file);
|
||||||
|
closeImageUploadModal();
|
||||||
|
openCropModal(file);
|
||||||
|
}
|
||||||
|
|
||||||
const file = imgInp.files?.[0];
|
return (
|
||||||
|
<Box sx={{
|
||||||
if (!file) return;
|
position: "relative",
|
||||||
if (file.size > 5 * 2 ** 20) return enqueueSnackbar("Размер картинки слишком велик");
|
|
||||||
|
|
||||||
onFileChange?.(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragenterHC = () => {
|
|
||||||
setReady(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragexitHC = () => {
|
|
||||||
setReady(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dropHC = (event: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setReady(false);
|
|
||||||
|
|
||||||
const file = event.dataTransfer.files[0];
|
|
||||||
if (file.size < 5 * 2 ** 20) return enqueueSnackbar("Размер картинки слишком велик");
|
|
||||||
|
|
||||||
onFileChange?.(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragOverHC = (event: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
};
|
|
||||||
|
|
||||||
return imageUrl ? (
|
|
||||||
<Box
|
|
||||||
onDragEnter={dragenterHC}
|
|
||||||
onDragExit={dragexitHC}
|
|
||||||
onDrop={dropHC}
|
|
||||||
onDragOver={dragOverHC}
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "120px",
|
height: "120px",
|
||||||
position: "relative",
|
backgroundColor: theme.palette.background.default,
|
||||||
|
border: `1px solid ${theme.palette.grey2.main}`,
|
||||||
|
borderRadius: "8px",
|
||||||
|
...sx,
|
||||||
|
}}>
|
||||||
|
<UploadImageModal
|
||||||
|
isOpen={isImageUploadOpen}
|
||||||
|
onClose={closeImageUploadModal}
|
||||||
|
handleImageChange={handleImageUpload}
|
||||||
|
/>
|
||||||
|
<CropModal
|
||||||
|
isOpen={isCropModalOpen}
|
||||||
|
imageBlob={imageBlob}
|
||||||
|
originalImageUrl={originalImageUrl}
|
||||||
|
setCropModalImageBlob={setCropModalImageBlob}
|
||||||
|
onClose={closeCropModal}
|
||||||
|
onSaveImageClick={onImageSaveClick}
|
||||||
|
/>
|
||||||
|
<ButtonBase
|
||||||
|
onClick={imageUrl ? () => openCropModal(imageUrl) : openImageUploadModal}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
border: `1px solid ${ready ? "red" : theme.palette.grey2.main}`,
|
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
...sx,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{imageUrl ?
|
||||||
<img
|
<img
|
||||||
height={heightImg}
|
height={heightImg}
|
||||||
width={widthImg}
|
width={widthImg}
|
||||||
@ -91,51 +92,9 @@ export const DropZone = ({ text, sx, heightImg, widthImg, onFileChange, onDelete
|
|||||||
style={{
|
style={{
|
||||||
objectFit: "scale-down",
|
objectFit: "scale-down",
|
||||||
}}
|
}}
|
||||||
alt="img"
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
:
|
||||||
onClick={onDeleteClick}
|
<>
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
color: theme.palette.orange.main,
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "19px",
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<ButtonBase component="label" sx={{ justifyContent: "flex-start" }}>
|
|
||||||
<input
|
|
||||||
onChange={(event) => imgHC(event.target)}
|
|
||||||
hidden
|
|
||||||
accept="image/*"
|
|
||||||
multiple
|
|
||||||
type="file"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
onDragEnter={dragenterHC}
|
|
||||||
onDragExit={dragexitHC}
|
|
||||||
onDrop={dropHC}
|
|
||||||
onDragOver={dragOverHC}
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
height: "120px",
|
|
||||||
position: "relative",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
border: `1px solid ${ready ? "red" : theme.palette.grey2.main}`,
|
|
||||||
borderRadius: "8px",
|
|
||||||
opacity: imageUrl ? "0.5" : 1,
|
|
||||||
...sx,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<UploadIcon />
|
<UploadIcon />
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -150,7 +109,25 @@ export const DropZone = ({ text, sx, heightImg, widthImg, onFileChange, onDelete
|
|||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</>
|
||||||
|
}
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
{imageUrl &&
|
||||||
|
<IconButton
|
||||||
|
onClick={onDeleteClick}
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
color: theme.palette.orange.main,
|
||||||
|
borderRadius: "8px",
|
||||||
|
borderBottomRightRadius: 0,
|
||||||
|
borderTopLeftRadius: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
import { create } from "zustand";
|
|
||||||
import { devtools } from "zustand/middleware";
|
|
||||||
|
|
||||||
|
|
||||||
type CropModalStore = {
|
|
||||||
imageBlob: Blob | null;
|
|
||||||
originalImageUrl: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const initialState: CropModalStore = {
|
|
||||||
imageBlob: null,
|
|
||||||
originalImageUrl: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useCropModalStore = create<CropModalStore>()(
|
|
||||||
devtools(
|
|
||||||
() => initialState,
|
|
||||||
{
|
|
||||||
name: "CropModalStore",
|
|
||||||
enabled: process.env.NODE_ENV === "development",
|
|
||||||
trace: process.env.NODE_ENV === "development",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setCropModal = async (
|
|
||||||
imageBlob: Blob | string | null,
|
|
||||||
originalImageUrl: string | null | undefined,
|
|
||||||
) => {
|
|
||||||
if (typeof imageBlob === "string") {
|
|
||||||
const response = await fetch(imageBlob);
|
|
||||||
imageBlob = await response.blob();
|
|
||||||
}
|
|
||||||
|
|
||||||
useCropModalStore.setState({
|
|
||||||
imageBlob,
|
|
||||||
originalImageUrl: originalImageUrl ?? null,
|
|
||||||
}, false, {
|
|
||||||
type: "setCropModal",
|
|
||||||
imageBlob,
|
|
||||||
originalImageUrl,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const closeCropModal = () => useCropModalStore.setState(
|
|
||||||
initialState,
|
|
||||||
false,
|
|
||||||
"closeCropModal"
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setCropModalImageBlob = async (image: Blob | string | null) => {
|
|
||||||
if (typeof image === "string") {
|
|
||||||
const response = await fetch(image);
|
|
||||||
image = await response.blob();
|
|
||||||
}
|
|
||||||
|
|
||||||
useCropModalStore.setState({
|
|
||||||
imageBlob: image,
|
|
||||||
}, false, {
|
|
||||||
type: "setCropModalImageBlob",
|
|
||||||
image,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setCropModalOriginalImageUrl = (originalImageUrl: string | null | undefined) => useCropModalStore.setState(
|
|
||||||
{ originalImageUrl: originalImageUrl ?? null },
|
|
||||||
false,
|
|
||||||
"setCropModalOriginalImageUrl"
|
|
||||||
);
|
|
||||||
@ -16,7 +16,6 @@ import { FC, useMemo, useRef, useState } from "react";
|
|||||||
import ReactCrop, { Crop, PixelCrop } from "react-image-crop";
|
import ReactCrop, { Crop, PixelCrop } from "react-image-crop";
|
||||||
import "react-image-crop/dist/ReactCrop.css";
|
import "react-image-crop/dist/ReactCrop.css";
|
||||||
import { canvasPreview } from "./utils/canvasPreview";
|
import { canvasPreview } from "./utils/canvasPreview";
|
||||||
import { setCropModalImageBlob, useCropModalStore } from "@root/cropModal";
|
|
||||||
|
|
||||||
|
|
||||||
const styleSlider: SxProps<Theme> = {
|
const styleSlider: SxProps<Theme> = {
|
||||||
@ -45,19 +44,20 @@ const styleSlider: SxProps<Theme> = {
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
imageBlob: Blob | null;
|
||||||
|
originalImageUrl: string | null;
|
||||||
|
setCropModalImageBlob: (imageBlob: Blob) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSaveImageClick?: (imageBlob: Blob) => void;
|
onSaveImageClick: (imageBlob: Blob) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CropModal: FC<Props> = ({isOpen, onSaveImageClick, onClose }) => {
|
export const CropModal: FC<Props> = ({ isOpen, imageBlob, originalImageUrl, setCropModalImageBlob, onSaveImageClick, onClose }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [crop, setCrop] = useState<Crop>();
|
const [crop, setCrop] = useState<Crop>();
|
||||||
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
|
||||||
const imageBlob = useCropModalStore(state => state.imageBlob);
|
|
||||||
const originalImageUrl = useCropModalStore(state => state.originalImageUrl);
|
|
||||||
const [darken, setDarken] = useState(0);
|
const [darken, setDarken] = useState(0);
|
||||||
const [rotate, setRotate] = useState(0);
|
const [rotate, setRotate] = useState(0);
|
||||||
const [width, setWidth] = useState<number>(0);
|
const [width, setWidth] = useState<number>(240);
|
||||||
const cropImageElementRef = useRef<HTMLImageElement>(null);
|
const cropImageElementRef = useRef<HTMLImageElement>(null);
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(786));
|
const isMobile = useMediaQuery(theme.breakpoints.down(786));
|
||||||
|
|
||||||
@ -99,7 +99,6 @@ export const CropModal: FC<Props> = ({isOpen, onSaveImageClick, onClose }) => {
|
|||||||
const response = await fetch(originalImageUrl);
|
const response = await fetch(originalImageUrl);
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
|
|
||||||
onSaveImageClick?.(blob);
|
|
||||||
setCropModalImageBlob(blob);
|
setCropModalImageBlob(blob);
|
||||||
setCrop(undefined);
|
setCrop(undefined);
|
||||||
setCompletedCrop(undefined);
|
setCompletedCrop(undefined);
|
||||||
@ -126,7 +125,7 @@ export const CropModal: FC<Props> = ({isOpen, onSaveImageClick, onClose }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={ isOpen}
|
open={isOpen}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
aria-labelledby="modal-modal-title"
|
aria-labelledby="modal-modal-title"
|
||||||
aria-describedby="modal-modal-description"
|
aria-describedby="modal-modal-description"
|
||||||
@ -297,3 +296,35 @@ export const CropModal: FC<Props> = ({isOpen, onSaveImageClick, onClose }) => {
|
|||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function useCropModalState(initialOpenState = false) {
|
||||||
|
const [isCropModalOpen, setOpened] = useState(initialOpenState);
|
||||||
|
const [imageBlob, setCropModalImageBlob] = useState<Blob | null>(null);
|
||||||
|
const [originalImageUrl, setOriginalImageUrl] = useState<string | null>(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;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user