WIP: migrated store and useSvelteFlow hook to signals & solved state propagation for provider

This commit is contained in:
peterkogo
2024-12-06 13:53:48 +01:00
parent ed1ead7b5a
commit f29898f0bd
49 changed files with 1616 additions and 1137 deletions
@@ -0,0 +1,24 @@
import { getContext } from 'svelte';
import type { StoreContext, SvelteFlowStore } from './types';
import { key } from './';
export function useStore(): SvelteFlowStore {
const storeContext = getContext<StoreContext>(key);
if (!storeContext) {
throw new Error(
'In order to use useStore you need to wrap your component in a <SvelteFlowProvider />'
);
}
if (process.env.NODE_ENV === 'development') {
if (storeContext.provider && !$effect.tracking()) {
console.warn(
'Use $derived(useStore()), when not calling inside a child of the <SvelteFlow /> component.'
);
}
}
return storeContext.getStore();
}
+132 -203
View File
@@ -1,14 +1,11 @@
import { getContext, setContext } from 'svelte';
import { derived, get, writable } from 'svelte/store';
import { get } from 'svelte/store';
import {
createMarkerIds,
fitView as fitViewSystem,
panBy as panBySystem,
updateNodeInternals as updateNodeInternalsSystem,
addEdge as addEdgeUtil,
initialConnection,
errorMessages,
pointToRendererPoint,
type UpdateNodePositions,
type InternalNodeUpdate,
type ViewportHelperFunctionOptions,
@@ -17,60 +14,37 @@ import {
type CoordinateExtent,
type UpdateConnection,
type ConnectionState,
type NodeOrigin,
getFitViewNodes,
updateAbsolutePositions,
getDimensions
} from '@xyflow/system';
import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types';
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store';
import type { SvelteFlowStore } from './types';
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store.svelte';
import { type StoreSignals, type SvelteFlowStore, type SvelteFlowStoreActions } from './types';
import { syncNodeStores, syncEdgeStores, syncViewportStores } from './utils';
import { getVisibleEdges } from './visible-edges';
import { getVisibleNodes } from './visible-nodes';
// import { getVisibleEdges } from './visible-edges';
// import { getVisibleNodes } from './visible-nodes';
export const key = Symbol();
export function createStore({
nodes,
edges,
width,
height,
fitView: fitViewOnCreate,
nodeOrigin,
nodeExtent
}: {
nodes?: Node[];
edges?: Edge[];
width?: number;
height?: number;
fitView?: boolean;
nodeOrigin?: NodeOrigin;
nodeExtent?: CoordinateExtent;
}): SvelteFlowStore {
const store = getInitialStore({
nodes,
edges,
width,
height,
fitView: fitViewOnCreate,
nodeOrigin,
nodeExtent
});
export { useStore } from './hook.svelte';
export function createStore(signals: StoreSignals): SvelteFlowStore {
const store = getInitialStore(signals);
function setNodeTypes(nodeTypes: NodeTypes) {
store.nodeTypes.set({
store.nodeTypes = {
...initialNodeTypes,
...nodeTypes
});
};
}
function setEdgeTypes(edgeTypes: EdgeTypes) {
store.edgeTypes.set({
store.edgeTypes = {
...initialEdgeTypes,
...edgeTypes
});
};
}
function addEdge(edgeParams: Edge | Connection) {
@@ -79,10 +53,8 @@ export function createStore({
}
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
const nodeLookup = get(store.nodeLookup);
for (const [id, dragItem] of nodeDragItems) {
const node = nodeLookup.get(id)?.internals.userNode;
const node = store.nodeLookup.get(id)?.internals.userNode;
if (!node) {
continue;
@@ -96,33 +68,33 @@ export function createStore({
};
function updateNodeInternals(updates: Map<string, InternalNodeUpdate>) {
const nodeLookup = get(store.nodeLookup);
const parentLookup = get(store.parentLookup);
const { changes, updatedInternals } = updateNodeInternalsSystem(
updates,
nodeLookup,
get(store.parentLookup),
get(store.domNode),
get(store.nodeOrigin)
store.nodeLookup,
store.parentLookup,
store.domNode,
store.nodeOrigin
);
if (!updatedInternals) {
return;
}
updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent });
updateAbsolutePositions(store.nodeLookup, store.parentLookup, {
nodeOrigin: store.nodeOrigin,
nodeExtent: store.nodeExtent
});
if (!get(store.fitViewOnInitDone) && get(store.fitViewOnInit)) {
const fitViewOptions = get(store.fitViewOptions);
if (!store.fitViewOnInitDone && store.fitViewOnInit) {
const fitViewOnInitDone = fitViewSync({
...fitViewOptions,
nodes: fitViewOptions?.nodes
...store.fitViewOptions,
nodes: store.fitViewOptions?.nodes
});
store.fitViewOnInitDone.set(fitViewOnInitDone);
store.fitViewOnInitDone = fitViewOnInitDone;
}
for (const change of changes) {
const node = nodeLookup.get(change.id)?.internals.userNode;
const node = store.nodeLookup.get(change.id)?.internals.userNode;
if (!node) {
continue;
@@ -149,14 +121,14 @@ export function createStore({
store.nodes.update((nds) => nds);
if (!get(store.nodesInitialized)) {
store.nodesInitialized.set(true);
if (!store.nodesInitialized) {
store.nodesInitialized = true;
}
}
function fitView(options?: FitViewOptions) {
const panZoom = get(store.panZoom);
const domNode = get(store.domNode);
const panZoom = store.panZoom;
const domNode = store.domNode;
if (!panZoom || !domNode) {
return Promise.resolve(false);
@@ -164,15 +136,15 @@ export function createStore({
const { width, height } = getDimensions(domNode);
const fitViewNodes = getFitViewNodes(get(store.nodeLookup), options);
const fitViewNodes = getFitViewNodes(store.nodeLookup, options);
return fitViewSystem(
{
nodes: fitViewNodes,
width,
height,
minZoom: get(store.minZoom),
maxZoom: get(store.maxZoom),
minZoom: store.minZoom,
maxZoom: store.maxZoom,
panZoom
},
options
@@ -180,21 +152,21 @@ export function createStore({
}
function fitViewSync(options?: FitViewOptions) {
const panZoom = get(store.panZoom);
const panZoom = store.panZoom;
if (!panZoom) {
return false;
}
const fitViewNodes = getFitViewNodes(get(store.nodeLookup), options);
const fitViewNodes = getFitViewNodes(store.nodeLookup, options);
fitViewSystem(
{
nodes: fitViewNodes,
width: get(store.width),
height: get(store.height),
minZoom: get(store.minZoom),
maxZoom: get(store.maxZoom),
width: store.width,
height: store.height,
minZoom: store.minZoom,
maxZoom: store.maxZoom,
panZoom
},
options
@@ -204,7 +176,7 @@ export function createStore({
}
function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) {
const panZoom = get(store.panZoom);
const panZoom = store.panZoom;
if (!panZoom) {
return Promise.resolve(false);
}
@@ -221,29 +193,29 @@ export function createStore({
}
function setMinZoom(minZoom: number) {
const panZoom = get(store.panZoom);
const panZoom = store.panZoom;
if (panZoom) {
panZoom.setScaleExtent([minZoom, get(store.maxZoom)]);
store.minZoom.set(minZoom);
panZoom.setScaleExtent([minZoom, store.maxZoom]);
store.minZoom = minZoom;
}
}
function setMaxZoom(maxZoom: number) {
const panZoom = get(store.panZoom);
const panZoom = store.panZoom;
if (panZoom) {
panZoom.setScaleExtent([get(store.minZoom), maxZoom]);
store.maxZoom.set(maxZoom);
panZoom.setScaleExtent([store.minZoom, maxZoom]);
store.maxZoom = maxZoom;
}
}
function setTranslateExtent(extent: CoordinateExtent) {
const panZoom = get(store.panZoom);
const panZoom = store.panZoom;
if (panZoom) {
panZoom.setTranslateExtent(extent);
store.translateExtent.set(extent);
store.translateExtent = extent;
}
}
@@ -259,7 +231,7 @@ export function createStore({
}
function setPaneClickDistance(distance: number) {
get(store.panZoom)?.setClickDistance(distance);
store.panZoom?.setClickDistance(distance);
}
function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) {
@@ -270,39 +242,8 @@ export function createStore({
if (resetEdges) store.edges.set(get(store.edges));
}
// store.deleteKeyPressed.subscribe(async (deleteKeyPressed) => {
// if (deleteKeyPressed) {
// const nodes = get(store.nodes);
// const edges = get(store.edges);
// const selectedNodes = nodes.filter((node) => node.selected);
// const selectedEdges = edges.filter((edge) => edge.selected);
// const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
// nodesToRemove: selectedNodes,
// edgesToRemove: selectedEdges,
// nodes,
// edges,
// onBeforeDelete: get(store.onbeforedelete)
// });
// if (matchingNodes.length || matchingEdges.length) {
// store.nodes.update((nds) =>
// nds.filter((node) => !matchingNodes.some((mN) => mN.id === node.id))
// );
// store.edges.update((eds) =>
// eds.filter((edge) => !matchingEdges.some((mE) => mE.id === edge.id))
// );
// get(store.ondelete)?.({
// nodes: matchingNodes,
// edges: matchingEdges
// });
// }
// }
// });
function addSelectedNodes(ids: string[]) {
const isMultiSelection = get(store.multiselectionKeyPressed);
const isMultiSelection = store.multiselectionKeyPressed;
store.nodes.update((ns) =>
ns.map((node) => {
@@ -329,7 +270,7 @@ export function createStore({
}
function addSelectedEdges(ids: string[]) {
const isMultiSelection = get(store.multiselectionKeyPressed);
const isMultiSelection = store.multiselectionKeyPressed;
store.edges.update((edges) =>
edges.map((edge) => {
@@ -362,12 +303,12 @@ export function createStore({
return;
}
store.selectionRect.set(null);
store.selectionRectMode.set(null);
store.selectionRect = null;
store.selectionRectMode = null;
if (!node.selected) {
addSelectedNodes([id]);
} else if (node.selected && get(store.multiselectionKeyPressed)) {
} else if (node.selected && store.multiselectionKeyPressed) {
unselectNodesAndEdges({ nodes: [node], edges: [] });
}
}
@@ -376,78 +317,33 @@ export function createStore({
const viewport = get(store.viewport);
return panBySystem({
delta,
panZoom: get(store.panZoom),
panZoom: store.panZoom,
transform: [viewport.x, viewport.y, viewport.zoom],
translateExtent: get(store.translateExtent),
width: get(store.width),
height: get(store.height)
translateExtent: store.translateExtent,
width: store.width,
height: store.height
});
}
const _connection = writable<ConnectionState>(initialConnection);
// const _connection = writable<ConnectionState>(initialConnection);
const updateConnection: UpdateConnection = (newConnection: ConnectionState) => {
_connection.set({ ...newConnection });
store.rawConnection = { ...newConnection };
};
function cancelConnection() {
_connection.set(initialConnection);
store.rawConnection = initialConnection;
}
function reset() {
store.fitViewOnInitDone.set(false);
store.selectionRect.set(null);
store.selectionRectMode.set(null);
store.snapGrid.set(null);
store.isValidConnection.set(() => true);
store.fitViewOnInitDone = false;
store.selectionRect = null;
store.selectionRectMode = null;
unselectNodesAndEdges();
cancelConnection();
}
return {
// state
...store,
// derived state
visibleEdges: getVisibleEdges(store),
visibleNodes: getVisibleNodes(store),
connection: derived([_connection, store.viewport], ([connection, viewport]) => {
return connection.inProgress
? {
...connection,
to: pointToRendererPoint(connection.to, [viewport.x, viewport.y, viewport.zoom])
}
: { ...connection };
}),
markers: derived(
[store.edges, store.defaultMarkerColor, store.flowId],
([edges, defaultColor, id]) => createMarkerIds(edges, { defaultColor, id })
),
initialized: (() => {
let initialized = false;
const initialNodesLength = get(store.nodes).length;
const initialEdgesLength = get(store.edges).length;
return derived(
[store.nodesInitialized, store.edgesInitialized, store.viewportInitialized],
([nodesInitialized, edgesInitialized, viewportInitialized]) => {
// If it was already initialized, return true from then on
if (initialized) return initialized;
// if it hasn't been initialised check if it's now
if (initialNodesLength === 0) {
initialized = viewportInitialized;
} else if (initialEdgesLength === 0) {
initialized = viewportInitialized && nodesInitialized;
} else {
initialized = viewportInitialized && nodesInitialized && edgesInitialized;
}
return initialized;
}
);
})(),
// actions
const storeWithActions = Object.assign(store, {
syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes),
syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges),
syncViewport: (viewport) => syncViewportStores(store.panZoom, store.viewport, viewport),
@@ -471,43 +367,76 @@ export function createStore({
updateConnection,
cancelConnection,
reset
};
}
} satisfies SvelteFlowStoreActions);
export function useStore(): SvelteFlowStore {
const store = getContext<{ getStore: () => SvelteFlowStore }>(key);
return storeWithActions;
if (!store) {
throw new Error(
'In order to use useStore you need to wrap your component in a <SvelteFlowProvider />'
);
}
// return {
// // state
// ...store,
return store.getStore();
}
// // derived state
// // visibleEdges: getVisibleEdges(store),
// // visibleNodes: getVisibleNodes(store),
// // connection: derived([_connection, store.viewport], ([connection, viewport]) => {
// // return connection.inProgress
// // ? {
// // ...connection,
// // to: pointToRendererPoint(connection.to, [viewport.x, viewport.y, viewport.zoom])
// // }
// // : { ...connection };
// // }),
// // markers: derived(
// // [store.edges, store.defaultMarkerColor, store.flowId],
// // ([edges, defaultColor, id]) => createMarkerIds(edges, { defaultColor, id })
// // ),
// // initialized: (() => {
// // let initialized = false;
// // const initialNodesLength = get(store.nodes).length;
// // const initialEdgesLength = get(store.edges).length;
// // return derived(
// // [store.nodesInitialized, store.edgesInitialized, store.viewportInitialized],
// // ([nodesInitialized, edgesInitialized, viewportInitialized]) => {
// // // If it was already initialized, return true from then on
// // if (initialized) return initialized;
export function createStoreContext({
nodes,
edges,
width,
height,
fitView,
nodeOrigin,
nodeExtent
}: {
nodes?: Node[];
edges?: Edge[];
width?: number;
height?: number;
fitView?: boolean;
nodeOrigin?: NodeOrigin;
nodeExtent?: CoordinateExtent;
}) {
const store = createStore({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent });
// // // if it hasn't been initialised check if it's now
// // if (initialNodesLength === 0) {
// // initialized = viewportInitialized;
// // } else if (initialEdgesLength === 0) {
// // initialized = viewportInitialized && nodesInitialized;
// // } else {
// // initialized = viewportInitialized && nodesInitialized && edgesInitialized;
// // }
setContext(key, {
getStore: () => store
});
// // return initialized;
// // }
// // );
// // })(),
return store;
// // actions
// syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes),
// syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges),
// // syncViewport: (viewport) => syncViewportStores(store.panZoom, store.viewport, viewport),
// setNodeTypes,
// setEdgeTypes,
// addEdge,
// updateNodePositions,
// updateNodeInternals,
// zoomIn,
// zoomOut,
// fitView: (options?: FitViewOptions) => fitView(options),
// setMinZoom,
// setMaxZoom,
// setTranslateExtent,
// setPaneClickDistance,
// unselectNodesAndEdges,
// addSelectedNodes,
// addSelectedEdges,
// handleNodeSelection,
// panBy,
// updateConnection,
// cancelConnection,
// reset
// };
}
@@ -0,0 +1,318 @@
import { get, writable, type Writable } from 'svelte/store';
import {
infiniteExtent,
SelectionMode,
ConnectionMode,
ConnectionLineType,
devWarn,
adoptUserNodes,
getViewportForBounds,
updateConnectionLookup,
initialConnection,
type SelectionRect,
type SnapGrid,
type MarkerProps,
type PanZoomInstance,
type CoordinateExtent,
type NodeOrigin,
type OnError,
type Viewport,
type OnConnect,
type OnConnectStart,
type OnConnectEnd,
type NodeLookup,
type ConnectionState,
getInternalNodesBounds,
type ColorMode,
createMarkerIds,
type EdgeLookup,
type ConnectionLookup,
type ParentLookup,
pointToRendererPoint
} from '@xyflow/system';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
import InputNode from '$lib/components/nodes/InputNode.svelte';
import OutputNode from '$lib/components/nodes/OutputNode.svelte';
import GroupNode from '$lib/components/nodes/GroupNode.svelte';
import {
BezierEdgeInternal,
SmoothStepEdgeInternal,
StraightEdgeInternal,
StepEdgeInternal
} from '$lib/components/edges';
import type {
NodeTypes,
EdgeTypes,
FitViewOptions,
OnDelete,
OnEdgeCreate,
OnBeforeDelete,
IsValidConnection
} from '$lib/types';
import { createNodesStore, createEdgesStore } from './utils';
import type { StoreSignals } from './types';
export const initialNodeTypes = {
input: InputNode,
output: OutputNode,
default: DefaultNode,
group: GroupNode
};
export const initialEdgeTypes = {
straight: StraightEdgeInternal,
smoothstep: SmoothStepEdgeInternal,
default: BezierEdgeInternal,
step: StepEdgeInternal
};
export const getInitialStore = (signals: StoreSignals) => {
class SvelteFlowStore {
nodes: ReturnType<typeof createNodesStore>;
edges: ReturnType<typeof createEdgesStore>;
nodeLookup: NodeLookup = new Map();
parentLookup: ParentLookup = new Map();
connectionLookup: ConnectionLookup = new Map();
edgeLookup: EdgeLookup = new Map();
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);
flowId: string = $derived(signals.props.id ?? '1');
minZoom: number = $derived(signals.props.minZoom ?? 0.5);
maxZoom: number = $derived(signals.props.maxZoom ?? 2);
nodeOrigin: NodeOrigin = $derived(signals.props.nodeOrigin ?? [0, 0]);
nodeExtent: CoordinateExtent = $derived(signals.props.nodeExtent ?? infiniteExtent);
translateExtent: CoordinateExtent = $derived(signals.props.translateExtent ?? infiniteExtent);
nodeDragThreshold: number = $derived(signals.props.nodeDragThreshold ?? 1);
autoPanOnNodeDrag: boolean = $derived(signals.props.autoPanOnNodeDrag ?? true);
autoPanOnConnect: boolean = $derived(signals.props.autoPanOnConnect ?? true);
fitViewOnInitDone: boolean = $state(false);
fitViewOnInit: boolean = $derived(signals.props.fitView ?? false);
fitViewOptions: FitViewOptions | undefined = $derived(signals.props.fitViewOptions);
panZoom: PanZoomInstance | null = $state(null);
snapGrid: SnapGrid | null = $derived(signals.props.snapGrid ?? null);
dragging: boolean = $state(false);
selectionRect: SelectionRect | null = $state(null);
selectionKeyPressed: boolean = $state(false);
multiselectionKeyPressed: boolean = $state(false);
deleteKeyPressed: boolean = $state(false);
panActivationKeyPressed: boolean = $state(false);
zoomActivationKeyPressed: boolean = $state(false);
selectionRectMode: string | null = $state(null);
selectionMode: SelectionMode = $derived(signals.props.selectionMode ?? SelectionMode.Partial);
nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes });
edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes });
viewport: Writable<Viewport> = writable(
signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 }
);
connectionMode: ConnectionMode = $derived(
signals.props.connectionMode ?? ConnectionMode.Strict
);
rawConnection: ConnectionState = $state(initialConnection);
connection: ConnectionState = $derived.by(() => {
if (this.rawConnection.inProgress) {
const viewport = get(this.viewport);
return {
...this.rawConnection,
to: pointToRendererPoint(this.rawConnection.to, [viewport.x, viewport.y, viewport.zoom])
};
} else {
return { ...this.rawConnection };
}
});
connectionLineType: ConnectionLineType = $derived(
signals.props.connectionLineType ?? ConnectionLineType.Bezier
);
connectionRadius: number = $derived(signals.props.connectionRadius ?? 20);
isValidConnection: IsValidConnection = $derived(
signals.props.isValidConnection ?? (() => true)
);
nodesDraggable: boolean = $derived(signals.props.nodesDraggable ?? true);
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), {
defaultColor: this.defaultMarkerColor,
id: this.flowId
});
});
onlyRenderVisibleElements: boolean = $derived(signals.props.onlyRenderVisibleElements ?? false);
onerror: OnError = $derived(signals.props.onerror ?? devWarn);
ondelete?: OnDelete = $derived(signals.props.ondelete ?? undefined);
onedgecreate?: OnEdgeCreate = $derived(signals.props.onedgecreate);
onconnect?: OnConnect = $derived(signals.props.onconnect);
onconnectstart?: OnConnectStart = $derived(signals.props.onconnectstart);
onconnectend?: OnConnectEnd = $derived(signals.props.onconnectend);
onbeforedelete?: OnBeforeDelete = $derived(signals.props.onbeforedelete);
nodesInitialized: boolean = $state(false);
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;
initialized: boolean = $derived.by(() => {
let initialized = false;
// if it hasn't been initialised check if it's now
if (this.initialNodesLength === 0) {
initialized = this.viewportInitialized;
} else if (this.initialEdgesLength === 0) {
initialized = this.viewportInitialized && this.nodesInitialized;
} else {
initialized = this.viewportInitialized && this.nodesInitialized && this.edgesInitialized;
}
return initialized;
});
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 && this.width && this.height) {
const bounds = getInternalNodesBounds(this.nodeLookup, {
filter: (node) =>
!!((node.width || node.initialWidth) && (node.height || node.initialHeight))
});
this.viewport.set(getViewportForBounds(bounds, this.width, this.height, 0.5, 2, 0.1));
}
}
resetStoreValues() {
// TODO: reset all store values here
}
}
return new SvelteFlowStore();
// return new Store();
// const nodeLookup: NodeLookup = new Map();
// const parentLookup = new Map();
// const connectionLookup = new Map();
// const edgeLookup = new Map();
// const storeNodeOrigin = nodeOrigin ?? [0, 0];
// const storeNodeExtent = nodeExtent ?? infiniteExtent;
// adoptUserNodes(nodes, nodeLookup, parentLookup, {
// nodeExtent: storeNodeExtent,
// nodeOrigin: storeNodeOrigin,
// elevateNodesOnSelect: false,
// checkEquality: false
// });
// updateConnectionLookup(connectionLookup, edgeLookup, edges);
// let viewport: Viewport = { x: 0, y: 0, zoom: 1 };
// if (fitView && width && height) {
// const bounds = getInternalNodesBounds(nodeLookup, {
// filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight))
// });
// viewport = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
// }
// return {
// flowId: writable<string | null>(null),
// nodes: createNodesStore(nodes, nodeLookup, parentLookup, storeNodeOrigin, storeNodeExtent),
// nodeLookup: readable<NodeLookup<InternalNode>>(nodeLookup),
// parentLookup: readable<ParentLookup<InternalNode>>(parentLookup),
// edgeLookup: readable<EdgeLookup<Edge>>(edgeLookup),
// visibleNodes: readable<InternalNode[]>([]),
// edges: createEdgesStore(edges, connectionLookup, edgeLookup),
// visibleEdges: readable<EdgeLayouted[]>([]),
// connectionLookup: readable<ConnectionLookup>(connectionLookup),
// height: writable<number>(500),
// width: writable<number>(500),
// minZoom: writable<number>(0.5),
// maxZoom: writable<number>(2),
// nodeOrigin: writable<NodeOrigin>(storeNodeOrigin),
// nodeDragThreshold: writable<number>(1),
// nodeExtent: writable<CoordinateExtent>(storeNodeExtent),
// translateExtent: writable<CoordinateExtent>(infiniteExtent),
// autoPanOnNodeDrag: writable<boolean>(true),
// autoPanOnConnect: writable<boolean>(true),
// fitViewOnInit: writable<boolean>(false),
// fitViewOnInitDone: writable<boolean>(false),
// fitViewOptions: writable<FitViewOptions>(undefined),
// panZoom: writable<PanZoomInstance | null>(null),
// snapGrid: writable<SnapGrid | null>(null),
// dragging: writable<boolean>(false),
// selectionRect: writable<SelectionRect | null>(null),
// selectionKeyPressed: writable<boolean>(false),
// multiselectionKeyPressed: writable<boolean>(false),
// deleteKeyPressed: writable<boolean>(false),
// panActivationKeyPressed: writable<boolean>(false),
// zoomActivationKeyPressed: writable<boolean>(false),
// selectionRectMode: writable<string | null>(null),
// selectionMode: writable<SelectionMode>(SelectionMode.Partial),
// nodeTypes: writable<NodeTypes>(initialNodeTypes),
// edgeTypes: writable<EdgeTypes>(initialEdgeTypes),
// viewport: writable<Viewport>(viewport),
// connectionMode: writable<ConnectionMode>(ConnectionMode.Strict),
// domNode: writable<HTMLDivElement | null>(null),
// connection: readable<ConnectionState>(initialConnection),
// connectionLineType: writable<ConnectionLineType>(ConnectionLineType.Bezier),
// connectionRadius: writable<number>(20),
// isValidConnection: writable<IsValidConnection>(() => true),
// nodesDraggable: writable<boolean>(true),
// nodesConnectable: writable<boolean>(true),
// elementsSelectable: writable<boolean>(true),
// selectNodesOnDrag: writable<boolean>(true),
// markers: readable<MarkerProps[]>([]),
// defaultMarkerColor: writable<string>('#b1b1b7'),
// lib: readable<string>('svelte'),
// onlyRenderVisibleElements: writable<boolean>(false),
// onerror: writable<OnError>(devWarn),
// ondelete: writable<OnDelete>(undefined),
// onedgecreate: writable<OnEdgeCreate>(undefined),
// onconnect: writable<OnConnect>(undefined),
// onconnectstart: writable<OnConnectStart>(undefined),
// onconnectend: writable<OnConnectEnd>(undefined),
// onbeforedelete: writable<OnBeforeDelete>(undefined),
// nodesInitialized: writable<boolean>(false),
// edgesInitialized: writable<boolean>(false),
// viewportInitialized: writable<boolean>(false),
// initialized: readable<boolean>(false)
// };
};
+17 -1
View File
@@ -10,8 +10,9 @@ import type {
Viewport
} from '@xyflow/system';
import type { getInitialStore } from './initial-store';
import type { getInitialStore } from './initial-store.svelte';
import type { Node, Edge, NodeTypes, EdgeTypes, FitViewOptions } from '$lib/types';
import type { SvelteFlowProps } from '$lib/container/SvelteFlow';
export type SvelteFlowStoreActions = {
syncNodeStores: (nodesStore: Writable<Node[]>) => void;
@@ -39,6 +40,21 @@ export type SvelteFlowStoreActions = {
reset(): void;
};
export type ContainerSignals = { domNode?: HTMLDivElement | null; width?: number; height?: number };
export type StoreSignals = {
props: Partial<SvelteFlowProps> & ContainerSignals;
container?: ContainerSignals;
};
export type SvelteFlowStoreState = ReturnType<typeof getInitialStore>;
export type SvelteFlowStore = SvelteFlowStoreState & SvelteFlowStoreActions;
export type StoreContext = {
getStore: () => SvelteFlowStore;
provider: boolean;
};
export type ProviderContext = StoreContext & {
setStore: (store: SvelteFlowStore) => void;
};
+2 -2
View File
@@ -78,7 +78,7 @@ export function syncEdgeStores(
// it is possible to pass a viewport store to SvelteFlow for having more control
// if that's the case we need to sync the internal viewport with the user viewport
export const syncViewportStores = (
panZoomStore: Writable<PanZoomInstance | null>,
panZoomStore: PanZoomInstance | null,
viewportStore: Writable<Viewport>,
userViewportStore?: Writable<Viewport>
) => {
@@ -86,7 +86,7 @@ export const syncViewportStores = (
return;
}
const panZoom = get(panZoomStore);
const panZoom = panZoomStore;
const viewportStoreSetter = viewportStore.set;
const userViewportStoreSetter = userViewportStore.set;