fix crop modal open state

This commit is contained in:
nflnkr 2023-12-02 12:35:35 +03:00
parent 52bf638baf
commit 1c2f9c2d5e
13 changed files with 120 additions and 118 deletions

@ -14,19 +14,19 @@ import {
useMediaQuery,
useTheme
} from "@mui/material";
import { openCropModal } from "@root/cropModal";
import { closeImageUploadModal, openImageUploadModal } from "@root/imageUploadModal";
import { setCropModal } from "@root/cropModal";
import { addQuestionVariant, uploadQuestionImage } from "@root/questions/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
import { CropModal } from "@ui_kit/Modal/CropModal";
import { useState } from "react";
import { useDisclosure } from "../../../utils/useDisclosure";
import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon";
import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
import { AnswerDraggableList } from "../AnswerDraggableList";
import ButtonsOptionsAndPict from "../ButtonsOptionsAndPict";
import { UploadImageModal } from "../UploadImage/UploadImageModal";
import SwitchOptionsAndPict from "./switchOptionsAndPict";
import { useCurrentQuiz } from "@root/quizes/hooks";
interface Props {
@ -40,6 +40,8 @@ export default function OptionsAndPicture({ question }: Props) {
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const quizQid = useCurrentQuiz()?.qid;
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
const SSHC = (data: string) => {
setSwitchState(data);
@ -60,7 +62,8 @@ export default function OptionsAndPicture({ question }: Props) {
variant.originalImageUrl = url;
});
closeImageUploadModal();
openCropModal(file, url);
setCropModal(file, url);
openCropModal();
};
function handleCropModalSaveClick(imageBlob: Blob) {
@ -87,13 +90,16 @@ export default function OptionsAndPicture({ question }: Props) {
<AddOrEditImageButton
imageSrc={variant.extendedText}
onImageClick={() => {
if (!("originalImageUrl" in variant)) return;
setSelectedVariantId(variant.id);
if (variant.extendedText) return openCropModal(
variant.extendedText,
variant.originalImageUrl
);
if (variant.extendedText) {
openCropModal();
setCropModal(
variant.extendedText,
variant.originalImageUrl
);
return;
}
openImageUploadModal();
}}
@ -112,13 +118,16 @@ export default function OptionsAndPicture({ question }: Props) {
<AddOrEditImageButton
imageSrc={variant.extendedText}
onImageClick={() => {
if (!("originalImageUrl" in variant)) return;
setSelectedVariantId(variant.id);
if (variant.extendedText) return openCropModal(
variant.extendedText,
variant.originalImageUrl
);
if (variant.extendedText) {
openCropModal();
setCropModal(
variant.extendedText,
variant.originalImageUrl
);
return;
}
openImageUploadModal();
}}
@ -132,8 +141,8 @@ export default function OptionsAndPicture({ question }: Props) {
</>
)}
/>
<UploadImageModal imgHC={handleImageUpload} />
<CropModal onSaveImageClick={handleCropModalSaveClick} />
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
<Box
sx={{
width: "100%",

@ -5,8 +5,7 @@ import {
useMediaQuery,
useTheme
} from "@mui/material";
import { openCropModal } from "@root/cropModal";
import { closeImageUploadModal, openImageUploadModal } from "@root/imageUploadModal";
import { setCropModal } from "@root/cropModal";
import { addQuestionVariant, uploadQuestionImage } from "@root/questions/actions";
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
import { CropModal } from "@ui_kit/Modal/CropModal";
@ -18,6 +17,7 @@ import ButtonsOptions from "../ButtonsOptions";
import { UploadImageModal } from "../UploadImage/UploadImageModal";
import SwitchAnswerOptionsPict from "./switchOptionsPict";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useDisclosure } from "../../../utils/useDisclosure";
interface Props {
@ -30,6 +30,8 @@ export default function OptionsPicture({ question }: Props) {
const [selectedVariantId, setSelectedVariantId] = useState<string | null>(null);
const [switchState, setSwitchState] = useState("setting");
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
const SSHC = (data: string) => {
setSwitchState(data);
@ -50,7 +52,8 @@ export default function OptionsPicture({ question }: Props) {
variant.originalImageUrl = url;
});
closeImageUploadModal();
openCropModal(file, url);
setCropModal(file, url);
openCropModal();
};
function handleCropModalSaveClick(imageBlob: Blob) {
@ -77,14 +80,15 @@ export default function OptionsPicture({ question }: Props) {
<AddOrEditImageButton
imageSrc={variant.extendedText}
onImageClick={() => {
if (!("originalImageUrl" in variant)) return;
setSelectedVariantId(variant.id);
if (variant.extendedText) {
return openCropModal(
openCropModal();
setCropModal(
variant.extendedText,
variant.originalImageUrl
);
return;
}
openImageUploadModal();
@ -104,14 +108,15 @@ export default function OptionsPicture({ question }: Props) {
<AddOrEditImageButton
imageSrc={variant.extendedText}
onImageClick={() => {
if (!("originalImageUrl" in variant)) return;
setSelectedVariantId(variant.id);
if (variant.extendedText) {
return openCropModal(
openCropModal();
setCropModal(
variant.extendedText,
variant.originalImageUrl
);
return;
}
openImageUploadModal();
@ -126,8 +131,8 @@ export default function OptionsPicture({ question }: Props) {
</>
)}
/>
<UploadImageModal imgHC={handleImageUpload} />
<CropModal onSaveImageClick={handleCropModalSaveClick} />
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<Link
component="button"

@ -1,7 +1,6 @@
import { VideofileIcon } from "@icons/questionsPage/VideofileIcon";
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
import { openCropModal } from "@root/cropModal";
import { closeImageUploadModal, openImageUploadModal } from "@root/imageUploadModal";
import { setCropModal } from "@root/cropModal";
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
@ -14,6 +13,7 @@ import ButtonsOptions from "../ButtonsOptions";
import { UploadImageModal } from "../UploadImage/UploadImageModal";
import { UploadVideoModal } from "../UploadVideoModal";
import SwitchPageOptions from "./switchPageOptions";
import { useDisclosure } from "../../../utils/useDisclosure";
type Props = {
@ -29,6 +29,8 @@ export default function PageOptions({ disableInput, question }: Props) {
const isFigmaTablet = useMediaQuery(theme.breakpoints.down(990));
const isMobile = useMediaQuery(theme.breakpoints.down(780));
const quizQid = useCurrentQuiz()?.qid;
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
const setText = useDebouncedCallback((value) => {
updateQuestion(question.id, question => {
@ -52,7 +54,8 @@ export default function PageOptions({ disableInput, question }: Props) {
question.content.originalPicture = url;
});
closeImageUploadModal();
openCropModal(fileList[0], url);
setCropModal(fileList[0], url);
openCropModal();
}
function handleCropModalSaveClick(imageBlob: Blob) {
@ -105,7 +108,7 @@ export default function PageOptions({ disableInput, question }: Props) {
imageSrc={question.content.picture}
onImageClick={() => {
if (question.content.picture) {
return openCropModal(
return setCropModal(
question.content.picture,
question.content.originalPicture
);
@ -130,8 +133,8 @@ export default function PageOptions({ disableInput, question }: Props) {
Изображение
</Typography>
</Box>
<UploadImageModal imgHC={handleImageUpload} />
<CropModal onSaveImageClick={handleCropModalSaveClick} />
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
<Typography> или</Typography>
<Box
sx={{

@ -14,17 +14,19 @@ import * as React from "react";
import UnsplashIcon from "../../../assets/icons/Unsplash.svg";
import type { DragEvent } from "react";
import { closeImageUploadModal, useImageUploadModalStore } from "@root/imageUploadModal";
interface ModalkaProps {
isOpen: boolean;
onClose: () => void;
imgHC: (imgInp: FileList | null) => void;
}
export const UploadImageModal: React.FC<ModalkaProps> = ({
imgHC,
isOpen,
onClose,
}) => {
const theme = useTheme();
const isOpen = useImageUploadModalStore(state => state.isOpen)
const dropZone = React.useRef<HTMLDivElement>(null);
const [ready, setReady] = React.useState(false);
@ -44,7 +46,7 @@ export const UploadImageModal: React.FC<ModalkaProps> = ({
return (
<Modal
open={isOpen}
onClose={closeImageUploadModal}
onClose={onClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>

@ -1,14 +1,14 @@
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { Box, ButtonBase, Typography, useTheme } from "@mui/material";
import { closeImageUploadModal, openImageUploadModal } from "@root/imageUploadModal";
import { 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 { openCropModal } from "@root/cropModal";
import { setCropModal } from "@root/cropModal";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useDisclosure } from "../../../utils/useDisclosure";
type UploadImageProps = {
@ -18,6 +18,8 @@ type UploadImageProps = {
export default function UploadImage({ question }: UploadImageProps) {
const theme = useTheme();
const quizQid = useCurrentQuiz()?.qid;
const [isCropModalOpen, openCropModal, closeCropModal] = useDisclosure();
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
const handleImageUpload = async (files: FileList | null) => {
if (!files?.length) return;
@ -27,7 +29,8 @@ export default function UploadImage({ question }: UploadImageProps) {
question.content.originalBack = url;
});
closeImageUploadModal();
openCropModal(files[0], url);
setCropModal(files[0], url);
openCropModal();
};
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
@ -83,8 +86,8 @@ export default function UploadImage({ question }: UploadImageProps) {
/>
}
</ButtonBase>
<UploadImageModal imgHC={handleImageUpload} />
<CropModal onSaveImageClick={handleCropModalSaveClick} />
<UploadImageModal isOpen={isImageUploadOpen} onClose={closeImageUploadModal} imgHC={handleImageUpload} />
<CropModal isOpen={isCropModalOpen} onClose={closeCropModal} onSaveImageClick={handleCropModalSaveClick} />
</Box>
);
}

@ -1,9 +1,7 @@
import { Box, Typography, useTheme, useMediaQuery } from "@mui/material";
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
import AddImage from "@icons/questionsPage/addImage";
import AddVideofile from "@icons/questionsPage/addVideofile";
import { CropModal } from "@ui_kit/Modal/CropModal";
import { openCropModal } from "@root/cropModal";
export default function ImageAndVideoButtons() {
const theme = useTheme();
@ -12,7 +10,6 @@ export default function ImageAndVideoButtons() {
return (
<Box sx={{ display: "flex", alignItems: "center", gap: "12px", mt: "20px", mb: "20px" }}>
<AddImage onClick={undefined/* TODO () => openCropModal("", "") */} />
<CropModal />
<Typography
sx={{
fontWeight: 400,

@ -3,13 +3,11 @@ import { devtools } from "zustand/middleware";
type CropModalStore = {
isCropModalOpen: boolean;
imageBlob: Blob | null;
originalImageUrl: string | null;
};
const initialState: CropModalStore = {
isCropModalOpen: false,
export const initialState: CropModalStore = {
imageBlob: null,
originalImageUrl: null,
};
@ -25,23 +23,23 @@ export const useCropModalStore = create<CropModalStore>()(
),
);
export const openCropModal = async (image: Blob | string, originalImageUrl: string | null | undefined) => {
if (typeof image === "string") {
const response = await fetch(image);
const blob = await response.blob();
useCropModalStore.setState({
isCropModalOpen: true,
imageBlob: blob,
originalImageUrl,
}, false, "openCropModal");
return;
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({
isCropModalOpen: true,
imageBlob: image,
}, false, "openCropModal");
imageBlob,
originalImageUrl: originalImageUrl ?? null,
}, false, {
type: "setCropModal",
imageBlob,
originalImageUrl,
});
};
export const closeCropModal = () => useCropModalStore.setState(
@ -50,14 +48,22 @@ export const closeCropModal = () => useCropModalStore.setState(
"closeCropModal"
);
export const setCropModalImageBlob = (imageBlob: Blob | null) => useCropModalStore.setState(
{ imageBlob },
false,
"setCropModalImageUrl"
);
export const setCropModalImageBlob = async (image: Blob | string | null) => {
if (typeof image === "string") {
const response = await fetch(image);
image = await response.blob();
}
export const setCropModalOriginalImageUrl = (originalImageUrl: string | null) => useCropModalStore.setState(
{ originalImageUrl },
useCropModalStore.setState({
imageBlob: image,
}, false, {
type: "setCropModalImageBlob",
image,
});
};
export const setCropModalOriginalImageUrl = (originalImageUrl: string | null | undefined) => useCropModalStore.setState(
{ originalImageUrl: originalImageUrl ?? null },
false,
"setCropModalOriginalImageUrl"
);

@ -1,29 +0,0 @@
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
type ImageUploadModalStore = {
isOpen: boolean;
};
const initialState: ImageUploadModalStore = {
isOpen: false,
};
export const useImageUploadModalStore = create<ImageUploadModalStore>()(
persist(
devtools(
() => initialState,
{
name: "ImageUploadModalStore",
}
),
{
name: "ImageUploadModalStore",
}
)
);
export const openImageUploadModal = () => useImageUploadModalStore.setState({ isOpen: true });
export const closeImageUploadModal = () => useImageUploadModalStore.setState({ isOpen: false });

@ -152,7 +152,6 @@ export const setVariantImageUrl = (
if (!("variants" in question.content)) return;
const variant = question.content.variants[variantIndex];
if (!("originalImageUrl" in variant)) return;
if (variant.extendedText === url) return;
@ -177,7 +176,6 @@ export const setVariantOriginalImageUrl = (
if (!("variants" in question.content)) return;
const variant = question.content.variants[variantIndex];
if (!("originalImageUrl" in variant)) return;
if (variant.originalImageUrl === url) return;

@ -12,11 +12,11 @@ import {
useMediaQuery,
useTheme,
} from "@mui/material";
import { closeCropModal, setCropModalImageBlob, useCropModalStore } from "@root/cropModal";
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";
import { setCropModalImageBlob, useCropModalStore } from "@root/cropModal";
const styleSlider: SxProps<Theme> = {
@ -44,16 +44,17 @@ const styleSlider: SxProps<Theme> = {
};
interface Props {
isOpen: boolean;
onClose: () => void;
onSaveImageClick?: (imageBlob: Blob) => void;
}
export const CropModal: FC<Props> = ({ onSaveImageClick }) => {
export const CropModal: FC<Props> = ({isOpen, onSaveImageClick, onClose }) => {
const theme = useTheme();
const isCropModalOpen = useCropModalStore(state => state.isCropModalOpen);
const imageBlob = useCropModalStore(state => state.imageBlob);
const originalImageUrl = useCropModalStore(state => state.originalImageUrl);
const [crop, setCrop] = useState<Crop>();
const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
const imageBlob = useCropModalStore(state => state.imageBlob);
const originalImageUrl = useCropModalStore(state => state.originalImageUrl);
const [darken, setDarken] = useState(0);
const [rotate, setRotate] = useState(0);
const [width, setWidth] = useState<number>(0);
@ -89,7 +90,7 @@ export const CropModal: FC<Props> = ({ onSaveImageClick }) => {
if (imageBlob) onSaveImageClick?.(imageBlob);
setCrop(undefined);
setCompletedCrop(undefined);
closeCropModal();
onClose();
}
async function handleLoadOriginalImage() {
@ -125,8 +126,8 @@ export const CropModal: FC<Props> = ({ onSaveImageClick }) => {
return (
<Modal
open={isCropModalOpen}
onClose={closeCropModal}
open={ isOpen}
onClose={onClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
@ -295,4 +296,4 @@ export const CropModal: FC<Props> = ({ onSaveImageClick }) => {
</Box>
</Modal>
);
};
};

@ -1,13 +1,11 @@
import { Box, Button } from "@mui/material";
import { FC } from "react";
import { CropModal } from "./CropModal";
const ImageCrop: FC = () => {
return (
<Box>
<Button onClick={undefined}>Открыть модалку</Button>
<CropModal />
</Box>
);
};

@ -8,11 +8,7 @@ export class RequestQueue<T = unknown> {
enqueue(action: () => Promise<T>) {
return new Promise((resolve, reject) => {
if (this.items.length === 2) {
this.items[1] = { action, resolve, reject };
} else {
this.items.push({ action, resolve, reject });
}
this.items.push({ action, resolve, reject });
this.dequeue();
});
}

@ -0,0 +1,13 @@
import { useState, useCallback } from "react";
export function useDisclosure(initialState = false) {
const [opened, setOpened] = useState(initialState);
return [
opened,
useCallback(() => setOpened(true), []),
useCallback(() => setOpened(false), []),
useCallback(() => setOpened(prev => !prev), []),
] as const;
}