cleaning up Svelte 4 leftovers

This commit is contained in:
peterkogo
2024-12-09 15:59:06 +01:00
parent d0fce3984d
commit 1d321ebcd6
14 changed files with 86 additions and 586 deletions

View File

@@ -124,16 +124,22 @@
]);
function updateNode() {
nodes[0].position.x += 20;
// nodes = $nodes;
// We might as well just use updateNode() here
// this is just to show what is required to update a node
const newNode = { ...nodes[0] };
newNode.position.x += 20;
nodes[0] = newNode;
nodes = [...nodes];
}
function updateEdge() {
edges[0].type = edges[0].type === 'default' ? 'smoothstep' : 'default';
// edges = $edges;
// We might as well just use updateEdge() here
// this is just to show what is required to update an edge
const newEdge = { ...edges[0] };
newEdge.type = newEdge.type === 'default' ? 'smoothstep' : 'default';
edges[0] = newEdge;
edges = [...edges];
}
// $inspect(nodes);
</script>
<SvelteFlow
@@ -152,38 +158,38 @@
initialViewport={{ x: 100, y: 100, zoom: 2 }}
snapGrid={[25, 25]}
oninit={() => console.log('on init')}
on:nodeclick={(event) => console.log('on node click', event)}
on:nodemouseenter={(event) => console.log('on node enter', event)}
on:nodemouseleave={(event) => console.log('on node leave', event)}
on:edgeclick={(event) => console.log('edge click', event)}
on:edgemouseenter={(event) => console.log('edge enter', event)}
on:edgemouseleave={(event) => console.log('edge leave', event)}
onnodeclick={(event) => console.log('on node click', event)}
onnodemouseenter={(event) => console.log('on node enter', event)}
onnodemouseleave={(event) => console.log('on node leave', event)}
onedgeclick={(event) => console.log('edge click', event)}
onedgemouseenter={(event) => console.log('edge enter', event)}
onedgemouseleave={(event) => console.log('edge leave', event)}
onconnectstart={(event) => console.log('on connect start', event)}
onconnect={(event) => console.log('on connect', event)}
onconnectend={(event) => console.log('on connect end', event)}
on:paneclick={(event) => console.log('on pane click', event)}
on:panecontextmenu={(event) => {
onpaneclick={(event) => console.log('on pane click', event)}
onpanecontextmenu={(event) => {
console.log('on pane contextmenu', event);
}}
on:nodedrag={(event) => {
onnodedrag={(event) => {
console.log('on node drag', event);
}}
on:nodedragstart={(event) => {
onnodedragstart={(event) => {
console.log('on node drag start', event);
}}
on:nodedragstop={(event) => {
onnodedragstop={({ event }) => {
console.log('on node drag stop', event);
}}
on:nodecontextmenu={(event) => {
event.detail.event.preventDefault();
onnodecontextmenu={({ event }) => {
event.preventDefault();
console.log('on node contextmenu', event);
}}
on:edgecontextmenu={({ detail: { event, edge } }) => {
onedgecontextmenu={({ event, edge }) => {
event.preventDefault();
console.log('on edge contextmenu', edge);
}}
on:selectionclick={(event) => console.log('on selection click', event)}
on:selectioncontextmenu={(event) => console.log('on selection contextmenu', event)}
onselectionclick={(event) => console.log('on selection click', event)}
onselectioncontextmenu={(event) => console.log('on selection contextmenu', event)}
onbeforedelete={async ({ nodes, edges }) => {
console.log('on before delete', nodes, edges);
const deleteElements = confirm('Are you sure you want to delete the selected elements?');
@@ -199,8 +205,7 @@
{#snippet before()}
<ControlButton>xy</ControlButton>
{/snippet}
<ControlButton aria-label="log" on:click={() => console.log('control button')}
>log</ControlButton
<ControlButton aria-label="log" onclick={() => console.log('control button')}>log</ControlButton
>
</Controls>
<Background variant={BackgroundVariant.Dots} />

View File

@@ -5,7 +5,7 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
preprocess: vitePreprocess({ script: true }),
compilerOptions: {
runes: true
},

View File

@@ -8,7 +8,10 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
"strict": true,
"target": "ES2022",
"verbatimModuleSyntax": true,
"isolatedModules": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//

View File

@@ -83,13 +83,13 @@
"typescript": "^5.5.0"
},
"peerDependencies": {
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
"svelte": "^5.0.0"
},
"files": [
"dist"
],
"typesVersions": {
">4.0": {
">5.0": {
"index": [
"./dist/lib/index.d.ts"
]

View File

@@ -7,7 +7,6 @@
if (event.target !== container) {
return;
}
handler?.(event);
};
}
@@ -17,7 +16,7 @@
const isSelected = ids.includes(item.id);
if (item.selected !== isSelected) {
item.selected = isSelected;
return { ...item, selected: isSelected };
}
return item;
@@ -33,7 +32,6 @@
getConnectedEdges
} from '@xyflow/system';
import { useStore } from '$lib/store';
import type { Node, Edge, InternalNode } from '$lib/types';
import type { PaneProps } from './types';
let {
@@ -45,8 +43,6 @@
children
}: PaneProps = $props();
// const { nodes, edges } = store;
// svelte-ignore non_reactive_update
let container: HTMLDivElement;
let containerBounds: DOMRect | null = null;
@@ -143,14 +139,14 @@
prevSelectedNodeIds.length !== selectedNodeIds.length ||
selectedNodeIds.some((id) => !prevSelectedNodeIds.includes(id))
) {
// store.nodes.update((nodes) => nodes.map(toggleSelected(selectedNodeIds)));
store.nodes = store.nodes.map(toggleSelected(selectedNodeIds));
}
if (
prevSelectedEdgeIds.length !== selectedEdgeIds.length ||
selectedEdgeIds.some((id) => !prevSelectedEdgeIds.includes(id))
) {
// store.edges.update((edges) => edges.map(toggleSelected(selectedEdgeIds)));
store.edges = store.edges.map(toggleSelected(selectedEdgeIds));
}
store.selectionRectMode = 'user';

View File

@@ -1,6 +1,3 @@
<!-- TODO: why is this neccessary for types to work?? -->
<svelte:options runes={true} />
<script lang="ts">
import { getContext, setContext, onDestroy } from 'svelte';
import cc from 'classcat';
@@ -19,7 +16,7 @@
import { key, createStore } from '$lib/store';
import type { SvelteFlowProps } from './types';
import { useColorModeClass } from '$lib/hooks/useColorModeClass';
import { type ProviderContext, type ContainerSignals, type StoreContext } from '$lib/store/types';
import { type ProviderContext, type StoreContext } from '$lib/store/types';
let {
style,
@@ -62,7 +59,6 @@
panOnScroll = false,
panOnDrag = true,
selectionOnDrag = true,
defaultEdgeOptions,
connectionLineContainerStyle = '',
connectionLineStyle = '',
attributionPosition,
@@ -101,11 +97,13 @@
}
});
// Set store for provider context
const providerContext = getContext<ProviderContext>(key);
if (providerContext) {
providerContext.setStore(store);
}
// Overwrite store context to give children direct access
setContext(key, {
provider: false,
getStore() {

View File

@@ -31,7 +31,8 @@ import type {
OnDelete,
OnEdgeCreate,
OnBeforeDelete,
IsValidConnection
IsValidConnection,
DefaultNodeOptions
} from '$lib/types';
import type { Writable } from 'svelte/store';
import type { Snippet } from 'svelte';
@@ -301,6 +302,22 @@ export type SvelteFlowProps = NodeEvents &
* }
*/
defaultEdgeOptions?: DefaultEdgeOptions;
/** Defaults to be applied to all new nodes that are added to the flow.
*
* Properties on a new nodes will override these defaults if they exist.
* @example
* const defaultNodeOptions = {
* type: 'customNodeType',
* position: {x: 0, y: 0}
* data: { label: 'custom label' },
* hidden: false,
* deletable: true,
* selected: false,
* focusable: true,
* zIndex: 12,
* }
*/
defaultNodeOptions?: DefaultNodeOptions;
/** Sets a fixed width for the flow */
width?: number;
/** Sets a fixed height for the flow */

View File

@@ -1,24 +0,0 @@
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();
}

View File

@@ -21,14 +21,12 @@ import {
import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types';
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';
export const key = Symbol();
export { useStore } from './hook.svelte';
export { useStore } from '../hooks/useStore';
export function createStore(signals: StoreSignals): SvelteFlowStore {
const store = getInitialStore(signals);
@@ -82,8 +80,10 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
...store.fitViewOptions,
nodes: store.fitViewOptions?.nodes
});
if (fitViewOnInitDone) {
store.fitViewOnInitDone = fitViewOnInitDone;
}
}
const newNodes = new Map<string, Node>();
for (const change of changes) {
@@ -115,9 +115,7 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
newNodes.set(change.id, node);
}
// 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;
@@ -244,24 +242,23 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
const isMultiSelection = store.multiselectionKeyPressed;
// store.nodes.update((ns) =>
store.nodes.forEach((node) => {
store.nodes = store.nodes.map((node) => {
const nodeWillBeSelected = ids.includes(node.id);
const selected = isMultiSelection ? node.selected || nodeWillBeSelected : nodeWillBeSelected;
if (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;
return node;
});
// );
if (!isMultiSelection) {
// store.edges.forEach((es) =>
store.edges.forEach((edge) => {
edge.selected = false;
// return edge;
store.edges = store.edges.map((edge) => {
return edge.selected ? { ...edge, selected: false } : edge;
});
// );
}
}
@@ -319,7 +316,6 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
});
}
// const _connection = writable<ConnectionState>(initialConnection);
const updateConnection: UpdateConnection = (newConnection: ConnectionState) => {
store.rawConnection = { ...newConnection };
};
@@ -338,9 +334,6 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
}
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),
setNodeTypes,
setEdgeTypes,
addEdge,

View File

@@ -49,7 +49,9 @@ import type {
OnDelete,
OnEdgeCreate,
OnBeforeDelete,
IsValidConnection
IsValidConnection,
Edge,
Node
} from '$lib/types';
import type { StoreSignals } from './types';
@@ -69,6 +71,8 @@ export const initialEdgeTypes = {
};
export const getInitialStore = (signals: StoreSignals) => {
// We use a class here, because Svelte adds getters & setter for us.
// Inline classes have some performance implications but we just call it once.
class SvelteFlowStore {
get nodes() {
return signals.nodes;
@@ -86,6 +90,7 @@ export const getInitialStore = (signals: StoreSignals) => {
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,
@@ -100,7 +105,7 @@ export const getInitialStore = (signals: StoreSignals) => {
return true;
});
domNode: HTMLDivElement | null = $derived(signals.domNode ?? signals.props.domNode ?? null);
domNode: HTMLDivElement | null = $derived(signals.domNode ?? signals.domNode ?? null);
width: number = $derived(signals.width ?? signals.props.width ?? 0);
height: number = $derived(signals.height ?? signals.props.height ?? 0);
@@ -112,6 +117,9 @@ export const getInitialStore = (signals: StoreSignals) => {
nodeExtent: CoordinateExtent = $derived(signals.props.nodeExtent ?? infiniteExtent);
translateExtent: CoordinateExtent = $derived(signals.props.translateExtent ?? infiniteExtent);
defaultEdgeOptions: Partial<Edge> = $derived(signals.props.defaultEdgeOptions ?? {});
defaultNodeOptions: Partial<Node> = $derived(signals.props.defaultNodeOptions ?? {});
nodeDragThreshold: number = $derived(signals.props.nodeDragThreshold ?? 1);
autoPanOnNodeDrag: boolean = $derived(signals.props.autoPanOnNodeDrag ?? true);
autoPanOnConnect: boolean = $derived(signals.props.autoPanOnConnect ?? true);
@@ -207,6 +215,7 @@ export const getInitialStore = (signals: StoreSignals) => {
colorMode: ColorMode = $derived(signals.props.colorMode ?? 'light');
constructor() {
// Process intial fitView here
if (signals.props.fitView && !signals.props.initialViewport && this.width && this.height) {
const bounds = getInternalNodesBounds(this.nodeLookup, {
filter: (node) =>
@@ -221,95 +230,4 @@ export const getInitialStore = (signals: StoreSignals) => {
}
}
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)
// };
};

View File

@@ -1,178 +0,0 @@
import { readable, 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 ConnectionLookup,
type OnConnect,
type OnConnectStart,
type OnConnectEnd,
type NodeLookup,
type EdgeLookup,
type ConnectionState,
type ParentLookup,
getInternalNodesBounds
} 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,
EdgeLayouted,
Node,
Edge,
FitViewOptions,
OnDelete,
OnEdgeCreate,
OnBeforeDelete,
IsValidConnection,
InternalNode
} from '$lib/types';
import { createNodesStore, createEdgesStore } from './utils';
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 = ({
nodes = [],
edges = [],
width,
height,
fitView,
nodeOrigin,
nodeExtent
}: {
nodes?: Node[];
edges?: Edge[];
width?: number;
height?: number;
fitView?: boolean;
nodeOrigin?: NodeOrigin;
nodeExtent?: CoordinateExtent;
}) => {
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)
};
};

View File

@@ -1,4 +1,3 @@
import type { Writable } from 'svelte/store';
import type {
InternalNodeUpdate,
XYPosition,
@@ -14,9 +13,6 @@ 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;
// syncViewport: (viewportStore?: Writable<Viewport>) => void;
setNodeTypes: (nodeTypes: NodeTypes) => void;
setEdgeTypes: (edgeTypes: EdgeTypes) => void;
addEdge: (edge: Edge | Connection) => void;
@@ -39,9 +35,8 @@ export type SvelteFlowStoreActions = {
reset(): void;
};
export type ContainerSignals = { domNode?: HTMLDivElement | null; width?: number; height?: number };
export type StoreSignals = {
props: Partial<SvelteFlowProps> & ContainerSignals;
props: Partial<SvelteFlowProps>;
domNode?: HTMLDivElement;
width?: number;
height?: number;

View File

@@ -1,222 +0,0 @@
import {
writable,
get,
type Unsubscriber,
type Subscriber,
type Updater,
type Writable
} from 'svelte/store';
import {
adoptUserNodes,
updateConnectionLookup,
type Viewport,
type PanZoomInstance,
type ConnectionLookup,
type EdgeLookup,
type NodeLookup,
type ParentLookup,
type NodeOrigin,
infiniteExtent,
type CoordinateExtent
} from '@xyflow/system';
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, InternalNode, Node } from '$lib/types';
// we need to sync the user nodes and the internal nodes so that the user can receive the updates
// made by Svelte Flow (like dragging or selecting a node).
export function syncNodeStores(
nodesStore: ReturnType<typeof createNodesStore>,
userNodesStore: Writable<Node[]>
) {
const nodesStoreSetter = nodesStore.set;
const userNodesStoreSetter = userNodesStore.set;
const currentNodesStore = get(nodesStore);
const currentUserNodesStore = get(userNodesStore);
// depending how the user initializes the nodes, we need to decide if we want to use
// the user nodes or the internal nodes for initialization. A user can use a SvelteFlowProvider
// without providing any nodes, in that case we want to use the nodes passed by the user.
// By default we are using the store nodes, because they already have the absolute positions.
const initWithUserNodes = currentNodesStore.length === 0 && currentUserNodesStore.length > 0;
let val = initWithUserNodes ? currentUserNodesStore : currentNodesStore;
nodesStore.set(val);
const _set = (nds: Node[]) => {
const updatedNodes = nodesStoreSetter(nds);
val = updatedNodes;
userNodesStoreSetter(val);
return updatedNodes;
};
nodesStore.set = userNodesStore.set = _set;
nodesStore.update = userNodesStore.update = (fn: (nds: Node[]) => Node[]) => _set(fn(val));
}
// same for edges
export function syncEdgeStores(
edgesStore: ReturnType<typeof createEdgesStore>,
userEdgesStore: Writable<Edge[]>
) {
const nodesStoreSetter = edgesStore.set;
const userEdgesStoreSetter = userEdgesStore.set;
let val = get(userEdgesStore);
edgesStore.set(val);
const _set = (eds: Edge[]) => {
nodesStoreSetter(eds);
userEdgesStoreSetter(eds);
val = eds;
};
edgesStore.set = userEdgesStore.set = _set;
edgesStore.update = userEdgesStore.update = (fn: (nds: Edge[]) => Edge[]) => _set(fn(val));
}
// 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: PanZoomInstance | null,
viewportStore: Writable<Viewport>,
userViewportStore?: Writable<Viewport>
) => {
if (!userViewportStore) {
return;
}
const panZoom = panZoomStore;
const viewportStoreSetter = viewportStore.set;
const userViewportStoreSetter = userViewportStore.set;
let val = userViewportStore ? get(userViewportStore) : { x: 0, y: 0, zoom: 1 };
viewportStore.set(val);
viewportStore.set = (vp: Viewport) => {
viewportStoreSetter(vp);
userViewportStoreSetter(vp);
val = vp;
return vp;
};
userViewportStore.set = (vp: Viewport) => {
panZoom?.syncViewport(vp);
viewportStoreSetter(vp);
userViewportStoreSetter(vp);
val = vp;
return vp;
};
viewportStore.update = (fn: (vp: Viewport) => Viewport) => {
viewportStore.set(fn(val));
};
userViewportStore.update = (fn: (vp: Viewport) => Viewport) => {
userViewportStore.set(fn(val));
};
};
export type NodeStoreOptions = {
elevateNodesOnSelect?: boolean;
};
type Update<T> = Writable<T>['update'];
// we are creating a custom store for the internals nodes in order to update the zIndex and positionAbsolute.
// The user only passes in relative positions, so we need to calculate the absolute positions based on the parent nodes.
export const createNodesStore = (
nodes: Node[],
nodeLookup: NodeLookup<InternalNode>,
parentLookup: ParentLookup<InternalNode>,
nodeOrigin: NodeOrigin = [0, 0],
nodeExtent: CoordinateExtent = infiniteExtent
): {
subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber;
update: (this: void, updater: Updater<Node[]>) => void;
set: (this: void, value: Node[]) => Node[];
setDefaultOptions: (opts: DefaultNodeOptions) => void;
setOptions: (opts: NodeStoreOptions) => void;
} => {
const { subscribe, set } = writable<Node[]>([]);
let value = nodes;
let defaults = {};
let elevateNodesOnSelect = true;
const _set = (nds: Node[]): Node[] => {
adoptUserNodes(nds, nodeLookup, parentLookup, {
elevateNodesOnSelect,
nodeOrigin,
nodeExtent,
defaults,
checkEquality: false
});
value = nds;
set(value);
return value;
};
const _update: Update<Node[]> = (fn: (nds: Node[]) => Node[]) => _set(fn(value));
const setDefaultOptions = (options: DefaultNodeOptions) => {
defaults = options;
};
const setOptions = (options: NodeStoreOptions) => {
elevateNodesOnSelect = options.elevateNodesOnSelect ?? elevateNodesOnSelect;
};
_set(value);
return {
subscribe,
set: _set,
update: _update,
setDefaultOptions,
setOptions
};
};
export const createEdgesStore = (
edges: Edge[],
connectionLookup: ConnectionLookup,
edgeLookup: EdgeLookup<Edge>,
defaultOptions?: DefaultEdgeOptions
): Writable<Edge[]> & { setDefaultOptions: (opts: DefaultEdgeOptions) => void } => {
const { subscribe, set } = writable<Edge[]>([]);
let value = edges;
let defaults = defaultOptions || {};
const _set: typeof set = (eds: Edge[]) => {
const nextEdges = defaults ? eds.map((edge) => ({ ...defaults, ...edge })) : eds;
updateConnectionLookup(connectionLookup, edgeLookup, nextEdges);
value = nextEdges;
set(value);
};
const _update: Update<Edge[]> = (fn: (eds: Edge[]) => Edge[]) => _set(fn(value));
const setDefaultOptions = (options: DefaultEdgeOptions) => {
defaults = options;
};
_set(value);
return {
subscribe,
set: _set,
update: _update,
setDefaultOptions
};
};

View File

@@ -8,8 +8,7 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"noErrorTruncation": true
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias