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

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

@ -78,65 +78,7 @@ interface QuestionStore {
openedModalSettings: string;
}
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,
{
id,
export const DEFAULT_QUESTION: Omit<Question, "id"> = {
title: "",
description: "",
type: "",
@ -201,7 +143,66 @@ export const createQuestion = (quizId: number, placeIndex = -1) => {
created_at: "",
updated_at: "",
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 });