Files
EasyFlow/easyflow-ui-admin/packages/tinyflow-ui/src/store/stores.svelte.ts
陈子默 0968e3bfa5 feat: 完善用户确认节点选项与输出流转
- 重构确认节点单选多选配置及输出参数契约

- 统一管理端、用户中心、匿名分享和公共接口恢复流程

- 增加保存校验、错误契约及交互测试
2026-09-04 14:55:55 +08:00

114 lines
3.8 KiB
TypeScript

import { type Edge, type Node, type Viewport } from '@xyflow/svelte';
import { getContext, setContext } from 'svelte';
import type { TinyflowNodeNormalizer } from '../utils/nodeInteraction';
const DEFAULT_VIEWPORT: Viewport = { x: 250, y: 100, zoom: 1 };
const TINYFLOW_STORE_CONTEXT = Symbol('tinyflow_store');
export const createStore = () => {
let nodesInternal = $state.raw([] as Node[]);
let edgesInternal = $state.raw([] as Edge[]);
let viewport = $state.raw({ ...DEFAULT_VIEWPORT } as Viewport);
let normalizeNode: TinyflowNodeNormalizer = (node) => node;
const pendingEditFlushers = new Set<() => void>();
const normalizeNodes = (nodes: Node[]) => nodes.map(normalizeNode);
return {
// nodes: nodesInternal,
// edges: edgesInternal,
// viewport,
setNodeNormalizer: (normalizer: TinyflowNodeNormalizer) => {
normalizeNode = normalizer;
nodesInternal = normalizeNodes(nodesInternal);
},
init: (nodes: Node[], edges: Edge[], nextViewport?: Viewport | null) => {
nodesInternal = normalizeNodes(nodes);
edgesInternal = edges;
viewport = nextViewport ? { ...nextViewport } : { ...DEFAULT_VIEWPORT };
},
getNodes: () => nodesInternal,
setNodes: (nodes: Node[]) => {
nodesInternal = normalizeNodes(nodes);
},
getEdges: () => edgesInternal,
setEdges: (edges: Edge[]) => {
edgesInternal = edges;
},
getViewport: () => viewport,
setViewport: (v: Viewport) => {
viewport = v;
},
registerPendingEditFlusher: (flusher: () => void) => {
pendingEditFlushers.add(flusher);
return () => pendingEditFlushers.delete(flusher);
},
flushPendingEdits: () => {
[...pendingEditFlushers].forEach((flusher) => flusher());
},
getNode: (id: string) => nodesInternal.find((node) => node.id === id),
addNode: (node: Node) => {
nodesInternal = [...nodesInternal, normalizeNode(node)];
},
removeNode: (id: string) => {
nodesInternal = nodesInternal.filter((node) => node.id !== id);
},
updateNode: (id: string, node: Node) => {
nodesInternal = nodesInternal.map((n) =>
n.id === id ? normalizeNode({ ...n, ...node }) : n,
);
},
updateNodes: (update: (nodes: Node[]) => Node[]) => {
nodesInternal = normalizeNodes(update(nodesInternal));
},
updateNodeData: (id: string, data: Node['data']) => {
nodesInternal = nodesInternal.map((n) =>
n.id === id ? { ...n, data: { ...n.data, ...data } } : n,
);
},
selectNodeOnly: (id: string) => {
nodesInternal = nodesInternal.map((n) =>
n.id === id ? { ...n, selected: true } : { ...n, selected: false },
);
},
getEdge: (id: string) => edgesInternal.find((edge) => edge.id === id),
addEdge: (edge: Edge) => {
edgesInternal = [...edgesInternal, edge];
},
removeEdge: (id: string) => {
edgesInternal = edgesInternal.filter((edge) => edge.id !== id);
},
updateEdge: (id: string, edge: Edge) => {
edgesInternal = edgesInternal.map((e) =>
e.id === id ? { ...e, ...edge } : e,
);
},
updateEdges: (update: (edges: Edge[]) => Edge[]) => {
edgesInternal = update(edgesInternal);
},
updateEdgeData: (id: string, data: Edge['data']) => {
edgesInternal = edgesInternal.map((e) =>
e.id === id ? { ...e, data: { ...e.data, ...data } } : e,
);
},
};
};
export type TinyflowStore = ReturnType<typeof createStore>;
export const createTinyflowStoreContext = () => {
const store = createStore();
setContext(TINYFLOW_STORE_CONTEXT, store);
return store;
};
export const useTinyflowStore = () => {
const store = getContext<TinyflowStore | undefined>(TINYFLOW_STORE_CONTEXT);
if (!store) {
throw new Error('Tinyflow store context is not initialized');
}
return store;
};