diff --git a/app/src/ui/components/canvas/Canvas.tsx b/app/src/ui/components/canvas/Canvas.tsx index d23ba8f7..7aff9151 100644 --- a/app/src/ui/components/canvas/Canvas.tsx +++ b/app/src/ui/components/canvas/Canvas.tsx @@ -22,17 +22,20 @@ type CanvasProps = ReactFlowProps & { onDropNewEdge?: (base: any) => any; }; -export function Canvas({ - nodes: _nodes, - edges: _edges, - externalProvider, - backgroundStyle = "lines", - minimap = false, - children, - onDropNewNode, - onDropNewEdge, - ...props -}: CanvasProps) { +export function Canvas( + { + nodes: _nodes, + edges: _edges, + externalProvider, + backgroundStyle = "lines", + minimap = false, + children, + onDropNewNode, + onDropNewEdge, + ...props + }: CanvasProps, + ref?: any, +) { const [nodes, setNodes, onNodesChange] = useNodesState(_nodes ?? []); const [edges, setEdges, onEdgesChange] = useEdgesState(_edges ?? []); const { screenToFlowPosition } = useReactFlow(); @@ -176,7 +179,6 @@ export function Canvas({ onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} nodesConnectable={false} - /*panOnDrag={isSpacePressed}*/ panOnDrag={true} zoomOnScroll={isCommandPressed} panOnScroll={!isCommandPressed} diff --git a/app/src/ui/components/canvas/layouts/index.ts b/app/src/ui/components/canvas/layouts/index.ts index 520c3f2e..fbcf8ee4 100644 --- a/app/src/ui/components/canvas/layouts/index.ts +++ b/app/src/ui/components/canvas/layouts/index.ts @@ -44,8 +44,10 @@ export const layoutWithDagre = ({ nodes, edges, graph }: LayoutProps) => { const position = dagreGraph.node(node.id); return { ...node, - x: position.x - (node.width ?? 0) / 2, - y: position.y - (node.height ?? 0) / 2, + position: { + x: position.x - (node.width ?? 0) / 2, + y: position.y - (node.height ?? 0) / 2, + }, }; }), edges, diff --git a/app/src/ui/components/canvas/panels/index.tsx b/app/src/ui/components/canvas/panels/index.tsx index 8d423ff5..7fedb118 100644 --- a/app/src/ui/components/canvas/panels/index.tsx +++ b/app/src/ui/components/canvas/panels/index.tsx @@ -35,16 +35,14 @@ export function Panels({ children, ...props }: PanelsProps) { )} {props.zoom && ( - <> - - - - {percent}% - - - - - + + + + {percent}% + + + + )} {props.minimap && ( <> diff --git a/app/src/ui/modules/data/components/canvas/DataSchemaCanvas.tsx b/app/src/ui/modules/data/components/canvas/DataSchemaCanvas.tsx index 6ef3babb..ad639b64 100644 --- a/app/src/ui/modules/data/components/canvas/DataSchemaCanvas.tsx +++ b/app/src/ui/modules/data/components/canvas/DataSchemaCanvas.tsx @@ -6,6 +6,10 @@ import { layoutWithDagre } from "ui/components/canvas/layouts"; import { Panels } from "ui/components/canvas/panels"; import { EntityTableNode } from "./EntityTableNode"; import { useTheme } from "ui/client/use-theme"; +import { Panel } from "ui/components/canvas/panels/Panel"; +import { TbLayout } from "react-icons/tb"; +import { useState } from "react"; +import { type CanvasPosition, dataCanvasStore } from "ui/store"; function entitiesToNodes(entities: AppDataConfig["entities"]): Node[] { return Object.entries(entities ?? {}).map(([name, entity]) => { @@ -65,12 +69,45 @@ const nodeTypes = { entity: EntityTableNode.Component, } as const; +function getNodeAutoLayout(nodes: Node[], edges: any[]): CanvasPosition[] { + const nodeLayout = layoutWithDagre({ + nodes: nodes.map((n) => ({ + id: n.id, + ...EntityTableNode.getSize(n.data), + })), + edges, + graph: { + rankdir: "LR", + marginx: 50, + marginy: 50, + }, + }); + + return nodeLayout.nodes.map((n) => ({ + id: n.id, + ...n.position, + })); +} + +function setNodesLayout(nodes: Node[], layout: CanvasPosition[]) { + return nodes.map((node) => { + const pos = layout.find((l) => l.id === node.id); + if (pos) { + return { + ...node, + position: { x: pos.x, y: pos.y }, + }; + } + return node; + }); +} + export function DataSchemaCanvas() { const { config: { data }, } = useBknd(); const { theme } = useTheme(); - const nodes = entitiesToNodes(data.entities); + const edges = relationsToEdges(data.relations).map((e) => ({ ...e, style: { @@ -85,31 +122,35 @@ export function DataSchemaCanvas() { }, })); - const nodeLayout = layoutWithDagre({ - nodes: nodes.map((n) => ({ - id: n.id, - ...EntityTableNode.getSize(n.data), - })), - edges, - graph: { - rankdir: "LR", - marginx: 50, - marginy: 50, - }, - }); + const entityNodes = entitiesToNodes(data.entities); + const positions = dataCanvasStore((state) => state.positions); + const setPositions = dataCanvasStore((state) => state.setPositions); + const resetPositions = dataCanvasStore((state) => state.reset); - nodeLayout.nodes.forEach((node) => { - const n = nodes.find((n) => n.id === node.id); - if (n) { - n.position = { x: node.x, y: node.y }; - } - }); + const layout = positions ? positions : getNodeAutoLayout(entityNodes, edges); + const [nodes, setNodes] = useState[]>(setNodesLayout(entityNodes, layout)); + + function setLayout(positions: CanvasPosition[] = getNodeAutoLayout(entityNodes, edges)) { + setNodes(setNodesLayout(entityNodes, positions)); + } + + function resetLayout() { + resetPositions(); + setLayout(); + } return ( { + const positions = nodes + .map((n) => (n.id === node.id ? node : n)) + .map((n) => ({ id: n.id, ...n.position })); + setPositions(positions); + setLayout(positions); + }} nodeTypes={nodeTypes} minZoom={0.1} maxZoom={2} @@ -118,7 +159,11 @@ export function DataSchemaCanvas() { maxZoom: 0.8, }} > - + + + + + ); diff --git a/app/src/ui/store/datacanvas.ts b/app/src/ui/store/datacanvas.ts new file mode 100644 index 00000000..e708b7bd --- /dev/null +++ b/app/src/ui/store/datacanvas.ts @@ -0,0 +1,25 @@ +import { create } from "zustand"; +import { combine, persist } from "zustand/middleware"; + +export type CanvasPosition = { + id: string; + x: number; + y: number; +}; + +export const dataCanvasStore = create( + persist( + combine( + { + positions: null as CanvasPosition[] | null, + }, + (set) => ({ + setPositions: (positions: CanvasPosition[]) => set(() => ({ positions })), + reset: () => set(() => ({ positions: null })), + }), + ), + { + name: "datacanvas", + }, + ), +); diff --git a/app/src/ui/store/index.ts b/app/src/ui/store/index.ts index 264ea61c..3d1792b5 100644 --- a/app/src/ui/store/index.ts +++ b/app/src/ui/store/index.ts @@ -1 +1,2 @@ export { appShellStore } from "./appshell"; +export { dataCanvasStore, type CanvasPosition } from "./datacanvas";