WIP redesigned questions-branching window

This commit is contained in:
aleksandr-raw 2024-05-14 19:23:04 +04:00
parent b6e4695dcf
commit 9131826058
14 changed files with 291 additions and 59 deletions

@ -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>
);

@ -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>
);

@ -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>
);

@ -1,19 +1,18 @@
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";
import { useCurrentQuiz } from "@root/quizes/hooks";
import AddIcon from "@mui/icons-material/Add";
import RemoveIcon from "@mui/icons-material/Remove";
import {
cleardragQuestionContentId,
setModalQuestionParentContentId,
setModalQuestionTargetContentId,
updateModalInfoWhyCantCreate,
updateOpenedModalSettingsId,
} from "@root/uiTools/actions";
import { useUiTools } from "@root/uiTools/store";
import { ProblemIcon } from "@ui_kit/ProblemIcon";
import type { Core } from "cytoscape";
import { enqueueSnackbar } from "notistack";
import { useEffect, useLayoutEffect, useMemo, useRef } from "react";
@ -25,8 +24,13 @@ import { addNode, layoutOptions, storeToNodes } from "./helper";
import { useRemoveNode } from "./hooks/useRemoveNode";
import "./style/styles.css";
import { stylesheet } from "./style/stylesheet";
import { Box, Button, useMediaQuery, useTheme } from "@mui/material";
import { AlignIcon } from "@icons/questionsPage/AlignIcon";
import { ExpandIcon } from "@icons/questionsPage/ExpandIcon";
function CsComponent() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(660));
const desireToOpenABranchingModal = useUiTools(
(state) => state.desireToOpenABranchingModal,
);
@ -46,7 +50,6 @@ function CsComponent() {
(question): question is AnyTypedQuizQuestion =>
question.type !== null && question.type !== "result",
);
return storeToNodes(questions);
}, [trashQuestions]);
@ -97,43 +100,137 @@ function CsComponent() {
);
return (
<>
<Box
sx={{
width: "100%",
}}
>
<CsNodeButtons csElements={csElements} cyRef={cyRef} />
<Box mb="20px">
<Button
<Box sx={{ position: "relative" }}>
<Box
sx={{
height: "27px",
color: "#7E2AEA",
textDecoration: "underline",
fontSize: "16px",
}}
variant="text"
onClick={() => {
cyRef.current?.fit(undefined, 70);
position: "absolute",
bottom: isMobile ? "15px" : "20px",
left: isMobile ? "15px" : "20px",
display: "flex",
gap: "10px",
}}
>
Выровнять
</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?.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>
<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>
<CytoscapeComponent
wheelSensitivity={0.1}
elements={csElements}
style={{
height: isMobile ? "327px" : "481px",
background: "#F2F3F7",
overflow: "hidden",
borderRadius: "12px",
width: "100%",
}}
stylesheet={stylesheet}
layout={layoutOptions}
cy={(cy) => {
cyRef.current = cy;
}}
autoungrabify={true}
autounselectify={true}
boxSelectionEnabled={false}
/>
</Box>
<CytoscapeComponent
wheelSensitivity={0.1}
elements={csElements}
style={{
height: "480px",
background: "#F2F3F7",
overflow: "hidden",
}}
stylesheet={stylesheet}
layout={layoutOptions}
cy={(cy) => {
cyRef.current = cy;
}}
autoungrabify={true}
autounselectify={true}
boxSelectionEnabled={false}
/>
<DeleteNodeModal removeNode={removeNode} />
</>
</Box>
);
}

@ -9,8 +9,8 @@ import {
} from "@root/uiTools/actions";
import { Core, EventObject, NodeSingular } from "cytoscape";
import {
MutableRefObject,
forwardRef,
MutableRefObject,
useEffect,
useMemo,
useRef,
@ -56,6 +56,7 @@ export default function CsNodeButtons({ csElements, cyRef }: Props) {
left: 0,
width: "100%",
height: "100%",
borderRadius: "12px",
}}
>
{nodeElements.flatMap((csElement) => [
@ -181,7 +182,8 @@ const applyButtonStyleByType: Record<
},
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");
@ -295,8 +297,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,
@ -364,7 +366,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,7 +89,7 @@ export const FirstNodeField = () => {
<Box
ref={Container}
sx={{
height: "100%",
height: isMobile ? "327px" : "481px",
width: "100%",
backgroundColor: "#f2f3f7",
display: "flex",
@ -88,9 +97,41 @@ export const FirstNodeField = () => {
justifyContent: "center",
color: "#4d4d4d",
fontSize: "50px",
borderRadius: "12px",
padding: "20px",
}}
>
+
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "20px",
width: "275px",
}}
>
<Typography
sx={{
color: "#9A9AAF",
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>
);
};

@ -1,4 +1,3 @@
import { QuestionType } from "@model/question/question";
import { QuizQuestionResult } from "@model/questionTypes/result";
import {
AnyTypedQuizQuestion,
@ -66,7 +65,7 @@ export const storeToNodes = (questions: AnyTypedQuizQuestion[]) => {
question.title === "" || question.title === " "
? "noname"
: question.title;
if (label.length > 10) label = label.slice(0, 10).toLowerCase() + "…";
if (label.length > 20) label = label.slice(0, 20).toLowerCase() + "…";
nodes.push({
data: {
@ -307,7 +306,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;

@ -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,
@ -16,12 +18,12 @@ export const BranchingMap = () => {
id="cytoscape-container"
sx={{
overflow: "hidden",
padding: "20px",
padding: isMobile ? "15px" : "20px",
background: "#FFFFFF",
borderRadius: "12px",
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
marginBottom: "40px",
height: "568px",
width: "100%",
border: dragQuestionContentId === null ? "none" : "#7e2aea 2px dashed",
}}
>

@ -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,4 +1,4 @@
import { Box, Modal, Button, Typography } from "@mui/material";
import { Box, Button, Modal, Typography } from "@mui/material";
import { useQuestionsStore } from "@root/questions/store";
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { useUiTools } from "@root/uiTools/store";

@ -18,6 +18,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) {
@ -35,7 +36,7 @@ export const QuestionSwitchWindowTool = ({
marginBottom: isMobile ? "20px" : undefined,
}}
>
<Box sx={{ flexBasis: "796px" }}>
<Box sx={{ width: isTablet ? "100%" : "796px" }}>
{openBranchingPage ? (
<BranchingMap />
) : (

@ -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" }}>

@ -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",