frontPanel/src/ui_kit/QuizPreview/QuizPreviewLayout.tsx

291 lines
9.1 KiB
TypeScript
Raw Normal View History

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