UNPKG

@ichooss/xflow

Version:

[English (US)](README.md) | 简体中文

212 lines (194 loc) 5.87 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { produce, original } from 'immer'; import { create } from 'zustand'; import type { UseBoundStore, StoreApi } from 'zustand'; import type { NodeOptions, EdgeOptions } from '../types'; import { apply } from '../util'; import { StringExt } from '@antv/x6'; export type Command = | 'init' | 'addNodes' | 'removeNodes' | 'updateNode' | 'addEdges' | 'removeEdges' | 'updateEdge'; export type ChangeItem = { command: Command; // eslint-disable-next-line @typescript-eslint/no-explicit-any data: any; }; export type State = { changeList: ChangeItem[]; nodes: NodeOptions[]; edges: EdgeOptions[]; }; type ActionOptions = { silent?: boolean; }; type UpdateNodeDataOrFn = | Partial<NodeOptions> | ((node: NodeOptions) => Partial<NodeOptions>); type UpdateEdgeDataOrFn = | Partial<EdgeOptions> | ((edge: EdgeOptions) => Partial<NodeOptions>); export type Actions = { initData: ( data: { nodes: NodeOptions[]; edges: EdgeOptions[] }, options?: ActionOptions, ) => void; addNodes: (ns: NodeOptions[], options?: ActionOptions) => void; removeNodes: (ids: string[], options?: ActionOptions) => void; updateNode: (id: string, data: UpdateNodeDataOrFn, options?: ActionOptions) => void; addEdges: (es: EdgeOptions[], options?: ActionOptions) => void; removeEdges: (ids: string[], options?: ActionOptions) => void; updateEdge: (id: string, data: UpdateEdgeDataOrFn, options?: ActionOptions) => void; clearChangeList: () => void; }; export type GraphStore = UseBoundStore<StoreApi<State & Actions>>; export const createGraphStore = () => { return create<State & Actions>((set) => ({ nodes: [], edges: [], changeList: [], initData: (data, options) => set( produce((state: State) => { state.nodes = data.nodes; state.edges = data.edges; if (!options?.silent) { state.changeList.push({ command: 'init', data, }); } }), ), addNodes: (ns, options) => set( produce((state: State) => { if (!ns.length) return; // generate id for nodes ns.forEach((n) => { if (!n.id) { n.id = StringExt.uuid(); } }); const duplicated = state.nodes.find((n) => ns.some((m) => m.id && m.id === n.id), ); if (!duplicated) { state.nodes.push(...ns); if (!options?.silent) { state.changeList.push({ command: 'addNodes', data: ns, }); } } else { console.error(`node id=${duplicated.id} already existed`); } }), ), removeNodes: (ids, options) => set( produce((state: State) => { if (!ids.length) return; state.nodes = state.nodes.filter((n) => !ids.includes(n.id!)); if (!options?.silent) { state.changeList.push({ command: 'removeNodes', data: ids, }); } }), ), updateNode: (id, data, options) => set( produce((state: State) => { const node = state.nodes.find((n) => n.id === id); if (node) { const changed = typeof data === 'function' ? data(original(node) || {}) : data; if (changed.id !== undefined || changed.shape !== undefined) { console.error(`id and shape can't be changed`); return; } apply(node, changed); if (!options?.silent) { state.changeList.push({ command: 'updateNode', data: { id, data: changed }, }); } } }), ), addEdges: (es, options) => set( produce((state: State) => { if (!es.length) return; // generate id for edges es.forEach((e) => { if (!e.id) { e.id = StringExt.uuid(); } }); const duplicated = state.edges.find((e) => es.some((m) => m.id && m.id === e.id), ); if (!duplicated) { state.edges.push(...es); if (!options?.silent) { state.changeList.push({ command: 'addEdges', data: es, }); } } else { console.error(`edge id=${duplicated.id} already existed`); } }), ), removeEdges: (ids, options) => set( produce((state: State) => { if (!ids.length) return; state.edges = state.edges.filter((n) => !ids.includes(n.id)); if (!options?.silent) { state.changeList.push({ command: 'removeEdges', data: ids, }); } }), ), updateEdge: (id, data, options) => set( produce((state: State) => { const edge = state.edges.find((n) => n.id === id); if (edge) { const changed = typeof data === 'function' ? data(original(edge) || {}) : data; if (changed.id !== undefined || changed.shape !== undefined) { console.error(`id and shape can't be changed`); return; } apply(edge, changed); if (!options?.silent) { state.changeList.push({ command: 'updateEdge', data: { id, data: changed }, }); } } }), ), clearChangeList: () => set( produce((state: State) => { state.changeList = []; }), ), })); };