feat: drag&drop
This commit is contained in:
parent
54cf5803f5
commit
54a06c4666
@ -158,7 +158,6 @@ export default function QuestionsPageCard({
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<>{isExpanded && console.log(listQuestions[totalIndex])}</>
|
||||
<FormControl fullWidth variant="standard" sx={{ p: 0 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
import AnswerOptions from "./answerOptions/AnswerOptions";
|
||||
import OptionsPicture from "./OptionsPicture/OptionsPicture";
|
||||
@ -15,48 +15,48 @@ import {useParams} from "react-router-dom";
|
||||
import { questionStore } from "@root/questions";
|
||||
|
||||
interface Props {
|
||||
totalIndex: number,
|
||||
totalIndex: number;
|
||||
}
|
||||
|
||||
export default function SwitchQuestionsPage({ totalIndex }: Props) {
|
||||
const params = Number(useParams().quizId);
|
||||
const {listQuestions} = questionStore()
|
||||
const switchState = listQuestions[totalIndex].type
|
||||
const { listQuestions } = questionStore();
|
||||
const switchState = listQuestions[totalIndex].type;
|
||||
switch (switchState) {
|
||||
case 'variant':
|
||||
return (<AnswerOptions totalIndex={totalIndex}/>);
|
||||
case "variant":
|
||||
return <AnswerOptions totalIndex={totalIndex} />;
|
||||
|
||||
case 'images':
|
||||
return (<OptionsPicture totalIndex={totalIndex}/>);
|
||||
case "images":
|
||||
return <OptionsPicture totalIndex={totalIndex} />;
|
||||
|
||||
case 'varimg':
|
||||
return (<OptionsAndPicture totalIndex={totalIndex}/>);
|
||||
case "varimg":
|
||||
return <OptionsAndPicture totalIndex={totalIndex} />;
|
||||
|
||||
case 'emoji':
|
||||
return (<Emoji totalIndex={totalIndex}/>);
|
||||
case "emoji":
|
||||
return <Emoji totalIndex={totalIndex} />;
|
||||
|
||||
case 'text':
|
||||
return (<OwnTextField totalIndex={totalIndex}/>);
|
||||
case "text":
|
||||
return <OwnTextField totalIndex={totalIndex} />;
|
||||
|
||||
case 'select':
|
||||
return (<DropDown totalIndex={totalIndex}/>);
|
||||
case "select":
|
||||
return <DropDown totalIndex={totalIndex} />;
|
||||
|
||||
case 'date':
|
||||
return (<DataOptions totalIndex={totalIndex}/>);
|
||||
case "date":
|
||||
return <DataOptions totalIndex={totalIndex} />;
|
||||
|
||||
case 'number':
|
||||
return (<SliderOptions totalIndex={totalIndex}/>);
|
||||
case "number":
|
||||
return <SliderOptions totalIndex={totalIndex} />;
|
||||
|
||||
case 'file':
|
||||
return (<UploadFile totalIndex={totalIndex}/>);
|
||||
case "file":
|
||||
return <UploadFile totalIndex={totalIndex} />;
|
||||
|
||||
case 'page':
|
||||
return (<PageOptions totalIndex={totalIndex}/>);
|
||||
case "page":
|
||||
return <PageOptions totalIndex={totalIndex} />;
|
||||
|
||||
case 'rating':
|
||||
return (<RatingOptions totalIndex={totalIndex}/>);
|
||||
case "rating":
|
||||
return <RatingOptions totalIndex={totalIndex} />;
|
||||
|
||||
default:
|
||||
return (<></>)
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,159 @@
|
||||
import { useState } from "react";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import {
|
||||
TextField,
|
||||
FormControl,
|
||||
InputAdornment,
|
||||
IconButton,
|
||||
ListItem,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import { questionStore, updateQuestionsList } from "@root/questions";
|
||||
|
||||
import PointsIcon from "@icons/questionsPage/PointsIcon";
|
||||
import DeleteIcon from "@icons/questionsPage/deleteIcon";
|
||||
import MessageIcon from "@icons/messagIcon";
|
||||
import Popover from "@mui/material/Popover";
|
||||
import TextareaAutosize from "@mui/base/TextareaAutosize";
|
||||
|
||||
import type { ChangeEvent, KeyboardEvent } from "react";
|
||||
import type { Variants } from "@root/questions";
|
||||
|
||||
type AnswerItemProps = {
|
||||
index: number;
|
||||
totalIndex: number;
|
||||
variants: Variants[];
|
||||
variant: Variants;
|
||||
};
|
||||
|
||||
export const AnswerItem = ({
|
||||
index,
|
||||
totalIndex,
|
||||
variants,
|
||||
variant,
|
||||
}: AnswerItemProps) => {
|
||||
const { listQuestions } = questionStore();
|
||||
const theme = useTheme();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const onChangeText = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const answerNew = variants.slice();
|
||||
answerNew[index].answer = event.target.value;
|
||||
|
||||
updateQuestionsList(totalIndex, {
|
||||
content: { ...listQuestions[totalIndex].content, variants: answerNew },
|
||||
});
|
||||
};
|
||||
|
||||
const addNewAnswer = () => {
|
||||
const answerNew = variants.slice();
|
||||
answerNew.push({ answer: "", answerLong: "", hints: "" });
|
||||
|
||||
updateQuestionsList(totalIndex, {
|
||||
content: { ...listQuestions[totalIndex].content, variants: answerNew },
|
||||
});
|
||||
};
|
||||
|
||||
const deleteAnswer = () => {
|
||||
const answerNew = variants.slice();
|
||||
answerNew.splice(index, 1);
|
||||
|
||||
updateQuestionsList(totalIndex, {
|
||||
content: { ...listQuestions[totalIndex].content, variants: answerNew },
|
||||
});
|
||||
};
|
||||
|
||||
const changeAnswerHint = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const answerNew = variants.slice();
|
||||
answerNew[index].hints = event.target.value;
|
||||
|
||||
updateQuestionsList(totalIndex, {
|
||||
content: { ...listQuestions[totalIndex].content, variants: answerNew },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Draggable draggableId={String(index)} index={index}>
|
||||
{(provided) => (
|
||||
<ListItem ref={provided.innerRef} {...provided.draggableProps}>
|
||||
<FormControl
|
||||
key={index}
|
||||
fullWidth
|
||||
variant="standard"
|
||||
sx={{ p: "0 0 20px 0" }}
|
||||
>
|
||||
<TextField
|
||||
value={variant.answer}
|
||||
fullWidth
|
||||
autoFocus
|
||||
placeholder={"Добавьте ответ"}
|
||||
onChange={onChangeText}
|
||||
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) =>
|
||||
event.code === "Enter" && addNewAnswer()
|
||||
}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment
|
||||
{...provided.dragHandleProps}
|
||||
position="start"
|
||||
>
|
||||
<PointsIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-describedby="my-popover-id"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<MessageIcon />
|
||||
</IconButton>
|
||||
<Popover
|
||||
id="my-popover-id"
|
||||
open={isOpen}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||
>
|
||||
<TextareaAutosize
|
||||
style={{ margin: "10px" }}
|
||||
placeholder="Подсказка для этого ответа"
|
||||
value={variant.hints}
|
||||
onChange={changeAnswerHint}
|
||||
/>
|
||||
</Popover>
|
||||
<IconButton onClick={deleteAnswer}>
|
||||
<DeleteIcon color={theme.palette.grey2.main} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
height: "48px",
|
||||
borderRadius: "10px",
|
||||
background: "#ffffff",
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: { fontSize: "18px", lineHeight: "21px", py: 0 },
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</ListItem>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,23 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Droppable } from "react-beautiful-dnd";
|
||||
|
||||
import type { DroppableProps } from "react-beautiful-dnd";
|
||||
|
||||
export const StrictModeDroppable = ({ children, ...props }: DroppableProps) => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const animation = requestAnimationFrame(() => setEnabled(true));
|
||||
|
||||
return () => {
|
||||
setEnabled(false);
|
||||
cancelAnimationFrame(animation);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Droppable {...props}>{children}</Droppable>;
|
||||
};
|
||||
@ -0,0 +1,11 @@
|
||||
export const reorder = <T>(
|
||||
list: T[],
|
||||
startIndex: number,
|
||||
endIndex: number
|
||||
): T[] => {
|
||||
const result = Array.from(list);
|
||||
const [removed] = result.splice(startIndex, 1);
|
||||
result.splice(endIndex, 0, removed);
|
||||
|
||||
return result;
|
||||
};
|
||||
@ -0,0 +1,51 @@
|
||||
import { Box } from "@mui/material";
|
||||
import { DragDropContext } from "react-beautiful-dnd";
|
||||
|
||||
import { StrictModeDroppable } from "./StrictModeDroppable";
|
||||
import { AnswerItem } from "./AnswerItem";
|
||||
|
||||
import { updateVariants } from "@root/questions";
|
||||
|
||||
import { reorder } from "./helper";
|
||||
|
||||
import type { DropResult } from "react-beautiful-dnd";
|
||||
import type { Variants } from "@root/questions";
|
||||
|
||||
type AnswerDraggableListProps = {
|
||||
variants: Variants[];
|
||||
totalIndex: number;
|
||||
};
|
||||
|
||||
export const AnswerDraggableList = ({
|
||||
variants,
|
||||
totalIndex,
|
||||
}: AnswerDraggableListProps) => {
|
||||
const onDragEnd = ({ destination, source }: DropResult) => {
|
||||
if (destination) {
|
||||
const newItems = reorder(variants, source.index, destination.index);
|
||||
|
||||
updateVariants(totalIndex, newItems);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<StrictModeDroppable droppableId="droppable-list">
|
||||
{(provided) => (
|
||||
<Box ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{variants.map((variant, index) => (
|
||||
<AnswerItem
|
||||
key={variant.answer + index}
|
||||
index={index}
|
||||
totalIndex={totalIndex}
|
||||
variants={variants}
|
||||
variant={variant}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</Box>
|
||||
)}
|
||||
</StrictModeDroppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
};
|
||||
@ -1,27 +1,11 @@
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Link,
|
||||
useTheme,
|
||||
TextField,
|
||||
FormControl,
|
||||
InputAdornment,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { Box, Typography, Link, useTheme } from "@mui/material";
|
||||
import EnterIcon from "../../../assets/icons/questionsPage/enterIcon";
|
||||
import SwitchAnswerOptions from "./switchAnswerOptions";
|
||||
import { AnswerDraggableList } from "./AnswerDraggableList";
|
||||
import ButtonsOptionsAndPict from "../ButtonsOptionsAndPict";
|
||||
import QuestionsPageCard from "../DraggableList/QuestionPageCard";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Variants, questionStore, updateQuestionsList } from "@root/questions";
|
||||
import PointsIcon from "@icons/questionsPage/PointsIcon";
|
||||
import DeleteIcon from "@icons/questionsPage/deleteIcon";
|
||||
import MessageIcon from "@icons/messagIcon";
|
||||
import Popover from "@mui/material/Popover";
|
||||
import TextareaAutosize from "@mui/base/TextareaAutosize";
|
||||
import { questionStore, updateQuestionsList } from "@root/questions";
|
||||
|
||||
import type { KeyboardEvent } from "react";
|
||||
// Импортируем интерфейс Varian
|
||||
|
||||
interface Props {
|
||||
@ -29,33 +13,17 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function AnswerOptions({ totalIndex }: Props) {
|
||||
const [switchState, setSwitchState] = useState("setting");
|
||||
const { listQuestions } = questionStore();
|
||||
const Answer = listQuestions[totalIndex].content.variants;
|
||||
console.log(Answer);
|
||||
const variants = listQuestions[totalIndex].content.variants;
|
||||
const theme = useTheme();
|
||||
const [switchState, setSwitchState] = React.useState("setting");
|
||||
|
||||
const SSHC = (data: string) => {
|
||||
setSwitchState(data);
|
||||
};
|
||||
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const id = "my-popover-id";
|
||||
|
||||
const addNewAnswer = () => {
|
||||
const answerNew = Answer.slice(); // Create a shallow copy of the array
|
||||
const answerNew = variants.slice(); // Create a shallow copy of the array
|
||||
answerNew.push({ answer: "", answerLong: "", hints: "" });
|
||||
|
||||
updateQuestionsList(totalIndex, {
|
||||
@ -66,7 +34,7 @@ export default function AnswerOptions({ totalIndex }: Props) {
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ padding: "0 20px 20px 20px" }}>
|
||||
{Answer.length === 0 ? (
|
||||
{variants.length === 0 ? (
|
||||
<Typography
|
||||
sx={{
|
||||
padding: "0 0 33px 80px",
|
||||
@ -79,99 +47,7 @@ export default function AnswerOptions({ totalIndex }: Props) {
|
||||
Добавьте ответ
|
||||
</Typography>
|
||||
) : (
|
||||
<Box>
|
||||
{Answer.map((variant: Variants, index: number) => (
|
||||
<FormControl
|
||||
key={index}
|
||||
fullWidth
|
||||
variant="standard"
|
||||
sx={{ p: "0 0 20px 0" }}
|
||||
>
|
||||
<TextField
|
||||
value={variant.answer}
|
||||
fullWidth
|
||||
autoFocus
|
||||
placeholder={"Добавьте ответ"}
|
||||
onChange={(e) => {
|
||||
const answerNew = Answer.slice(); // Create a shallow copy of the array
|
||||
answerNew[index].answer = e.target.value;
|
||||
let clonContent = listQuestions[totalIndex].content;
|
||||
clonContent.variants = answerNew;
|
||||
updateQuestionsList(totalIndex, { content: clonContent });
|
||||
}}
|
||||
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) =>
|
||||
event.code === "Enter" && addNewAnswer()
|
||||
}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<PointsIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton aria-describedby={id} onClick={handleClick}>
|
||||
<MessageIcon />
|
||||
</IconButton>
|
||||
<Popover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
>
|
||||
<TextareaAutosize
|
||||
style={{ margin: "10px" }}
|
||||
placeholder="Подсказка для этого ответа"
|
||||
value={variant.hints}
|
||||
onChange={(e) => {
|
||||
const answerNew = Answer.slice(); // Create a shallow copy of the array
|
||||
answerNew[index].hints = e.target.value;
|
||||
let clonContent =
|
||||
listQuestions[totalIndex].content;
|
||||
clonContent.variants = answerNew;
|
||||
updateQuestionsList(totalIndex, {
|
||||
content: clonContent,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Popover>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
const answerNew = Answer.slice(); // Create a shallow copy of the array
|
||||
answerNew.splice(index, 1);
|
||||
let clonContent = listQuestions[totalIndex].content;
|
||||
clonContent.variants = answerNew;
|
||||
updateQuestionsList(totalIndex, {
|
||||
content: clonContent,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DeleteIcon color={theme.palette.grey2.main} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
height: "48px",
|
||||
borderRadius: "10px",
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
fontSize: "18px",
|
||||
lineHeight: "21px",
|
||||
py: 0,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
))}
|
||||
</Box>
|
||||
<AnswerDraggableList variants={variants} totalIndex={totalIndex} />
|
||||
)}
|
||||
|
||||
<Box
|
||||
|
||||
@ -56,6 +56,14 @@ export const updateQuestionsListDragAndDrop = (
|
||||
questionStore.setState({ listQuestions: updatedQuestions });
|
||||
};
|
||||
|
||||
export const updateVariants = (index: number, variants: Variants[]) => {
|
||||
const listQuestions = [...questionStore.getState()["listQuestions"]];
|
||||
|
||||
listQuestions[index].content.variants = variants;
|
||||
|
||||
questionStore.setState({ listQuestions });
|
||||
};
|
||||
|
||||
export const createQuestion = (id: number) => {
|
||||
const idQ = getRandom(1000000, 10000000);
|
||||
const newData = [...questionStore.getState()["listQuestions"]]; //пересоздание массива
|
||||
|
||||
Loading…
Reference in New Issue
Block a user