Init commit
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
ReactFlow,
|
||||
ReactFlowProvider,
|
||||
Background,
|
||||
Controls,
|
||||
MiniMap,
|
||||
addEdge,
|
||||
applyNodeChanges,
|
||||
applyEdgeChanges,
|
||||
type Node,
|
||||
type Edge,
|
||||
type OnConnect,
|
||||
type NodeChange,
|
||||
type EdgeChange,
|
||||
} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { nodeTypes } from './nodes';
|
||||
import { debounce, serializeDocument } from './serialize';
|
||||
import type { DiagramDocument } from './types';
|
||||
import './diagram.css';
|
||||
|
||||
interface DiagramEditorProps {
|
||||
initialValue: DiagramDocument | null;
|
||||
onChange: (doc: DiagramDocument) => void;
|
||||
}
|
||||
|
||||
function toRfNodes(doc: DiagramDocument | null): Node[] {
|
||||
return (doc?.nodes ?? []).map((node) => ({
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
position: node.position,
|
||||
data: node.data,
|
||||
}));
|
||||
}
|
||||
|
||||
function toRfEdges(doc: DiagramDocument | null): Edge[] {
|
||||
return (doc?.edges ?? []).map((edge) => ({
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
type: edge.type,
|
||||
data: edge.data,
|
||||
}));
|
||||
}
|
||||
|
||||
function DiagramCanvas({ initialValue, onChange }: DiagramEditorProps) {
|
||||
const [nodes, setNodes] = useState<Node[]>(() => toRfNodes(initialValue));
|
||||
const [edges, setEdges] = useState<Edge[]>(() => toRfEdges(initialValue));
|
||||
|
||||
const emitChange = useMemo(
|
||||
() => debounce((n: Node[], e: Edge[]) => onChange(serializeDocument(n, e)), 400),
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const onNodesChange = useCallback(
|
||||
(changes: NodeChange[]) => {
|
||||
setNodes((current) => {
|
||||
const next = applyNodeChanges(changes, current);
|
||||
emitChange(next, edges);
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[edges, emitChange],
|
||||
);
|
||||
|
||||
const onEdgesChange = useCallback(
|
||||
(changes: EdgeChange[]) => {
|
||||
setEdges((current) => {
|
||||
const next = applyEdgeChanges(changes, current);
|
||||
emitChange(nodes, next);
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[nodes, emitChange],
|
||||
);
|
||||
|
||||
const onConnect: OnConnect = useCallback(
|
||||
(connection) => {
|
||||
setEdges((current) => {
|
||||
const next = addEdge(connection, current);
|
||||
emitChange(nodes, next);
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[nodes, emitChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="diagram-canvas">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
nodeTypes={nodeTypes}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
fitView
|
||||
>
|
||||
<Background />
|
||||
<Controls />
|
||||
<MiniMap />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DiagramEditor(props: DiagramEditorProps) {
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<DiagramCanvas {...props} />
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user