2023-12-04 13:33:43 +00:00
|
|
|
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
2023-11-29 15:45:15 +00:00
|
|
|
|
import Cytoscape from "cytoscape";
|
|
|
|
|
|
import CytoscapeComponent from "react-cytoscapejs";
|
|
|
|
|
|
import popper from "cytoscape-popper";
|
2023-12-20 12:34:07 +00:00
|
|
|
|
import { Button } from "@mui/material";
|
|
|
|
|
|
import { withErrorBoundary } from "react-error-boundary";
|
|
|
|
|
|
import { enqueueSnackbar } from "notistack";
|
|
|
|
|
|
|
2023-11-29 15:45:15 +00:00
|
|
|
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
2023-12-20 10:46:38 +00:00
|
|
|
|
import { updateRootContentId } from "@root/quizes/actions";
|
|
|
|
|
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
2023-11-29 15:45:15 +00:00
|
|
|
|
import { useQuestionsStore } from "@root/questions/store";
|
2023-12-20 12:34:07 +00:00
|
|
|
|
import { useUiTools } from "@root/uiTools/store";
|
2023-12-20 10:46:38 +00:00
|
|
|
|
import {
|
|
|
|
|
|
deleteQuestion,
|
|
|
|
|
|
updateQuestion,
|
|
|
|
|
|
getQuestionByContentId,
|
|
|
|
|
|
clearRuleForAll,
|
|
|
|
|
|
createFrontResult,
|
|
|
|
|
|
} from "@root/questions/actions";
|
|
|
|
|
|
import { updateOpenedModalSettingsId } from "@root/uiTools/actions";
|
2023-12-14 09:40:53 +00:00
|
|
|
|
import { cleardragQuestionContentId } from "@root/uiTools/actions";
|
2023-11-29 15:45:15 +00:00
|
|
|
|
|
2023-12-20 10:46:38 +00:00
|
|
|
|
import { useRemoveNode } from "./hooks/useRemoveNode";
|
|
|
|
|
|
import { usePopper } from "./hooks/usePopper";
|
|
|
|
|
|
|
2023-11-29 15:45:15 +00:00
|
|
|
|
import { storeToNodes } from "./helper";
|
2023-12-20 10:46:38 +00:00
|
|
|
|
import { stylesheet } from "./stylesheet";
|
2023-11-29 15:45:15 +00:00
|
|
|
|
|
|
|
|
|
|
import "./styles.css";
|
|
|
|
|
|
|
2023-12-20 12:34:07 +00:00
|
|
|
|
import type { Core } from "cytoscape";
|
2023-11-29 15:45:15 +00:00
|
|
|
|
|
|
|
|
|
|
Cytoscape.use(popper);
|
|
|
|
|
|
|
2023-12-20 10:46:38 +00:00
|
|
|
|
type CsComponentProps = {
|
2023-12-01 19:56:13 +00:00
|
|
|
|
modalQuestionParentContentId: string;
|
|
|
|
|
|
modalQuestionTargetContentId: string;
|
2023-12-02 13:11:57 +00:00
|
|
|
|
setOpenedModalQuestions: (open: boolean) => void;
|
|
|
|
|
|
setModalQuestionParentContentId: (id: string) => void;
|
|
|
|
|
|
setModalQuestionTargetContentId: (id: string) => void;
|
2023-12-20 10:46:38 +00:00
|
|
|
|
};
|
2023-12-01 19:56:13 +00:00
|
|
|
|
|
2023-12-11 10:08:54 +00:00
|
|
|
|
function CsComponent({
|
2023-12-01 19:56:13 +00:00
|
|
|
|
modalQuestionParentContentId,
|
|
|
|
|
|
modalQuestionTargetContentId,
|
|
|
|
|
|
setOpenedModalQuestions,
|
|
|
|
|
|
setModalQuestionParentContentId,
|
2023-12-20 10:46:38 +00:00
|
|
|
|
setModalQuestionTargetContentId,
|
|
|
|
|
|
}: CsComponentProps) {
|
2023-11-29 15:45:15 +00:00
|
|
|
|
const quiz = useCurrentQuiz();
|
|
|
|
|
|
|
2023-12-20 10:46:38 +00:00
|
|
|
|
const { dragQuestionContentId, desireToOpenABranchingModal } = useUiTools();
|
|
|
|
|
|
const trashQuestions = useQuestionsStore().questions;
|
|
|
|
|
|
const questions = trashQuestions.filter(
|
|
|
|
|
|
(question) => question.type !== "result" && question.type !== null
|
|
|
|
|
|
);
|
2023-11-29 15:45:15 +00:00
|
|
|
|
const [startCreate, setStartCreate] = useState("");
|
|
|
|
|
|
const [startRemove, setStartRemove] = useState("");
|
|
|
|
|
|
|
|
|
|
|
|
const cyRef = useRef<Core | null>(null);
|
2023-12-01 08:12:59 +00:00
|
|
|
|
const layoutsContainer = useRef<HTMLDivElement | null>(null);
|
2023-11-29 15:45:15 +00:00
|
|
|
|
const plusesContainer = useRef<HTMLDivElement | null>(null);
|
|
|
|
|
|
const crossesContainer = useRef<HTMLDivElement | null>(null);
|
|
|
|
|
|
const gearsContainer = useRef<HTMLDivElement | null>(null);
|
|
|
|
|
|
|
2023-12-20 10:55:38 +00:00
|
|
|
|
const { layoutOptions } = usePopper({
|
|
|
|
|
|
layoutsContainer,
|
|
|
|
|
|
plusesContainer,
|
|
|
|
|
|
crossesContainer,
|
|
|
|
|
|
gearsContainer,
|
|
|
|
|
|
setModalQuestionParentContentId,
|
|
|
|
|
|
setOpenedModalQuestions,
|
|
|
|
|
|
setStartCreate,
|
|
|
|
|
|
setStartRemove,
|
|
|
|
|
|
});
|
|
|
|
|
|
const { removeNode } = useRemoveNode({
|
|
|
|
|
|
cyRef,
|
|
|
|
|
|
layoutOptions,
|
|
|
|
|
|
layoutsContainer,
|
|
|
|
|
|
plusesContainer,
|
|
|
|
|
|
crossesContainer,
|
|
|
|
|
|
gearsContainer,
|
|
|
|
|
|
});
|
2023-12-20 10:46:38 +00:00
|
|
|
|
|
2023-12-05 23:34:40 +00:00
|
|
|
|
useLayoutEffect(() => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
const cy = cyRef?.current;
|
2023-12-05 23:34:40 +00:00
|
|
|
|
if (desireToOpenABranchingModal) {
|
2023-12-07 00:03:43 +00:00
|
|
|
|
setTimeout(() => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
cy?.getElementById(desireToOpenABranchingModal)?.data(
|
|
|
|
|
|
"eroticeyeblink",
|
|
|
|
|
|
true
|
|
|
|
|
|
);
|
|
|
|
|
|
}, 250);
|
2023-12-05 23:34:40 +00:00
|
|
|
|
} else {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
cy?.elements().data("eroticeyeblink", false);
|
2023-12-05 23:34:40 +00:00
|
|
|
|
}
|
2023-12-20 10:46:38 +00:00
|
|
|
|
}, [desireToOpenABranchingModal]);
|
2023-12-05 23:34:40 +00:00
|
|
|
|
useLayoutEffect(() => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
updateOpenedModalSettingsId();
|
|
|
|
|
|
// updateRootContentId(quiz.id, "")
|
|
|
|
|
|
// clearRuleForAll()
|
|
|
|
|
|
}, []);
|
2023-12-02 13:11:57 +00:00
|
|
|
|
useEffect(() => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
if (
|
|
|
|
|
|
modalQuestionTargetContentId.length !== 0 &&
|
|
|
|
|
|
modalQuestionParentContentId.length !== 0
|
|
|
|
|
|
) {
|
|
|
|
|
|
addNode({
|
|
|
|
|
|
parentNodeContentId: modalQuestionParentContentId,
|
|
|
|
|
|
targetNodeContentId: modalQuestionTargetContentId,
|
|
|
|
|
|
});
|
2023-12-02 13:11:57 +00:00
|
|
|
|
}
|
2023-12-01 08:12:59 +00:00
|
|
|
|
|
2023-12-20 10:46:38 +00:00
|
|
|
|
setModalQuestionParentContentId("");
|
|
|
|
|
|
setModalQuestionTargetContentId("");
|
|
|
|
|
|
}, [modalQuestionTargetContentId]);
|
|
|
|
|
|
|
|
|
|
|
|
const addNode = ({
|
|
|
|
|
|
parentNodeContentId,
|
|
|
|
|
|
targetNodeContentId,
|
|
|
|
|
|
}: {
|
|
|
|
|
|
parentNodeContentId: string;
|
|
|
|
|
|
targetNodeContentId?: string;
|
|
|
|
|
|
}) => {
|
2023-12-13 10:53:59 +00:00
|
|
|
|
//запрещаем работу родителя-ребенка если это один и тот же вопрос
|
2023-12-20 10:46:38 +00:00
|
|
|
|
if (parentNodeContentId === targetNodeContentId) return;
|
2023-12-13 10:53:59 +00:00
|
|
|
|
|
2023-12-20 10:46:38 +00:00
|
|
|
|
const cy = cyRef?.current;
|
|
|
|
|
|
const parentNodeChildren = cy?.$(
|
|
|
|
|
|
'edge[source = "' + parentNodeContentId + '"]'
|
|
|
|
|
|
)?.length;
|
2023-12-02 21:20:41 +00:00
|
|
|
|
//если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
|
2023-12-20 10:46:38 +00:00
|
|
|
|
const targetQuestion = {
|
|
|
|
|
|
...getQuestionByContentId(targetNodeContentId || dragQuestionContentId),
|
|
|
|
|
|
} as AnyTypedQuizQuestion;
|
|
|
|
|
|
if (
|
|
|
|
|
|
Object.keys(targetQuestion).length !== 0 &&
|
|
|
|
|
|
parentNodeContentId &&
|
|
|
|
|
|
parentNodeChildren !== undefined
|
|
|
|
|
|
) {
|
|
|
|
|
|
clearDataAfterAddNode({
|
|
|
|
|
|
parentNodeContentId,
|
|
|
|
|
|
targetQuestion,
|
|
|
|
|
|
parentNodeChildren,
|
|
|
|
|
|
});
|
|
|
|
|
|
cy?.data("changed", true);
|
|
|
|
|
|
if (quiz) {
|
|
|
|
|
|
createFrontResult(quiz.backendId, targetQuestion.content.id);
|
|
|
|
|
|
}
|
2023-12-09 20:49:08 +00:00
|
|
|
|
const es = cy?.add([
|
2023-12-01 08:12:59 +00:00
|
|
|
|
{
|
|
|
|
|
|
data: {
|
2023-12-01 19:56:13 +00:00
|
|
|
|
id: targetQuestion.content.id,
|
2023-12-20 10:46:38 +00:00
|
|
|
|
label:
|
|
|
|
|
|
targetQuestion.title === "" || targetQuestion.title === " "
|
|
|
|
|
|
? "noname"
|
|
|
|
|
|
: targetQuestion.title,
|
|
|
|
|
|
},
|
2023-12-01 08:12:59 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
data: {
|
2023-12-01 19:56:13 +00:00
|
|
|
|
source: parentNodeContentId,
|
2023-12-20 10:46:38 +00:00
|
|
|
|
target: targetQuestion.content.id,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
]);
|
|
|
|
|
|
cy?.layout(layoutOptions).run();
|
|
|
|
|
|
console.log(es);
|
|
|
|
|
|
cy?.center(es);
|
2023-12-02 09:59:31 +00:00
|
|
|
|
} else {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
enqueueSnackbar("Добавляемый вопрос не найден");
|
2023-12-02 13:11:57 +00:00
|
|
|
|
}
|
2023-12-20 10:46:38 +00:00
|
|
|
|
};
|
2023-12-11 15:40:42 +00:00
|
|
|
|
|
2023-12-20 10:46:38 +00:00
|
|
|
|
const clearDataAfterAddNode = ({
|
|
|
|
|
|
parentNodeContentId,
|
|
|
|
|
|
targetQuestion,
|
|
|
|
|
|
parentNodeChildren,
|
|
|
|
|
|
}: {
|
|
|
|
|
|
parentNodeContentId: string;
|
|
|
|
|
|
targetQuestion: AnyTypedQuizQuestion;
|
|
|
|
|
|
parentNodeChildren: number;
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
const parentQuestion = {
|
|
|
|
|
|
...getQuestionByContentId(parentNodeContentId),
|
|
|
|
|
|
} as AnyTypedQuizQuestion;
|
2023-12-11 15:40:42 +00:00
|
|
|
|
|
|
|
|
|
|
//смотрим не добавлен ли родителю result. Если да - убираем его. Веточкам result не нужен
|
|
|
|
|
|
trashQuestions.forEach((targetQuestion) => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
if (
|
|
|
|
|
|
targetQuestion.type === "result" &&
|
|
|
|
|
|
targetQuestion.content.rule.parentId === parentQuestion.content.id
|
|
|
|
|
|
) {
|
|
|
|
|
|
console.log("deleteQ", targetQuestion.id);
|
2023-12-11 15:40:42 +00:00
|
|
|
|
deleteQuestion(targetQuestion.id);
|
|
|
|
|
|
}
|
2023-12-20 10:46:38 +00:00
|
|
|
|
});
|
2023-12-11 15:40:42 +00:00
|
|
|
|
|
2023-12-02 09:59:31 +00:00
|
|
|
|
//предупреждаем добавленный вопрос о том, кто его родитель
|
2023-12-20 10:46:38 +00:00
|
|
|
|
updateQuestion(targetQuestion.content.id, (question) => {
|
|
|
|
|
|
question.content.rule.parentId = parentNodeContentId;
|
|
|
|
|
|
question.content.rule.main = [];
|
|
|
|
|
|
});
|
2023-12-11 15:40:42 +00:00
|
|
|
|
//предупреждаем родителя о новом потомке (если он ещё не знает о нём)
|
2023-12-20 10:46:38 +00:00
|
|
|
|
if (
|
|
|
|
|
|
!parentQuestion.content.rule.children.includes(targetQuestion.content.id)
|
|
|
|
|
|
)
|
|
|
|
|
|
updateQuestion(parentNodeContentId, (question) => {
|
|
|
|
|
|
question.content.rule.children = [
|
|
|
|
|
|
...question.content.rule.children,
|
|
|
|
|
|
targetQuestion.content.id,
|
|
|
|
|
|
];
|
|
|
|
|
|
});
|
2023-12-01 19:56:13 +00:00
|
|
|
|
|
2023-12-02 13:11:57 +00:00
|
|
|
|
//Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
|
2023-12-11 15:40:42 +00:00
|
|
|
|
if (parentQuestion.content.rule.children >= 1) {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
updateOpenedModalSettingsId(targetQuestion.content.id);
|
2023-12-02 13:11:57 +00:00
|
|
|
|
}
|
2023-12-20 10:46:38 +00:00
|
|
|
|
};
|
2023-12-02 09:59:31 +00:00
|
|
|
|
|
2023-11-29 15:45:15 +00:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (startCreate) {
|
2023-12-01 19:56:13 +00:00
|
|
|
|
addNode({ parentNodeContentId: startCreate });
|
2023-12-20 10:46:38 +00:00
|
|
|
|
cleardragQuestionContentId();
|
2023-11-29 15:45:15 +00:00
|
|
|
|
setStartCreate("");
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [startCreate]);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (startRemove) {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
removeNode(startRemove);
|
2023-11-29 15:45:15 +00:00
|
|
|
|
setStartRemove("");
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [startRemove]);
|
|
|
|
|
|
|
2023-12-02 09:59:31 +00:00
|
|
|
|
useEffect(() => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
document
|
|
|
|
|
|
.querySelector("#root")
|
|
|
|
|
|
?.addEventListener("mouseup", cleardragQuestionContentId);
|
2023-12-02 09:59:31 +00:00
|
|
|
|
const cy = cyRef.current;
|
2023-12-20 10:46:38 +00:00
|
|
|
|
const eles = cy?.add(
|
|
|
|
|
|
storeToNodes(
|
|
|
|
|
|
questions.filter(
|
|
|
|
|
|
(question) => question.type && question.type !== "result"
|
|
|
|
|
|
) as AnyTypedQuizQuestion[]
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
cy?.data("changed", true);
|
2023-12-05 23:34:40 +00:00
|
|
|
|
// cy.data('changed', true)
|
2023-12-20 10:46:38 +00:00
|
|
|
|
const elecs = eles?.layout(layoutOptions).run();
|
|
|
|
|
|
cy?.on("add", () => cy.data("changed", true));
|
|
|
|
|
|
cy?.fit();
|
2023-12-02 09:59:31 +00:00
|
|
|
|
//cy?.layout().run()
|
2023-11-29 15:45:15 +00:00
|
|
|
|
|
|
|
|
|
|
return () => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
document
|
|
|
|
|
|
.querySelector("#root")
|
|
|
|
|
|
?.removeEventListener("mouseup", cleardragQuestionContentId);
|
2023-12-01 08:12:59 +00:00
|
|
|
|
layoutsContainer.current?.remove();
|
2023-11-29 15:45:15 +00:00
|
|
|
|
plusesContainer.current?.remove();
|
|
|
|
|
|
crossesContainer.current?.remove();
|
|
|
|
|
|
gearsContainer.current?.remove();
|
|
|
|
|
|
};
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2023-12-02 20:47:28 +00:00
|
|
|
|
<>
|
2023-12-11 10:08:54 +00:00
|
|
|
|
<Button
|
|
|
|
|
|
sx={{
|
|
|
|
|
|
mb: "20px",
|
|
|
|
|
|
height: "27px",
|
|
|
|
|
|
color: "#7E2AEA",
|
|
|
|
|
|
textDecoration: "underline",
|
|
|
|
|
|
fontSize: "16px",
|
|
|
|
|
|
}}
|
|
|
|
|
|
variant="text"
|
|
|
|
|
|
onClick={() => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
cyRef.current?.fit();
|
2023-12-11 10:08:54 +00:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
Выровнять
|
|
|
|
|
|
</Button>
|
2023-12-04 09:30:14 +00:00
|
|
|
|
<CytoscapeComponent
|
|
|
|
|
|
wheelSensitivity={0.1}
|
|
|
|
|
|
elements={[]}
|
|
|
|
|
|
// elements={createGraphElements(tree, quiz)}
|
|
|
|
|
|
style={{ height: "480px", background: "#F2F3F7" }}
|
|
|
|
|
|
stylesheet={stylesheet}
|
2023-12-20 10:46:38 +00:00
|
|
|
|
layout={layoutOptions}
|
2023-12-04 09:30:14 +00:00
|
|
|
|
cy={(cy) => {
|
|
|
|
|
|
cyRef.current = cy;
|
|
|
|
|
|
}}
|
2023-12-11 10:08:54 +00:00
|
|
|
|
autoungrabify={true}
|
2023-12-04 09:30:14 +00:00
|
|
|
|
/>
|
2023-12-14 16:50:02 +00:00
|
|
|
|
{/* <button onClick={() => {
|
2023-12-04 09:30:14 +00:00
|
|
|
|
console.log("NODES____________________________")
|
|
|
|
|
|
cyRef.current?.elements().forEach((ele: any) => {
|
|
|
|
|
|
console.log(ele.data())
|
|
|
|
|
|
})
|
|
|
|
|
|
}}>nodes</button>
|
|
|
|
|
|
<button onClick={() => {
|
|
|
|
|
|
console.log("ELEMENTS____________________________")
|
2023-12-04 07:50:55 +00:00
|
|
|
|
console.log(questions)
|
2023-12-14 16:50:02 +00:00
|
|
|
|
}}>elements</button> */}
|
2023-12-02 20:47:28 +00:00
|
|
|
|
</>
|
2023-11-29 15:45:15 +00:00
|
|
|
|
);
|
2023-12-20 10:46:38 +00:00
|
|
|
|
}
|
2023-12-07 00:03:43 +00:00
|
|
|
|
|
2023-12-11 10:08:54 +00:00
|
|
|
|
function Clear() {
|
|
|
|
|
|
const quiz = useCurrentQuiz();
|
2023-12-20 10:46:38 +00:00
|
|
|
|
if (quiz) {
|
|
|
|
|
|
updateRootContentId(quiz.id, "");
|
|
|
|
|
|
}
|
|
|
|
|
|
clearRuleForAll();
|
|
|
|
|
|
return <></>;
|
2023-12-07 00:03:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default withErrorBoundary(CsComponent, {
|
2023-12-11 10:08:54 +00:00
|
|
|
|
fallback: <Clear />,
|
2023-12-07 00:03:43 +00:00
|
|
|
|
onError: (error, info) => {
|
2023-12-20 10:46:38 +00:00
|
|
|
|
enqueueSnackbar("Дерево порвалось");
|
|
|
|
|
|
console.log(info);
|
|
|
|
|
|
console.log(error);
|
2023-12-07 00:03:43 +00:00
|
|
|
|
},
|
2023-12-09 19:31:06 +00:00
|
|
|
|
});
|