fix: preview block

This commit is contained in:
IlyaDoronin 2023-10-17 16:24:37 +03:00
parent afdf281631
commit 8c125e4d21
2 changed files with 245 additions and 213 deletions

@ -1,130 +1,132 @@
import { PointsIcon } from "@icons/questionsPage/PointsIcon";
import { Box, IconButton } from "@mui/material"; import { Box, IconButton } from "@mui/material";
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview"; import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
import { useLayoutEffect, useRef } from "react"; import { useLayoutEffect, useRef } from "react";
import { Rnd } from "react-rnd"; import { Rnd } from "react-rnd";
import QuizPreviewLayout from "./QuizPreviewLayout"; import QuizPreviewLayout from "./QuizPreviewLayout";
import ResizeIcon from "./ResizeIcon"; import ResizeIcon from "./ResizeIcon";
import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityIcon from "@mui/icons-material/Visibility";
const DRAG_PARENT_MARGIN = 0;
const DRAG_PARENT_MARGIN = 25; const NAVBAR_HEIGHT = 0;
const NAVBAR_HEIGHT = 81; const DRAG_PARENT_BOTTOM_MARGIN = 0;
const DRAG_PARENT_BOTTOM_MARGIN = 65;
interface RndPositionAndSize { interface RndPositionAndSize {
x: number; x: number;
y: number; y: number;
width: string; width: string;
height: string; height: string;
} }
export default function QuizPreview() { export default function QuizPreview() {
const isPreviewShown = useQuizPreviewStore(state => state.isPreviewShown); const isPreviewShown = useQuizPreviewStore((state) => state.isPreviewShown);
const rndParentRef = useRef<HTMLDivElement>(null); const rndParentRef = useRef<HTMLDivElement>(null);
const rndRef = useRef<Rnd | null>(null); const rndRef = useRef<Rnd | null>(null);
const rndPositionAndSizeRef = useRef<RndPositionAndSize>({ x: 0, y: 0, width: "340", height: "480" }); const rndPositionAndSizeRef = useRef<RndPositionAndSize>({
const isFirstShowRef = useRef<boolean>(true); x: 0,
y: 0,
width: "340",
height: "480",
});
const isFirstShowRef = useRef<boolean>(true);
useLayoutEffect(function stickPreviewToBottomRight() { useLayoutEffect(
const rnd = rndRef.current; function stickPreviewToBottomRight() {
const rndSelfElement = rnd?.getSelfElement(); const rnd = rndRef.current;
if (!rnd || !rndSelfElement || !rndParentRef.current || !isFirstShowRef.current) return; const rndSelfElement = rnd?.getSelfElement();
if (
!rnd ||
!rndSelfElement ||
!rndParentRef.current ||
!isFirstShowRef.current
)
return;
const rndParentRect = rndParentRef.current.getBoundingClientRect(); const rndParentRect = rndParentRef.current.getBoundingClientRect();
const rndRect = rndSelfElement.getBoundingClientRect(); const rndRect = rndSelfElement.getBoundingClientRect();
const x = rndParentRect.width - rndRect.width; const x = rndParentRect.width - rndRect.width;
const y = rndParentRect.height - rndRect.height; const y = rndParentRect.height - rndRect.height;
rnd.updatePosition({ x, y }); rnd.updatePosition({ x, y });
rndPositionAndSizeRef.current.x = x; rndPositionAndSizeRef.current.x = x;
rndPositionAndSizeRef.current.y = y; rndPositionAndSizeRef.current.y = y;
isFirstShowRef.current = false; isFirstShowRef.current = false;
}, [isPreviewShown]); },
[isPreviewShown]
);
return ( return (
<Box <Box
ref={rndParentRef} ref={rndParentRef}
sx={{ sx={{
position: "fixed", position: "fixed",
top: NAVBAR_HEIGHT + DRAG_PARENT_MARGIN, top: NAVBAR_HEIGHT + DRAG_PARENT_MARGIN,
left: DRAG_PARENT_MARGIN, left: DRAG_PARENT_MARGIN,
bottom: DRAG_PARENT_BOTTOM_MARGIN, bottom: DRAG_PARENT_BOTTOM_MARGIN,
right: DRAG_PARENT_MARGIN, right: DRAG_PARENT_MARGIN,
// backgroundColor: "rgba(0, 100, 0, 0.2)", // backgroundColor: "rgba(0, 100, 0, 0.2)",
pointerEvents: "none", pointerEvents: "none",
zIndex: 100, zIndex: 100,
}} }}
>
{isPreviewShown && (
<Rnd
minHeight={20}
minWidth={20}
bounds="parent"
ref={rndRef}
dragHandleClassName="quiz-preview-draghandle"
default={{
x: rndPositionAndSizeRef.current.x,
y: rndPositionAndSizeRef.current.y,
width: rndPositionAndSizeRef.current.width,
height: rndPositionAndSizeRef.current.height,
}}
onResizeStop={(e, direction, ref, delta, position) => {
rndPositionAndSizeRef.current.x = position.x;
rndPositionAndSizeRef.current.y = position.y;
rndPositionAndSizeRef.current.width = ref.style.width;
rndPositionAndSizeRef.current.height = ref.style.height;
}}
onDragStop={(e, d) => {
rndPositionAndSizeRef.current.x = d.x;
rndPositionAndSizeRef.current.y = d.y;
}}
onDragStart={(e, d) => {
e.preventDefault();
}}
enableResizing={{
topLeft: isPreviewShown,
}}
resizeHandleComponent={{
topLeft: <ResizeIcon />,
}}
resizeHandleStyles={{
topLeft: {
top: "-1px",
left: "-1px",
},
}}
style={{
overflow: "hidden",
pointerEvents: "auto",
}}
> >
{isPreviewShown && <QuizPreviewLayout />
<Rnd </Rnd>
minHeight={300} )}
minWidth={340} <IconButton
bounds="parent" onClick={toggleQuizPreview}
ref={rndRef} sx={{
dragHandleClassName="quiz-preview-draghandle" position: "absolute",
default={{ right: 0,
x: rndPositionAndSizeRef.current.x, bottom: -54,
y: rndPositionAndSizeRef.current.y, pointerEvents: "auto",
width: rndPositionAndSizeRef.current.width, }}
height: rndPositionAndSizeRef.current.height >
}} <VisibilityIcon sx={{ height: "30px", width: "30px" }} />
onResizeStop={(e, direction, ref, delta, position) => { </IconButton>
rndPositionAndSizeRef.current.x = position.x; </Box>
rndPositionAndSizeRef.current.y = position.y; );
rndPositionAndSizeRef.current.width = ref.style.width;
rndPositionAndSizeRef.current.height = ref.style.height;
}}
onDragStop={(e, d) => {
rndPositionAndSizeRef.current.x = d.x;
rndPositionAndSizeRef.current.y = d.y;
}}
onDragStart={(e, d) => {
e.preventDefault();
}}
enableResizing={{
topLeft: isPreviewShown,
}}
resizeHandleComponent={{
topLeft: <ResizeIcon />
}}
resizeHandleStyles={{
topLeft: {
top: "-1px",
left: "-1px",
}
}}
style={{
pointerEvents: "auto",
}}
>
<QuizPreviewLayout />
<IconButton
className="quiz-preview-draghandle"
sx={{
position: "absolute",
bottom: -54,
right: 46,
cursor: "move",
}}
>
<PointsIcon style={{ color: "#4D4D4D", fontSize: "30px" }} />
</IconButton>
</Rnd>
}
<IconButton
onClick={toggleQuizPreview}
sx={{
position: "absolute",
right: 0,
bottom: -54,
pointerEvents: "auto",
}}
>
<VisibilityIcon sx={{ height: "30px", width: "30px" }} />
</IconButton>
</Box>
);
} }

