diff --git a/src/App.tsx b/src/App.tsx index d751c5b..1b26b16 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ -import React, { useEffect, useEffectEvent, useState } from 'react'; +import React, { useEffectEvent, useRef, useState } from 'react'; import { Layout, theme, Breadcrumb, Button, Space, Tree } from 'antd'; -import Graph from './components/Graph'; +import Graph, { type GraphHandle } from './components/Graph'; import type { BreadcrumbItemType } from 'antd/es/breadcrumb/Breadcrumb'; import { useKeysdownStore } from './stores/ArrayStore'; import { @@ -36,10 +36,20 @@ const App: React.FC = () => { onKeyUp(ev.key); }); const treeData = useGraphLayersTreeStore(store => store.tree); + const nodesFlatById = useGraphLayersTreeStore(store => store.nodesFlatById); + const parentIdByChildId = useGraphLayersTreeStore(store => store.parentIdByChildId); + const graphRef = useRef(null); - useEffect(() => { - console.info(treeData); - }, [treeData]) + function buildPathToNode(nodeId: string) { + const path: Array<{ id: string; name: string | undefined }> = []; + let current: string | undefined = nodeId; + while (current && nodesFlatById.has(current)) { + const node = nodesFlatById.get(current)!; + path.unshift({ id: current, name: node.title as string | undefined }); + current = parentIdByChildId.get(current); + } + return [{ id: 'main', name: 'Main' }, ...path]; + } return ( @@ -54,6 +64,11 @@ const App: React.FC = () => { style={{ borderRadius: 0 }} + onSelect={(keys) => { + const nodeId = keys[0] as string; + if (!nodeId) return; + graphRef.current?.navigateTo(nodeId, buildPathToNode(nodeId)); + }} /> @@ -110,7 +125,7 @@ const App: React.FC = () => { borderRadius: '6px' }} > - + diff --git a/src/components/Graph.tsx b/src/components/Graph.tsx index fe39a10..df4c184 100644 --- a/src/components/Graph.tsx +++ b/src/components/Graph.tsx @@ -1,4 +1,4 @@ -import { createContext, useEffect, useRef, useState } from "react"; +import { createContext, forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react"; import Viz from 'viz.js'; import { Module, render } from 'viz.js/full.render.js'; import * as d3 from 'd3'; @@ -63,7 +63,11 @@ export interface OpenNodeContext { const viz = new Viz({ Module, render }); export const graphContext = createContext(null); -export default function Graph({ setGraphPath }: { setGraphPath: React.Dispatch> }) { +export interface GraphHandle { + navigateTo: (nodeId: string, path: Array<{ id: string; name: string | undefined }>) => void; +} + +const Graph = forwardRef> }>(function Graph({ setGraphPath }, ref) { const containerRef = useRef(null); const [graph, setGraph] = useState(defaultGraph()); const [contextMenuOpened, openContextMenu] = useState(false); @@ -242,6 +246,14 @@ export default function Graph({ setGraphPath }: { setGraphPath: React.Dispatch ({ + navigateTo(nodeId, path) { + const newPath = path.map(p => createPathSegment(p.id, p.name)); + setGraphsPath(newPath); + selectGraphId(nodeId); + } + })); + return (
@@ -262,7 +274,9 @@ export default function Graph({ setGraphPath }: { setGraphPath: React.Dispatch
) -} +}); + +export default Graph; export function defaultGraph(): GraphModel { const start = crypto.randomUUID();