diff --git a/TODO.md b/TODO.md index 30d419c..e9ebec1 100644 --- a/TODO.md +++ b/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 diff --git a/src/components/CustomNode/TextNode.tsx b/src/components/CustomNode/TextNode.tsx index 63e1045..2e64b7e 100644 --- a/src/components/CustomNode/TextNode.tsx +++ b/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 = ({ 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) => { 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 = ({ {inViewport && isParent && hasCollapse && !hideCollapse && ( - + {isExpanded ? ( + + ) : ( + + )} )} diff --git a/src/components/CustomNode/index.tsx b/src/components/CustomNode/index.tsx index b9540dd..7638ea7 100644 --- a/src/components/CustomNode/index.tsx +++ b/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"; diff --git a/src/hooks/store/useGraph.tsx b/src/hooks/store/useGraph.tsx index c443068..3b382a1 100644 --- a/src/hooks/store/useGraph.tsx +++ b/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((set, get) => ({ @@ -31,29 +33,36 @@ const useGraph = create((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), }); diff --git a/src/utils/getOutgoers.ts b/src/utils/getOutgoers.ts index a5ae23d..1c3712d 100644 --- a/src/utils/getOutgoers.ts +++ b/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]; };