merging
This commit is contained in:
commit
0df1417dda
22
src/assets/icons/questionsPage/AlignIcon.tsx
Normal file
22
src/assets/icons/questionsPage/AlignIcon.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { FC } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
export const AlignIcon: FC = () => (
|
||||
<Box sx={{ width: `20px`, height: "20px" }}>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19 8.18348L11.8 8.22299V1M8.2 19V11.8165L1 11.7752"
|
||||
stroke="white"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
35
src/assets/icons/questionsPage/EditIcon.tsx
Normal file
35
src/assets/icons/questionsPage/EditIcon.tsx
Normal file
File diff suppressed because one or more lines are too long
29
src/assets/icons/questionsPage/ExpandIcon.tsx
Normal file
29
src/assets/icons/questionsPage/ExpandIcon.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { FC } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
export const ExpandIcon: FC = () => (
|
||||
<Box sx={{ width: `24px`, height: "24px" }}>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M18 6.58545L12.4984 2.17075C12.434 2.11677 12.3566 2.07382 12.271 2.04447C12.1853 2.01513 12.0931 2 12 2C11.9069 2 11.8147 2.01513 11.729 2.04447C11.6434 2.07382 11.566 2.11677 11.5016 2.17075L6 6.58545M18 17.4146L12.4984 21.8293C12.434 21.8832 12.3566 21.9262 12.271 21.9555C12.1853 21.9849 12.0931 22 12 22C11.9069 22 11.8147 21.9849 11.729 21.9555C11.6434 21.9262 11.566 21.8832 11.5016 21.8293L6 17.4146"
|
||||
stroke="white"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.9988 14.0054C13.0682 14.0054 13.9351 13.1385 13.9351 12.0691C13.9351 10.9997 13.0682 10.1328 11.9988 10.1328C10.9294 10.1328 10.0625 10.9997 10.0625 12.0691C10.0625 13.1385 10.9294 14.0054 11.9988 14.0054Z"
|
||||
stroke="white"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
29
src/assets/icons/questionsPage/GrayPlus.tsx
Normal file
29
src/assets/icons/questionsPage/GrayPlus.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { FC } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
export const GrayPlus: FC = () => (
|
||||
<Box sx={{ width: `32px` }}>
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M16.0029 1V31"
|
||||
stroke="#9A9AAF"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M31 15.9941L1 15.9941"
|
||||
stroke="#9A9AAF"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
38
src/assets/icons/questionsPage/RoundedCheckedIcon.tsx
Normal file
38
src/assets/icons/questionsPage/RoundedCheckedIcon.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { FC } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
export const RoundedCheckedIcon: FC = () => (
|
||||
<Box
|
||||
sx={{
|
||||
width: `26px`,
|
||||
height: "26px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="26"
|
||||
height="27"
|
||||
viewBox="0 0 26 27"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="1"
|
||||
width="25"
|
||||
height="25"
|
||||
rx="12.5"
|
||||
fill="#F2F3F7"
|
||||
stroke="#F2F3F7"
|
||||
/>
|
||||
<path
|
||||
d="M8 13.5L12.2857 17.5L18 10"
|
||||
stroke="#9A9AAF"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
@ -1,6 +1,6 @@
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { devlog } from "@frontend/kitui";
|
||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||
import { Box, Button } from "@mui/material";
|
||||
import { clearRuleForAll } from "@root/questions/actions";
|
||||
import { useQuestionsStore } from "@root/questions/store";
|
||||
import { updateRootContentId } from "@root/quizes/actions";
|
||||
@ -14,7 +14,6 @@ import {
|
||||
import { useUiTools } from "@root/uiTools/store";
|
||||
import type { Core } from "cytoscape";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef } from "react";
|
||||
import CytoscapeComponent from "react-cytoscapejs";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { DeleteNodeModal } from "../DeleteNodeModal";
|
||||
@ -23,8 +22,15 @@ import { addNode, layoutOptions, storeToNodes } from "./helper";
|
||||
import { useRemoveNode } from "./hooks/useRemoveNode";
|
||||
import "./style/styles.css";
|
||||
import { stylesheet } from "./style/stylesheet";
|
||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { InfoBanner } from "./InfoBanner/InfoBanner";
|
||||
import { PositionControl } from "./PositionControl/PositionControl";
|
||||
import { ZoomControl } from "./ZoomControl/ZoomControl";
|
||||
|
||||
function CsComponent() {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
const [isBannerVisible, setBannerVisible] = useState(true);
|
||||
const desireToOpenABranchingModal = useUiTools((state) => state.desireToOpenABranchingModal);
|
||||
const modalQuestionParentContentId = useUiTools((state) => state.modalQuestionParentContentId);
|
||||
const modalQuestionTargetContentId = useUiTools((state) => state.modalQuestionTargetContentId);
|
||||
@ -82,37 +88,25 @@ function CsComponent() {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box mb="20px">
|
||||
<Button
|
||||
sx={{
|
||||
height: "27px",
|
||||
color: "#7E2AEA",
|
||||
textDecoration: "underline",
|
||||
fontSize: "16px",
|
||||
}}
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
cyRef.current?.fit(undefined, 70);
|
||||
}}
|
||||
>
|
||||
Выровнять
|
||||
</Button>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
height: "480px",
|
||||
background: "#F2F3F7",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<CsNodeButtons csElements={csElements} cyRef={cyRef} />
|
||||
<Box sx={{ position: "relative" }}>
|
||||
{isBannerVisible && <InfoBanner setBannerVisible={setBannerVisible} />}
|
||||
<PositionControl cyRef={cyRef} />
|
||||
<ZoomControl cyRef={cyRef} />
|
||||
<CytoscapeComponent
|
||||
wheelSensitivity={0.1}
|
||||
elements={csElements}
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
height: isMobile ? "327px" : "481px",
|
||||
background: "#F2F3F7",
|
||||
overflow: "hidden",
|
||||
borderRadius: "12px",
|
||||
width: "100%",
|
||||
}}
|
||||
stylesheet={stylesheet}
|
||||
layout={layoutOptions}
|
||||
@ -123,10 +117,9 @@ function CsComponent() {
|
||||
autounselectify={true}
|
||||
boxSelectionEnabled={false}
|
||||
/>
|
||||
<CsNodeButtons csElements={csElements} cyRef={cyRef} />
|
||||
</Box>
|
||||
<DeleteNodeModal removeNode={removeNode} />
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,14 +8,29 @@ import {
|
||||
updateOpenedModalSettingsId,
|
||||
} from "@root/uiTools/actions";
|
||||
import { Core, EventObject, NodeSingular } from "cytoscape";
|
||||
import { MutableRefObject, forwardRef, useEffect, useMemo, useRef } from "react";
|
||||
import { addNode, isElementANode, isNodeInViewport, isQuestionProhibited, storeToNodes } from "./helper";
|
||||
import {
|
||||
forwardRef,
|
||||
MutableRefObject,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
canAddChildToQuestion,
|
||||
isElementANode,
|
||||
isNodeInViewport,
|
||||
isQuestionProhibited,
|
||||
storeToNodes,
|
||||
} from "./helper";
|
||||
|
||||
const csButtonTypes = ["delete", "add", "settings", "select"] as const;
|
||||
|
||||
type CsButtonType = (typeof csButtonTypes)[number];
|
||||
|
||||
type CsNodeButtonsByType = Partial<Record<CsButtonType, HTMLButtonElement | null>>;
|
||||
type CsNodeButtonsByType = Partial<
|
||||
Record<CsButtonType, HTMLButtonElement | null>
|
||||
>;
|
||||
|
||||
type CsButtonsById = Record<string, CsNodeButtonsByType | undefined>;
|
||||
|
||||
@ -29,7 +44,10 @@ export default function CsNodeButtons({ csElements, cyRef }: Props) {
|
||||
|
||||
const buttons = useMemo(() => {
|
||||
const nodeElements = csElements.filter(isElementANode);
|
||||
buttonRefsById.current = nodeElements.reduce<CsButtonsById>((acc, node) => ((acc[node.data.id] = {}), acc), {});
|
||||
buttonRefsById.current = nodeElements.reduce<CsButtonsById>(
|
||||
(acc, node) => ((acc[node.data.id] = {}), acc),
|
||||
{},
|
||||
);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -38,10 +56,7 @@ export default function CsNodeButtons({ csElements, cyRef }: Props) {
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
pointerEvents: "none",
|
||||
"> *": {
|
||||
pointerEvents: "auto",
|
||||
},
|
||||
borderRadius: "12px",
|
||||
}}
|
||||
>
|
||||
{nodeElements.flatMap((csElement) => [
|
||||
@ -61,23 +76,24 @@ export default function CsNodeButtons({ csElements, cyRef }: Props) {
|
||||
const buttonData = buttonRefsById.current[csElement.data.id];
|
||||
if (buttonData) buttonData.add = r;
|
||||
}}
|
||||
onPointerUp={() => {
|
||||
addNode({ parentNodeContentId: csElement.data.id });
|
||||
cleardragQuestionContentId();
|
||||
onClick={() => {
|
||||
setModalQuestionParentContentId(csElement.data.id);
|
||||
setOpenedModalQuestions(canAddChildToQuestion(csElement.data.id));
|
||||
}}
|
||||
/>,
|
||||
!csElement.data.isRoot && !isQuestionProhibited(csElement.data.qtype) && (
|
||||
<CsSettingsButton
|
||||
key={`settings-${csElement.data.id}`}
|
||||
ref={(r) => {
|
||||
const buttonData = buttonRefsById.current[csElement.data.id];
|
||||
if (buttonData) buttonData.settings = r;
|
||||
}}
|
||||
onClick={() => {
|
||||
updateOpenedModalSettingsId(csElement.data.id);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
!csElement.data.isRoot &&
|
||||
!isQuestionProhibited(csElement.data.qtype) && (
|
||||
<CsSettingsButton
|
||||
key={`settings-${csElement.data.id}`}
|
||||
ref={(r) => {
|
||||
const buttonData = buttonRefsById.current[csElement.data.id];
|
||||
if (buttonData) buttonData.settings = r;
|
||||
}}
|
||||
onClick={() => {
|
||||
updateOpenedModalSettingsId(csElement.data.id);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
//оболочка узла
|
||||
<CsSelectButton
|
||||
key={`select-${csElement.data.id}`}
|
||||
@ -87,7 +103,7 @@ export default function CsNodeButtons({ csElements, cyRef }: Props) {
|
||||
}}
|
||||
onClick={() => {
|
||||
setModalQuestionParentContentId(csElement.data.id);
|
||||
setOpenedModalQuestions(!(isQuestionProhibited(csElement.data.type) && csElement.data.children > 0));
|
||||
setOpenedModalQuestions(canAddChildToQuestion(csElement.data.id));
|
||||
}}
|
||||
/>,
|
||||
])}
|
||||
@ -130,7 +146,10 @@ export default function CsNodeButtons({ csElements, cyRef }: Props) {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return buttons;
|
||||
const container = cyRef.current?.container();
|
||||
const buttonsPortal = container ? createPortal(buttons, container) : null;
|
||||
|
||||
return buttonsPortal;
|
||||
}
|
||||
|
||||
const applyButtonStyleByType: Record<
|
||||
@ -139,8 +158,10 @@ const applyButtonStyleByType: Record<
|
||||
> = {
|
||||
delete(button, node, zoom) {
|
||||
const nodePosition = node.renderedPosition();
|
||||
const shiftX = node.renderedWidth() / 2 - (CLOSE_BUTTON_WIDTH / 2 + 6) * zoom;
|
||||
const shiftY = node.renderedHeight() / 2 - (CLOSE_BUTTON_HEIGHT / 2 + 6) * zoom;
|
||||
const shiftX =
|
||||
node.renderedWidth() / 2 - (CLOSE_BUTTON_WIDTH / 2 + 6) * zoom;
|
||||
const shiftY =
|
||||
node.renderedHeight() / 2 - (CLOSE_BUTTON_HEIGHT / 2 + 6) * zoom;
|
||||
|
||||
if (!isNodeInViewport(node, 100)) {
|
||||
return button.style.setProperty("display", "none");
|
||||
@ -151,12 +172,13 @@ const applyButtonStyleByType: Record<
|
||||
button.style.setProperty("top", `${nodePosition.y}px`);
|
||||
button.style.setProperty(
|
||||
"transform",
|
||||
`translate3d(calc(-50% + ${shiftX}px), calc(-50% - ${shiftY}px), 0) scale(${zoom})`
|
||||
`translate3d(calc(-50% + ${shiftX}px), calc(-50% - ${shiftY}px), 0) scale(${zoom})`,
|
||||
);
|
||||
},
|
||||
add(button, node, zoom) {
|
||||
const nodePosition = node.renderedPosition();
|
||||
const shiftX = node.renderedWidth() / 2 + (ADD_BUTTON_WIDTH / 2) * zoom;
|
||||
const shiftX =
|
||||
node.renderedWidth() / 2 + (ADD_BUTTON_WIDTH / 2 - 10) * zoom;
|
||||
|
||||
if (!isNodeInViewport(node, 100)) {
|
||||
return button.style.setProperty("display", "none");
|
||||
@ -165,11 +187,15 @@ const applyButtonStyleByType: Record<
|
||||
button.style.setProperty("display", "flex");
|
||||
button.style.setProperty("left", `${nodePosition.x}px`);
|
||||
button.style.setProperty("top", `${nodePosition.y}px`);
|
||||
button.style.setProperty("transform", `translate3d(calc(-50% + ${shiftX}px), -50%, 0) scale(${zoom})`);
|
||||
button.style.setProperty(
|
||||
"transform",
|
||||
`translate3d(calc(-50% + ${shiftX}px), -50%, 0) scale(${zoom})`,
|
||||
);
|
||||
},
|
||||
settings(button, node, zoom) {
|
||||
const nodePosition = node.renderedPosition();
|
||||
const shiftX = -node.renderedWidth() / 2 - (SETTINGS_BUTTON_WIDTH / 2) * zoom;
|
||||
const shiftX =
|
||||
-node.renderedWidth() / 2 - (SETTINGS_BUTTON_WIDTH / 2) * zoom;
|
||||
|
||||
if (!isNodeInViewport(node, 100)) {
|
||||
return button.style.setProperty("display", "none");
|
||||
@ -178,7 +204,10 @@ const applyButtonStyleByType: Record<
|
||||
button.style.setProperty("display", "flex");
|
||||
button.style.setProperty("left", `${nodePosition.x}px`);
|
||||
button.style.setProperty("top", `${nodePosition.y}px`);
|
||||
button.style.setProperty("transform", `translate3d(calc(-50% + ${shiftX}px), -50%, 0) scale(${zoom})`);
|
||||
button.style.setProperty(
|
||||
"transform",
|
||||
`translate3d(calc(-50% + ${shiftX}px), -50%, 0) scale(${zoom})`,
|
||||
);
|
||||
},
|
||||
select(button, node, zoom) {
|
||||
const nodePosition = node.renderedPosition();
|
||||
@ -190,7 +219,10 @@ const applyButtonStyleByType: Record<
|
||||
button.style.setProperty("display", "flex");
|
||||
button.style.setProperty("left", `${nodePosition.x}px`);
|
||||
button.style.setProperty("top", `${nodePosition.y}px`);
|
||||
button.style.setProperty("transform", `translate3d(-50%, -50%, 0) scale(${zoom})`);
|
||||
button.style.setProperty(
|
||||
"transform",
|
||||
`translate3d(-50%, -50%, 0) scale(${zoom})`,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@ -233,9 +265,9 @@ const ADD_BUTTON_HEIGHT = ADD_BUTTON_WIDTH;
|
||||
const CsAddButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
{
|
||||
onPointerUp: () => void;
|
||||
onClick: () => void;
|
||||
}
|
||||
>(({ onPointerUp }, ref) => (
|
||||
>(({ onClick }, ref) => (
|
||||
<IconButton
|
||||
ref={ref}
|
||||
sx={{
|
||||
@ -252,7 +284,7 @@ const CsAddButton = forwardRef<
|
||||
backgroundColor: "#f9f9fc",
|
||||
},
|
||||
}}
|
||||
onPointerUp={onPointerUp}
|
||||
onClick={onClick}
|
||||
onMouseDownCapture={(event) => event.stopPropagation()}
|
||||
onTouchStartCapture={(event) => event.stopPropagation()}
|
||||
>
|
||||
@ -260,8 +292,8 @@ const CsAddButton = forwardRef<
|
||||
</IconButton>
|
||||
));
|
||||
|
||||
const SETTINGS_BUTTON_WIDTH = 70;
|
||||
const SETTINGS_BUTTON_HEIGHT = 60;
|
||||
const SETTINGS_BUTTON_WIDTH = 50;
|
||||
const SETTINGS_BUTTON_HEIGHT = 40;
|
||||
|
||||
const CsSettingsButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
@ -329,7 +361,8 @@ const CsSelectButton = forwardRef<
|
||||
width: SELECT_BUTTON_WIDTH,
|
||||
height: SELECT_BUTTON_HEIGHT,
|
||||
backgroundColor: "rgb(0 0 0 / 0)",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #9A9AAF",
|
||||
borderRadius: "6px",
|
||||
p: 0,
|
||||
zIndex: 0,
|
||||
}}
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { Box } from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
clearRuleForAll,
|
||||
createResult,
|
||||
@ -14,8 +20,11 @@ import {
|
||||
import { useUiTools } from "@root/uiTools/store";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
||||
import { GrayPlus } from "@icons/questionsPage/GrayPlus";
|
||||
|
||||
export const FirstNodeField = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
const quiz = useCurrentQuiz();
|
||||
const modalQuestionTargetContentId = useUiTools(
|
||||
(state) => state.modalQuestionTargetContentId,
|
||||
@ -80,17 +89,49 @@ export const FirstNodeField = () => {
|
||||
<Box
|
||||
ref={Container}
|
||||
sx={{
|
||||
height: "100%",
|
||||
height: isMobile ? "327px" : "481px",
|
||||
width: "100%",
|
||||
backgroundColor: "#f2f3f7",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: "#4d4d4d",
|
||||
color: theme.palette.grey3.main,
|
||||
fontSize: "50px",
|
||||
borderRadius: "12px",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
+
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
width: "275px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.grey2.main,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Добавьте созданные вопросы и настройте связи между ними
|
||||
</Typography>
|
||||
<Button
|
||||
sx={{
|
||||
width: "70px",
|
||||
height: "70px",
|
||||
borderRadius: "12px",
|
||||
background: "rgba(154, 154, 175, 0.09)",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<GrayPlus />
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
99
src/pages/Questions/BranchingMap/InfoBanner/InfoBanner.tsx
Normal file
99
src/pages/Questions/BranchingMap/InfoBanner/InfoBanner.tsx
Normal file
@ -0,0 +1,99 @@
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { EditIcon } from "@icons/questionsPage/EditIcon";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { Dispatch, FC, SetStateAction } from "react";
|
||||
|
||||
type InfoBannerProps = {
|
||||
setBannerVisible: Dispatch<SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
export const InfoBanner: FC<InfoBannerProps> = ({ setBannerVisible }) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "30px",
|
||||
right: "30px",
|
||||
padding: "20px",
|
||||
borderRadius: "12px",
|
||||
background: "#fff",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
zIndex: 2,
|
||||
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
|
||||
animation: "fadeIn 0.5s",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: isMobile ? "253px" : "345px",
|
||||
color: "#9A9AAF",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography>Добавьте больше вопросов кнопкой</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
width: "20px",
|
||||
height: "20px",
|
||||
color: "rgba(154, 154, 175, 0.5)",
|
||||
backgroundColor: "#eeeff4",
|
||||
borderRadius: "6px",
|
||||
border: "1.5px dashed rgba(154, 154, 175, 0.5)",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Add fontSize={"small"} />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
Настраивайте условия их отображения в квизе с помощью
|
||||
</Typography>
|
||||
<EditIcon />
|
||||
</Box>
|
||||
</Box>
|
||||
<IconButton
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "-10px",
|
||||
right: "-10px",
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
background: "#4D4D4D",
|
||||
opacity: "0.2",
|
||||
BorderRadius: "50%",
|
||||
color: "#fff",
|
||||
"&:hover": {
|
||||
background: "black",
|
||||
},
|
||||
}}
|
||||
onClick={() => setBannerVisible(false)}
|
||||
>
|
||||
<CloseIcon fontSize={"medium"} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,64 @@
|
||||
import { Box, Button, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { ExpandIcon } from "@icons/questionsPage/ExpandIcon";
|
||||
import { AlignIcon } from "@icons/questionsPage/AlignIcon";
|
||||
import { FC, MutableRefObject } from "react";
|
||||
import { Core } from "cytoscape";
|
||||
|
||||
type PositionControlProps = {
|
||||
cyRef: MutableRefObject<Core | null>;
|
||||
};
|
||||
|
||||
export const PositionControl: FC<PositionControlProps> = ({ cyRef }) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
bottom: isMobile ? "15px" : "20px",
|
||||
left: isMobile ? "15px" : "20px",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
minWidth: "36px",
|
||||
width: "36px",
|
||||
height: "36px",
|
||||
background: "#9A9AAF",
|
||||
opacity: "0.7",
|
||||
zIndex: 2,
|
||||
}}
|
||||
onClick={() => {
|
||||
cyRef.current?.zoom(1);
|
||||
cyRef.current?.center();
|
||||
}}
|
||||
>
|
||||
<ExpandIcon />
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
minWidth: "36px",
|
||||
width: "36px",
|
||||
height: "36px",
|
||||
background: "#9A9AAF",
|
||||
opacity: "0.7",
|
||||
zIndex: 2,
|
||||
}}
|
||||
onClick={() => {
|
||||
cyRef.current?.fit(undefined, 70);
|
||||
}}
|
||||
>
|
||||
<AlignIcon />
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
72
src/pages/Questions/BranchingMap/ZoomControl/ZoomControl.tsx
Normal file
72
src/pages/Questions/BranchingMap/ZoomControl/ZoomControl.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { Box, Button, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { FC, MutableRefObject } from "react";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import RemoveIcon from "@mui/icons-material/Remove";
|
||||
import { Core } from "cytoscape";
|
||||
|
||||
type PositionControlProps = {
|
||||
cyRef: MutableRefObject<Core | null>;
|
||||
};
|
||||
|
||||
export const ZoomControl: FC<PositionControlProps> = ({ cyRef }) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
bottom: isMobile ? "15px" : "20px",
|
||||
right: isMobile ? "15px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
background: "#EEE4FC",
|
||||
padding: "16px",
|
||||
borderRadius: "8px",
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
minWidth: "36px",
|
||||
width: "36px",
|
||||
height: "36px",
|
||||
background: "#7E2AEA",
|
||||
fontSize: "16px",
|
||||
color: "#fff",
|
||||
zIndex: 2,
|
||||
}}
|
||||
onClick={() => {
|
||||
const currentZoom = cyRef.current?.zoom() || 1;
|
||||
cyRef.current?.zoom(currentZoom + 0.1);
|
||||
}}
|
||||
>
|
||||
<AddIcon />
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
minWidth: "36px",
|
||||
width: "36px",
|
||||
height: "36px",
|
||||
background: "#7E2AEA",
|
||||
fontSize: "16px",
|
||||
color: "#fff",
|
||||
zIndex: 2,
|
||||
}}
|
||||
onClick={() => {
|
||||
const currentZoom = cyRef.current?.zoom() || 1;
|
||||
cyRef.current?.zoom(currentZoom - 0.1);
|
||||
}}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,4 +1,3 @@
|
||||
import { QuestionType } from "@model/question/question";
|
||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||
import {
|
||||
AnyTypedQuizQuestion,
|
||||
@ -66,7 +65,8 @@ export const storeToNodes = (questions: AnyTypedQuizQuestion[]) => {
|
||||
question.title === "" || question.title === " "
|
||||
? "noname"
|
||||
: question.title;
|
||||
if (label.length > 10) label = label.slice(0, 10).toLowerCase() + "…";
|
||||
label = label.trim().replace(/\s+/g, " ");
|
||||
if (label.length > 20) label = label.slice(0, 20).toLowerCase() + "…";
|
||||
|
||||
nodes.push({
|
||||
data: {
|
||||
@ -307,7 +307,7 @@ export function calcNodePosition(node: any) {
|
||||
const width = n.data("subtreeWidth");
|
||||
|
||||
n.data("oldPos", {
|
||||
x: 250 * n.data("layer"),
|
||||
x: 350 * n.data("layer"),
|
||||
y: yoffset + width / 2,
|
||||
});
|
||||
yoffset += width;
|
||||
@ -326,6 +326,21 @@ export function calcNodePosition(node: any) {
|
||||
}
|
||||
}
|
||||
|
||||
export const canAddChildToQuestion = (parentNodeContentId: string) => {
|
||||
const parentQuestion = {
|
||||
...getQuestionByContentId(parentNodeContentId),
|
||||
} as AnyTypedQuizQuestion;
|
||||
if (
|
||||
parentQuestion.type !== undefined &&
|
||||
isQuestionProhibited(parentQuestion.type) &&
|
||||
parentQuestion.content.rule.children.length > 0
|
||||
) {
|
||||
enqueueSnackbar("У вопроса этого типа может быть только 1 потомок");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const addNode = ({
|
||||
parentNodeContentId,
|
||||
targetNodeContentId,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box } from "@mui/material";
|
||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { useUiTools } from "@root/uiTools/store";
|
||||
import { BranchingQuestionsModal } from "../BranchingQuestionsModal";
|
||||
@ -6,6 +6,8 @@ import CsComponent from "./CsComponent";
|
||||
import { FirstNodeField } from "./FirstNodeField";
|
||||
|
||||
export const BranchingMap = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
const quiz = useCurrentQuiz();
|
||||
const dragQuestionContentId = useUiTools((state) => state.dragQuestionContentId);
|
||||
|
||||
@ -13,13 +15,15 @@ export const BranchingMap = () => {
|
||||
<Box
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
padding: "20px",
|
||||
background: "#FFFFFF",
|
||||
padding: isMobile ? "15px" : "20px",
|
||||
background: theme.palette.common.white,
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||
marginBottom: "40px",
|
||||
height: "568px",
|
||||
border: dragQuestionContentId === null ? "none" : "#7e2aea 2px dashed",
|
||||
width: "100%",
|
||||
border:
|
||||
dragQuestionContentId === null
|
||||
? "none"
|
||||
: `${theme.palette.brightPurple.main} 2px dashed`,
|
||||
}}
|
||||
>
|
||||
{quiz?.config.haveRoot ? <CsComponent /> : <FirstNodeField />}
|
||||
|
@ -9,13 +9,14 @@ export const stylesheet: Stylesheet[] = [
|
||||
height: 130,
|
||||
backgroundColor: "#FFFFFF",
|
||||
label: "data(label)",
|
||||
"font-size": "16",
|
||||
"font-size": "12",
|
||||
color: "#4D4D4D",
|
||||
"text-halign": "center",
|
||||
"text-halign": "right",
|
||||
"text-valign": "center",
|
||||
"text-wrap": "wrap",
|
||||
"text-max-width": "130px",
|
||||
"text-overflow-wrap": "whitespace",
|
||||
"text-margin-x": -115,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -40,7 +41,7 @@ export const stylesheet: Stylesheet[] = [
|
||||
"line-color": "#DEDFE7",
|
||||
"curve-style": "taxi",
|
||||
"taxi-direction": "horizontal",
|
||||
"taxi-turn": 60,
|
||||
"taxi-turn": 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1,34 +1,26 @@
|
||||
import { useState, useRef, useEffect, useLayoutEffect } from "react";
|
||||
import { useLayoutEffect, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
Link,
|
||||
Modal,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Tooltip,
|
||||
Typography,
|
||||
useTheme,
|
||||
Checkbox,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
AnyTypedQuizQuestion,
|
||||
createBranchingRuleMain,
|
||||
} from "../../../model/questionTypes/shared";
|
||||
import { Select } from "../Select";
|
||||
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
import InfoIcon from "@icons/Info";
|
||||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
||||
|
||||
import { TypeSwitch, BlockRule } from "./Settings";
|
||||
import { TypeSwitch } from "./Settings";
|
||||
import {
|
||||
getQuestionById,
|
||||
getQuestionByContentId,
|
||||
getQuestionById,
|
||||
updateQuestion,
|
||||
} from "@root/questions/actions";
|
||||
import { updateOpenedModalSettingsId } from "@root/uiTools/actions";
|
||||
@ -101,7 +93,7 @@ export default function BranchingQuestions() {
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
maxWidth: "620px",
|
||||
width: "100%",
|
||||
width: "90%",
|
||||
bgcolor: "background.paper",
|
||||
borderRadius: "12px",
|
||||
boxShadow: 24,
|
||||
@ -114,30 +106,30 @@ export default function BranchingQuestions() {
|
||||
boxSizing: "border-box",
|
||||
background: "#F2F3F7",
|
||||
height: "70px",
|
||||
padding: "0 25px",
|
||||
padding: "25px 20px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
color: "#9A9AAF",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ color: "#4d4d4d" }}>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
maxWidth: isSmallMobile
|
||||
? "250px"
|
||||
: isMobile
|
||||
? "350px"
|
||||
: "450px",
|
||||
display: "inline-block",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{targetQuestion.title}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
lineHeight: "1",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
maxWidth: isSmallMobile
|
||||
? "250px"
|
||||
: isMobile
|
||||
? "350px"
|
||||
: "450px",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
{targetQuestion.title}
|
||||
</Typography>
|
||||
{isMobile ? (
|
||||
<TooltipClickInfo
|
||||
title={
|
||||
@ -150,7 +142,7 @@ export default function BranchingQuestions() {
|
||||
placement="top"
|
||||
>
|
||||
<Box>
|
||||
<InfoIcon />
|
||||
<InfoIcon sx={{ padding: 0 }} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
@ -1,4 +1,14 @@
|
||||
import { Box, Modal, Button, Typography } from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControlLabel,
|
||||
Modal,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { useQuestionsStore } from "@root/questions/store";
|
||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||
import { useUiTools } from "@root/uiTools/store";
|
||||
@ -6,12 +16,20 @@ import {
|
||||
setModalQuestionTargetContentId,
|
||||
setOpenedModalQuestions,
|
||||
} from "@root/uiTools/actions";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
import { RoundedCheckedIcon } from "@icons/questionsPage/RoundedCheckedIcon";
|
||||
|
||||
export const BranchingQuestionsModal = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
|
||||
const trashQuestions = useQuestionsStore().questions;
|
||||
const questions = trashQuestions.filter(
|
||||
(question) => question.type !== "result",
|
||||
);
|
||||
|
||||
const openedModalQuestions = useUiTools(
|
||||
(state) => state.openedModalQuestions,
|
||||
);
|
||||
@ -21,14 +39,30 @@ export const BranchingQuestionsModal = () => {
|
||||
};
|
||||
|
||||
const typedQuestions: AnyTypedQuizQuestion[] = questions.filter(
|
||||
(question) =>
|
||||
question.type &&
|
||||
!question.content.rule.parentId &&
|
||||
question.type !== "result",
|
||||
(question) => question.type && question.type !== "result",
|
||||
) as AnyTypedQuizQuestion[];
|
||||
|
||||
if (typedQuestions.length === 0) return <></>;
|
||||
|
||||
const [selectedQuestion, setSelectedQuestion] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (openedModalQuestions) {
|
||||
setSelectedQuestion(null);
|
||||
}
|
||||
}, [openedModalQuestions]);
|
||||
|
||||
const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedQuestion(event.target.value);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (selectedQuestion) {
|
||||
setModalQuestionTargetContentId(selectedQuestion);
|
||||
}
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal open={openedModalQuestions} onClose={handleClose}>
|
||||
<Box
|
||||
@ -39,48 +73,91 @@ export const BranchingQuestionsModal = () => {
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
maxWidth: "620px",
|
||||
width: "100%",
|
||||
width: "90%",
|
||||
bgcolor: "background.paper",
|
||||
borderRadius: "12px",
|
||||
boxShadow: 24,
|
||||
padding: "30px 0",
|
||||
height: "80vh",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ margin: "0 auto", maxWidth: "350px" }}>
|
||||
{typedQuestions.map((question) => (
|
||||
<Button
|
||||
<Box
|
||||
sx={{ width: "100%", background: theme.palette.background.default }}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
padding: "25px 0 25px 20px",
|
||||
color: theme.palette.grey2.main,
|
||||
}}
|
||||
>
|
||||
Выберите вопрос, который вы хотите добавить в ветвление
|
||||
</Typography>
|
||||
</Box>
|
||||
<RadioGroup
|
||||
value={selectedQuestion}
|
||||
onChange={handleRadioChange}
|
||||
sx={{
|
||||
height: "346px",
|
||||
width: "100%",
|
||||
overflow: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexWrap: "nowrap",
|
||||
}}
|
||||
>
|
||||
{typedQuestions.map((question, index) => (
|
||||
<FormControlLabel
|
||||
key={question.content.id}
|
||||
onClick={() => {
|
||||
setModalQuestionTargetContentId(question.content.id);
|
||||
handleClose();
|
||||
}}
|
||||
value={question.content.id}
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck />}
|
||||
icon={
|
||||
question.content.rule.parentId ? (
|
||||
<RoundedCheckedIcon />
|
||||
) : (
|
||||
<RadioIcon />
|
||||
)
|
||||
}
|
||||
/>
|
||||
}
|
||||
label={question.title || "нет заголовка"}
|
||||
disabled={!!question.content.rule.parentId}
|
||||
sx={{
|
||||
padding: "8px 12px",
|
||||
margin: 0,
|
||||
width: "100%",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: "12px",
|
||||
background: "#FFFFFF",
|
||||
borderRadius: "8px",
|
||||
marginBottom: "20px",
|
||||
boxShadow: "0px 10px 30px #e7e7e7",
|
||||
backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='rgb(154, 154, 175)' strokeWidth='2' stroke-dasharray='8 8' stroke-dashoffset='0' strokeLinecap='square'/%3e%3c/svg%3e");
|
||||
border-radius: 8px;`,
|
||||
"&:last-child": { marginBottom: 0 },
|
||||
backgroundColor:
|
||||
index % 2 === 0
|
||||
? theme.palette.common.white
|
||||
: "rgba(242, 243, 247, 0.5)",
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
width: "100%",
|
||||
color: "#000",
|
||||
}}
|
||||
>
|
||||
{question.title || "нет заголовка"}
|
||||
</Typography>
|
||||
</Button>
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<Box
|
||||
sx={{
|
||||
margin: "20px",
|
||||
width: "auto",
|
||||
display: "flex",
|
||||
justifyContent: isMobile ? "space-between" : "end",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{ width: isMobile ? "150px" : "130px", height: "44px" }}
|
||||
onClick={handleClose}
|
||||
variant={"outlined"}
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
sx={{ width: isMobile ? "150px" : "130px", height: "44px" }}
|
||||
variant={"contained"}
|
||||
onClick={handleConfirm}
|
||||
>
|
||||
Готово
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Box, Skeleton, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { deleteTimeoutedQuestions } from "@utils/deleteTimeoutedQuestions";
|
||||
import { useCallback } from "react";
|
||||
import { BranchingMap } from "./BranchingMap";
|
||||
import { lazy, Suspense, useCallback } from "react";
|
||||
import { DraggableList } from "./DraggableList";
|
||||
import { SwitchBranchingPanel } from "./SwitchBranchingPanel";
|
||||
|
||||
const BranchingMap = lazy(() =>
|
||||
import("./BranchingMap").then((module) => ({ default: module.BranchingMap })),
|
||||
);
|
||||
interface Props {
|
||||
openBranchingPage: boolean;
|
||||
setOpenBranchingPage: (a: boolean) => void;
|
||||
@ -18,6 +20,7 @@ export const QuestionSwitchWindowTool = ({
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
|
||||
const openBranchingPageHC = useCallback(() => {
|
||||
if (!openBranchingPage) {
|
||||
@ -30,14 +33,26 @@ export const QuestionSwitchWindowTool = ({
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
marginBottom: isMobile ? "20px" : undefined,
|
||||
marginBottom: isMobile ? "25px" : "30px",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flexBasis: "796px" }}>
|
||||
<Box sx={{ width: isTablet ? "100%" : "796px" }}>
|
||||
{openBranchingPage ? (
|
||||
<BranchingMap />
|
||||
<Suspense
|
||||
fallback={
|
||||
<Skeleton
|
||||
sx={{
|
||||
maxWidth: "796px",
|
||||
width: "100%",
|
||||
height: isMobile ? "357px" : "521px",
|
||||
transform: "none",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<BranchingMap />
|
||||
</Suspense>
|
||||
) : (
|
||||
<DraggableList
|
||||
openBranchingPage={openBranchingPage}
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { Box, Button, IconButton, Typography, useTheme } from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
collapseAllQuestions,
|
||||
createUntypedQuestion,
|
||||
@ -11,7 +18,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { updateEditSomeQuestion } from "@root/uiTools/actions";
|
||||
import { useUiTools } from "@root/uiTools/store";
|
||||
import QuizPreview from "@ui_kit/QuizPreview/QuizPreview";
|
||||
import { useLayoutEffect, useRef } from "react";
|
||||
import { useLayoutEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import AddPlus from "../../assets/icons/questionsPage/addPlus";
|
||||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||
@ -30,6 +37,7 @@ export default function QuestionsPage({
|
||||
widthMain,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
const { openedModalSettingsId } = useUiTools();
|
||||
const quiz = useCurrentQuiz();
|
||||
useLayoutEffect(() => {
|
||||
@ -47,7 +55,7 @@ export default function QuestionsPage({
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
margin: "60px 0 40px 0",
|
||||
margin: isMobile ? "25px 0" : "40px 0",
|
||||
}}
|
||||
>
|
||||
<Typography variant={"h5"} sx={{ wordBreak: "break-word" }}>
|
||||
|
@ -1,14 +1,6 @@
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Switch,
|
||||
useTheme,
|
||||
Button,
|
||||
useMediaQuery,
|
||||
} from "@mui/material";
|
||||
|
||||
import { QuestionsList } from "./QuestionsList";
|
||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { PanelSwitchQuestionListGraph } from "@ui_kit/Toolbars/PanelSwitchQuestionListGraph";
|
||||
|
||||
interface Props {
|
||||
openBranchingPage: boolean;
|
||||
setOpenBranchingPage: () => void;
|
||||
@ -31,9 +23,6 @@ export const SwitchBranchingPanel = ({
|
||||
setOpenBranchingPage={setOpenBranchingPage}
|
||||
/>
|
||||
)}
|
||||
{openBranchingPage && (
|
||||
<QuestionsList setOpenBranchingPage={setOpenBranchingPage} />
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<></>
|
||||
|
@ -177,7 +177,7 @@ export default function Main({ sidebar, header, footer, Page }: Props) {
|
||||
gap: isMobile ? "5px" : "15px",
|
||||
background: "#FFF",
|
||||
borderTop: "#f2f3f7 2px solid",
|
||||
zIndex: 1,
|
||||
zIndex: 3,
|
||||
position: isMobile ? "fixed" : undefined,
|
||||
bottom: isMobile ? 0 : undefined,
|
||||
}}
|
||||
|
@ -17,7 +17,7 @@ export const SmallSwitchQuestionListGraph = ({
|
||||
width: "77px",
|
||||
height: "51px",
|
||||
position: "fixed",
|
||||
zIndex: "1111",
|
||||
zIndex: "999",
|
||||
right: "0",
|
||||
top: "200px",
|
||||
background: "#333647",
|
||||
|
Loading…
Reference in New Issue
Block a user