123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- import { ReactZoomPanPinchRef } from "react-zoom-pan-pinch";
- import { CanvasDirection } from "reaflow/dist/layout/elkLayout";
- import { parser } from "src/utils/core/jsonParser";
- import { getChildrenEdges } from "src/utils/getChildrenEdges";
- import { getOutgoers } from "src/utils/getOutgoers";
- import { create } from "zustand";
- import useJson from "./useJson";
- const initialStates = {
- zoomPanPinch: undefined as ReactZoomPanPinchRef | undefined,
- direction: "RIGHT" as CanvasDirection,
- loading: true,
- graphCollapsed: false,
- foldNodes: false,
- fullscreen: false,
- nodes: [] as NodeData[],
- edges: [] as EdgeData[],
- collapsedNodes: [] as string[],
- collapsedEdges: [] as string[],
- collapsedParents: [] as string[],
- selectedNode: {} as NodeData,
- path: "",
- };
- export type Graph = typeof initialStates;
- interface GraphActions {
- setGraph: (json?: string, options?: Partial<Graph>[]) => void;
- setLoading: (loading: boolean) => void;
- setDirection: (direction: CanvasDirection) => void;
- setZoomPanPinch: (ref: ReactZoomPanPinchRef) => void;
- expandNodes: (nodeId: string) => void;
- collapseNodes: (nodeId: string) => void;
- collapseGraph: () => void;
- expandGraph: () => void;
- toggleFold: (value: boolean) => void;
- toggleFullscreen: (value: boolean) => void;
- zoomIn: () => void;
- zoomOut: () => void;
- centerView: () => void;
- setSelectedNode: ({ nodeData}: { nodeData: NodeData }) => void;
- }
- const useGraph = create<Graph & GraphActions>((set, get) => ({
- ...initialStates,
- setSelectedNode: ({ nodeData}) => set({ selectedNode: nodeData}),
- setGraph: (data, options) => {
- const { nodes, edges } = parser(data ?? useJson.getState().json);
- set({
- nodes,
- edges,
- collapsedParents: [],
- collapsedNodes: [],
- collapsedEdges: [],
- graphCollapsed: false,
- loading: true,
- ...options,
- });
- },
- setDirection: direction => set({ direction }),
- setLoading: loading => set({ loading }),
- expandNodes: nodeId => {
- const [childrenNodes, matchingNodes] = getOutgoers(
- nodeId,
- get().nodes,
- get().edges,
- get().collapsedParents
- );
- const childrenEdges = getChildrenEdges(childrenNodes, get().edges);
- let nodesConnectedToParent = childrenEdges.reduce((nodes: string[], edge) => {
- edge.from && !nodes.includes(edge.from) && nodes.push(edge.from);
- edge.to && !nodes.includes(edge.to) && nodes.push(edge.to);
- return nodes;
- }, []);
- const matchingNodesConnectedToParent = matchingNodes.filter(node =>
- nodesConnectedToParent.includes(node)
- );
- const nodeIds = childrenNodes.map(node => node.id).concat(matchingNodesConnectedToParent);
- const edgeIds = childrenEdges.map(edge => edge.id);
- const collapsedParents = get().collapsedParents.filter(cp => cp !== nodeId);
- const collapsedNodes = get().collapsedNodes.filter(nodeId => !nodeIds.includes(nodeId));
- const collapsedEdges = get().collapsedEdges.filter(edgeId => !edgeIds.includes(edgeId));
- set({
- collapsedParents,
- collapsedNodes,
- collapsedEdges,
- graphCollapsed: !!collapsedNodes.length,
- });
- },
- collapseNodes: nodeId => {
- const [childrenNodes] = getOutgoers(nodeId, get().nodes, get().edges);
- const childrenEdges = getChildrenEdges(childrenNodes, get().edges);
- const nodeIds = childrenNodes.map(node => node.id);
- const edgeIds = childrenEdges.map(edge => edge.id);
- set({
- collapsedParents: get().collapsedParents.concat(nodeId),
- collapsedNodes: get().collapsedNodes.concat(nodeIds),
- collapsedEdges: get().collapsedEdges.concat(edgeIds),
- graphCollapsed: !!get().collapsedNodes.concat(nodeIds).length,
- });
- },
- collapseGraph: () => {
- const edges = get().edges;
- const tos = edges.map(edge => edge.to);
- const froms = edges.map(edge => edge.from);
- const parentNodesIds = froms.filter(id => !tos.includes(id));
- const secondDegreeNodesIds = edges
- .filter(edge => parentNodesIds.includes(edge.from))
- .map(edge => edge.to);
- const collapsedParents = get()
- .nodes.filter(node => !parentNodesIds.includes(node.id) && node.data.isParent)
- .map(node => node.id);
- const collapsedNodes = get()
- .nodes.filter(
- node => !parentNodesIds.includes(node.id) && !secondDegreeNodesIds.includes(node.id)
- )
- .map(node => node.id);
- set({
- collapsedParents,
- collapsedNodes,
- collapsedEdges: get()
- .edges.filter(edge => !parentNodesIds.includes(edge.from))
- .map(edge => edge.id),
- graphCollapsed: true,
- });
- },
- expandGraph: () => {
- set({
- collapsedNodes: [],
- collapsedEdges: [],
- collapsedParents: [],
- graphCollapsed: false,
- });
- },
- zoomIn: () => {
- const zoomPanPinch = get().zoomPanPinch;
- if (zoomPanPinch) {
- zoomPanPinch.setTransform(
- zoomPanPinch?.state.positionX,
- zoomPanPinch?.state.positionY,
- zoomPanPinch?.state.scale + 0.4
- );
- }
- },
- zoomOut: () => {
- const zoomPanPinch = get().zoomPanPinch;
- if (zoomPanPinch) {
- zoomPanPinch.setTransform(
- zoomPanPinch?.state.positionX,
- zoomPanPinch?.state.positionY,
- zoomPanPinch?.state.scale - 0.4
- );
- }
- },
- centerView: () => {
- const zoomPanPinch = get().zoomPanPinch;
- const canvas = document.querySelector(".jsoncrack-canvas") as HTMLElement;
- if (zoomPanPinch && canvas) zoomPanPinch.zoomToElement(canvas);
- },
- toggleFold: foldNodes => {
- set({ foldNodes });
- get().setGraph();
- },
- toggleFullscreen: fullscreen => set({ fullscreen }),
- setZoomPanPinch: zoomPanPinch => set({ zoomPanPinch }),
- }));
- export default useGraph;
|