135 lines
4.4 KiB
TypeScript
135 lines
4.4 KiB
TypeScript
import React, { useEffectEvent, useRef, useState } from 'react';
|
|
import { Layout, theme, Breadcrumb, Button, Space, Tree } from 'antd';
|
|
import Graph, { type GraphHandle } from './components/Graph';
|
|
import type { BreadcrumbItemType } from 'antd/es/breadcrumb/Breadcrumb';
|
|
import { useKeysdownStore } from './stores/ArrayStore';
|
|
import {
|
|
MenuFoldOutlined,
|
|
MenuUnfoldOutlined,
|
|
SaveOutlined,
|
|
FolderOpenOutlined,
|
|
} from '@ant-design/icons';
|
|
import { saveConceptSketch } from './utils/saveGraph';
|
|
import { loadConceptSketch } from './utils/loadGraph';
|
|
import { useGraphLayersTreeStore } from './stores/TreeStore';
|
|
|
|
const { Header, Content, Sider } = Layout;
|
|
|
|
const App: React.FC = () => {
|
|
const {
|
|
token: { colorBgContainer },
|
|
} = theme.useToken();
|
|
const [graphLevel, setGraphLevel] = useState<BreadcrumbItemType[]>([])
|
|
const [collapsed, setCollapsed] = useState(true);
|
|
const addKey = useKeysdownStore(state => state.add);
|
|
const removeKey = useKeysdownStore(state => state.remove);
|
|
const onKeydown = useEffectEvent((key: string) => {
|
|
addKey(key);
|
|
});
|
|
const onKeyUp = useEffectEvent((key: string) => {
|
|
removeKey(key);
|
|
})
|
|
document.addEventListener('keydown', (ev) => {
|
|
onKeydown(ev.key)
|
|
});
|
|
document.addEventListener('keyup', (ev) => {
|
|
onKeyUp(ev.key);
|
|
});
|
|
const treeData = useGraphLayersTreeStore(store => store.tree);
|
|
const nodesFlatById = useGraphLayersTreeStore(store => store.nodesFlatById);
|
|
const parentIdByChildId = useGraphLayersTreeStore(store => store.parentIdByChildId);
|
|
const graphRef = useRef<GraphHandle>(null);
|
|
|
|
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 (
|
|
<Layout>
|
|
<Sider trigger={null} collapsible collapsed={collapsed} collapsedWidth={0} style={{
|
|
background: colorBgContainer,
|
|
}}>
|
|
<Header style={{ padding: 0 }}></Header>
|
|
<Tree
|
|
checkable
|
|
treeData={treeData}
|
|
defaultExpandAll={true}
|
|
style={{
|
|
borderRadius: 0
|
|
}}
|
|
onSelect={(keys) => {
|
|
const nodeId = keys[0] as string;
|
|
if (!nodeId) return;
|
|
graphRef.current?.navigateTo(nodeId, buildPathToNode(nodeId));
|
|
}}
|
|
/>
|
|
</Sider>
|
|
<Layout>
|
|
<Header style={{ padding: 0, background: colorBgContainer, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
<Space>
|
|
<div style={{ background: '#001529' }}>
|
|
<Button
|
|
type="text"
|
|
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
style={{
|
|
fontSize: '16px',
|
|
width: 32,
|
|
height: 32,
|
|
color: colorBgContainer,
|
|
}}
|
|
/>
|
|
</div>
|
|
<Breadcrumb items={graphLevel} />
|
|
</Space>
|
|
<div style={{ background: '#001529', display: 'flex' }}>
|
|
<Button
|
|
type="text"
|
|
icon={<FolderOpenOutlined />}
|
|
onClick={loadConceptSketch}
|
|
title="Load JSON"
|
|
style={{
|
|
fontSize: '16px',
|
|
width: 32,
|
|
height: 32,
|
|
color: colorBgContainer,
|
|
}}
|
|
/>
|
|
<Button
|
|
type="text"
|
|
icon={<SaveOutlined />}
|
|
onClick={saveConceptSketch}
|
|
title="Save as JSON"
|
|
style={{
|
|
fontSize: '16px',
|
|
width: 32,
|
|
height: 32,
|
|
color: colorBgContainer,
|
|
}}
|
|
/>
|
|
</div>
|
|
</Header>
|
|
<Content
|
|
style={{
|
|
margin: '8px 16px',
|
|
padding: 24,
|
|
minHeight: 280,
|
|
background: colorBgContainer,
|
|
borderRadius: '6px'
|
|
}}
|
|
>
|
|
<Graph ref={graphRef} setGraphPath={setGraphLevel} />
|
|
</Content>
|
|
</Layout>
|
|
</Layout>
|
|
);
|
|
};
|
|
|
|
export default App; |