瀏覽代碼

prevent expand btn expanding all

AykutSarac 2 年之前
父節點
當前提交
41638eb35b
共有 5 個文件被更改,包括 42 次插入22 次删除
  1. 0 1
      TODO.md
  2. 11 5
      src/components/CustomNode/TextNode.tsx
  3. 1 0
      src/components/CustomNode/index.tsx
  4. 18 9
      src/hooks/store/useGraph.tsx
  5. 12 7
      src/utils/getOutgoers.ts

+ 0 - 1
TODO.md

@@ -5,7 +5,6 @@
 
 ## Enhancements:
 
-- [ ] Prevent expand button expanding all nodes, should expand only target node
 - [ ] Clear rewrite of [parsing algorithm](/src/utils/jsonParser.ts)
 - [ ] Performance Optimization
 - [ ] Fix `useInViewport` hook for performance

+ 11 - 5
src/components/CustomNode/TextNode.tsx

@@ -1,5 +1,5 @@
 import React from "react";
-import { MdCompareArrows } from "react-icons/md";
+import { MdLink, MdLinkOff } from "react-icons/md";
 import { useInViewport } from "react-in-viewport";
 import { CustomNodeProps } from "src/components/CustomNode";
 import useConfig from "src/hooks/store/useConfig";
@@ -57,13 +57,15 @@ const TextNode: React.FC<TextNodeProps> = ({
   const hideCollapse = useStored((state) => state.hideCollapse);
   const expandNodes = useGraph((state) => state.expandNodes);
   const collapseNodes = useGraph((state) => state.collapseNodes);
-  const [isExpanded, setIsExpanded] = React.useState(true);
+  const isExpanded = useGraph((state) =>
+    state.collapsedParents.includes(node.id)
+  );
 
   const handleExpand = (e: React.MouseEvent<HTMLButtonElement>) => {
     e.stopPropagation();
-    setIsExpanded(!isExpanded);
+    // setIsExpanded(!isExpanded);
 
-    if (isExpanded) collapseNodes(node.id);
+    if (!isExpanded) collapseNodes(node.id);
     else expandNodes(node.id);
   };
 
@@ -94,7 +96,11 @@ const TextNode: React.FC<TextNodeProps> = ({
 
         {inViewport && isParent && hasCollapse && !hideCollapse && (
           <StyledExpand onClick={handleExpand}>
-            <MdCompareArrows size={18} />
+            {isExpanded ? (
+              <MdLinkOff size={18} />
+            ) : (
+              <MdLink size={18} />
+            )}
           </StyledExpand>
         )}
       </StyledTextNodeWrapper>

+ 1 - 0
src/components/CustomNode/index.tsx

@@ -1,5 +1,6 @@
 import React from "react";
 import { Node, NodeProps } from "reaflow";
+import useGraph from "src/hooks/store/useGraph";
 import ObjectNode from "./ObjectNode";
 import TextNode from "./TextNode";
 

+ 18 - 9
src/hooks/store/useGraph.tsx

@@ -7,6 +7,7 @@ export interface Graph {
   edges: EdgeData[];
   collapsedNodes: string[];
   collapsedEdges: string[];
+  collapsedParents: string[];
 }
 
 interface GraphActions {
@@ -20,6 +21,7 @@ const initialStates: Graph = {
   edges: [],
   collapsedNodes: [],
   collapsedEdges: [],
+  collapsedParents: [],
 };
 
 const useGraph = create<Graph & GraphActions>((set, get) => ({
@@ -31,29 +33,36 @@ const useGraph = create<Graph & GraphActions>((set, get) => ({
       [key]: value,
     }),
   expandNodes: (nodeId) => {
-    const childrenNodes = getOutgoers(nodeId, get().nodes, get().edges);
+    const [childrenNodes, matchingNodes] = getOutgoers(
+      nodeId,
+      get().nodes,
+      get().edges,
+      get().collapsedParents
+    );
     const childrenEdges = getChildrenEdges(childrenNodes, get().edges);
 
-    const nodeIds = childrenNodes.map((node) => node.id);
+    const nodeIds = childrenNodes.map((node) => node.id).concat(matchingNodes);
     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({
-      collapsedNodes: get().collapsedNodes.filter(
-        (nodeId) => !nodeIds.includes(nodeId)
-      ),
-      collapsedEdges: get().collapsedEdges.filter(
-        (edgeId) => !edgeIds.includes(edgeId)
-      ),
+      collapsedParents,
+      collapsedNodes,
+      collapsedEdges,
     });
   },
   collapseNodes: (nodeId) => {
-    const childrenNodes = getOutgoers(nodeId, get().nodes, get().edges);
+    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),
     });

+ 12 - 7
src/utils/getOutgoers.ts

@@ -1,18 +1,23 @@
 export const getOutgoers = (
   nodeId: string,
   nodes: NodeData[],
-  edges: EdgeData[]
-): NodeData[] => {
-  const allOutgoers: NodeData[] = [];
+  edges: EdgeData[],
+  parent: string[] = []
+): [NodeData[], string[]] => {
+  const outgoerNodes: NodeData[] = [];
+  const matchingNodes: string[] = [];
 
   const runner = (nodeId: string) => {
     const outgoerIds = edges.filter((e) => e.from === nodeId).map((e) => e.to);
-    const nodeList = nodes.filter((n) => outgoerIds.includes(n.id));
-    allOutgoers.push(...nodeList);
+    const nodeList = nodes.filter((n) => {
+      if (parent.includes(n.id) && !matchingNodes.includes(n.id)) matchingNodes.push(n.id);
+      return outgoerIds.includes(n.id) && !parent.includes(n.id);
+    });
+
+    outgoerNodes.push(...nodeList);
     nodeList.forEach((node) => runner(node.id));
   };
 
   runner(nodeId);
-
-  return allOutgoers;
+  return [outgoerNodes, matchingNodes];
 };