@ -1,6 +1,10 @@
import { Box, Button, LinearProgress, Paper, Typography } from "@mui/material"; import { Box, Button, LinearProgress, Paper, Typography } from "@mui/material";
import { questionStore } from "@root/questions"; import { questionStore } from "@root/questions";
import { decrementCurrentQuestionIndex, incrementCurrentQuestionIndex, useQuizPreviewStore } from "@root/quizPreview"; import {
decrementCurrentQuestionIndex,
incrementCurrentQuestionIndex,
useQuizPreviewStore,
} from "@root/quizPreview";
import { DefiniteQuestionType } from "model/questionTypes/shared"; import { DefiniteQuestionType } from "model/questionTypes/shared";
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@ -17,117 +21,143 @@ import Text from "./QuizPreviewQuestionTypes/Text";
import Variant from "./QuizPreviewQuestionTypes/Variant"; import Variant from "./QuizPreviewQuestionTypes/Variant";
import Varimg from "./QuizPreviewQuestionTypes/Varimg"; import Varimg from "./QuizPreviewQuestionTypes/Varimg";
const QuestionPreviewComponentByType: Record<DefiniteQuestionType, FC<any>> = { const QuestionPreviewComponentByType: Record<DefiniteQuestionType, FC<any>> = {
variant: Variant, variant: Variant,
images: Images, images: Images,
varimg: Varimg, varimg: Varimg,
emoji: Emoji, emoji: Emoji,
text: Text, text: Text,
select: Select, select: Select,
date: Date, date: Date,
number: Number, number: Number,
file: File, file: File,
page: Page, page: Page,
rating: Rating, rating: Rating,
}; };
export default function QuizPreviewLayout() { export default function QuizPreviewLayout() {
const quizId = useParams().quizId ?? 0; const quizId = useParams().quizId ?? 0;
const listQuestions = questionStore(state => state.listQuestions); const listQuestions = questionStore((state) => state.listQuestions);
const currentQuizStep = useQuizPreviewStore(state => state.currentQuestionIndex); const currentQuizStep = useQuizPreviewStore(
(state) => state.currentQuestionIndex
);
const quizQuestions = listQuestions[quizId] ?? []; const quizQuestions = listQuestions[quizId] ?? [];
const nonDeletedQuizQuestions = quizQuestions.filter(question => !question.deleted); const nonDeletedQuizQuestions = quizQuestions.filter(
const maxCurrentQuizStep = nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0; (question) => !question.deleted
const currentProgress = Math.floor((currentQuizStep / maxCurrentQuizStep) * 100); );
const maxCurrentQuizStep =
nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
const currentProgress = Math.floor(
(currentQuizStep / maxCurrentQuizStep) * 100
);
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep]; const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
const QuestionComponent = currentQuestion const QuestionComponent = currentQuestion
? QuestionPreviewComponentByType[currentQuestion.type as DefiniteQuestionType] ? QuestionPreviewComponentByType[
: null; currentQuestion.type as DefiniteQuestionType
]
: null;
const questionElement = QuestionComponent const questionElement = QuestionComponent ? (
? <QuestionComponent key={currentQuestion.id} question={currentQuestion} /> <QuestionComponent key={currentQuestion.id} question={currentQuestion} />
: null; ) : null;
useEffect(function resetCurrentQuizStep() { useEffect(
if (currentQuizStep > maxCurrentQuizStep) { function resetCurrentQuizStep() {
decrementCurrentQuestionIndex(); if (currentQuizStep > maxCurrentQuizStep) {
} decrementCurrentQuestionIndex();
}, [currentQuizStep, maxCurrentQuizStep]); }
},
[currentQuizStep, maxCurrentQuizStep]
);
return ( return (
<Paper sx={{ <Paper
height: "100%", className="quiz-preview-draghandle"
sx={{
height: "100%",
display: "flex",
flexDirection: "column",
flexGrow: 1,
borderRadius: "12px",
pointerEvents: "auto",
}}
>
<Box
sx={{
p: "16px",
whiteSpace: "break-spaces",
overflowY: "auto",
flexGrow: 1,
"&::-webkit-scrollbar": { width: 0 },
}}
>
{questionElement}
</Box>
<Box
sx={{
mt: "auto",
p: "16px",
display: "flex",
borderTop: "1px solid #E3E3E3",
alignItems: "center",
}}
>
<Box
sx={{
flexGrow: 1,
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
flexGrow: 1, gap: 1,
borderRadius: "12px", }}
pointerEvents: "auto", >
}}> <Typography>
<Box sx={{ {nonDeletedQuizQuestions.length > 0
p: "16px", ? `Вопрос ${currentQuizStep + 1} из ${
whiteSpace: "break-spaces", nonDeletedQuizQuestions.length
overflowY: "auto", }`
flexGrow: 1, : "Нет вопросов"}
}}> </Typography>
{questionElement} {nonDeletedQuizQuestions.length > 0 && (
</Box> <LinearProgress
<Box sx={{ variant="determinate"
mt: "auto", value={currentProgress}
p: "16px", sx={{
display: "flex", "&.MuiLinearProgress-colorPrimary": {
borderTop: "1px solid #E3E3E3", backgroundColor: "fadePurple.main",
alignItems: "center", },
}}> "& .MuiLinearProgress-barColorPrimary": {
<Box sx={{ backgroundColor: "brightPurple.main",
flexGrow: 1, },
display: "flex", }}
flexDirection: "column", />
gap: 1, )}
}}> </Box>
<Typography> <Box
{nonDeletedQuizQuestions.length > 0 sx={{
? `Вопрос ${currentQuizStep + 1} из ${nonDeletedQuizQuestions.length}` ml: 2,
: "Нет вопросов" display: "flex",
} gap: 1,
</Typography> }}
{nonDeletedQuizQuestions.length > 0 && >
<LinearProgress <Button
variant="determinate" variant="outlined"
value={currentProgress} onClick={decrementCurrentQuestionIndex}
sx={{ disabled={currentQuizStep === 0}
"&.MuiLinearProgress-colorPrimary": { sx={{ px: 1, minWidth: 0 }}
backgroundColor: "fadePurple.main", >
}, <ArrowLeft />
"& .MuiLinearProgress-barColorPrimary": { </Button>
backgroundColor: "brightPurple.main", <Button
}, variant="contained"
}} onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
/> disabled={currentQuizStep >= maxCurrentQuizStep}
} >
</Box> Далее
<Box sx={{ </Button>
ml: 2, </Box>
display: "flex", </Box>
gap: 1, </Paper>
}}> );
<Button
variant="outlined"
onClick={decrementCurrentQuestionIndex}
disabled={currentQuizStep === 0}
sx={{ px: 1, minWidth: 0 }}
>
<ArrowLeft />
</Button>
<Button
variant="contained"
onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
disabled={currentQuizStep >= maxCurrentQuizStep}
>Далее</Button>
</Box>
</Box>
</Paper>
);
} }