frontPanel/src/ui_kit/QuizPreview/QuizPreviewLayout.tsx

291 lines
9.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
Box,
Button,
LinearProgress,
Paper,
Typography,
FormControl,
Select as MuiSelect,
MenuItem,
useTheme,
} from "@mui/material";
import { useQuestionsStore } from "@root/questions/store";
import {
decrementCurrentQuestionIndex,
incrementCurrentQuestionIndex,
useQuizPreviewStore,
setCurrentQuestionIndex,
} from "@root/quizPreview";
import {
AnyTypedQuizQuestion,
UntypedQuizQuestion,
} from "model/questionTypes/shared";
import { useEffect, useRef, useState } from "react";
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
import Date from "./QuizPreviewQuestionTypes/Date";
import Emoji from "./QuizPreviewQuestionTypes/Emoji";
import File from "./QuizPreviewQuestionTypes/File";
import Images from "./QuizPreviewQuestionTypes/Images";
import Number from "./QuizPreviewQuestionTypes/Number";
import Page from "./QuizPreviewQuestionTypes/Page";
import Rating from "./QuizPreviewQuestionTypes/Rating";
import Select, { ArrowDownTheme } from "./QuizPreviewQuestionTypes/Select";
import Text from "./QuizPreviewQuestionTypes/Text";
import Variant from "./QuizPreviewQuestionTypes/Variant";
import Varimg from "./QuizPreviewQuestionTypes/Varimg";
import { notReachable } from "../../utils/notReachable";
import ArrowDownIcon from "@icons/ArrowDownIcon";
export default function QuizPreviewLayout() {
const theme = useTheme();
const questions = useQuestionsStore((state) => state.questions);
const currentQuizStep = useQuizPreviewStore(
(state) => state.currentQuestionIndex,
);
const [widthPreview, setWidthPreview] = useState(null);
const nonDeletedQuizQuestions = questions.filter(
(question) => !question.deleted && question.type !== "result",
);
const maxCurrentQuizStep =
nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
const currentProgress = Math.floor(
(currentQuizStep / maxCurrentQuizStep) * 100,
);
const PreviewWin = useRef(0);
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
useEffect(
function resetCurrentQuizStep() {
if (currentQuizStep > maxCurrentQuizStep) {
decrementCurrentQuestionIndex();
}
},
[currentQuizStep, maxCurrentQuizStep],
);
const observer = useRef(
new ResizeObserver((entries) => {
const { width } = entries[0].contentRect;
setWidthPreview(width);
}),
);
useEffect(() => {
observer.current.observe(PreviewWin.current);
}, [PreviewWin, observer]);
return (
<Paper
ref={PreviewWin}
className="quiz-preview-draghandle"
data-cy="quiz-preview-layout"
sx={{
height: "100%",
display: "flex",
flexDirection: "column",
flexGrow: 1,
borderRadius: "12px",
pointerEvents: "auto",
backgroundColor: theme.palette.background.default,
}}
>
<Box
sx={{
p: "40px 20px 20px",
whiteSpace: "break-spaces",
overflowY: "auto",
flexGrow: 1,
"&::-webkit-scrollbar": { width: 0, display: "none" },
msOverflowStyle: "none",
scrollbarWidth: "none",
}}
>
<QuestionPreviewComponent
question={currentQuestion}
widthPreview={widthPreview}
/>
</Box>
<Box
sx={{
mt: "auto",
p: "16px",
borderTop: "1px solid #E3E3E3",
}}
>
<Box sx={{ marginBottom: "10px" }}>
<FormControl
fullWidth
size="small"
sx={{ width: "100%", minWidth: "200px", height: "48px" }}
>
<MuiSelect
id="category-select"
variant="outlined"
value={currentQuizStep}
placeholder="Заголовок вопроса"
onChange={({ target }) =>
setCurrentQuestionIndex(window.Number(target.value))
}
sx={{
height: "48px",
borderRadius: "8px",
"& .MuiOutlinedInput-notchedOutline": {
border: `1px solid ${theme.palette.primary.main} !important`,
},
"& .MuiSelect-icon": {
color: theme.palette.primary.main,
},
}}
MenuProps={{
PaperProps: {
sx: {
mt: "8px",
p: "4px",
borderRadius: "8px",
border: "1px solid #EEE4FC",
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
backgroundColor: theme.palette.background.default,
},
},
MenuListProps: {
sx: {
py: 0,
display: "flex",
flexDirection: "column",
gap: "8px",
"& .Mui-selected": {
backgroundColor: theme.palette.background.default,
color: theme.palette.primary.main,
},
},
},
}}
inputProps={{
sx: {
color: theme.palette.primary.main,
display: "flex",
alignItems: "center",
px: "9px",
gap: "20px",
},
}}
IconComponent={ArrowDownTheme}
>
{Object.values(questions.filter((q) => q.type !== "result")).map(
({ id, title }, index) => (
<MenuItem
key={id}
value={index}
sx={{
display: "flex",
alignItems: "center",
gap: "20px",
p: "4px",
borderRadius: "5px",
color: "#9A9AAF",
}}
>
{`${index + 1}. ${title}`}
</MenuItem>
),
)}
</MuiSelect>
</FormControl>
</Box>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Box
sx={{
flexGrow: 1,
display: "flex",
flexDirection: "column",
gap: 1,
}}
>
<Typography>
{nonDeletedQuizQuestions.length > 0
? `Вопрос ${currentQuizStep + 1} из ${
nonDeletedQuizQuestions.length
}`
: "Нет вопросов"}
</Typography>
{nonDeletedQuizQuestions.length > 0 && (
<LinearProgress
variant="determinate"
value={currentProgress}
sx={{
"&.MuiLinearProgress-colorPrimary": {
backgroundColor: "fadePurple.main",
},
"& .MuiLinearProgress-barColorPrimary": {
backgroundColor: "brightPurple.main",
},
}}
/>
)}
</Box>
<Box
sx={{
ml: 2,
display: "flex",
gap: 1,
}}
>
<Button
variant="outlined"
onClick={decrementCurrentQuestionIndex}
disabled={currentQuizStep === 0}
sx={{ px: 1, minWidth: 0 }}
>
<ArrowLeft color={theme.palette.primary.main} />
</Button>
<Button
variant="contained"
onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
disabled={currentQuizStep >= maxCurrentQuizStep}
>
Далее
</Button>
</Box>
</Box>
</Box>
</Paper>
);
}
function QuestionPreviewComponent({
question,
widthPreview,
}: {
question: AnyTypedQuizQuestion | UntypedQuizQuestion | undefined;
widthPreview?: number;
}) {
if (!question || question.type === null) return null;
switch (question.type) {
case "variant":
return <Variant question={question} widthPreview={widthPreview} />;
case "images":
return <Images question={question} widthPreview={widthPreview} />;
case "varimg":
return <Varimg question={question} widthPreview={widthPreview} />;
case "emoji":
return <Emoji question={question} widthPreview={widthPreview} />;
case "text":
return <Text question={question} widthPreview={widthPreview} />;
case "select":
return <Select question={question} widthPreview={widthPreview} />;
case "date":
return <Date question={question} widthPreview={widthPreview} />;
case "number":
return <Number question={question} widthPreview={widthPreview} />;
case "file":
return <File question={question} widthPreview={widthPreview} />;
case "page":
return <Page question={question} widthPreview={widthPreview} />;
case "rating":
return <Rating question={question} widthPreview={widthPreview} />;
default:
notReachable(question);
}
}