feat: ChooseAnswerModal

This commit is contained in:
IlyaDoronin 2023-09-20 15:42:14 +03:00
parent 10f9a5adb1
commit add32f0b3f
4 changed files with 283 additions and 135 deletions

@ -0,0 +1,127 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import {
Box,
Typography,
Popper,
Grow,
Paper,
MenuList,
MenuItem,
ClickAwayListener,
Modal,
Button,
} from "@mui/material";
import {
questionStore,
updateQuestionsList,
DEFAULT_QUESTION,
} from "@root/questions";
import { BUTTON_TYPE_QUESTIONS } from "../TypeQuestions";
import type { RefObject } from "react";
type ChooseAnswerModalProps = {
open: boolean;
onClose: () => void;
anchorRef: RefObject<HTMLDivElement>;
totalIndex: number;
};
export const ChooseAnswerModal = ({
open,
onClose,
anchorRef,
totalIndex,
}: ChooseAnswerModalProps) => {
const [openModal, setOpenModal] = useState<boolean>(false);
const [selectedValue, setSelectedValue] = useState<string>("");
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
return (
<>
<Popper
placement="right-start"
open={open}
anchorEl={anchorRef.current}
transition
>
{({ TransitionProps }) => (
<Grow {...TransitionProps}>
<Paper>
<ClickAwayListener onClickAway={onClose}>
<MenuList autoFocusItem={open}>
{BUTTON_TYPE_QUESTIONS.map(({ icon, title, value }) => (
<MenuItem
key={value}
onClick={() => {
onClose();
setOpenModal(true);
setSelectedValue(value);
}}
sx={{ display: "flex", gap: "10px" }}
>
<Box>{icon}</Box>
<Typography>{title}</Typography>
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
<Modal open={openModal} onClose={() => setOpenModal(false)}>
<Box
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
padding: "30px",
borderRadius: "10px",
background: "#FFFFFF",
}}
>
<Typography variant="h6">
Все настройки, кроме заголовка вопроса будут сброшены
</Typography>
<Box
sx={{
marginTop: "30px",
display: "flex",
justifyContent: "center",
gap: "15px",
}}
>
<Button
variant="contained"
sx={{ minWidth: "150px" }}
onClick={() => setOpenModal(false)}
>
Отмена
</Button>
<Button
variant="contained"
sx={{ minWidth: "150px" }}
onClick={() => {
setOpenModal(false);
const question = listQuestions[quizId][totalIndex];
updateQuestionsList(quizId, totalIndex, {
...DEFAULT_QUESTION,
expanded: question.expanded,
type: selectedValue,
});
}}
>
Подтвердить
</Button>
</Box>
</Box>
</Modal>
</>
);
};

@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, useRef } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { import {
Box, Box,
@ -13,6 +13,8 @@ import {
useTheme, useTheme,
} from "@mui/material"; } from "@mui/material";
import { useDebouncedCallback } from "use-debounce"; import { useDebouncedCallback } from "use-debounce";
import { ChooseAnswerModal } from "./ChooseAnswerModal";
import TypeQuestions from "../TypeQuestions"; import TypeQuestions from "../TypeQuestions";
import SwitchQuestionsPage from "../SwitchQuestionsPage"; import SwitchQuestionsPage from "../SwitchQuestionsPage";
@ -142,6 +144,7 @@ export default function QuestionsPageCard({
isDragging, isDragging,
}: Props) { }: Props) {
const [plusVisible, setPlusVisible] = useState<boolean>(false); const [plusVisible, setPlusVisible] = useState<boolean>(false);
const [open, setOpen] = useState<boolean>(false);
const quizId = Number(useParams().quizId); const quizId = Number(useParams().quizId);
const theme = useTheme(); const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isTablet = useMediaQuery(theme.breakpoints.down(1000));
@ -149,6 +152,7 @@ export default function QuestionsPageCard({
const { listQuestions } = questionStore(); const { listQuestions } = questionStore();
const { type: switchState, expanded: isExpanded } = const { type: switchState, expanded: isExpanded } =
listQuestions[quizId][totalIndex]; listQuestions[quizId][totalIndex];
const anchorRef = useRef(null);
const debounced = useDebouncedCallback((title) => { const debounced = useDebouncedCallback((title) => {
updateQuestionsList(quizId, totalIndex, { title }); updateQuestionsList(quizId, totalIndex, { title });
}, 1000); }, 1000);
@ -188,9 +192,22 @@ export default function QuestionsPageCard({
onChange={({ target }) => debounced(target.value)} onChange={({ target }) => debounced(target.value)}
InputProps={{ InputProps={{
startAdornment: ( startAdornment: (
<InputAdornment position="start"> <Box>
<InputAdornment
ref={anchorRef}
position="start"
sx={{ cursor: "pointer" }}
onClick={() => setOpen((isOpened) => !isOpened)}
>
{IconAndrom(isExpanded, switchState)} {IconAndrom(isExpanded, switchState)}
</InputAdornment> </InputAdornment>
<ChooseAnswerModal
open={open}
onClose={() => setOpen(false)}
anchorRef={anchorRef}
totalIndex={totalIndex}
/>
</Box>
), ),
}} }}
sx={{ sx={{

@ -10,7 +10,7 @@ import Slider from "../../assets/icons/questionsPage/slider";
import Download from "../../assets/icons/questionsPage/download"; import Download from "../../assets/icons/questionsPage/download";
import Page from "../../assets/icons/questionsPage/page"; import Page from "../../assets/icons/questionsPage/page";
import RatingIcon from "../../assets/icons/questionsPage/rating"; import RatingIcon from "../../assets/icons/questionsPage/rating";
import { Box, useTheme } from "@mui/material"; import { Box } from "@mui/material";
import React from "react"; import React from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { questionStore, updateQuestionsList } from "@root/questions"; import { questionStore, updateQuestionsList } from "@root/questions";
@ -19,72 +19,75 @@ interface Props {
totalIndex: number; totalIndex: number;
} }
export default function TypeQuestions({ totalIndex }: Props) { type ButtonTypeQuestion = {
const theme = useTheme();
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const switchState = listQuestions[quizId][totalIndex].type;
const buttonTypeQuestions: {
icon: JSX.Element; icon: JSX.Element;
title: string; title: string;
value: string; value: string;
}[] = [ };
export const BUTTON_TYPE_QUESTIONS: ButtonTypeQuestion[] = [
{ {
icon: <Answer color={theme.palette.grey2.main} />, icon: <Answer color="#9A9AAF" />,
title: "Варианты ответов", title: "Варианты ответов",
value: "variant", value: "variant",
}, },
{ {
icon: <OptionsPict color={theme.palette.grey2.main} />, icon: <OptionsPict color="#9A9AAF" />,
title: "Варианты с картинками", title: "Варианты с картинками",
value: "images", value: "images",
}, },
{ {
icon: <OptionsAndPict color={theme.palette.grey2.main} />, icon: <OptionsAndPict color="#9A9AAF" />,
title: "Варианты и картинка", title: "Варианты и картинка",
value: "varimg", value: "varimg",
}, },
{ {
icon: <Emoji color={theme.palette.grey2.main} />, icon: <Emoji color="#9A9AAF" />,
title: "Эмоджи", title: "Эмоджи",
value: "emoji", value: "emoji",
}, },
{ {
icon: <Input color={theme.palette.grey2.main} />, icon: <Input color="#9A9AAF" />,
title: "Своё поле для ввода", title: "Своё поле для ввода",
value: "text", value: "text",
}, },
{ {
icon: <DropDown color={theme.palette.grey2.main} />, icon: <DropDown color="#9A9AAF" />,
title: "Выпадающий список", title: "Выпадающий список",
value: "select", value: "select",
}, },
{ {
icon: <Date color={theme.palette.grey2.main} />, icon: <Date color="#9A9AAF" />,
title: "Дата", title: "Дата",
value: "date", value: "date",
}, },
{ {
icon: <Slider color={theme.palette.grey2.main} />, icon: <Slider color="#9A9AAF" />,
title: "Ползунок", title: "Ползунок",
value: "number", value: "number",
}, },
{ {
icon: <Download color={theme.palette.grey2.main} />, icon: <Download color="#9A9AAF" />,
title: "Загрузка файла", title: "Загрузка файла",
value: "file", value: "file",
}, },
{ {
icon: <Page color={theme.palette.grey2.main} />, icon: <Page color="#9A9AAF" />,
title: "Страница", title: "Страница",
value: "page", value: "page",
}, },
{ {
icon: <RatingIcon color={theme.palette.grey2.main} />, icon: <RatingIcon color="#9A9AAF" />,
title: "Рейтинг", title: "Рейтинг",
value: "rating", value: "rating",
}, },
]; ];
export default function TypeQuestions({ totalIndex }: Props) {
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const switchState = listQuestions[quizId][totalIndex].type;
return ( return (
<Box <Box
sx={{ sx={{
@ -94,7 +97,7 @@ export default function TypeQuestions({ totalIndex }: Props) {
padding: "8px 20px 20px", padding: "8px 20px 20px",
}} }}
> >
{buttonTypeQuestions.map(({ icon, title, value }) => ( {BUTTON_TYPE_QUESTIONS.map(({ icon, title, value }) => (
<QuestionsMiniButton <QuestionsMiniButton
key={title} key={title}
onClick={() => { onClick={() => {

@ -78,65 +78,7 @@ interface QuestionStore {
openedModalSettings: string; openedModalSettings: string;
} }
export const questionStore = create<QuestionStore>()( export const DEFAULT_QUESTION: Omit<Question, "id"> = {
persist<QuestionStore>(
() => ({
listQuestions: {},
openedModalSettings: "",
}),
{
name: "question",
}
)
);
export const updateQuestionsList = (
quizId: number,
index: number,
data: Partial<Question>
) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionListClone[quizId][index] = {
...questionListClone[quizId][index],
...data,
};
questionStore.setState({ listQuestions: questionListClone });
};
export const updateQuestionsListDragAndDrop = (
quizId: number,
updatedQuestions: Question[]
) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionStore.setState({
listQuestions: { ...questionListClone, [quizId]: updatedQuestions },
});
};
export const updateVariants = (
quizId: number,
index: number,
variants: Variants[]
) => {
const listQuestions = { ...questionStore.getState()["listQuestions"] };
listQuestions[quizId][index].content.variants = variants;
questionStore.setState({ listQuestions });
};
export const createQuestion = (quizId: number, placeIndex = -1) => {
const id = getRandom(1000000, 10000000);
const newData = { ...questionStore.getState()["listQuestions"] };
if (!newData[quizId]) {
newData[quizId] = [];
}
newData[quizId].splice(
placeIndex < 0 ? newData[quizId].length : placeIndex,
0,
{
id,
title: "", title: "",
description: "", description: "",
type: "", type: "",
@ -201,7 +143,66 @@ export const createQuestion = (quizId: number, placeIndex = -1) => {
created_at: "", created_at: "",
updated_at: "", updated_at: "",
expanded: false, expanded: false,
};
export const questionStore = create<QuestionStore>()(
persist<QuestionStore>(
() => ({
listQuestions: {},
openedModalSettings: "",
}),
{
name: "question",
} }
)
);
export const updateQuestionsList = (
quizId: number,
index: number,
data: Partial<Question>
) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionListClone[quizId][index] = {
...questionListClone[quizId][index],
...data,
};
questionStore.setState({ listQuestions: questionListClone });
};
export const updateQuestionsListDragAndDrop = (
quizId: number,
updatedQuestions: Question[]
) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionStore.setState({
listQuestions: { ...questionListClone, [quizId]: updatedQuestions },
});
};
export const updateVariants = (
quizId: number,
index: number,
variants: Variants[]
) => {
const listQuestions = { ...questionStore.getState()["listQuestions"] };
listQuestions[quizId][index].content.variants = variants;
questionStore.setState({ listQuestions });
};
export const createQuestion = (quizId: number, placeIndex = -1) => {
const id = getRandom(1000000, 10000000);
const newData = { ...questionStore.getState()["listQuestions"] };
if (!newData[quizId]) {
newData[quizId] = [];
}
newData[quizId].splice(
placeIndex < 0 ? newData[quizId].length : placeIndex,
0,
{ ...DEFAULT_QUESTION, id }
); );
questionStore.setState({ listQuestions: newData }); questionStore.setState({ listQuestions: newData });