first working version with using $state for nodes & edges
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { get } from 'svelte/store';
|
||||
import {
|
||||
fitView as fitViewSystem,
|
||||
panBy as panBySystem,
|
||||
@@ -23,6 +22,7 @@ import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/type
|
||||
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store.svelte';
|
||||
import { type StoreSignals, type SvelteFlowStore, type SvelteFlowStoreActions } from './types';
|
||||
import { syncNodeStores, syncEdgeStores } from './utils';
|
||||
import drag from '$lib/actions/drag';
|
||||
// import { getVisibleEdges } from './visible-edges';
|
||||
// import { getVisibleNodes } from './visible-nodes';
|
||||
|
||||
@@ -48,23 +48,15 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
|
||||
}
|
||||
|
||||
function addEdge(edgeParams: Edge | Connection) {
|
||||
const edges = get(store.edges);
|
||||
store.edges.set(addEdgeUtil(edgeParams, edges));
|
||||
// TODO: let's see
|
||||
store.edges = addEdgeUtil(edgeParams, store.edges);
|
||||
}
|
||||
|
||||
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
|
||||
for (const [id, dragItem] of nodeDragItems) {
|
||||
const node = store.nodeLookup.get(id)?.internals.userNode;
|
||||
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
node.position = dragItem.position;
|
||||
node.dragging = dragging;
|
||||
}
|
||||
|
||||
store.nodes.update((nds) => nds);
|
||||
store.nodes = store.nodes.map((node) => {
|
||||
const dragItem = nodeDragItems.get(node.id);
|
||||
return dragItem ? { ...node, position: dragItem.position, dragging } : node;
|
||||
});
|
||||
};
|
||||
|
||||
function updateNodeInternals(updates: Map<string, InternalNodeUpdate>) {
|
||||
@@ -93,13 +85,16 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
|
||||
store.fitViewOnInitDone = fitViewOnInitDone;
|
||||
}
|
||||
|
||||
const newNodes = new Map<string, Node>();
|
||||
for (const change of changes) {
|
||||
const node = store.nodeLookup.get(change.id)?.internals.userNode;
|
||||
const userNode = store.nodeLookup.get(change.id)?.internals.userNode;
|
||||
|
||||
if (!node) {
|
||||
if (!userNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const node = { ...userNode };
|
||||
|
||||
switch (change.type) {
|
||||
case 'dimensions': {
|
||||
const measured = { ...node.measured, ...change.dimensions };
|
||||
@@ -114,12 +109,15 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
|
||||
}
|
||||
case 'position':
|
||||
node.position = change.position ?? node.position;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
newNodes.set(change.id, node);
|
||||
}
|
||||
|
||||
store.nodes.update((nds) => nds);
|
||||
// store.nodes.update((nds) => nds);
|
||||
store.nodes = store.nodes.map((node) => newNodes.get(node.id) ?? node);
|
||||
// signals.updateNodes((node) => newNodes.get(node.id) ?? node);
|
||||
|
||||
if (!store.nodesInitialized) {
|
||||
store.nodesInitialized = true;
|
||||
@@ -235,68 +233,65 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
|
||||
}
|
||||
|
||||
function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) {
|
||||
const resetNodes = resetSelectedElements(params?.nodes || get(store.nodes));
|
||||
if (resetNodes) store.nodes.set(get(store.nodes));
|
||||
resetSelectedElements(params?.nodes || store.nodes);
|
||||
// if (resetNodes) store.nodes.set(get(store.nodes));
|
||||
|
||||
const resetEdges = resetSelectedElements(params?.edges || get(store.edges));
|
||||
if (resetEdges) store.edges.set(get(store.edges));
|
||||
resetSelectedElements(params?.edges || store.edges);
|
||||
// if (resetEdges) store.edges.set(get(store.edges));
|
||||
}
|
||||
|
||||
function addSelectedNodes(ids: string[]) {
|
||||
const isMultiSelection = store.multiselectionKeyPressed;
|
||||
|
||||
store.nodes.update((ns) =>
|
||||
ns.map((node) => {
|
||||
const nodeWillBeSelected = ids.includes(node.id);
|
||||
const selected = isMultiSelection
|
||||
? node.selected || nodeWillBeSelected
|
||||
: nodeWillBeSelected;
|
||||
// store.nodes.update((ns) =>
|
||||
store.nodes.forEach((node) => {
|
||||
const nodeWillBeSelected = ids.includes(node.id);
|
||||
const selected = isMultiSelection ? node.selected || nodeWillBeSelected : nodeWillBeSelected;
|
||||
|
||||
// we need to mutate the node here in order to have the correct selected state in the drag handler
|
||||
node.selected = selected;
|
||||
// we need to mutate the node here in order to have the correct selected state in the drag handler
|
||||
node.selected = selected;
|
||||
|
||||
return node;
|
||||
})
|
||||
);
|
||||
// return node;
|
||||
});
|
||||
// );
|
||||
|
||||
if (!isMultiSelection) {
|
||||
store.edges.update((es) =>
|
||||
es.map((edge) => {
|
||||
edge.selected = false;
|
||||
return edge;
|
||||
})
|
||||
);
|
||||
// store.edges.forEach((es) =>
|
||||
store.edges.forEach((edge) => {
|
||||
edge.selected = false;
|
||||
// return edge;
|
||||
});
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
function addSelectedEdges(ids: string[]) {
|
||||
const isMultiSelection = store.multiselectionKeyPressed;
|
||||
|
||||
store.edges.update((edges) =>
|
||||
edges.map((edge) => {
|
||||
const edgeWillBeSelected = ids.includes(edge.id);
|
||||
const selected = isMultiSelection
|
||||
? edge.selected || edgeWillBeSelected
|
||||
: edgeWillBeSelected;
|
||||
// store.edges.update((edges) =>
|
||||
store.edges.forEach((edge) => {
|
||||
const edgeWillBeSelected = ids.includes(edge.id);
|
||||
const selected = isMultiSelection ? edge.selected || edgeWillBeSelected : edgeWillBeSelected;
|
||||
|
||||
edge.selected = selected;
|
||||
edge.selected = selected;
|
||||
|
||||
return edge;
|
||||
})
|
||||
);
|
||||
// return edge;
|
||||
});
|
||||
// );
|
||||
|
||||
if (!isMultiSelection) {
|
||||
store.nodes.update((ns) =>
|
||||
ns.map((node) => {
|
||||
node.selected = false;
|
||||
return node;
|
||||
})
|
||||
);
|
||||
// store.nodes.update((ns) =>
|
||||
store.nodes.forEach((node) => {
|
||||
node.selected = false;
|
||||
// return node;
|
||||
});
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
function handleNodeSelection(id: string) {
|
||||
const node = get(store.nodes)?.find((n) => n.id === id);
|
||||
const node = store.nodes?.find((n) => n.id === id);
|
||||
// const node = store.nodeLookup.get(id)?.internals.userNode;
|
||||
|
||||
if (!node) {
|
||||
console.warn('012', errorMessages['error012'](id));
|
||||
@@ -343,8 +338,8 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
|
||||
}
|
||||
|
||||
const storeWithActions = Object.assign(store, {
|
||||
syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes),
|
||||
syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges),
|
||||
// syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes),
|
||||
// syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges),
|
||||
// syncViewport: (viewport) => syncViewportStores(store.panZoom, store.viewport, viewport),
|
||||
setNodeTypes,
|
||||
setEdgeTypes,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { get, writable, type Writable } from 'svelte/store';
|
||||
import {
|
||||
infiniteExtent,
|
||||
SelectionMode,
|
||||
@@ -52,7 +51,7 @@ import type {
|
||||
OnBeforeDelete,
|
||||
IsValidConnection
|
||||
} from '$lib/types';
|
||||
import { createNodesStore, createEdgesStore } from './utils';
|
||||
|
||||
import type { StoreSignals } from './types';
|
||||
|
||||
export const initialNodeTypes = {
|
||||
@@ -71,19 +70,39 @@ export const initialEdgeTypes = {
|
||||
|
||||
export const getInitialStore = (signals: StoreSignals) => {
|
||||
class SvelteFlowStore {
|
||||
nodes: ReturnType<typeof createNodesStore>;
|
||||
edges: ReturnType<typeof createEdgesStore>;
|
||||
|
||||
get nodes() {
|
||||
return signals.nodes;
|
||||
}
|
||||
set nodes(nodes) {
|
||||
signals.nodes = nodes;
|
||||
}
|
||||
get edges() {
|
||||
return signals.edges;
|
||||
}
|
||||
set edges(edges) {
|
||||
signals.edges = edges;
|
||||
}
|
||||
nodeLookup: NodeLookup = new Map();
|
||||
parentLookup: ParentLookup = new Map();
|
||||
connectionLookup: ConnectionLookup = new Map();
|
||||
edgeLookup: EdgeLookup = new Map();
|
||||
adoptNodes: true = $derived.by(() => {
|
||||
adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
|
||||
nodeExtent: this.nodeExtent,
|
||||
nodeOrigin: this.nodeOrigin,
|
||||
elevateNodesOnSelect: false,
|
||||
checkEquality: true
|
||||
});
|
||||
return true;
|
||||
});
|
||||
adoptEdges: true = $derived.by(() => {
|
||||
updateConnectionLookup(this.connectionLookup, this.edgeLookup, signals.edges);
|
||||
return true;
|
||||
});
|
||||
|
||||
domNode: HTMLDivElement | null = $derived(
|
||||
signals.container?.domNode ?? signals.props.domNode ?? null
|
||||
);
|
||||
width: number = $derived(signals.container?.width ?? signals.props.width ?? 0);
|
||||
height: number = $derived(signals.container?.height ?? signals.props.height ?? 0);
|
||||
domNode: HTMLDivElement | null = $derived(signals.domNode ?? signals.props.domNode ?? null);
|
||||
width: number = $derived(signals.width ?? signals.props.width ?? 0);
|
||||
height: number = $derived(signals.height ?? signals.props.height ?? 0);
|
||||
|
||||
flowId: string = $derived(signals.props.id ?? '1');
|
||||
minZoom: number = $derived(signals.props.minZoom ?? 0.5);
|
||||
@@ -149,12 +168,10 @@ export const getInitialStore = (signals: StoreSignals) => {
|
||||
nodesConnectable: boolean = $derived(signals.props.nodesConnectable ?? true);
|
||||
elementsSelectable: boolean = $derived(signals.props.elementsSelectable ?? true);
|
||||
selectNodesOnDrag: boolean = $derived(signals.props.selectNodesOnDrag ?? true);
|
||||
// // [store.edges, store.defaultMarkerColor, store.flowId],
|
||||
// // ([edges, defaultColor, id]) => createMarkerIds(edges, { defaultColor, id })
|
||||
|
||||
defaultMarkerColor: string = $derived(signals.props.defaultMarkerColor ?? '#b1b1b7');
|
||||
markers: MarkerProps[] = $derived.by(() => {
|
||||
const edges = signals.props.edges ?? writable([]);
|
||||
return createMarkerIds(get(edges), {
|
||||
return createMarkerIds(signals.edges, {
|
||||
defaultColor: this.defaultMarkerColor,
|
||||
id: this.flowId
|
||||
});
|
||||
@@ -172,8 +189,8 @@ export const getInitialStore = (signals: StoreSignals) => {
|
||||
edgesInitialized: boolean = $state(false);
|
||||
viewportInitialized: boolean = $state(false);
|
||||
|
||||
initialNodesLength: number = signals.props.nodes ? get(signals.props.nodes)?.length : 0;
|
||||
initialEdgesLength: number = signals.props.edges ? get(signals.props.edges)?.length : 0;
|
||||
initialNodesLength: number = signals.nodes.length;
|
||||
initialEdgesLength: number = signals.edges.length;
|
||||
initialized: boolean = $derived.by(() => {
|
||||
let initialized = false;
|
||||
// if it hasn't been initialised check if it's now
|
||||
@@ -190,27 +207,6 @@ export const getInitialStore = (signals: StoreSignals) => {
|
||||
colorMode: ColorMode = $derived(signals.props.colorMode ?? 'light');
|
||||
|
||||
constructor() {
|
||||
const nodes = signals.props.nodes ? get(signals.props.nodes) : [];
|
||||
const edges = signals.props.edges ? get(signals.props.edges) : [];
|
||||
|
||||
this.nodes = createNodesStore(
|
||||
nodes,
|
||||
this.nodeLookup,
|
||||
this.parentLookup,
|
||||
this.nodeOrigin,
|
||||
this.nodeExtent
|
||||
);
|
||||
|
||||
adoptUserNodes(nodes, this.nodeLookup, this.parentLookup, {
|
||||
nodeExtent: this.nodeExtent,
|
||||
nodeOrigin: this.nodeOrigin,
|
||||
elevateNodesOnSelect: false,
|
||||
checkEquality: false
|
||||
});
|
||||
|
||||
this.edges = createEdgesStore(edges, this.connectionLookup, this.edgeLookup);
|
||||
updateConnectionLookup(this.connectionLookup, this.edgeLookup, edges);
|
||||
|
||||
if (signals.props.fitView && !signals.props.initialViewport && this.width && this.height) {
|
||||
const bounds = getInternalNodesBounds(this.nodeLookup, {
|
||||
filter: (node) =>
|
||||
|
||||
@@ -6,8 +6,7 @@ import type {
|
||||
Connection,
|
||||
UpdateNodePositions,
|
||||
CoordinateExtent,
|
||||
UpdateConnection,
|
||||
Viewport
|
||||
UpdateConnection
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { getInitialStore } from './initial-store.svelte';
|
||||
@@ -15,8 +14,8 @@ import type { Node, Edge, NodeTypes, EdgeTypes, FitViewOptions } from '$lib/type
|
||||
import type { SvelteFlowProps } from '$lib/container/SvelteFlow';
|
||||
|
||||
export type SvelteFlowStoreActions = {
|
||||
syncNodeStores: (nodesStore: Writable<Node[]>) => void;
|
||||
syncEdgeStores: (edgeStore: Writable<Edge[]>) => void;
|
||||
// syncNodeStores: (nodesStore: Writable<Node[]>) => void;
|
||||
// syncEdgeStores: (edgeStore: Writable<Edge[]>) => void;
|
||||
// syncViewport: (viewportStore?: Writable<Viewport>) => void;
|
||||
setNodeTypes: (nodeTypes: NodeTypes) => void;
|
||||
setEdgeTypes: (edgeTypes: EdgeTypes) => void;
|
||||
@@ -43,7 +42,11 @@ export type SvelteFlowStoreActions = {
|
||||
export type ContainerSignals = { domNode?: HTMLDivElement | null; width?: number; height?: number };
|
||||
export type StoreSignals = {
|
||||
props: Partial<SvelteFlowProps> & ContainerSignals;
|
||||
container?: ContainerSignals;
|
||||
domNode?: HTMLDivElement;
|
||||
width?: number;
|
||||
height?: number;
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
};
|
||||
|
||||
export type SvelteFlowStoreState = ReturnType<typeof getInitialStore>;
|
||||
|
||||
Reference in New Issue
Block a user