introduce generics (wip)

This commit is contained in:
peterkogo
2025-04-17 11:23:13 +02:00
parent 1083170b1d
commit cf1e0bee81
8 changed files with 88 additions and 61 deletions
+8 -7
View File
@@ -25,8 +25,10 @@ export const key = Symbol();
export { useStore } from '../hooks/useStore';
export function createStore(signals: StoreSignals): SvelteFlowStore {
const store = getInitialStore(signals);
export function createStore<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
signals: StoreSignals<NodeType, EdgeType>
): SvelteFlowStore<NodeType, EdgeType> {
const store = getInitialStore<NodeType, EdgeType>(signals);
function setNodeTypes(nodeTypes: NodeTypes) {
store.nodeTypes = {
@@ -42,9 +44,8 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
};
}
function addEdge(edgeParams: Edge | Connection) {
console.log(edgeParams);
store.edges = addEdgeUtil(edgeParams, store.edges);
function addEdge(edgeParams: EdgeType | Connection) {
store.edges = addEdgeUtil<EdgeType>(edgeParams, store.edges);
}
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
@@ -76,7 +77,7 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
store.resolveFitView();
}
const newNodes = new Map<string, Node>();
const newNodes = new Map<string, NodeType>();
for (const change of changes) {
const userNode = store.nodeLookup.get(change.id)?.internals.userNode;
@@ -389,7 +390,7 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
updateConnection,
cancelConnection,
reset
} satisfies SvelteFlowStoreActions);
} satisfies SvelteFlowStoreActions<NodeType, EdgeType>);
return storeWithActions;
}
@@ -56,6 +56,7 @@ import type {
OnBeforeDelete,
IsValidConnection,
Edge,
Node,
EdgeLayouted,
InternalNode,
OnBeforeReconnect
@@ -79,7 +80,9 @@ export const initialEdgeTypes = {
step: StepEdgeInternal
};
export const getInitialStore = (signals: StoreSignals) => {
export function getInitialStore<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
signals: StoreSignals<NodeType, EdgeType>
) {
// We use a class here, because Svelte adds getters & setter for us.
// Inline classes have some performance implications but we just call it once (max twice).
class SvelteFlowStore {
@@ -115,7 +118,7 @@ export const getInitialStore = (signals: StoreSignals) => {
});
viewportInitialized: boolean = $derived(this.panZoom !== null);
_edges: Edge[] = $derived.by(() => {
_edges: EdgeType[] = $derived.by(() => {
updateConnectionLookup(this.connectionLookup, this.edgeLookup, signals.edges);
return signals.edges;
});
@@ -135,10 +138,10 @@ export const getInitialStore = (signals: StoreSignals) => {
signals.edges = edges;
}
nodeLookup: NodeLookup = new Map();
parentLookup: ParentLookup = new Map();
nodeLookup: NodeLookup<InternalNode<NodeType>> = new Map();
parentLookup: ParentLookup<InternalNode<NodeType>> = new Map();
connectionLookup: ConnectionLookup = new Map();
edgeLookup: EdgeLookup = new Map();
edgeLookup: EdgeLookup<EdgeType> = new Map();
_prevVisibleEdges = new Map<string, EdgeLayouted>();
visible = $derived.by(() => {
@@ -184,7 +187,7 @@ export const getInitialStore = (signals: StoreSignals) => {
});
} else {
visibleNodes = this.nodeLookup;
visibleEdges = getLayoutedEdges(options as EdgeLayoutAllOptions);
visibleEdges = getLayoutedEdges(options as EdgeLayoutAllOptions<NodeType, EdgeType>);
}
return {
@@ -286,18 +289,18 @@ export const getInitialStore = (signals: StoreSignals) => {
onlyRenderVisibleElements: boolean = $derived(signals.props.onlyRenderVisibleElements ?? false);
onerror: OnError = $derived(signals.props.onflowerror ?? devWarn);
ondelete?: OnDelete = $derived(signals.props.ondelete);
onbeforedelete?: OnBeforeDelete = $derived(signals.props.onbeforedelete);
ondelete?: OnDelete<NodeType, EdgeType> = $derived(signals.props.ondelete);
onbeforedelete?: OnBeforeDelete<NodeType, EdgeType> = $derived(signals.props.onbeforedelete);
onbeforeconnect?: OnBeforeConnect = $derived(signals.props.onbeforeconnect);
onconnect?: OnConnect = $derived(signals.props.onconnect);
onconnectstart?: OnConnectStart = $derived(signals.props.onconnectstart);
onconnectend?: OnConnectEnd = $derived(signals.props.onconnectend);
onbeforereconnect?: OnBeforeReconnect = $derived(signals.props.onbeforereconnect);
onreconnect?: OnReconnect = $derived(signals.props.onreconnect);
onreconnectstart?: OnRecoonnectStart = $derived(signals.props.onreconnectstart);
onreconnectend?: OnReconnectEnd = $derived(signals.props.onreconnectend);
onbeforereconnect?: OnBeforeReconnect<EdgeType> = $derived(signals.props.onbeforereconnect);
onreconnect?: OnReconnect<EdgeType> = $derived(signals.props.onreconnect);
onreconnectstart?: OnRecoonnectStart<EdgeType> = $derived(signals.props.onreconnectstart);
onreconnectend?: OnReconnectEnd<EdgeType> = $derived(signals.props.onreconnectend);
clickConnect?: boolean = $derived(signals.props.clickConnect ?? true);
onclickconnectstart?: OnConnectStart = $derived(signals.props.onclickconnectstart);
@@ -374,7 +377,7 @@ export const getInitialStore = (signals: StoreSignals) => {
}
}
return new SvelteFlowStore();
};
}
// Only way to check if an object is a proxy
// is to see if is failes to perform a structured clone
+24 -15
View File
@@ -13,10 +13,10 @@ 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 = {
export type SvelteFlowStoreActions<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
setNodeTypes: (nodeTypes: NodeTypes) => void;
setEdgeTypes: (edgeTypes: EdgeTypes) => void;
addEdge: (edge: Edge | Connection) => void;
addEdge: (edge: EdgeType | Connection) => void;
zoomIn: (options?: ViewportHelperFunctionOptions) => Promise<boolean>;
zoomOut: (options?: ViewportHelperFunctionOptions) => Promise<boolean>;
setMinZoom: (minZoom: number) => void;
@@ -26,7 +26,7 @@ export type SvelteFlowStoreActions = {
fitView: (options?: FitViewOptions) => Promise<boolean>;
updateNodePositions: UpdateNodePositions;
updateNodeInternals: (updates: Map<string, InternalNodeUpdate>) => void;
unselectNodesAndEdges: (params?: { nodes?: Node[]; edges?: Edge[] }) => void;
unselectNodesAndEdges: (params?: { nodes?: NodeType[]; edges?: EdgeType[] }) => void;
addSelectedNodes: (ids: string[]) => void;
addSelectedEdges: (ids: string[]) => void;
handleNodeSelection: (id: string, unselect?: boolean, nodeRef?: HTMLDivElement | null) => void;
@@ -38,8 +38,8 @@ export type SvelteFlowStoreActions = {
reset(): void;
};
export type SvelteFlowRestProps = Omit<
SvelteFlowProps,
export type SvelteFlowRestProps<NodeType extends Node = Node, EdgeType extends Edge = Edge> = Omit<
SvelteFlowProps<NodeType, EdgeType>,
| 'width'
| 'height'
| 'class'
@@ -89,24 +89,33 @@ export type SvelteFlowRestProps = Omit<
| 'viewport'
>;
export type StoreSignals = {
props: SvelteFlowRestProps;
export type StoreSignals<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
props: SvelteFlowRestProps<NodeType, EdgeType>;
width?: number;
height?: number;
nodes: Node[];
edges: Edge[];
nodes: NodeType[];
edges: EdgeType[];
viewport?: Viewport;
};
export type SvelteFlowStoreState = ReturnType<typeof getInitialStore>;
export type SvelteFlowStoreState<
NodeType extends Node = Node,
EdgeType extends Edge = Edge
> = ReturnType<typeof getInitialStore<NodeType, EdgeType>>;
export type SvelteFlowStore = SvelteFlowStoreState & SvelteFlowStoreActions;
export type SvelteFlowStore<
NodeType extends Node = Node,
EdgeType extends Edge = Edge
> = SvelteFlowStoreState<NodeType, EdgeType> & SvelteFlowStoreActions<NodeType, EdgeType>;
export type StoreContext = {
getStore: () => SvelteFlowStore;
export type StoreContext<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
getStore: () => SvelteFlowStore<NodeType, EdgeType>;
provider: boolean;
};
export type ProviderContext = StoreContext & {
setStore: (store: SvelteFlowStore) => void;
export type ProviderContext<
NodeType extends Node = Node,
EdgeType extends Edge = Edge
> = StoreContext & {
setStore: (store: SvelteFlowStore<NodeType, EdgeType>) => void;
};
@@ -1,4 +1,4 @@
import type { DefaultEdgeOptions, Edge, EdgeLayouted, InternalNode } from '$lib/types';
import type { DefaultEdgeOptions, Node, Edge, EdgeLayouted, InternalNode } from '$lib/types';
import {
ConnectionMode,
getEdgePosition,
@@ -25,17 +25,18 @@ export function getVisibleNodes(
return visibleNodes;
}
export interface EdgeLayoutBaseOptions {
edges: Edge[];
export interface EdgeLayoutBaseOptions<NodeType extends Node = Node, EdgeType extends Edge = Edge> {
edges: EdgeType[];
defaultEdgeOptions: DefaultEdgeOptions;
elevateEdgesOnSelect: boolean;
previousEdges: Map<string, EdgeLayouted>;
nodeLookup: NodeLookup;
nodeLookup: NodeLookup<InternalNode<NodeType>>;
connectionMode: ConnectionMode;
onerror: OnError;
}
export interface EdgeLayoutAllOptions extends EdgeLayoutBaseOptions {
export interface EdgeLayoutAllOptions<NodeType extends Node = Node, EdgeType extends Edge = Edge>
extends EdgeLayoutBaseOptions<NodeType, EdgeType> {
onlyRenderVisible: never;
visibleNodes: never;
transform: never;
@@ -51,7 +52,9 @@ export interface EdgeLayoutOnlyVisibleOptions extends EdgeLayoutBaseOptions {
onlyRenderVisible: true;
}
export type EdgeLayoutOptions = EdgeLayoutAllOptions | EdgeLayoutOnlyVisibleOptions;
export type EdgeLayoutOptions<NodeType extends Node = Node, EdgeType extends Edge = Edge> =
| EdgeLayoutAllOptions<NodeType, EdgeType>
| EdgeLayoutOnlyVisibleOptions;
export function getLayoutedEdges(options: EdgeLayoutOptions): Map<string, EdgeLayouted> {
const {