From 50d34bfb00a3d9a09fd3be27d805ca4693576f29 Mon Sep 17 00:00:00 2001 From: dtoro Date: Mon, 9 Mar 2026 15:45:19 +0100 Subject: [PATCH] improve empty and initial state --- src/App.tsx | 88 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1e80c66..a37661c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,7 +31,16 @@ import { ContextMenuGroup } from "@/components/ui/context-menu" import { AppMenubar } from '@/components/AppMenubar' -import { ClipboardPaste } from 'lucide-react' +import { + Empty, + EmptyContent, + EmptyDescription, + EmptyHeader, + EmptyMedia, + EmptyTitle, +} from '@/components/ui/empty' +import { Button } from '@/components/ui/button' +import { ClipboardPaste, FolderOpen, FileStack, CircleDotDashed } from 'lucide-react' import { DEFAULT_NODE_STYLE, getDefaultDataForType, getNextNodeId } from './lib/flowUtils' import { getRegisteredNodeTypes, @@ -47,17 +56,31 @@ const snapToGrid = (x: number, y: number): { x: number; y: number } => ({ y: Math.round(y / SNAP_GRID[1]) * SNAP_GRID[1], }) +const NODE_GAP = 150 const initialNodes: AppNode[] = [ + { + id: 'var_001', + position: { x: 50, y: 100 }, + data: { value: 'Zoe', valueType: 'string' as const }, + type: 'variable', + style: DEFAULT_NODE_STYLE.variable, + }, { id: 'cfg_001', - position: { x: 50, y: 50 }, - data: { plantuml: '@startuml\nactor User\nparticipant "Render" as R\nUser -> R : config\n@enduml\n', title: 'config-cfg_001' }, + position: { x: 50 + DEFAULT_NODE_STYLE.variable.width + NODE_GAP, y: 50 }, + data: { + plantuml: '@startuml\nactor User\nparticipant "{{ var_001 }}" as R\nUser -> R : loves\n@enduml\n', + title: 'config-cfg_001', + }, type: 'config', style: DEFAULT_NODE_STYLE.config, }, { id: 'rnd_001', - position: { x: 350, y: 80 }, + position: { + x: 50 + DEFAULT_NODE_STYLE.variable.width + NODE_GAP + DEFAULT_NODE_STYLE.config.width + NODE_GAP, + y: 50, + }, data: {}, type: 'render', style: DEFAULT_NODE_STYLE.render, @@ -65,9 +88,20 @@ const initialNodes: AppNode[] = [ ] const initialEdges: AppEdge[] = [ + { id: 'e-var_001-cfg_001', source: 'var_001', target: 'cfg_001', type: 'animated' }, { id: 'e-cfg_001-rnd_001', source: 'cfg_001', target: 'rnd_001', type: 'animated' }, ] +function getExampleGraph(): { nodes: AppNode[]; edges: AppEdge[] } { + return { + nodes: initialNodes.map((n) => ({ + ...n, + data: n.data && typeof n.data === 'object' ? { ...(n.data as object) } : n.data, + })), + edges: initialEdges.map((e) => ({ ...e })), + } +} + const PROJECT_FILE_EXT = '.zui.json' const PROJECT_VERSION = 1 @@ -89,7 +123,7 @@ export default function App() { canUndo, canRedo, setStateImmediate, - } = useGraphStateWithHistory(initialNodes, initialEdges) + } = useGraphStateWithHistory(getExampleGraph().nodes, getExampleGraph().edges) const importInputRef = useRef(null) const [rfInstance, setRfInstance] = React.useState(null) @@ -219,6 +253,12 @@ export default function App() { importInputRef.current?.click() }, []) + const handleLoadExample = useCallback(() => { + const { nodes: exampleNodes, edges: exampleEdges } = getExampleGraph() + setStateImmediate({ nodes: exampleNodes, edges: exampleEdges }) + setProjectMessage({ type: 'success', text: 'Example loaded' }) + }, [setStateImmediate]) + const onImportFileChange = useCallback( (e: React.ChangeEvent) => { const file = e.target.files?.[0] @@ -406,11 +446,10 @@ export default function App() { {projectMessage && (
{projectMessage.text}
@@ -420,7 +459,34 @@ export default function App() { -
+
+ {nodes.length === 0 && ( +
+
+ + + + + + Start adding a new node! + + Right‑click to add nodes.
Import a project or paste a node. +
+
+ + + + +
+
+
+ )}