use custom poppers
This commit is contained in:
parent
36ba2dfb61
commit
3889c06be1
@ -9,22 +9,18 @@ import { cleardragQuestionContentId, setModalQuestionParentContentId, setModalQu
|
||||
import { useUiTools } from "@root/uiTools/store";
|
||||
import { ProblemIcon } from "@ui_kit/ProblemIcon";
|
||||
import type { Core } from "cytoscape";
|
||||
import Cytoscape from "cytoscape";
|
||||
import popper from "cytoscape-popper";
|
||||
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";
|
||||
import CsNodeButtons from "./CsNodeButtons";
|
||||
import { addNode, layoutOptions, storeToNodes } from "./helper";
|
||||
import { usePopper } from "./hooks/usePopper";
|
||||
import { useRemoveNode } from "./hooks/useRemoveNode";
|
||||
import "./style/styles.css";
|
||||
import { stylesheet } from "./style/stylesheet";
|
||||
|
||||
|
||||
Cytoscape.use(popper);
|
||||
|
||||
function CsComponent() {
|
||||
const desireToOpenABranchingModal = useUiTools(state => state.desireToOpenABranchingModal);
|
||||
const canCreatePublic = useUiTools(state => state.canCreatePublic);
|
||||
@ -32,10 +28,9 @@ function CsComponent() {
|
||||
const modalQuestionTargetContentId = useUiTools(state => state.modalQuestionTargetContentId);
|
||||
const trashQuestions = useQuestionsStore(state => state.questions);
|
||||
const cyRef = useRef<Core | null>(null);
|
||||
const { recreatePoppers, removeAllPoppers } = usePopper({ cyRef });
|
||||
const { removeNode } = useRemoveNode({ cyRef });
|
||||
|
||||
const cyElements = useMemo(() => {
|
||||
const csElements = useMemo(() => {
|
||||
const questions = trashQuestions.filter(
|
||||
(question): question is AnyTypedQuizQuestion => question.type !== null && question.type !== "result"
|
||||
);
|
||||
@ -73,53 +68,17 @@ function CsComponent() {
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("pointerup", cleardragQuestionContentId);
|
||||
removeAllPoppers();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(function removePoppersOnDrag() {
|
||||
const cy = cyRef.current;
|
||||
if (!cy) return;
|
||||
|
||||
let isPointerDown = false;
|
||||
let isDragging = false;
|
||||
|
||||
const onPointerDown = () => {
|
||||
isPointerDown = true;
|
||||
};
|
||||
const onPointerUp = () => {
|
||||
if (isDragging) {
|
||||
isDragging = false;
|
||||
recreatePoppers();
|
||||
}
|
||||
isPointerDown = false;
|
||||
};
|
||||
const handleMove = () => {
|
||||
if (isPointerDown) {
|
||||
isDragging = true;
|
||||
removeAllPoppers();
|
||||
}
|
||||
};
|
||||
|
||||
cy.on("vmousedown", onPointerDown);
|
||||
cy.on("vmousemove", handleMove);
|
||||
document.addEventListener("pointerup", onPointerUp);
|
||||
|
||||
return () => {
|
||||
cy.off("vmousedown", onPointerDown);
|
||||
cy.off("vmousemove", handleMove);
|
||||
document.removeEventListener("pointerup", onPointerUp);
|
||||
};
|
||||
}, [recreatePoppers, removeAllPoppers]);
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(function rerunLayout() {
|
||||
cyRef.current?.layout(layoutOptions).run();
|
||||
cyRef.current?.fit(undefined, 70);
|
||||
recreatePoppers();
|
||||
}, [cyElements, recreatePoppers]);
|
||||
}, [csElements]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CsNodeButtons csElements={csElements} cyRef={cyRef} />
|
||||
<Box mb="20px">
|
||||
<Button
|
||||
sx={{
|
||||
@ -130,7 +89,7 @@ function CsComponent() {
|
||||
}}
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
cyRef.current?.fit();
|
||||
cyRef.current?.fit(undefined, 70);
|
||||
}}
|
||||
>
|
||||
Выровнять
|
||||
@ -140,10 +99,9 @@ function CsComponent() {
|
||||
onClick={() => updateModalInfoWhyCantCreate(true)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<CytoscapeComponent
|
||||
wheelSensitivity={0.1}
|
||||
elements={cyElements}
|
||||
elements={csElements}
|
||||
style={{
|
||||
height: "480px",
|
||||
background: "#F2F3F7",
|
||||
@ -155,6 +113,8 @@ function CsComponent() {
|
||||
cyRef.current = cy;
|
||||
}}
|
||||
autoungrabify={true}
|
||||
autounselectify={true}
|
||||
boxSelectionEnabled={false}
|
||||
/>
|
||||
<DeleteNodeModal removeNode={removeNode} />
|
||||
</>
|
||||
|
||||
291
src/pages/Questions/BranchingMap/CsNodeButtons.tsx
Normal file
291
src/pages/Questions/BranchingMap/CsNodeButtons.tsx
Normal file
File diff suppressed because one or more lines are too long
@ -4,35 +4,60 @@ import { createResult, getQuestionByContentId, updateQuestion } from "@root/ques
|
||||
import { useQuestionsStore } from "@root/questions/store";
|
||||
import { updateOpenedModalSettingsId } from "@root/uiTools/actions";
|
||||
import { useUiTools } from "@root/uiTools/store";
|
||||
import { PresetLayoutOptions } from "cytoscape";
|
||||
import { NodeSingular, PresetLayoutOptions } from "cytoscape";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
|
||||
interface Nodes {
|
||||
export interface Node {
|
||||
data: {
|
||||
isRoot: boolean;
|
||||
id: string;
|
||||
label: string;
|
||||
parent?: string;
|
||||
};
|
||||
classes: string;
|
||||
}
|
||||
|
||||
interface Edges {
|
||||
export interface Edge {
|
||||
data: {
|
||||
source: string;
|
||||
target: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function isElementANode(element: Node | Edge): element is Node {
|
||||
return !("source" in element.data && "target" in element.data);
|
||||
}
|
||||
|
||||
export function isNodeInViewport(node: NodeSingular, padding: number = 0) {
|
||||
const extent = node.cy().extent();
|
||||
const bb = node.boundingBox();
|
||||
|
||||
return (
|
||||
bb.x2 > extent.x1 - padding
|
||||
&& bb.x1 < extent.x2 + padding
|
||||
&& bb.y2 > extent.y1 - padding
|
||||
&& bb.y1 < extent.y2 + padding
|
||||
);
|
||||
}
|
||||
|
||||
export const storeToNodes = (questions: AnyTypedQuizQuestion[]) => {
|
||||
const nodes: Nodes[] = [];
|
||||
const edges: Edges[] = [];
|
||||
const nodes: Node[] = [];
|
||||
const edges: Edge[] = [];
|
||||
questions.forEach((question) => {
|
||||
if (question.content.rule.parentId) {
|
||||
let label = question.title === "" || question.title === " "
|
||||
? "noname"
|
||||
: question.title;
|
||||
if (label.length > 25) label = label.slice(0, 25) + "…";
|
||||
|
||||
nodes.push({
|
||||
data: {
|
||||
isRoot: question.content.rule.parentId === "root",
|
||||
id: question.content.id,
|
||||
label: question.title === "" || question.title === " " ? "noname" : question.title
|
||||
}
|
||||
label,
|
||||
},
|
||||
classes: "multiline-auto",
|
||||
});
|
||||
// nodes.push({
|
||||
// data: {
|
||||
|
||||
@ -27,6 +27,7 @@ type NodeSingularWithPopper = NodeSingular & {
|
||||
popper: (config: PopperConfig) => PopperInstance;
|
||||
};
|
||||
|
||||
/** @deprecated */
|
||||
export const usePopper = ({
|
||||
cyRef,
|
||||
}: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user