кликабельно varimg
This commit is contained in:
parent
bbe0be2798
commit
311cdedce6
@ -0,0 +1,197 @@
|
||||
import { Box, ButtonBase, IconButton, Typography, useTheme } from "@mui/material";
|
||||
import { useState, useRef } from "react";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQuizStore } from "@/stores/useQuizStore";
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { Skeleton } from "@mui/material";
|
||||
import UploadIcon from "@/assets/icons/UploadIcon";
|
||||
import { sendFile } from "@/api/quizRelase";
|
||||
import { ACCEPT_SEND_FILE_TYPES_MAP, MAX_FILE_SIZE } from "../../tools/fileUpload";
|
||||
|
||||
// Пропсы компонента
|
||||
export type OwnVarimgImageProps = {
|
||||
imageUrl?: string;
|
||||
questionId: string;
|
||||
variantId: string;
|
||||
onValidationError: (error: "size" | "type") => void;
|
||||
};
|
||||
|
||||
export const OwnVarimgImage = ({ imageUrl, questionId, variantId, onValidationError }: OwnVarimgImageProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { quizId, preview } = useQuizStore();
|
||||
const { ownVariants, updateOwnVariant, updateAnswer } = useQuizViewStore((state) => state);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// Получаем ownVariant для этого варианта
|
||||
const ownVariantData = ownVariants.find((v) => v.id === variantId);
|
||||
|
||||
// Загрузка файла
|
||||
const uploadImage = async (file: File) => {
|
||||
if (isUploading) return;
|
||||
if (!file) return;
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
onValidationError("size");
|
||||
return;
|
||||
}
|
||||
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP.picture.some((fileType) =>
|
||||
file.name.toLowerCase().endsWith(fileType)
|
||||
);
|
||||
if (!isFileTypeAccepted) {
|
||||
onValidationError("type");
|
||||
return;
|
||||
}
|
||||
setIsUploading(true);
|
||||
try {
|
||||
const data = await sendFile({
|
||||
questionId,
|
||||
body: { file, name: file.name, preview },
|
||||
qid: quizId,
|
||||
});
|
||||
const fileId = data?.data.fileIDMap[questionId];
|
||||
const localImageUrl = URL.createObjectURL(file);
|
||||
// @ts-ignore
|
||||
updateOwnVariant(variantId, "", "", fileId, localImageUrl, file);
|
||||
updateAnswer(questionId, variantId, 0);
|
||||
setSelectedFile(file);
|
||||
} catch (error) {
|
||||
console.error("Error uploading image:", error);
|
||||
enqueueSnackbar(t("The answer was not counted"));
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Обработчик выбора файла
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
uploadImage(file);
|
||||
}
|
||||
};
|
||||
|
||||
// Открытие диалога выбора файла
|
||||
const handleClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (fileInputRef.current) fileInputRef.current.value = "";
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
// Удаление изображения
|
||||
const handleRemoveImage = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setSelectedFile(null);
|
||||
updateOwnVariant(variantId, "", "", "", "");
|
||||
};
|
||||
|
||||
// Определяем, что показывать
|
||||
let imageToDisplay: string | null = null;
|
||||
if (selectedFile) {
|
||||
imageToDisplay = URL.createObjectURL(selectedFile);
|
||||
} else if (ownVariantData?.variant.localImageUrl) {
|
||||
// @ts-ignore
|
||||
if (ownVariantData.variant.file) {
|
||||
// @ts-ignore
|
||||
imageToDisplay = URL.createObjectURL(ownVariantData.variant.file);
|
||||
} else {
|
||||
imageToDisplay = ownVariantData.variant.localImageUrl;
|
||||
}
|
||||
} else if (imageUrl) {
|
||||
imageToDisplay = imageUrl;
|
||||
}
|
||||
|
||||
if (isUploading) {
|
||||
return (
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ width: "100%", height: "100%", borderRadius: "12px" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonBase
|
||||
component="div"
|
||||
onClick={handleClick}
|
||||
disabled={isUploading}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "12px",
|
||||
transition: "border-color 0.3s, background-color 0.3s",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
opacity: isUploading ? 0.7 : 1,
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(0, 0, 0, 0.1)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
id={`own-image-input-${variantId}`}
|
||||
onChange={handleFileChange}
|
||||
accept={ACCEPT_SEND_FILE_TYPES_MAP.picture.join(",")}
|
||||
hidden
|
||||
/>
|
||||
{imageToDisplay ? (
|
||||
<>
|
||||
<Box sx={{ width: "100%", height: "100%", position: "relative" }}>
|
||||
<img
|
||||
src={imageToDisplay}
|
||||
alt="Preview"
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
/>
|
||||
</Box>
|
||||
<IconButton
|
||||
onClick={handleRemoveImage}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 8,
|
||||
right: 8,
|
||||
zIndex: 1,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
color: "white",
|
||||
height: "25px",
|
||||
width: "25px",
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
opacity: 0.5,
|
||||
}}
|
||||
>
|
||||
<UploadIcon />
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{ p: 2, textAlign: "center" }}
|
||||
>
|
||||
добавьте свою картинку
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</ButtonBase>
|
||||
);
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Box, RadioGroup, Typography, useTheme } from "@mui/material";
|
||||
import { Box, ButtonBase, RadioGroup, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { VarimgVariant } from "./VarimgVariant";
|
||||
|
||||
@ -30,6 +30,14 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
|
||||
const ownVariant = ownVariants.find((variant) => variant.id === currentQuestion.id);
|
||||
const variant = currentQuestion.content.variants.find(({ id }) => answer === id);
|
||||
const ownVariantInQuestion = useMemo(
|
||||
() => currentQuestion.content.variants.find((v) => v.isOwn),
|
||||
[currentQuestion.content.variants]
|
||||
);
|
||||
const ownVariantData = ownVariants.find((v) => v.id === answer);
|
||||
const ownImageUrl = ownVariantData?.variant.file
|
||||
? URL.createObjectURL(ownVariantData.variant.file)
|
||||
: ownVariantData?.variant.localImageUrl;
|
||||
|
||||
useEffect(() => {
|
||||
if (!ownVariant) {
|
||||
@ -58,6 +66,19 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||
return currentQuestion.content.back;
|
||||
}
|
||||
}, [variant]);
|
||||
|
||||
const handlePlaceholderClick = () => {
|
||||
if (ownVariantInQuestion) {
|
||||
document.getElementById(`own-image-input-${ownVariantInQuestion.id}`)?.click();
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreviewAreaClick = () => {
|
||||
if (ownVariantInQuestion) {
|
||||
document.getElementById(`own-image-input-${ownVariantInQuestion.id}`)?.click();
|
||||
}
|
||||
};
|
||||
|
||||
if (moment.isMoment(answer)) throw new Error("Answer is Moment in Variant question");
|
||||
|
||||
return (
|
||||
@ -121,7 +142,9 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||
))}
|
||||
</Box>
|
||||
</RadioGroup>
|
||||
<Box
|
||||
<ButtonBase
|
||||
onClick={handlePreviewAreaClick}
|
||||
disabled={!ownVariantInQuestion}
|
||||
sx={{
|
||||
maxWidth: "450px",
|
||||
width: "100%",
|
||||
@ -135,34 +158,44 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||
backgroundColor: "#9A9AAF30",
|
||||
color: theme.palette.text.primary,
|
||||
textAlign: "center",
|
||||
"&:hover": {
|
||||
backgroundColor: ownVariantInQuestion ? "rgba(0,0,0,0.04)" : "transparent",
|
||||
},
|
||||
}}
|
||||
onClick={(event) => event.preventDefault()}
|
||||
>
|
||||
{answer ? (
|
||||
choiceImgUrlAnswer ? (
|
||||
{(() => {
|
||||
if (answer) {
|
||||
const imageUrl = variant?.isOwn && ownImageUrl ? ownImageUrl : choiceImgUrlAnswer;
|
||||
if (imageUrl) {
|
||||
return (
|
||||
<img
|
||||
key={choiceImgUrlAnswer}
|
||||
src={choiceImgUrlAnswer}
|
||||
key={imageUrl}
|
||||
src={imageUrl}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<BlankImage />
|
||||
)
|
||||
) : choiceImgUrlQuestion !== " " && choiceImgUrlQuestion !== null && choiceImgUrlQuestion.length > 0 ? (
|
||||
);
|
||||
}
|
||||
return <BlankImage />;
|
||||
}
|
||||
|
||||
if (choiceImgUrlQuestion && choiceImgUrlQuestion.trim().length > 0) {
|
||||
return (
|
||||
<img
|
||||
src={choiceImgUrlQuestion}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
alt=""
|
||||
/>
|
||||
) : currentQuestion.content.replText !== " " && currentQuestion.content.replText.length > 0 ? (
|
||||
currentQuestion.content.replText
|
||||
) : variant?.extendedText || isMobile ? (
|
||||
t("Select an answer option below")
|
||||
) : (
|
||||
t("Select an answer option on the left")
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentQuestion.content.replText && currentQuestion.content.replText.trim().length > 0) {
|
||||
return currentQuestion.content.replText;
|
||||
}
|
||||
|
||||
return isMobile ? t("Select an answer option below") : t("Select an answer option on the left");
|
||||
})()}
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user