useGraph.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import { ReactZoomPanPinchRef } from "react-zoom-pan-pinch";
  2. import { CanvasDirection } from "reaflow";
  3. import { Graph } from "src/components/Graph";
  4. import { getChildrenEdges } from "src/utils/getChildrenEdges";
  5. import { getOutgoers } from "src/utils/getOutgoers";
  6. import { parser } from "src/utils/jsonParser";
  7. import create from "zustand";
  8. import useJson from "./useJson";
  9. const initialStates = {
  10. zoomPanPinch: undefined as ReactZoomPanPinchRef | undefined,
  11. direction: "RIGHT" as CanvasDirection,
  12. loading: true,
  13. graphCollapsed: false,
  14. foldNodes: false,
  15. fullscreen: false,
  16. performanceMode: true,
  17. nodes: [] as NodeData[],
  18. edges: [] as EdgeData[],
  19. collapsedNodes: [] as string[],
  20. collapsedEdges: [] as string[],
  21. collapsedParents: [] as string[],
  22. };
  23. export type Graph = typeof initialStates;
  24. interface GraphActions {
  25. setGraph: (json?: string, options?: Partial<Graph>[]) => void;
  26. setLoading: (loading: boolean) => void;
  27. setDirection: (direction: CanvasDirection) => void;
  28. setZoomPanPinch: (ref: ReactZoomPanPinchRef) => void;
  29. expandNodes: (nodeId: string) => void;
  30. collapseNodes: (nodeId: string) => void;
  31. collapseGraph: () => void;
  32. expandGraph: () => void;
  33. toggleFold: (value: boolean) => void;
  34. toggleFullscreen: (value: boolean) => void;
  35. togglePerfMode: (value: boolean) => void;
  36. zoomIn: () => void;
  37. zoomOut: () => void;
  38. centerView: () => void;
  39. }
  40. const useGraph = create<Graph & GraphActions>((set, get) => ({
  41. ...initialStates,
  42. setGraph: (data, options) => {
  43. const { nodes, edges } = parser(data ?? useJson.getState().json);
  44. set({
  45. nodes,
  46. edges,
  47. collapsedParents: [],
  48. collapsedNodes: [],
  49. collapsedEdges: [],
  50. graphCollapsed: false,
  51. loading: true,
  52. ...options,
  53. });
  54. },
  55. setDirection: direction => set({ direction }),
  56. setLoading: loading => set({ loading }),
  57. expandNodes: nodeId => {
  58. const [childrenNodes, matchingNodes] = getOutgoers(
  59. nodeId,
  60. get().nodes,
  61. get().edges,
  62. get().collapsedParents
  63. );
  64. const childrenEdges = getChildrenEdges(childrenNodes, get().edges);
  65. let nodesConnectedToParent = childrenEdges.reduce((nodes: string[], edge) => {
  66. edge.from && !nodes.includes(edge.from) && nodes.push(edge.from);
  67. edge.to && !nodes.includes(edge.to) && nodes.push(edge.to);
  68. return nodes;
  69. }, []);
  70. const matchingNodesConnectedToParent = matchingNodes.filter(node =>
  71. nodesConnectedToParent.includes(node)
  72. );
  73. const nodeIds = childrenNodes.map(node => node.id).concat(matchingNodesConnectedToParent);
  74. const edgeIds = childrenEdges.map(edge => edge.id);
  75. const collapsedParents = get().collapsedParents.filter(cp => cp !== nodeId);
  76. const collapsedNodes = get().collapsedNodes.filter(nodeId => !nodeIds.includes(nodeId));
  77. const collapsedEdges = get().collapsedEdges.filter(edgeId => !edgeIds.includes(edgeId));
  78. set({
  79. collapsedParents,
  80. collapsedNodes,
  81. collapsedEdges,
  82. graphCollapsed: !!collapsedNodes.length,
  83. });
  84. },
  85. collapseNodes: nodeId => {
  86. const [childrenNodes] = getOutgoers(nodeId, get().nodes, get().edges);
  87. const childrenEdges = getChildrenEdges(childrenNodes, get().edges);
  88. const nodeIds = childrenNodes.map(node => node.id);
  89. const edgeIds = childrenEdges.map(edge => edge.id);
  90. set({
  91. collapsedParents: get().collapsedParents.concat(nodeId),
  92. collapsedNodes: get().collapsedNodes.concat(nodeIds),
  93. collapsedEdges: get().collapsedEdges.concat(edgeIds),
  94. graphCollapsed: !!get().collapsedNodes.concat(nodeIds).length,
  95. });
  96. },
  97. collapseGraph: () => {
  98. const edges = get().edges;
  99. const tos = edges.map(edge => edge.to);
  100. const froms = edges.map(edge => edge.from);
  101. const parentNodesIds = froms.filter(id => !tos.includes(id));
  102. const secondDegreeNodesIds = edges
  103. .filter(edge => parentNodesIds.includes(edge.from))
  104. .map(edge => edge.to);
  105. const collapsedParents = get()
  106. .nodes.filter(node => !parentNodesIds.includes(node.id) && node.data.parent)
  107. .map(node => node.id);
  108. const collapsedNodes = get()
  109. .nodes.filter(
  110. node => !parentNodesIds.includes(node.id) && !secondDegreeNodesIds.includes(node.id)
  111. )
  112. .map(node => node.id);
  113. set({
  114. collapsedParents,
  115. collapsedNodes,
  116. collapsedEdges: get()
  117. .edges.filter(edge => !parentNodesIds.includes(edge.from))
  118. .map(edge => edge.id),
  119. graphCollapsed: true,
  120. });
  121. },
  122. expandGraph: () => {
  123. set({
  124. collapsedNodes: [],
  125. collapsedEdges: [],
  126. collapsedParents: [],
  127. graphCollapsed: false,
  128. });
  129. },
  130. zoomIn: () => {
  131. const zoomPanPinch = get().zoomPanPinch;
  132. if (zoomPanPinch) {
  133. zoomPanPinch.setTransform(
  134. zoomPanPinch?.state.positionX,
  135. zoomPanPinch?.state.positionY,
  136. zoomPanPinch?.state.scale + 0.4
  137. );
  138. }
  139. },
  140. zoomOut: () => {
  141. const zoomPanPinch = get().zoomPanPinch;
  142. if (zoomPanPinch) {
  143. zoomPanPinch.setTransform(
  144. zoomPanPinch?.state.positionX,
  145. zoomPanPinch?.state.positionY,
  146. zoomPanPinch?.state.scale - 0.4
  147. );
  148. }
  149. },
  150. centerView: () => {
  151. const zoomPanPinch = get().zoomPanPinch;
  152. const canvas = document.querySelector(".jsoncrack-canvas") as HTMLElement;
  153. if (zoomPanPinch && canvas) zoomPanPinch.zoomToElement(canvas);
  154. },
  155. toggleFold: foldNodes => {
  156. set({ foldNodes });
  157. get().setGraph();
  158. },
  159. togglePerfMode: performanceMode => set({ performanceMode }),
  160. toggleFullscreen: fullscreen => set({ fullscreen }),
  161. setZoomPanPinch: zoomPanPinch => set({ zoomPanPinch }),
  162. }));
  163. export default useGraph;