682 lines
23 KiB
TypeScript
682 lines
23 KiB
TypeScript
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||
import Cytoscape from "cytoscape";
|
||
import CytoscapeComponent from "react-cytoscapejs";
|
||
import popper from "cytoscape-popper";
|
||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||
import { updateRootContentId } from "@root/quizes/actions"
|
||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"
|
||
import { useQuestionsStore } from "@root/questions/store";
|
||
import { cleardragQuestionContentId, updateQuestion, updateOpenedModalSettingsId, getQuestionByContentId } from "@root/questions/actions";
|
||
|
||
import { storeToNodes } from "./helper";
|
||
|
||
import "./styles.css";
|
||
|
||
import type {
|
||
Stylesheet,
|
||
Core,
|
||
NodeSingular,
|
||
AbstractEventObject,
|
||
ElementDefinition,
|
||
} from "cytoscape";
|
||
import { QuestionsList } from "../BranchingPanel/QuestionsList";
|
||
import { enqueueSnackbar } from "notistack";
|
||
|
||
type PopperItem = {
|
||
id: () => string;
|
||
};
|
||
|
||
type Modifier = {
|
||
name: string;
|
||
options: unknown;
|
||
};
|
||
|
||
type PopperConfig = {
|
||
popper: {
|
||
placement: string;
|
||
modifiers?: Modifier[];
|
||
};
|
||
content: (items: PopperItem[]) => void;
|
||
};
|
||
|
||
type Popper = {
|
||
update: () => Promise<void>;
|
||
setOptions: (modifiers: { modifiers?: Modifier[] }) => void;
|
||
};
|
||
|
||
type NodeSingularWithPopper = NodeSingular & {
|
||
popper: (config: PopperConfig) => Popper;
|
||
};
|
||
|
||
const stylesheet: Stylesheet[] = [
|
||
{
|
||
selector: "node",
|
||
style: {
|
||
shape: "round-rectangle",
|
||
width: 130,
|
||
height: 130,
|
||
backgroundColor: "#FFFFFF",
|
||
label: "data(label)",
|
||
"font-size": "16",
|
||
color: "#4D4D4D",
|
||
"text-halign": "center",
|
||
"text-valign": "center",
|
||
"text-wrap": "wrap",
|
||
"text-max-width": "80",
|
||
},
|
||
},
|
||
{
|
||
selector: ".multiline-auto",
|
||
style: {
|
||
"text-wrap": "wrap",
|
||
"text-max-width": "80",
|
||
},
|
||
},
|
||
{
|
||
selector: "edge",
|
||
style: {
|
||
width: 30,
|
||
"line-color": "#DEDFE7",
|
||
"curve-style": "taxi",
|
||
"taxi-direction": "horizontal",
|
||
"taxi-turn": 60,
|
||
},
|
||
},
|
||
{
|
||
selector: ":selected",
|
||
style: {
|
||
"border-style": "solid",
|
||
"border-width": 1.5,
|
||
"border-color": "#9A9AAF",
|
||
},
|
||
},
|
||
];
|
||
|
||
Cytoscape.use(popper);
|
||
|
||
interface Props {
|
||
modalQuestionParentContentId: string;
|
||
modalQuestionTargetContentId: string;
|
||
setOpenedModalQuestions: (open: boolean) => void;
|
||
setModalQuestionParentContentId: (id: string) => void;
|
||
setModalQuestionTargetContentId: (id: string) => void;
|
||
}
|
||
|
||
|
||
export const CsComponent = ({
|
||
modalQuestionParentContentId,
|
||
modalQuestionTargetContentId,
|
||
setOpenedModalQuestions,
|
||
setModalQuestionParentContentId,
|
||
setModalQuestionTargetContentId
|
||
}: Props) => {
|
||
const quiz = useCurrentQuiz();
|
||
|
||
const { dragQuestionContentId, questions } = useQuestionsStore()
|
||
const [startCreate, setStartCreate] = useState("");
|
||
const [startRemove, setStartRemove] = useState("");
|
||
|
||
const cyRef = useRef<Core | null>(null);
|
||
const layoutsContainer = useRef<HTMLDivElement | null>(null);
|
||
const plusesContainer = useRef<HTMLDivElement | null>(null);
|
||
const crossesContainer = useRef<HTMLDivElement | null>(null);
|
||
const gearsContainer = useRef<HTMLDivElement | null>(null);
|
||
|
||
useLayoutEffect(() => {
|
||
updateOpenedModalSettingsId()
|
||
}, [])
|
||
useEffect(() => {
|
||
if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) {
|
||
addNode({ parentNodeContentId: modalQuestionParentContentId, targetNodeContentId: modalQuestionTargetContentId })
|
||
}
|
||
setModalQuestionParentContentId("")
|
||
setModalQuestionTargetContentId("")
|
||
|
||
}, [modalQuestionTargetContentId])
|
||
|
||
const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => {
|
||
const cy = cyRef?.current
|
||
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
|
||
//если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
|
||
const targetQuestion = { ...getQuestionByContentId(targetNodeContentId || dragQuestionContentId) } as AnyTypedQuizQuestion
|
||
|
||
if (Object.keys(targetQuestion).length !== 0 && Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
|
||
clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
|
||
cy?.data('changed', true)
|
||
cy?.add([
|
||
{
|
||
data: {
|
||
id: targetQuestion.content.id,
|
||
label: targetQuestion.title || "noname"
|
||
}
|
||
},
|
||
{
|
||
data: {
|
||
source: parentNodeContentId,
|
||
target: targetQuestion.content.id
|
||
}
|
||
}
|
||
])
|
||
cy?.layout(lyopts).run()
|
||
} else {
|
||
enqueueSnackbar("Добавляемый вопрос не найден")
|
||
}
|
||
}
|
||
|
||
const clearDataAfterAddNode = ({ parentNodeContentId, targetQuestion, parentNodeChildren }: { parentNodeContentId: string, targetQuestion: AnyTypedQuizQuestion, parentNodeChildren: number }) => {
|
||
//предупреждаем добавленный вопрос о том, кто его родитель
|
||
updateQuestion(targetQuestion.content.id, question => {
|
||
question.content.rule.parentId = parentNodeContentId
|
||
question.content.rule.main = []
|
||
})
|
||
|
||
//Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
|
||
if (parentNodeChildren >= 1) {
|
||
updateOpenedModalSettingsId(targetQuestion.content.id)
|
||
} else {
|
||
//Если ребёнок первый - добавляем его родителю как дефолтный
|
||
updateQuestion(parentNodeContentId, question => question.content.rule.default = targetQuestion.content.id)
|
||
}
|
||
}
|
||
|
||
const removeNode = ({ targetNodeContentId }: { targetNodeContentId: string }) => {
|
||
const deleteNodes = [] as string[]
|
||
const deleteEdges: any = []
|
||
const cy = cyRef?.current
|
||
|
||
const findChildrenToDelete = (node) => {
|
||
|
||
//Узнаём грани, идущие от этой ноды
|
||
cy?.$('edge[source = "' + node.id() + '"]')?.toArray().forEach((edge) => {
|
||
const edgeData = edge.data()
|
||
|
||
//записываем id грани для дальнейшего удаления
|
||
deleteEdges.push(edge)
|
||
//ищем ноду на конце грани, записываем её ID для дальнейшего удаления
|
||
const targetNode = cy?.$("#" + edgeData.target)
|
||
deleteNodes.push(targetNode.data().id)
|
||
//вызываем функцию для анализа потомков уже у этой ноды
|
||
findChildrenToDelete(targetNode)
|
||
})
|
||
|
||
}
|
||
findChildrenToDelete(cy?.getElementById(targetNodeContentId))
|
||
|
||
const targetQuestion = getQuestionByContentId(targetNodeContentId)
|
||
|
||
if (targetQuestion.content.rule.parentId === "root" && quiz) {
|
||
updateRootContentId(quiz?.id, "")
|
||
updateQuestion(targetNodeContentId, question => {
|
||
question.content.rule.parentId = ""
|
||
question.content.rule.main = []
|
||
question.content.rule.default = ""
|
||
})
|
||
} else {
|
||
const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source
|
||
if (targetNodeContentId && parentQuestionContentId) {
|
||
|
||
clearDataAfterRemoveNode({ targetQuestionContentId: targetNodeContentId, parentQuestionContentId })
|
||
cy?.remove(cy?.$('#' + targetNodeContentId)).layout(lyopts).run()
|
||
}
|
||
|
||
}
|
||
|
||
//После всех манипуляций удаляем грани из CS и ноды из бекенда
|
||
|
||
deleteNodes.forEach((nodeId) => {//Ноды
|
||
cy?.remove(cy?.$("#" + nodeId))
|
||
removeButtons(nodeId)
|
||
updateQuestion(nodeId, question => {
|
||
question.content.rule.parentId = ""
|
||
question.content.rule.main = []
|
||
question.content.rule.default = ""
|
||
})
|
||
})
|
||
|
||
deleteEdges.forEach((edge: any) => {//Грани
|
||
cy?.remove(edge)
|
||
})
|
||
|
||
removeButtons(targetNodeContentId)
|
||
cy?.data('changed', true)
|
||
cy?.layout(lyopts).run()
|
||
}
|
||
const clearDataAfterRemoveNode = ({ targetQuestionContentId, parentQuestionContentId }: { targetQuestionContentId: string, parentQuestionContentId: string }) => {
|
||
updateQuestion(targetQuestionContentId, question => {
|
||
question.content.rule.parentId = ""
|
||
question.content.rule.main = []
|
||
question.content.rule.default = ""
|
||
})
|
||
|
||
updateQuestion(parentQuestionContentId, question => {
|
||
//Заменяем id удаляемого вопроса либо на id одного из оставшихся, либо на пустую строку
|
||
if (question.content.rule.default === targetQuestionContentId) question.content.rule.default = ""
|
||
})
|
||
|
||
}
|
||
|
||
|
||
useEffect(() => {
|
||
if (startCreate) {
|
||
addNode({ parentNodeContentId: startCreate });
|
||
cleardragQuestionContentId()
|
||
setStartCreate("");
|
||
}
|
||
}, [startCreate]);
|
||
|
||
useEffect(() => {
|
||
if (startRemove) {
|
||
removeNode({ targetNodeContentId: startRemove });
|
||
setStartRemove("");
|
||
}
|
||
}, [startRemove]);
|
||
|
||
|
||
const readyLO = (e) => {
|
||
if (e.cy.data('firstNode') === 'nonroot') {
|
||
e.cy.data('firstNode', 'root')
|
||
e.cy.nodes().sort((a, b) => (a.data('root') ? 1 : -1)).layout(lyopts).run()
|
||
|
||
} else {
|
||
|
||
|
||
e.cy.data('changed', false)
|
||
e.cy.removeData('firstNode')
|
||
}
|
||
|
||
//удаляем иконки
|
||
e.cy.nodes().forEach((ele: any) => {
|
||
const data = ele.data()
|
||
data.id && removeButtons(data.id);
|
||
})
|
||
initialPopperIcons(e)
|
||
}
|
||
|
||
const lyopts = {
|
||
name: 'preset',
|
||
|
||
positions: (e) => {
|
||
if (!e.cy().data('changed')) {
|
||
return e.data('oldPos')
|
||
} else { e.removeData('oldPos') }
|
||
const id = e.id()
|
||
const incomming = e.cy().edges(`[target="${id}"]`)
|
||
const layer = 0
|
||
e.removeData('lastChild')
|
||
|
||
if (incomming.length === 0) {
|
||
if (e.cy().data('firstNode') === undefined)
|
||
e.cy().data('firstNode', 'root')
|
||
e.data('root', true)
|
||
const children = e.cy().edges(`[source="${id}"]`).targets()
|
||
e.data('layer', layer)
|
||
e.data('children', children.length)
|
||
const queue = []
|
||
children.forEach(n => {
|
||
queue.push({ task: n, layer: layer + 1 })
|
||
})
|
||
while (queue.length) {
|
||
const task = queue.pop()
|
||
task.task.data('layer', task.layer)
|
||
task.task.removeData('subtreeWidth')
|
||
const children = e.cy().edges(`[source="${task.task.id()}"]`).targets()
|
||
task.task.data('children', children.length)
|
||
if (children.length !== 0) {
|
||
children.forEach(n => queue.push({ task: n, layer: task.layer + 1 }))
|
||
}
|
||
}
|
||
queue.push({ parent: e, children: children })
|
||
while (queue.length) {
|
||
const task = queue.pop()
|
||
if (task.children.length === 0) {
|
||
task.parent.data('subtreeWidth', task.parent.height())
|
||
continue
|
||
}
|
||
const unprocessed = task?.children.filter(e => {
|
||
return (e.data('subtreeWidth') === undefined)
|
||
})
|
||
if (unprocessed.length !== 0) {
|
||
queue.push(task)
|
||
unprocessed.forEach(t => {
|
||
queue.push({ parent: t, children: t.cy().edges(`[source="${t.id()}"]`).targets() })
|
||
})
|
||
continue
|
||
}
|
||
|
||
task?.parent.data('subtreeWidth', task.children.reduce((p, n) => p + n.data('subtreeWidth'), 0))
|
||
}
|
||
const pos = { x: 0, y: 0 }
|
||
e.data('oldPos', pos)
|
||
return pos
|
||
} else {
|
||
if (e.cy().data('firstNode') !== 'root') {
|
||
e.cy().data('firstNode', 'nonroot')
|
||
return { x: 0, y: 0 }
|
||
}
|
||
if (e.cy().data('firstNode') === undefined)
|
||
e.cy().data('firstNode', 'nonroot')
|
||
const parent = e.cy().edges(`[target="${e.id()}"]`)[0].source()
|
||
const wing = (parent.data('children') === 1) ? 0 : parent.data('subtreeWidth') / 2 + 50
|
||
const lastOffset = parent.data('lastChild')
|
||
const step = wing * 2 / (parent.data('children') - 1)
|
||
//e.removeData('subtreeWidth')
|
||
if (lastOffset !== undefined) {
|
||
parent.data('lastChild', lastOffset + step)
|
||
const pos = { x: 250 * e.data('layer'), y: (lastOffset + step) }
|
||
e.data('oldPos', pos)
|
||
return pos
|
||
} else {
|
||
parent.data('lastChild', parent.position().y - wing)
|
||
const pos = { x: 250 * e.data('layer'), y: (parent.position().y - wing) }
|
||
e.data('oldPos', pos)
|
||
return pos
|
||
}
|
||
}
|
||
}, // map of (node id) => (position obj); or function(node){ return somPos; }
|
||
zoom: undefined, // the zoom level to set (prob want fit = false if set)
|
||
pan: true, // the pan level to set (prob want fit = false if set)
|
||
fit: false, // whether to fit to viewport
|
||
padding: 30, // padding on fit
|
||
animate: false, // whether to transition the node positions
|
||
animationDuration: 500, // duration of animation in ms if enabled
|
||
animationEasing: undefined, // easing of animation if enabled
|
||
animateFilter: function (node, i) { return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
|
||
ready: readyLO, // callback on layoutready
|
||
transform: function (node, position) { return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
|
||
}
|
||
|
||
useEffect(() => {
|
||
document.querySelector("#root")?.addEventListener("mouseup", cleardragQuestionContentId);
|
||
const cy = cyRef.current;
|
||
const eles = cy?.add(storeToNodes(questions))
|
||
cy.data('changed', true)
|
||
const elecs = eles.layout(lyopts).run()
|
||
cy?.on('add', () => cy.data('changed', true))
|
||
cy?.fit()
|
||
//cy?.layout().run()
|
||
|
||
return () => {
|
||
document.querySelector("#root")?.removeEventListener("mouseup", cleardragQuestionContentId);
|
||
layoutsContainer.current?.remove();
|
||
plusesContainer.current?.remove();
|
||
crossesContainer.current?.remove();
|
||
gearsContainer.current?.remove();
|
||
};
|
||
}, []);
|
||
|
||
|
||
const removeButtons = (id: string) => {
|
||
layoutsContainer.current
|
||
?.querySelector(`.popper-layout[data-id='${id}']`)
|
||
?.remove();
|
||
plusesContainer.current
|
||
?.querySelector(`.popper-plus[data-id='${id}']`)
|
||
?.remove();
|
||
crossesContainer.current
|
||
?.querySelector(`.popper-cross[data-id='${id}']`)
|
||
?.remove();
|
||
gearsContainer.current
|
||
?.querySelector(`.popper-gear[data-id='${id}']`)
|
||
?.remove();
|
||
};
|
||
|
||
|
||
const initialPopperIcons = (e) => {
|
||
const cy = e.cy
|
||
|
||
const container =
|
||
(document.body.querySelector(
|
||
".__________cytoscape_container"
|
||
) as HTMLDivElement) || null;
|
||
|
||
if (!container) {
|
||
return;
|
||
}
|
||
|
||
container.style.overflow = "hidden";
|
||
|
||
if (!plusesContainer.current) {
|
||
plusesContainer.current = document.createElement("div");
|
||
plusesContainer.current.setAttribute("id", "popper-pluses");
|
||
container.append(plusesContainer.current);
|
||
}
|
||
if (!crossesContainer.current) {
|
||
crossesContainer.current = document.createElement("div");
|
||
crossesContainer.current.setAttribute("id", "popper-crosses");
|
||
container.append(crossesContainer.current);
|
||
}
|
||
if (!gearsContainer.current) {
|
||
gearsContainer.current = document.createElement("div");
|
||
gearsContainer.current.setAttribute("id", "popper-gears");
|
||
container.append(gearsContainer.current);
|
||
}
|
||
if (!layoutsContainer.current) {
|
||
layoutsContainer.current = document.createElement("div");
|
||
layoutsContainer.current.setAttribute("id", "popper-layouts");
|
||
container.append(layoutsContainer.current);
|
||
}
|
||
|
||
const ext = cy.extent()
|
||
const nodesInView = cy.nodes().filter(n => {
|
||
const bb = n.boundingBox()
|
||
return bb.x2 > ext.x1 && bb.x1 < ext.x2 && bb.y2 > ext.y1 && bb.y1 < ext.y2
|
||
})
|
||
|
||
nodesInView
|
||
.toArray()
|
||
?.forEach((item) => {
|
||
const node = item as NodeSingularWithPopper;
|
||
|
||
const layoutsPopper = node.popper({
|
||
popper: {
|
||
placement: "left",
|
||
modifiers: [{ name: "flip", options: { boundary: node } }],
|
||
},
|
||
content: ([item]) => {
|
||
const itemId = item.id();
|
||
const itemElement = layoutsContainer.current?.querySelector(
|
||
`.popper-layout[data-id='${itemId}']`
|
||
);
|
||
if (itemElement) {
|
||
return itemElement;
|
||
}
|
||
|
||
const layoutElement = document.createElement("div");
|
||
layoutElement.style.zIndex = "0"
|
||
layoutElement.classList.add("popper-layout");
|
||
layoutElement.setAttribute("data-id", item.id());
|
||
layoutElement.addEventListener("mouseup", () => {
|
||
//Узнаём грани, идущие от этой ноды
|
||
setModalQuestionParentContentId(item.id())
|
||
setOpenedModalQuestions(true)
|
||
});
|
||
layoutsContainer.current?.appendChild(layoutElement);
|
||
|
||
return layoutElement;
|
||
},
|
||
});
|
||
|
||
const plusesPopper = node.popper({
|
||
popper: {
|
||
placement: "right",
|
||
modifiers: [{ name: "flip", options: { boundary: node } }],
|
||
},
|
||
content: ([item]) => {
|
||
const itemId = item.id();
|
||
const itemElement = plusesContainer.current?.querySelector(
|
||
`.popper-plus[data-id='${itemId}']`
|
||
);
|
||
if (itemElement) {
|
||
return itemElement;
|
||
}
|
||
|
||
const plusElement = document.createElement("div");
|
||
plusElement.classList.add("popper-plus");
|
||
plusElement.setAttribute("data-id", item.id());
|
||
plusElement.style.zIndex = "1"
|
||
plusElement.addEventListener("mouseup", () => {
|
||
setStartCreate(node.id());
|
||
});
|
||
|
||
plusesContainer.current?.appendChild(plusElement);
|
||
|
||
return plusElement;
|
||
},
|
||
});
|
||
|
||
const crossesPopper = node.popper({
|
||
popper: {
|
||
placement: "top-end",
|
||
modifiers: [{ name: "flip", options: { boundary: node } }],
|
||
},
|
||
content: ([item]) => {
|
||
const itemId = item.id();
|
||
const itemElement = crossesContainer.current?.querySelector(
|
||
`.popper-cross[data-id='${itemId}']`
|
||
);
|
||
if (itemElement) {
|
||
return itemElement;
|
||
}
|
||
|
||
const crossElement = document.createElement("div");
|
||
crossElement.classList.add("popper-cross");
|
||
crossElement.setAttribute("data-id", item.id());
|
||
crossElement.style.zIndex = "2"
|
||
crossesContainer.current?.appendChild(crossElement);
|
||
crossElement.addEventListener("mouseup", () => {
|
||
setStartRemove(node.id())
|
||
|
||
|
||
}
|
||
);
|
||
|
||
return crossElement;
|
||
},
|
||
});
|
||
|
||
const gearsPopper = node.popper({
|
||
popper: {
|
||
placement: "left",
|
||
modifiers: [{ name: "flip", options: { boundary: node } }],
|
||
},
|
||
content: ([item]) => {
|
||
const itemId = item.id();
|
||
if (item.cy().edges(`[target="${itemId}"]`).sources().length === 0) {
|
||
return;
|
||
}
|
||
|
||
const itemElement = gearsContainer.current?.querySelector(
|
||
`.popper-gear[data-id='${itemId}']`
|
||
);
|
||
if (itemElement) {
|
||
return itemElement;
|
||
}
|
||
|
||
const gearElement = document.createElement("div");
|
||
gearElement.classList.add("popper-gear");
|
||
gearElement.setAttribute("data-id", item.id());
|
||
gearElement.style.zIndex = "1"
|
||
gearsContainer.current?.appendChild(gearElement);
|
||
gearElement.addEventListener("mouseup", (e) => {
|
||
updateOpenedModalSettingsId(item.id())
|
||
});
|
||
|
||
return gearElement;
|
||
},
|
||
});
|
||
|
||
const update = async () => {
|
||
await plusesPopper.update();
|
||
await crossesPopper.update();
|
||
await gearsPopper.update();
|
||
await layoutsPopper.update();
|
||
};
|
||
|
||
const onZoom = (event: AbstractEventObject) => {
|
||
const zoom = event.cy.zoom();
|
||
|
||
update();
|
||
|
||
crossesPopper.setOptions({
|
||
modifiers: [
|
||
{ name: "flip", options: { boundary: node } },
|
||
{ name: "offset", options: { offset: [-5 * zoom, -30 * zoom] } },
|
||
],
|
||
});
|
||
|
||
layoutsPopper.setOptions({
|
||
modifiers: [
|
||
{ name: "flip", options: { boundary: node } },
|
||
{ name: "offset", options: { offset: [0, -130 * zoom] } },
|
||
],
|
||
});
|
||
|
||
layoutsContainer.current
|
||
?.querySelectorAll("#popper-layouts > .popper-layout")
|
||
.forEach((item) => {
|
||
const element = item as HTMLDivElement;
|
||
element.style.width = `${130 * zoom}px`;
|
||
element.style.height = `${130 * zoom}px`;
|
||
});
|
||
|
||
plusesContainer.current
|
||
?.querySelectorAll("#popper-pluses > .popper-plus")
|
||
.forEach((item) => {
|
||
const element = item as HTMLDivElement;
|
||
element.style.width = `${40 * zoom}px`;
|
||
element.style.height = `${40 * zoom}px`;
|
||
element.style.fontSize = `${40 * zoom}px`;
|
||
element.style.borderRadius = `${6 * zoom}px`;
|
||
});
|
||
|
||
crossesContainer.current
|
||
?.querySelectorAll("#popper-crosses > .popper-cross")
|
||
.forEach((item) => {
|
||
const element = item as HTMLDivElement;
|
||
element.style.width = `${24 * zoom}px`;
|
||
element.style.height = `${24 * zoom}px`;
|
||
element.style.fontSize = `${24 * zoom}px`;
|
||
element.style.borderRadius = `${6 * zoom}px`;
|
||
});
|
||
|
||
gearsContainer.current
|
||
?.querySelectorAll("#popper-gears > .popper-gear")
|
||
.forEach((item) => {
|
||
const element = item as HTMLDivElement;
|
||
element.style.width = `${60 * zoom}px`;
|
||
element.style.height = `${40 * zoom}px`;
|
||
});
|
||
};
|
||
|
||
node?.on("position", update);
|
||
cy?.on("pan zoom resize render", onZoom);
|
||
});
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<CytoscapeComponent
|
||
wheelSensitivity={0.1}
|
||
elements={[]}
|
||
// elements={createGraphElements(tree, quiz)}
|
||
style={{ height: "480px", background: "#F2F3F7" }}
|
||
stylesheet={stylesheet}
|
||
layout={(lyopts)}
|
||
cy={(cy) => {
|
||
cyRef.current = cy;
|
||
}}
|
||
/>
|
||
{/* <button onClick={() => {
|
||
console.log("NODES____________________________")
|
||
cyRef.current?.elements().forEach((ele: any) => {
|
||
console.log(ele.data())
|
||
})
|
||
}}>nodes</button>
|
||
<button onClick={() => {
|
||
console.log("ELEMENTS____________________________")
|
||
console.log(questions)
|
||
}}>elements</button> */}
|
||
</>
|
||
);
|
||
};
|