add question previews
This commit is contained in:
parent
650ddd7129
commit
a06c5c2010
@ -8,6 +8,7 @@
|
|||||||
"@emotion/styled": "^11.10.5",
|
"@emotion/styled": "^11.10.5",
|
||||||
"@mui/icons-material": "^5.10.14",
|
"@mui/icons-material": "^5.10.14",
|
||||||
"@mui/material": "^5.10.14",
|
"@mui/material": "^5.10.14",
|
||||||
|
"@mui/x-date-pickers": "^6.16.1",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^13.0.0",
|
"@testing-library/react": "^13.0.0",
|
||||||
"@testing-library/user-event": "^13.2.1",
|
"@testing-library/user-event": "^13.2.1",
|
||||||
@ -17,6 +18,7 @@
|
|||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@types/react-dnd": "^3.0.2",
|
"@types/react-dnd": "^3.0.2",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
"emoji-mart": "^5.5.2",
|
"emoji-mart": "^5.5.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
|
|||||||
39
src/assets/icons/CalendarIcon.tsx
Normal file
39
src/assets/icons/CalendarIcon.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
|
export default function CalendarIcon() {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
flexShrink: 0,
|
||||||
|
"&:hover path": {
|
||||||
|
stroke: "#581CA7",
|
||||||
|
},
|
||||||
|
"&:active path": {
|
||||||
|
stroke: "#FB5607",
|
||||||
|
},
|
||||||
|
"&:hover rect": {
|
||||||
|
stroke: "#581CA7",
|
||||||
|
},
|
||||||
|
"&:active rect": {
|
||||||
|
stroke: "#FB5607",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="1" y="2.5" width="18" height="18" rx="5" stroke="#7E2AEA" strokeWidth="1.5" />
|
||||||
|
<path d="M1 7.5H19" stroke="#7E2AEA" strokeWidth="1.5" strokeLinejoin="round" />
|
||||||
|
<path d="M14.5 1L14.5 4" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M5.5 1L5.5 4" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M4.5 11.5H5.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M9.5 11.5H10.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M14.5 11.5H15.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M4.5 15.5H5.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M9.5 15.5H10.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M14.5 15.5H15.5" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -6,7 +6,6 @@ import "./index.css";
|
|||||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||||
import lightTheme from "./utils/themes/light";
|
import lightTheme from "./utils/themes/light";
|
||||||
import { ThemeProvider } from "@mui/material";
|
import { ThemeProvider } from "@mui/material";
|
||||||
|
|
||||||
import StartPage from "./pages/startPage/StartPage";
|
import StartPage from "./pages/startPage/StartPage";
|
||||||
import Main from "./pages/main";
|
import Main from "./pages/main";
|
||||||
import QuestionsPage from "./pages/Questions/QuestionsPage";
|
import QuestionsPage from "./pages/Questions/QuestionsPage";
|
||||||
@ -16,40 +15,52 @@ import { Result } from "./pages/Result/Result";
|
|||||||
import { Setting } from "./pages/Result/Setting";
|
import { Setting } from "./pages/Result/Setting";
|
||||||
import MyQuizzesFull from "./pages/createQuize/MyQuizzesFull";
|
import MyQuizzesFull from "./pages/createQuize/MyQuizzesFull";
|
||||||
import ImageCrop from "@ui_kit/Modal/ImageCrop";
|
import ImageCrop from "@ui_kit/Modal/ImageCrop";
|
||||||
|
import { LocalizationProvider } from "@mui/x-date-pickers";
|
||||||
|
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||||
|
import "dayjs/locale/ru";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { ruRU } from '@mui/x-date-pickers/locales';
|
||||||
|
|
||||||
|
|
||||||
|
dayjs.locale("ru");
|
||||||
|
|
||||||
|
const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
|
||||||
|
|
||||||
const routeslink: {
|
const routeslink: {
|
||||||
path: string;
|
path: string;
|
||||||
page: JSX.Element;
|
page: JSX.Element;
|
||||||
header: boolean;
|
header: boolean;
|
||||||
sidebar: boolean;
|
sidebar: boolean;
|
||||||
}[] = [
|
}[] = [
|
||||||
{ path: "/", page: <MyQuizzesFull />, header: false, sidebar: false },
|
{ path: "/", page: <MyQuizzesFull />, header: false, sidebar: false },
|
||||||
{
|
{
|
||||||
path: "/questions/:quizId",
|
path: "/questions/:quizId",
|
||||||
page: <QuestionsPage />,
|
page: <QuestionsPage />,
|
||||||
header: true,
|
header: true,
|
||||||
sidebar: true,
|
sidebar: true,
|
||||||
},
|
},
|
||||||
{ path: "/contacts", page: <ContactFormPage />, header: true, sidebar: true },
|
{ path: "/contacts", page: <ContactFormPage />, header: true, sidebar: true },
|
||||||
{ path: "/result", page: <Result />, header: true, sidebar: true },
|
{ path: "/result", page: <Result />, header: true, sidebar: true },
|
||||||
{ path: "/settings", page: <Setting />, header: true, sidebar: true },
|
{ path: "/settings", page: <Setting />, header: true, sidebar: true },
|
||||||
{ path: "/install", page: <InstallQuiz />, header: true, sidebar: true },
|
{ path: "/install", page: <InstallQuiz />, header: true, sidebar: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
const root = createRoot(document.getElementById("root")!);
|
const root = createRoot(document.getElementById("root")!);
|
||||||
|
|
||||||
root.render(
|
root.render(
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<ThemeProvider theme={lightTheme}>
|
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale="ru" localeText={localeText}>
|
||||||
<BrowserRouter>
|
<ThemeProvider theme={lightTheme}>
|
||||||
<Routes>
|
<BrowserRouter>
|
||||||
{routeslink.map((e, i) => (
|
<Routes>
|
||||||
<Route key={i} path={e.path} element={<Main page={e.page} header={e.header} sidebar={e.sidebar} />} />
|
{routeslink.map((e, i) => (
|
||||||
))}
|
<Route key={i} path={e.path} element={<Main page={e.page} header={e.header} sidebar={e.sidebar} />} />
|
||||||
<Route path="quize-setting/:quizId" element={<StartPage />} />
|
))}
|
||||||
<Route path="crop" element={<ImageCrop />} />
|
<Route path="quize-setting/:quizId" element={<StartPage />} />
|
||||||
</Routes>
|
<Route path="crop" element={<ImageCrop />} />
|
||||||
</BrowserRouter>
|
</Routes>
|
||||||
</ThemeProvider>
|
</BrowserRouter>
|
||||||
</DndProvider>
|
</ThemeProvider>
|
||||||
|
</LocalizationProvider>
|
||||||
|
</DndProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,14 +4,14 @@ import { devtools } from "zustand/middleware";
|
|||||||
|
|
||||||
interface QuizPreviewStore {
|
interface QuizPreviewStore {
|
||||||
isPreviewShown: boolean;
|
isPreviewShown: boolean;
|
||||||
currentQuizStep: number;
|
currentQuestionIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useQuizPreviewStore = create<QuizPreviewStore>()(
|
export const useQuizPreviewStore = create<QuizPreviewStore>()(
|
||||||
devtools(
|
devtools(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
isPreviewShown: true,
|
isPreviewShown: true,
|
||||||
currentQuizStep: 0,
|
currentQuestionIndex: 0,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "quizPreview",
|
name: "quizPreview",
|
||||||
@ -28,10 +28,10 @@ export const toggleQuizPreview = () => useQuizPreviewStore.setState(
|
|||||||
state => ({ isPreviewShown: !state.isPreviewShown })
|
state => ({ isPreviewShown: !state.isPreviewShown })
|
||||||
);
|
);
|
||||||
|
|
||||||
export const incrementCurrentQuizStep = (maxStep: number) => useQuizPreviewStore.setState(
|
export const incrementCurrentQuestionIndex = (maxStep: number) => useQuizPreviewStore.setState(
|
||||||
state => ({ currentQuizStep: Math.min(state.currentQuizStep + 1, maxStep) })
|
state => ({ currentQuestionIndex: Math.min(state.currentQuestionIndex + 1, maxStep) })
|
||||||
);
|
);
|
||||||
|
|
||||||
export const decrementCurrentQuizStep = () => useQuizPreviewStore.setState(
|
export const decrementCurrentQuestionIndex = () => useQuizPreviewStore.setState(
|
||||||
state => ({ currentQuizStep: Math.max(state.currentQuizStep - 1, 0) })
|
state => ({ currentQuestionIndex: Math.max(state.currentQuestionIndex - 1, 0) })
|
||||||
);
|
);
|
||||||
|
|||||||
61
src/ui_kit/CustomSlider.tsx
Normal file
61
src/ui_kit/CustomSlider.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { Slider } from "@mui/material";
|
||||||
|
|
||||||
|
type CustomSliderProps = {
|
||||||
|
defaultValue?: number;
|
||||||
|
value?: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
step?: number;
|
||||||
|
onChange?: (value: number | number[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomSlider = ({
|
||||||
|
defaultValue,
|
||||||
|
value,
|
||||||
|
min = 0,
|
||||||
|
max = 100,
|
||||||
|
step,
|
||||||
|
onChange,
|
||||||
|
}: CustomSliderProps) => {
|
||||||
|
const handleChange = ({ type }: Event, newValue: number | number[]) => {
|
||||||
|
// Для корректной работы слайдера в FireFox
|
||||||
|
if (type !== "change") {
|
||||||
|
onChange?.(newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider
|
||||||
|
value={value}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
step={step}
|
||||||
|
onChange={handleChange}
|
||||||
|
valueLabelDisplay="auto"
|
||||||
|
sx={{
|
||||||
|
color: "#7E2AEA",
|
||||||
|
height: "12px",
|
||||||
|
"& .MuiSlider-track": {
|
||||||
|
border: "none",
|
||||||
|
},
|
||||||
|
"& .MuiSlider-rail": {
|
||||||
|
backgroundColor: "#F2F3F7",
|
||||||
|
border: `1px solid #9A9AAF`,
|
||||||
|
},
|
||||||
|
"& .MuiSlider-thumb": {
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
border: `6px solid "#7E2AEA`,
|
||||||
|
backgroundColor: "white",
|
||||||
|
boxShadow: `0px 0px 0px 3px white,
|
||||||
|
0px 4px 4px 3px #C3C8DD`,
|
||||||
|
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
||||||
|
boxShadow: `0px 0px 0px 3px white,
|
||||||
|
0px 4px 4px 3px #C3C8DD`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
68
src/ui_kit/LabeledDatePicker.tsx
Normal file
68
src/ui_kit/LabeledDatePicker.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import CalendarIcon from "@icons/CalendarIcon";
|
||||||
|
import { Typography, Box, SxProps, Theme, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
import { DatePicker } from "@mui/x-date-pickers";
|
||||||
|
import { Dayjs } from "dayjs";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label?: string;
|
||||||
|
sx?: SxProps<Theme>;
|
||||||
|
value?: Dayjs | null;
|
||||||
|
onChange?: (value: Dayjs | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LabeledDatePicker({ label, value, onChange, sx }: Props) {
|
||||||
|
const theme = useTheme();
|
||||||
|
const upLg = useMediaQuery(theme.breakpoints.up("md"));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
...sx,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label && (
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: "16px",
|
||||||
|
lineHeight: "20px",
|
||||||
|
color: "#4D4D4D",
|
||||||
|
mb: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<DatePicker
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
slots={{
|
||||||
|
openPickerIcon: () => <CalendarIcon />,
|
||||||
|
}}
|
||||||
|
slotProps={{
|
||||||
|
openPickerButton: {
|
||||||
|
sx: {
|
||||||
|
p: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
backgroundColor: "#F2F3F7",
|
||||||
|
borderRadius: "10px",
|
||||||
|
pr: "22px",
|
||||||
|
"& input": {
|
||||||
|
py: "11px",
|
||||||
|
pl: upLg ? "20px" : "13px",
|
||||||
|
lineHeight: "19px",
|
||||||
|
},
|
||||||
|
"& fieldset": {
|
||||||
|
borderColor: "#9A9AAF",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ 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 QuizPreviewContent from "./QuizPreviewContent";
|
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';
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ 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: "0", height: "0" });
|
const rndPositionAndSizeRef = useRef<RndPositionAndSize>({ x: 0, y: 0, width: "340", height: "480" });
|
||||||
const isFirstShowRef = useRef<boolean>(true);
|
const isFirstShowRef = useRef<boolean>(true);
|
||||||
|
|
||||||
useLayoutEffect(function stickPreviewToBottomRight() {
|
useLayoutEffect(function stickPreviewToBottomRight() {
|
||||||
@ -60,8 +60,8 @@ export default function QuizPreview() {
|
|||||||
>
|
>
|
||||||
{isPreviewShown &&
|
{isPreviewShown &&
|
||||||
<Rnd
|
<Rnd
|
||||||
minHeight={480}
|
minHeight={300}
|
||||||
minWidth={300}
|
minWidth={340}
|
||||||
bounds="parent"
|
bounds="parent"
|
||||||
ref={rndRef}
|
ref={rndRef}
|
||||||
dragHandleClassName="quiz-preview-draghandle"
|
dragHandleClassName="quiz-preview-draghandle"
|
||||||
@ -100,7 +100,7 @@ export default function QuizPreview() {
|
|||||||
pointerEvents: "auto",
|
pointerEvents: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<QuizPreviewContent />
|
<QuizPreviewLayout />
|
||||||
<IconButton
|
<IconButton
|
||||||
className="quiz-preview-draghandle"
|
className="quiz-preview-draghandle"
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@ -1,79 +0,0 @@
|
|||||||
import { Box, Button, LinearProgress, Paper } from "@mui/material";
|
|
||||||
import { Question, questionStore } from "@root/questions";
|
|
||||||
import { decrementCurrentQuizStep, incrementCurrentQuizStep, useQuizPreviewStore } from "@root/quizPreview";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
|
||||||
|
|
||||||
|
|
||||||
export default function QuizPreviewContent() {
|
|
||||||
const quizId = useParams().quizId ?? 0;
|
|
||||||
const listQuestions = questionStore(state => state.listQuestions);
|
|
||||||
const currentQuizStep = useQuizPreviewStore(state => state.currentQuizStep);
|
|
||||||
|
|
||||||
const quizQuestions: Question[] | undefined = listQuestions[quizId];
|
|
||||||
const currentProgress = Math.floor((currentQuizStep / quizQuestions.length) * 100);
|
|
||||||
const currentQuestion = quizQuestions[currentQuizStep];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper sx={{
|
|
||||||
height: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
flexGrow: 1,
|
|
||||||
borderRadius: "12px",
|
|
||||||
pointerEvents: "auto",
|
|
||||||
}}>
|
|
||||||
<Box sx={{
|
|
||||||
p: "16px",
|
|
||||||
whiteSpace: "break-spaces",
|
|
||||||
overflowY: "auto",
|
|
||||||
flexGrow: 1,
|
|
||||||
}}>
|
|
||||||
|
|
||||||
</Box>
|
|
||||||
<Box sx={{
|
|
||||||
mt: "auto",
|
|
||||||
p: "16px",
|
|
||||||
display: "flex",
|
|
||||||
borderTop: "1px solid #E3E3E3",
|
|
||||||
alignItems: "center",
|
|
||||||
}}>
|
|
||||||
<Box sx={{
|
|
||||||
flexGrow: 1,
|
|
||||||
}}>
|
|
||||||
<LinearProgress
|
|
||||||
variant="determinate"
|
|
||||||
value={currentProgress}
|
|
||||||
sx={{
|
|
||||||
"&.MuiLinearProgress-colorPrimary": {
|
|
||||||
backgroundColor: "fadePurple.main",
|
|
||||||
},
|
|
||||||
"& .MuiLinearProgress-barColorPrimary": {
|
|
||||||
backgroundColor: "brightPurple.main",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{
|
|
||||||
ml: 2,
|
|
||||||
display: "flex",
|
|
||||||
gap: 1,
|
|
||||||
}}>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
onClick={decrementCurrentQuizStep}
|
|
||||||
disabled={currentQuizStep === 0}
|
|
||||||
sx={{ px: 1, minWidth: 0 }}
|
|
||||||
>
|
|
||||||
<ArrowLeft />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => incrementCurrentQuizStep(quizQuestions.length)}
|
|
||||||
disabled={currentQuizStep === quizQuestions.length}
|
|
||||||
>Далее</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
133
src/ui_kit/QuizPreview/QuizPreviewLayout.tsx
Normal file
133
src/ui_kit/QuizPreview/QuizPreviewLayout.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { Box, Button, LinearProgress, Paper, Typography } from "@mui/material";
|
||||||
|
import { Question, questionStore } from "@root/questions";
|
||||||
|
import { decrementCurrentQuestionIndex, incrementCurrentQuestionIndex, useQuizPreviewStore } from "@root/quizPreview";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||||
|
import Variant from "./QuizPreviewQuestionTypes/Variant";
|
||||||
|
import { FC, useEffect } from "react";
|
||||||
|
import Images from "./QuizPreviewQuestionTypes/Images";
|
||||||
|
import Varimg from "./QuizPreviewQuestionTypes/Varimg";
|
||||||
|
import Emoji from "./QuizPreviewQuestionTypes/Emoji";
|
||||||
|
import Text from "./QuizPreviewQuestionTypes/Text";
|
||||||
|
import Select from "./QuizPreviewQuestionTypes/Select";
|
||||||
|
import Date from "./QuizPreviewQuestionTypes/Date";
|
||||||
|
import Number from "./QuizPreviewQuestionTypes/Number";
|
||||||
|
import File from "./QuizPreviewQuestionTypes/File";
|
||||||
|
import Page from "./QuizPreviewQuestionTypes/Page";
|
||||||
|
import Rating from "./QuizPreviewQuestionTypes/Rating";
|
||||||
|
|
||||||
|
|
||||||
|
type QuizQuestionType = "variant" | "images" | "varimg" | "emoji" | "text" | "select" | "date" | "number" | "file" | "page" | "rating";
|
||||||
|
|
||||||
|
const QuestionPreviewComponentByType: Record<QuizQuestionType, FC<{ question: Question; }>> = {
|
||||||
|
variant: Variant,
|
||||||
|
images: Images,
|
||||||
|
varimg: Varimg,
|
||||||
|
emoji: Emoji,
|
||||||
|
text: Text,
|
||||||
|
select: Select,
|
||||||
|
date: Date,
|
||||||
|
number: Number,
|
||||||
|
file: File,
|
||||||
|
page: Page,
|
||||||
|
rating: Rating,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function QuizPreviewLayout() {
|
||||||
|
const quizId = useParams().quizId ?? 0;
|
||||||
|
const listQuestions = questionStore(state => state.listQuestions);
|
||||||
|
const currentQuizStep = useQuizPreviewStore(state => state.currentQuestionIndex);
|
||||||
|
|
||||||
|
const quizQuestions: Question[] | undefined = listQuestions[quizId];
|
||||||
|
const maxCurrentQuizStep = quizQuestions?.length > 0 ? quizQuestions.length - 1 : 0;
|
||||||
|
const currentProgress = Math.floor((currentQuizStep / maxCurrentQuizStep) * 100);
|
||||||
|
|
||||||
|
const currentQuestion = quizQuestions[currentQuizStep];
|
||||||
|
const QuestionComponent = currentQuestion
|
||||||
|
? QuestionPreviewComponentByType[currentQuestion.type as QuizQuestionType]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const questionElement = QuestionComponent
|
||||||
|
? <QuestionComponent key={currentQuestion.id} question={currentQuestion} />
|
||||||
|
: null;
|
||||||
|
|
||||||
|
useEffect(function resetCurrentQuizStep() {
|
||||||
|
if (currentQuizStep > maxCurrentQuizStep) {
|
||||||
|
decrementCurrentQuestionIndex();
|
||||||
|
}
|
||||||
|
}, [currentQuizStep, maxCurrentQuizStep]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper sx={{
|
||||||
|
height: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
flexGrow: 1,
|
||||||
|
borderRadius: "12px",
|
||||||
|
pointerEvents: "auto",
|
||||||
|
}}>
|
||||||
|
<Box sx={{
|
||||||
|
p: "16px",
|
||||||
|
whiteSpace: "break-spaces",
|
||||||
|
overflowY: "auto",
|
||||||
|
flexGrow: 1,
|
||||||
|
}}>
|
||||||
|
{questionElement}
|
||||||
|
</Box>
|
||||||
|
<Box sx={{
|
||||||
|
mt: "auto",
|
||||||
|
p: "16px",
|
||||||
|
display: "flex",
|
||||||
|
borderTop: "1px solid #E3E3E3",
|
||||||
|
alignItems: "center",
|
||||||
|
}}>
|
||||||
|
<Box sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography>
|
||||||
|
{quizQuestions.length > 0
|
||||||
|
? `Вопрос ${currentQuizStep + 1} из ${quizQuestions.length}`
|
||||||
|
: "Нет вопросов"
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
{quizQuestions.length > 0 &&
|
||||||
|
<LinearProgress
|
||||||
|
variant="determinate"
|
||||||
|
value={currentProgress}
|
||||||
|
sx={{
|
||||||
|
"&.MuiLinearProgress-colorPrimary": {
|
||||||
|
backgroundColor: "fadePurple.main",
|
||||||
|
},
|
||||||
|
"& .MuiLinearProgress-barColorPrimary": {
|
||||||
|
backgroundColor: "brightPurple.main",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
<Box sx={{
|
||||||
|
ml: 2,
|
||||||
|
display: "flex",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Date.tsx
Normal file
22
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Date.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import LabeledDatePicker from "@ui_kit/LabeledDatePicker";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Date({ question }: Props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography variant="h6">{question.title}</Typography>
|
||||||
|
<LabeledDatePicker />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
39
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Emoji.tsx
Normal file
39
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Emoji.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import InfoIcon from "@icons/InfoIcon";
|
||||||
|
import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup, Tooltip, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { useState, ChangeEvent } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Emoji({ question }: Props) {
|
||||||
|
const [value, setValue] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValue((event.target as HTMLInputElement).value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
aria-labelledby="quiz-question-radio-group"
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{question.content.variants.map((variant, index) => (
|
||||||
|
<FormControlLabel key={index} value={variant.answer} control={<Radio />} label={
|
||||||
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||||
|
<Typography>{`${variant.emoji} ${variant.answer}`}</Typography>
|
||||||
|
<Tooltip title={variant.hints} placement="right">
|
||||||
|
<Box><InfoIcon /></Box>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
} />
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
44
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/File.tsx
Normal file
44
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/File.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Box, Button, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { ChangeEvent, useRef, useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function File({ question }: Props) {
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [files, setFiles] = useState<File[] | null>(null);
|
||||||
|
|
||||||
|
function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
|
if (!event.target.files) return setFiles(null);
|
||||||
|
setFiles(Array.from(event.target.files));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography variant="h6">{question.title}</Typography>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
>
|
||||||
|
Загрузить файл
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
type="file"
|
||||||
|
style={{
|
||||||
|
display: "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Images.tsx
Normal file
41
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Images.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import InfoIcon from "@icons/InfoIcon";
|
||||||
|
import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup, Tooltip, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { ChangeEvent, useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Images({ question }: Props) {
|
||||||
|
const [value, setValue] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValue((event.target as HTMLInputElement).value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
aria-labelledby="quiz-question-radio-group"
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{question.content.variants.map((variant, index) => (
|
||||||
|
<FormControlLabel key={index} value={variant.answer} control={<Radio />} label={
|
||||||
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||||
|
<Typography>{variant.answer}</Typography>
|
||||||
|
<Tooltip title={variant.hints} placement="right">
|
||||||
|
<Box>
|
||||||
|
<InfoIcon />
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
} />
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
35
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Number.tsx
Normal file
35
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Number.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { CustomSlider } from "@ui_kit/CustomSlider";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Number({ question }: Props) {
|
||||||
|
const [sliderValue, setSliderValue] = useState<number>(question.content.start);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography variant="h6">{question.title}</Typography>
|
||||||
|
<Box sx={{
|
||||||
|
px: 2,
|
||||||
|
}}>
|
||||||
|
<CustomSlider
|
||||||
|
value={sliderValue}
|
||||||
|
onChange={v => setSliderValue(v as number)}
|
||||||
|
min={parseInt(question.content.range.split("—")[0])}
|
||||||
|
max={parseInt(question.content.range.split("—")[1])}
|
||||||
|
defaultValue={question.content.start}
|
||||||
|
step={question.content.step}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Page.tsx
Normal file
23
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Page.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page({ question }: Props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography variant="h6">{question.title}</Typography>
|
||||||
|
<Box>
|
||||||
|
<Typography>{question.content.text}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
74
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Rating.tsx
Normal file
74
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Rating.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { FC, useState } from "react";
|
||||||
|
import FlagIcon from "../../../assets/icons/questionsPage/FlagIcon";
|
||||||
|
import StarIconMini from "../../../assets/icons/questionsPage/StarIconMini";
|
||||||
|
import HashtagIcon from "../../../assets/icons/questionsPage/hashtagIcon";
|
||||||
|
import HeartIcon from "../../../assets/icons/questionsPage/heartIcon";
|
||||||
|
import LightbulbIcon from "../../../assets/icons/questionsPage/lightbulbIcon";
|
||||||
|
import LikeIcon from "../../../assets/icons/questionsPage/likeIcon";
|
||||||
|
import TropfyIcon from "../../../assets/icons/questionsPage/tropfyIcon";
|
||||||
|
|
||||||
|
|
||||||
|
type RatingIconType = "star" | "trophie" | "flag" | "heart" | "like" | "bubble" | "hashtag";
|
||||||
|
|
||||||
|
const ratingIconComponentByType: Record<RatingIconType, FC<{ color: string; }>> = {
|
||||||
|
"star": StarIconMini,
|
||||||
|
"trophie": TropfyIcon,
|
||||||
|
"flag": FlagIcon,
|
||||||
|
"heart": HeartIcon,
|
||||||
|
"like": LikeIcon,
|
||||||
|
"bubble": LightbulbIcon,
|
||||||
|
"hashtag": HashtagIcon,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Rating({ question }: Props) {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
|
const [selectedRating, setSelectedRating] = useState<number>(0);
|
||||||
|
|
||||||
|
const RatingIconComponent = ratingIconComponentByType[question.content.form as RatingIconType];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography variant="h6">{question.title}</Typography>
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: isMobile ? "10px" : "15px",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
}}>
|
||||||
|
{Array.from(
|
||||||
|
{ length: question.content.steps },
|
||||||
|
(_, index) => index
|
||||||
|
).map((itemNumber) => (
|
||||||
|
<Box
|
||||||
|
key={itemNumber}
|
||||||
|
onClick={() => setSelectedRating(itemNumber + 1)}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
transform: "scale(1.5)",
|
||||||
|
":hover": {
|
||||||
|
transform: "scale(1.7)",
|
||||||
|
transition: "0.2s",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RatingIconComponent color={
|
||||||
|
selectedRating > itemNumber
|
||||||
|
? theme.palette.brightPurple.main
|
||||||
|
: theme.palette.grey2.main
|
||||||
|
} />
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
102
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Select.tsx
Normal file
102
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Select.tsx
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import ArrowDownIcon from "@icons/ArrowDownIcon";
|
||||||
|
import { Box, FormControl, MenuItem, Select, SelectChangeEvent, Typography, useTheme } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Text({ question }: Props) {
|
||||||
|
const theme = useTheme();
|
||||||
|
const [selectValue, setSelectValue] = useState<string>("");
|
||||||
|
|
||||||
|
function handleChange(event: SelectChangeEvent<string | null>) {
|
||||||
|
setSelectValue((event.target as HTMLInputElement).value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography variant="h6">{question.title}</Typography>
|
||||||
|
<FormControl
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
minWidth: "200px",
|
||||||
|
height: "48px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
id="category-select"
|
||||||
|
variant="outlined"
|
||||||
|
value={selectValue}
|
||||||
|
displayEmpty
|
||||||
|
onChange={handleChange}
|
||||||
|
sx={{
|
||||||
|
height: "48px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
"& .MuiOutlinedInput-notchedOutline": {
|
||||||
|
border: `1px solid ${theme.palette.brightPurple.main} !important`,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
MenuProps={{
|
||||||
|
PaperProps: {
|
||||||
|
sx: {
|
||||||
|
mt: "8px",
|
||||||
|
p: "4px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
border: "1px solid #EEE4FC",
|
||||||
|
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MenuListProps: {
|
||||||
|
sx: {
|
||||||
|
py: 0,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "8px",
|
||||||
|
"& .Mui-selected": {
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
inputProps={{
|
||||||
|
sx: {
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
px: "9px",
|
||||||
|
gap: "20px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
IconComponent={(props) => <ArrowDownIcon {...props} />}
|
||||||
|
>
|
||||||
|
{question.content.variants.map(variant => (
|
||||||
|
<MenuItem
|
||||||
|
key={variant.answer}
|
||||||
|
value={variant.answer}
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "20px",
|
||||||
|
p: "4px",
|
||||||
|
borderRadius: "5px",
|
||||||
|
color: theme.palette.grey2.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{variant.answer}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
24
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Text.tsx
Normal file
24
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Text.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Text({ question }: Props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
}}>
|
||||||
|
<Typography variant="h6">{question.title}</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
placeholder={question.content.placeholder}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Variant.tsx
Normal file
41
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Variant.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import InfoIcon from "@icons/InfoIcon";
|
||||||
|
import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup, Tooltip, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { ChangeEvent, useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Variant({ question }: Props) {
|
||||||
|
const [value, setValue] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValue((event.target as HTMLInputElement).value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
aria-labelledby="quiz-question-radio-group"
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{question.content.variants.map((variant, index) => (
|
||||||
|
<FormControlLabel key={index} value={variant.answer} control={<Radio />} label={
|
||||||
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||||
|
<Typography>{variant.answer}</Typography>
|
||||||
|
<Tooltip title={variant.hints} placement="right">
|
||||||
|
<Box>
|
||||||
|
<InfoIcon />
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
} />
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Varimg.tsx
Normal file
41
src/ui_kit/QuizPreview/QuizPreviewQuestionTypes/Varimg.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import InfoIcon from "@icons/InfoIcon";
|
||||||
|
import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup, Tooltip, Typography } from "@mui/material";
|
||||||
|
import { Question } from "@root/questions";
|
||||||
|
import { useState, ChangeEvent } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Varimg({ question }: Props) {
|
||||||
|
const [value, setValue] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValue((event.target as HTMLInputElement).value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
aria-labelledby="quiz-question-radio-group"
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{question.content.variants.map((variant, index) => (
|
||||||
|
<FormControlLabel key={index} value={variant.answer} control={<Radio />} label={
|
||||||
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||||
|
<Typography>{variant.answer}</Typography>
|
||||||
|
<Tooltip title={variant.hints} placement="right">
|
||||||
|
<Box>
|
||||||
|
<InfoIcon />
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
} />
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
107
yarn.lock
107
yarn.lock
@ -1048,6 +1048,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.11"
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.23.1":
|
||||||
|
version "7.23.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d"
|
||||||
|
integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3":
|
"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3":
|
||||||
version "7.20.7"
|
version "7.20.7"
|
||||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz"
|
resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz"
|
||||||
@ -1351,6 +1358,33 @@
|
|||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
|
"@floating-ui/core@^1.4.2":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c"
|
||||||
|
integrity sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/utils" "^0.1.3"
|
||||||
|
|
||||||
|
"@floating-ui/dom@^1.5.1":
|
||||||
|
version "1.5.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa"
|
||||||
|
integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/core" "^1.4.2"
|
||||||
|
"@floating-ui/utils" "^0.1.3"
|
||||||
|
|
||||||
|
"@floating-ui/react-dom@^2.0.2":
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.2.tgz#fab244d64db08e6bed7be4b5fcce65315ef44d20"
|
||||||
|
integrity sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/dom" "^1.5.1"
|
||||||
|
|
||||||
|
"@floating-ui/utils@^0.1.3":
|
||||||
|
version "0.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9"
|
||||||
|
integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==
|
||||||
|
|
||||||
"@humanwhocodes/config-array@^0.11.6":
|
"@humanwhocodes/config-array@^0.11.6":
|
||||||
version "0.11.7"
|
version "0.11.7"
|
||||||
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz"
|
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz"
|
||||||
@ -1671,6 +1705,19 @@
|
|||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^18.2.0"
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/base@^5.0.0-beta.17":
|
||||||
|
version "5.0.0-beta.18"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.18.tgz#f95d393cf80974e77c0823170cc15c854d5af84b"
|
||||||
|
integrity sha512-e9ZCy/ndhyt5MTshAS3qAUy/40UiO0jX+kAo6a+XirrPJE+rrQW+mKPSI0uyp+5z4Vh+z0pvNoJ2S2gSrNz3BQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.23.1"
|
||||||
|
"@floating-ui/react-dom" "^2.0.2"
|
||||||
|
"@mui/types" "^7.2.5"
|
||||||
|
"@mui/utils" "^5.14.12"
|
||||||
|
"@popperjs/core" "^2.11.8"
|
||||||
|
clsx "^2.0.0"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/core-downloads-tracker@^5.10.16":
|
"@mui/core-downloads-tracker@^5.10.16":
|
||||||
version "5.10.16"
|
version "5.10.16"
|
||||||
resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.16.tgz"
|
resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.16.tgz"
|
||||||
@ -1739,6 +1786,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.2.tgz"
|
resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.2.tgz"
|
||||||
integrity sha512-siex8cZDtWeC916cXOoUOnEQQejuMYmHtc4hM6VkKVYaBICz3VIiqyiAomRboTQHt2jchxQ5Q5ATlbcDekTxDA==
|
integrity sha512-siex8cZDtWeC916cXOoUOnEQQejuMYmHtc4hM6VkKVYaBICz3VIiqyiAomRboTQHt2jchxQ5Q5ATlbcDekTxDA==
|
||||||
|
|
||||||
|
"@mui/types@^7.2.5":
|
||||||
|
version "7.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.5.tgz#cd62a1fc5eb1044137ccab2053b431dd7cfc3cb8"
|
||||||
|
integrity sha512-S2BwfNczr7VwS6ki8GoAXJyARoeSJDLuxOEPs3vEMyTALlf9PrdHv+sluX7kk3iKrCg/ML2mIWwapZvWbkMCQA==
|
||||||
|
|
||||||
"@mui/utils@^5.10.16":
|
"@mui/utils@^5.10.16":
|
||||||
version "5.10.16"
|
version "5.10.16"
|
||||||
resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.10.16.tgz"
|
resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.10.16.tgz"
|
||||||
@ -1750,6 +1802,29 @@
|
|||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^18.2.0"
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/utils@^5.14.11", "@mui/utils@^5.14.12":
|
||||||
|
version "5.14.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.12.tgz#58b570839e22e0fba71e17d37d9c083fe233704d"
|
||||||
|
integrity sha512-RFNXnhKQlzIkIUig6mmv0r5VbtjPdWoaBPYicq25LETdZux59HAqoRdWw15T7lp3c7gXOoE8y67+hTB8C64m2g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.23.1"
|
||||||
|
"@types/prop-types" "^15.7.7"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/x-date-pickers@^6.16.1":
|
||||||
|
version "6.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.16.1.tgz#76341384ef51db5f405f779fe5f2e9456b2cdc53"
|
||||||
|
integrity sha512-4B2+DU7aywYdvmr10o2qai6kbbR26zta/v1y8x3bmTilI/KcbhZ2OlsyArPKmTRNC8VYirejSnhLkPR/+JIkPg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.23.1"
|
||||||
|
"@mui/base" "^5.0.0-beta.17"
|
||||||
|
"@mui/utils" "^5.14.11"
|
||||||
|
"@types/react-transition-group" "^4.4.7"
|
||||||
|
clsx "^2.0.0"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-transition-group "^4.4.5"
|
||||||
|
|
||||||
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
|
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
|
||||||
version "5.1.1-v1"
|
version "5.1.1-v1"
|
||||||
resolved "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz"
|
resolved "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz"
|
||||||
@ -1798,6 +1873,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz"
|
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz"
|
||||||
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
||||||
|
|
||||||
|
"@popperjs/core@^2.11.8":
|
||||||
|
version "2.11.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
||||||
|
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
||||||
|
|
||||||
"@react-dnd/asap@^5.0.1":
|
"@react-dnd/asap@^5.0.1":
|
||||||
version "5.0.2"
|
version "5.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488"
|
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488"
|
||||||
@ -2279,6 +2359,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz"
|
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz"
|
||||||
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||||
|
|
||||||
|
"@types/prop-types@^15.7.7":
|
||||||
|
version "15.7.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.8.tgz#805eae6e8f41bd19e88917d2ea200dc992f405d3"
|
||||||
|
integrity sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==
|
||||||
|
|
||||||
"@types/q@^1.5.1":
|
"@types/q@^1.5.1":
|
||||||
version "1.5.5"
|
version "1.5.5"
|
||||||
resolved "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz"
|
resolved "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz"
|
||||||
@ -2339,6 +2424,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-transition-group@^4.4.7":
|
||||||
|
version "4.4.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.7.tgz#bf69f269d74aa78b99097673ca6dd6824a68ef1c"
|
||||||
|
integrity sha512-ICCyBl5mvyqYp8Qeq9B5G/fyBSRC0zx3XM3sCC6KkcMsNeAHqXBKkmat4GqdJET5jtYUpZXrxI5flve5qhi2Eg==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^18.0.0":
|
"@types/react@*", "@types/react@^18.0.0":
|
||||||
version "18.0.26"
|
version "18.0.26"
|
||||||
resolved "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz"
|
resolved "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz"
|
||||||
@ -3405,6 +3497,11 @@ clsx@^1.1.0, clsx@^1.1.1, clsx@^1.2.1:
|
|||||||
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
||||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||||
|
|
||||||
|
clsx@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
|
||||||
|
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
|
||||||
|
|
||||||
co@^4.6.0:
|
co@^4.6.0:
|
||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
|
resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
|
||||||
@ -3849,6 +3946,11 @@ data-urls@^2.0.0:
|
|||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
|
dayjs@^1.11.10:
|
||||||
|
version "1.11.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
|
||||||
|
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
||||||
|
|
||||||
debug@2.6.9, debug@^2.6.0, debug@^2.6.9:
|
debug@2.6.9, debug@^2.6.0, debug@^2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
||||||
@ -8117,6 +8219,11 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.9:
|
|||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"
|
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz"
|
||||||
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||||
|
|
||||||
|
regenerator-runtime@^0.14.0:
|
||||||
|
version "0.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
||||||
|
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
|
||||||
|
|
||||||
regenerator-transform@^0.15.1:
|
regenerator-transform@^0.15.1:
|
||||||
version "0.15.1"
|
version "0.15.1"
|
||||||
resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz"
|
resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user