@dschz/solid-flow 0.2.4 → 0.3.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,10 @@
1
- import { For, Index, Show, batch, createComputed, createContext, createEffect, createMemo, createSignal, mapArray, mergeProps, onCleanup, onMount, splitProps, untrack, useContext } from "solid-js";
2
- import { ReactiveMap } from "@solid-primitives/map";
1
+ import { For, Show, createContext, createEffect, createMemo, createProjection, createSignal, createStore, flush, mapArray, merge, omit, onCleanup, onSettled, snapshot, untrack, useContext } from "solid-js";
3
2
  import { createMediaQuery } from "@solid-primitives/media";
4
- import { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, SelectionMode as SelectionMode$1, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, addEdge as addEdge$1, areConnectionMapsEqual, calcAutoPan, calculateNodePosition, clampPosition, clampPositionToParent, createDevWarn, createMarkerIds, elementSelectionKeys, evaluateAbsolutePosition, fitViewport, getBezierEdgeCenter, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getEdgeCenter, getEdgePosition, getEdgeToolbarTransform, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesBounds as getNodesBounds$1, getNodesInside, getOutgoers, getOverlappingArea, getSmoothStepPath, getSmoothStepPath as getSmoothStepPath$1, getStraightPath, getStraightPath as getStraightPath$1, getViewportForBounds, getViewportForBounds as getViewportForBounds$1, handleConnectionChange, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isNodeBase, isNumeric, isRectObject, mergeAriaLabelConfig, nodeHasDimensions, nodeToRect, panBy, pointToRendererPoint, rendererPointToPoint, shallowNodeData, snapPosition, updateAbsolutePositions, updateNodeInternals } from "@xyflow/system";
5
- import { createStore, produce, unwrap } from "solid-js/store";
6
- import clsx$1, { clsx } from "clsx";
7
- import { Dynamic, Portal, isServer } from "solid-js/web";
8
- import { createResizeObserver } from "@solid-primitives/resize-observer";
3
+ import { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, Position as Position$1, ResizeControlVariant, SelectionMode, SelectionMode as SelectionMode$1, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, addEdge as addEdge$1, areConnectionMapsEqual, calcAutoPan, calculateNodePosition, clampPosition, clampPositionToParent, createDevWarn, createMarkerIds, elementSelectionKeys, evaluateAbsolutePosition, fitViewport, getBezierEdgeCenter, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter, getEdgePosition, getEdgeToolbarTransform, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesBounds as getNodesBounds$1, getNodesInside, getOutgoers, getOverlappingArea, getSmoothStepPath, getSmoothStepPath as getSmoothStepPath$1, getStraightPath, getStraightPath as getStraightPath$1, getViewportForBounds, getViewportForBounds as getViewportForBounds$1, handleConnectionChange, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isNodeBase, isNumeric, isRectObject, mergeAriaLabelConfig, nodeHasDimensions, nodeToRect, panBy, pointToRendererPoint, rendererPointToPoint, shallowNodeData, snapPosition } from "@xyflow/system";
4
+ import { Dynamic, Portal, isServer } from "@solidjs/web";
5
+ import { createEventListener, createEventListenerMap } from "@solid-primitives/event-listener";
9
6
  //#region src/components/contexts/edgeId.tsx
10
- const EdgeIdContext = createContext();
7
+ const EdgeIdContext = createContext(null);
11
8
  function useEdgeId() {
12
9
  let ctx = useContext(EdgeIdContext);
13
10
  if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useEdgeId");
@@ -39,104 +36,67 @@ const isNode = (element) => isNodeBase(element), isEdge = (element) => isEdgeBas
39
36
  x: 1,
40
37
  y: 0
41
38
  }
42
- };
39
+ }, scheduleIdleCallback = typeof requestIdleCallback == "function" ? requestIdleCallback : (callback) => setTimeout(callback, 0);
43
40
  /**
44
- * Creates a signal that can also be updated if the accessor updates.
45
- * @param accessor - A function that returns the initial value for the signal
46
- * @returns A tuple of [getter, setter] similar to createSignal, but the signal is recreated when the accessor's value changes
47
- * @remarks This is useful when you need a signal that can be manually set but also updates when its source value changes
48
- * @example
49
- * ```ts
50
- * const [width, setWidth] = createWritable(() => config().width);
51
- * // width() will return config().width initially
52
- * // setWidth(100) will update the signal to 100
53
- * // If config().width changes, a new signal width() is updated to the new value
54
- * ```
41
+ * Reactive prop defaulting with skip-undefined semantics: a prop counts as
42
+ * "absent" when it reads `undefined`, so parents forwarding optional props
43
+ * (e.g. `<Handle position={props.targetPosition} />`) do not clobber defaults.
44
+ * This is deliberate policy on top of Solid 2.0's `merge`, where `undefined`
45
+ * is a real value that overrides.
55
46
  */
56
- function createWritable(accessor) {
57
- let signal = createMemo(() => createSignal(accessor()));
58
- return [() => signal()[0](), (v) => signal()[1](v)];
59
- }
60
- function createWritableStore(accessor) {
61
- let storeMemo = createMemo(() => {
62
- let [get, set] = createStore(accessor());
63
- return {
64
- get,
65
- set
66
- };
47
+ function propDefaults(props, defaults) {
48
+ let out = {}, keys = /* @__PURE__ */ new Set([...Object.keys(defaults), ...Object.keys(props)]);
49
+ for (let key of keys) Object.defineProperty(out, key, {
50
+ get: () => props[key] === void 0 ? defaults[key] : props[key],
51
+ enumerable: !0,
52
+ configurable: !0
67
53
  });
68
- return {
69
- get() {
70
- return storeMemo().get;
71
- },
72
- get set() {
73
- return storeMemo().set;
74
- }
75
- };
54
+ return out;
76
55
  }
77
- /**
78
- * Schedules a callback during idle time, falling back to a macrotask where
79
- * `requestIdleCallback` is unavailable (Safari, jsdom).
80
- */
81
- const scheduleIdleCallback = typeof requestIdleCallback == "function" ? requestIdleCallback : (callback) => setTimeout(callback, 0), EdgeLabelRenderer = (props) => {
56
+ //#endregion
57
+ //#region src/components/graph/edge/EdgeLabelRenderer.tsx
58
+ const EdgeLabelRenderer = (props) => {
82
59
  let { store } = useInternalSolidFlow(), labelNode = () => store.domNode?.querySelector(".solid-flow__edge-labels");
83
60
  return <Show when={labelNode()}>{(root) => <Portal mount={root()}>{props.children}</Portal>}</Show>;
84
61
  }, EdgeLabel = (props) => {
85
- let _props = mergeProps({
62
+ let _props = propDefaults(props, {
86
63
  x: 0,
87
64
  y: 0,
88
65
  selectEdgeOnClick: !1,
89
66
  transparent: !1,
90
67
  style: {}
91
- }, props), [local, rest] = splitProps(_props, [
92
- "x",
93
- "y",
94
- "width",
95
- "height",
96
- "selectEdgeOnClick",
97
- "transparent",
98
- "children",
99
- "class",
100
- "style"
101
- ]), { actions } = useInternalSolidFlow(), id = useEdgeId(), zIndex = () => actions.getEdge(id())?.zIndex;
68
+ }), rest = omit(_props, "x", "y", "width", "height", "selectEdgeOnClick", "transparent", "children", "class", "style"), { actions } = useInternalSolidFlow(), id = useEdgeId(), zIndex = () => actions.getEdge(id())?.zIndex;
102
69
  return <EdgeLabelRenderer>
103
- <div role="button" tabIndex={-1} class={clsx$1("solid-flow__edge-label", { transparent: local.transparent }, local.class)} style={{
70
+ <div role="button" tabindex={-1} class={[
71
+ "solid-flow__edge-label",
72
+ { transparent: _props.transparent },
73
+ _props.class
74
+ ]} style={{
104
75
  "pointer-events": "all",
105
- width: toPxString(local.width),
106
- height: toPxString(local.height),
107
- transform: `translate(-50%, -50%) translate(${local.x}px,${local.y}px)`,
108
- cursor: local.selectEdgeOnClick ? "pointer" : void 0,
76
+ width: toPxString(_props.width),
77
+ height: toPxString(_props.height),
78
+ transform: `translate(-50%, -50%) translate(${_props.x}px,${_props.y}px)`,
79
+ cursor: _props.selectEdgeOnClick ? "pointer" : void 0,
109
80
  "z-index": zIndex(),
110
- ...local.style
81
+ ..._props.style
111
82
  }} onClick={() => {
112
- local.selectEdgeOnClick && actions.handleEdgeSelection(id());
83
+ _props.selectEdgeOnClick && actions.handleEdgeSelection(id());
113
84
  }} {...rest}>
114
- {local.children}
85
+ {_props.children}
115
86
  </div>
116
87
  </EdgeLabelRenderer>;
117
88
  }, BaseEdge = (props) => {
118
- let _props = mergeProps({ interactionWidth: 20 }, props), [local, rest] = splitProps(_props, [
119
- "class",
120
- "style",
121
- "path",
122
- "interactionWidth",
123
- "label",
124
- "labelStyle",
125
- "labelX",
126
- "labelY",
127
- "markerStart",
128
- "markerEnd"
129
- ]);
89
+ let _props = propDefaults(props, { interactionWidth: 20 }), rest = omit(_props, "class", "style", "path", "interactionWidth", "label", "labelStyle", "labelX", "labelY", "markerStart", "markerEnd");
130
90
  return <>
131
- <path d={local.path} class={clsx$1(["solid-flow__edge-path", local.class])} marker-start={local.markerStart} marker-end={local.markerEnd} fill="none" style={local.style} {...rest} />
91
+ <path d={_props.path} class={["solid-flow__edge-path", _props.class]} marker-start={_props.markerStart} marker-end={_props.markerEnd} fill="none" style={_props.style} {...rest} />
132
92
 
133
- <Show when={local.interactionWidth > 0}>
134
- <path d={local.path} stroke-opacity={0} stroke-width={local.interactionWidth} fill="none" class="solid-flow__edge-interaction" />
93
+ <Show when={_props.interactionWidth > 0}>
94
+ <path d={_props.path} stroke-opacity={0} stroke-width={_props.interactionWidth} fill="none" class="solid-flow__edge-interaction" />
135
95
  </Show>
136
96
 
137
- <Show when={local.label}>
138
- <EdgeLabel x={local.labelX} y={local.labelY} style={local.labelStyle}>
139
- {local.label}
97
+ <Show when={_props.label}>
98
+ <EdgeLabel x={_props.labelX} y={_props.labelY} style={_props.labelStyle}>
99
+ {_props.label}
140
100
  </EdgeLabel>
141
101
  </Show>
142
102
  </>;
@@ -176,24 +136,16 @@ const scheduleIdleCallback = typeof requestIdleCallback == "function" ? requestI
176
136
  };
177
137
  return <BaseEdge path={pathData().path} labelX={pathData().labelX} labelY={pathData().labelY} label={props.label} labelStyle={props.labelStyle} markerStart={props.markerStart} markerEnd={props.markerEnd} interactionWidth={props.interactionWidth} style={props.style} />;
178
138
  }, EdgeReconnectAnchor = (props) => {
179
- let _props = mergeProps({
139
+ let _props = propDefaults(props, {
180
140
  size: 25,
181
141
  reconnecting: !1,
182
142
  style: {}
183
- }, props), [local, rest] = splitProps(_props, [
184
- "type",
185
- "class",
186
- "style",
187
- "position",
188
- "size",
189
- "reconnecting",
190
- "children"
191
- ]), { store, nodeLookup, edgeLookup, actions } = useInternalSolidFlow(), edgeId = useEdgeId(), [reconnecting, setReconnecting] = createSignal(!1);
143
+ }), rest = omit(_props, "type", "class", "style", "position", "size", "reconnecting", "children"), { store, nodeLookup, edgeLookup, actions } = useInternalSolidFlow(), edgeId = useEdgeId(), [reconnecting, setReconnecting] = createSignal(!1);
192
144
  if (!edgeId()) throw Error("[solid-flow]: EdgeReconnectAnchor must be used within an Edge component");
193
- let edge = () => edgeLookup.get(edgeId()), onPointerDown = (event) => {
145
+ let edge = () => edgeLookup[edgeId()], onPointerDown = (event) => {
194
146
  if (event.button !== 0) return;
195
- setReconnecting(!0), store.onReconnectStart?.(event, edge(), local.type);
196
- let opposite = local.type === "target" ? {
147
+ setReconnecting(!0), store.onReconnectStart?.(event, edge(), _props.type);
148
+ let opposite = _props.type === "target" ? {
197
149
  nodeId: edge().source,
198
150
  handleId: edge().sourceHandle ?? null,
199
151
  type: "source"
@@ -237,16 +189,21 @@ const scheduleIdleCallback = typeof requestIdleCallback == "function" ? requestI
237
189
  handleDomNode: event.currentTarget
238
190
  });
239
191
  };
240
- return <EdgeLabel x={local.position?.x} y={local.position?.y} style={local.style} {...rest}>
241
- <div onPointerDown={onPointerDown} class={clsx$1("solid-flow__edgeupdater", `solid-flow__edgeupdater-${local.type}`, store.noPanClass, local.class)} style={{
242
- width: toPxString(local.size),
243
- height: toPxString(local.size),
192
+ return <EdgeLabel x={_props.position?.x} y={_props.position?.y} style={_props.style} {...rest}>
193
+ <div onPointerDown={onPointerDown} class={[
194
+ "solid-flow__edgeupdater",
195
+ `solid-flow__edgeupdater-${_props.type}`,
196
+ store.noPanClass,
197
+ _props.class
198
+ ]} style={{
199
+ width: toPxString(_props.size),
200
+ height: toPxString(_props.size),
244
201
  background: "transparent",
245
202
  border: "none",
246
203
  cursor: "move",
247
- ...local.style
204
+ ..._props.style
248
205
  }}>
249
- <Show when={!reconnecting()}>{local.children}</Show>
206
+ <Show when={!reconnecting()}>{_props.children}</Show>
250
207
  </div>
251
208
  </EdgeLabel>;
252
209
  }, ARIA_NODE_DESC_KEY = "solid-flow__node-desc", ARIA_EDGE_DESC_KEY = "solid-flow__edge-desc", A11yDescriptions = () => {
@@ -283,19 +240,24 @@ const scheduleIdleCallback = typeof requestIdleCallback == "function" ? requestI
283
240
  }, onKeyDown = (event) => {
284
241
  store.disableKeyboardA11y || !elementSelectionKeys.includes(event.key) || !selectable() || (event.key === "Escape" ? actions.unselectNodesAndEdges({ edges: [edge()] }) : actions.addSelectedEdges([edge().id]));
285
242
  }, ariaLabel = () => edge().ariaLabel ?? `Edge from ${edge().source} to ${edge().target}`;
286
- return <EdgeIdContext.Provider value={edgeId}>
243
+ return <EdgeIdContext value={edgeId}>
287
244
  <Show when={!edge().hidden}>
288
245
  <svg class="solid-flow__edge-wrapper" style={{ "z-index": edge().zIndex }}>
289
- <g ref={edgeRef} data-id={edge().id} tabIndex={focusable() ? 0 : void 0} role={edge().ariaRole ?? (focusable() ? "group" : "img")} aria-label={ariaLabel()} aria-roledescription="edge" aria-describedby={focusable() ? `${ARIA_EDGE_DESC_KEY}-${store.id}` : void 0} class={clsx$1("solid-flow__edge", `solid-flow__edge-${edgeType()}`, {
290
- animated: edge().animated,
291
- selected: edge().selected,
292
- selectable: selectable()
293
- }, edge().class)} onClick={onClick} onKeyDown={(e) => focusable() && onKeyDown(e)} onContextMenu={(e) => onPointerEvent(e, "contextmenu")} onPointerEnter={(e) => onPointerEvent(e, "pointerenter")} onPointerLeave={(e) => onPointerEvent(e, "pointerleave")} {...edge().domAttributes}>
246
+ <g ref={edgeRef} data-id={edge().id} tabindex={focusable() ? 0 : void 0} role={edge().ariaRole ?? (focusable() ? "group" : "img")} aria-label={ariaLabel()} aria-roledescription="edge" aria-describedby={focusable() ? `${ARIA_EDGE_DESC_KEY}-${store.id}` : void 0} class={[
247
+ "solid-flow__edge",
248
+ `solid-flow__edge-${edgeType()}`,
249
+ {
250
+ animated: !!edge().animated,
251
+ selected: !!edge().selected,
252
+ selectable: !!selectable()
253
+ },
254
+ edge().class
255
+ ]} onClick={onClick} onKeyDown={(e) => focusable() && onKeyDown(e)} onContextMenu={(e) => onPointerEvent(e, "contextmenu")} onPointerEnter={(e) => onPointerEvent(e, "pointerenter")} onPointerLeave={(e) => onPointerEvent(e, "pointerleave")} {...edge().domAttributes}>
294
256
  <Dynamic component={edgeComponent()} id={edge().id} source={edge().source} target={edge().target} sourceX={edge().sourceX} sourceY={edge().sourceY} targetX={edge().targetX} targetY={edge().targetY} sourcePosition={edge().sourcePosition} targetPosition={edge().targetPosition} animated={edge().animated} selected={edge().selected} label={edge().label} labelStyle={edge().labelStyle} data={edge().data} style={edge().style} interactionWidth={edge().interactionWidth} selectable={selectable()} deletable={edge().deletable ?? !0} type={edgeType()} sourceHandleId={edge().sourceHandle} targetHandleId={edge().targetHandle} markerStart={markerStartUrl()} markerEnd={markerEndUrl()} />
295
257
  </g>
296
258
  </svg>
297
259
  </Show>
298
- </EdgeIdContext.Provider>;
260
+ </EdgeIdContext>;
299
261
  }, SmoothStepEdge = (props) => {
300
262
  let pathData = createMemo(() => {
301
263
  let [path, labelX, labelY] = getSmoothStepPath$1({
@@ -399,515 +361,305 @@ const scheduleIdleCallback = typeof requestIdleCallback == "function" ? requestI
399
361
  };
400
362
  };
401
363
  return <BaseEdge path={pathData().path} labelX={pathData().labelX} labelY={pathData().labelY} label={props.label} labelStyle={props.labelStyle} markerStart={props.markerStart} markerEnd={props.markerEnd} interactionWidth={props.interactionWidth} style={props.style} />;
402
- }, getEdgeId = (connection) => {
403
- let { source, sourceHandle, target, targetHandle } = connection;
404
- return `xy-edge__${source}${sourceHandle || ""}-${target}${targetHandle || ""}`;
405
- }, NodeConnectableContext = createContext();
406
- function useNodeConnectable() {
407
- let ctx = useContext(NodeConnectableContext);
408
- if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeConnectable");
409
- return ctx;
410
- }
364
+ }, getDefaultFlowStateProps = () => ({
365
+ id: "1",
366
+ nodes: [],
367
+ edges: [],
368
+ nodeOrigin: [0, 0],
369
+ nodeExtent: infiniteExtent,
370
+ defaultEdgeOptions: {},
371
+ colorMode: "system",
372
+ colorModeSSR: "light",
373
+ connectionMode: "strict",
374
+ connectionLineType: "default",
375
+ connectionRadius: 20,
376
+ nodeDragThreshold: 1,
377
+ minZoom: .5,
378
+ maxZoom: 2,
379
+ selectionMode: "partial",
380
+ fitView: !1,
381
+ noPanClass: "nopan",
382
+ noDragClass: "nodrag",
383
+ noWheelClass: "nowheel",
384
+ autoPanOnNodeDrag: !0,
385
+ autoPanOnConnect: !0,
386
+ autoPanOnNodeFocus: !0,
387
+ autoPanOnSelection: !0,
388
+ autoPanSpeed: 15,
389
+ elevateEdgesOnSelect: !0,
390
+ nodesDraggable: !0,
391
+ nodesConnectable: !0,
392
+ nodesFocusable: !0,
393
+ edgesFocusable: !0,
394
+ elementsSelectable: !0,
395
+ selectNodesOnDrag: !0,
396
+ elevateNodesOnSelect: !0,
397
+ zIndexMode: "basic",
398
+ onlyRenderVisibleElements: !1,
399
+ disableKeyboardA11y: !1,
400
+ defaultMarkerColor: "#b1b1b7",
401
+ ariaLiveMessage: "",
402
+ style: {},
403
+ isValidConnection: (() => !0),
404
+ onFlowError: createDevWarn("Solid Flow", "https://solidflow.dev/")
405
+ });
411
406
  //#endregion
412
- //#region src/components/graph/handle/Handle.tsx
413
- const Handle = (props) => {
414
- let _props = mergeProps({
415
- id: null,
416
- type: "source",
417
- position: "top",
418
- isConnectableStart: !0,
419
- isConnectableEnd: !0
420
- }, props), { store, nodeLookup, connectionLookup, actions } = useInternalSolidFlow(), [local, rest] = splitProps(_props, [
421
- "id",
422
- "type",
423
- "position",
424
- "isConnectable",
425
- "isConnectableStart",
426
- "isConnectableEnd",
427
- "isValidConnection",
428
- "onConnect",
429
- "onDisconnect",
430
- "children",
431
- "class",
432
- "style"
433
- ]), nodeId = useNodeId(), nodeConnectable = useNodeConnectable(), connectable = () => local.isConnectable ?? nodeConnectable(), isTarget = () => local.type === "target", handleId = () => local.id ?? null, connectionInProcess = () => !!store.connection.fromHandle, connectingFrom = () => store.connection.fromHandle && store.connection.fromHandle.nodeId === nodeId() && store.connection.fromHandle.type === local.type && store.connection.fromHandle.id === handleId(), connectingTo = () => store.connection.toHandle && store.connection.toHandle.nodeId === nodeId() && store.connection.toHandle.type === local.type && store.connection.toHandle.id === handleId(), isPossibleTargetHandle = () => store.connectionMode === "strict" ? store.connection.fromHandle?.type !== local.type : nodeId() !== store.connection.fromHandle?.nodeId || handleId() !== store.connection.fromHandle?.id, valid = () => !!(connectingTo() && store.connection.isValid), prevConnections = null, connections;
434
- createEffect(() => {
435
- if (!local.onConnect && !local.onDisconnect) return;
436
- let conectionKey = `${nodeId()}-${local.type}${local.id ? `-${local.id}` : ""}`;
437
- if (connections = connectionLookup.get(conectionKey), prevConnections && !areConnectionMapsEqual(connections, prevConnections)) {
438
- let _connections = connections ?? /* @__PURE__ */ new Map();
439
- handleConnectionChange(prevConnections, _connections, props.onDisconnect), handleConnectionChange(_connections, prevConnections, props.onConnect);
440
- }
441
- prevConnections = connections ?? /* @__PURE__ */ new Map();
442
- });
443
- let onConnectExtended = (connection) => {
444
- let handleConnection = {
445
- ...connection,
446
- id: getEdgeId(connection)
447
- }, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
448
- actions.addEdge(edge), store.onConnect?.(handleConnection);
449
- }, onPointerDown = (event) => {
450
- XYHandle.onPointerDown(event, {
451
- handleId: handleId(),
452
- nodeId: nodeId(),
453
- isTarget: isTarget(),
454
- connectionRadius: store.connectionRadius,
455
- domNode: store.domNode,
456
- nodeLookup,
457
- connectionMode: store.connectionMode,
458
- lib: store.lib,
459
- autoPanOnConnect: store.autoPanOnConnect,
460
- flowId: store.id,
461
- isValidConnection: local.isValidConnection ?? store.isValidConnection,
462
- updateConnection: actions.setConnection,
463
- cancelConnection: actions.cancelConnection,
464
- panBy: actions.panBy,
465
- onConnect: onConnectExtended,
466
- onConnectStart: (event, startParams) => {
467
- store.onConnectStart?.(event, {
468
- nodeId: startParams.nodeId,
469
- handleId: startParams.handleId,
470
- handleType: startParams.handleType
471
- });
472
- },
473
- onConnectEnd: store.onConnectEnd,
474
- getTransform: () => store.transform,
475
- getFromHandle: () => store.connection.fromHandle,
476
- autoPanSpeed: store.autoPanSpeed,
477
- dragThreshold: store.connectionDragThreshold,
478
- handleDomNode: event.currentTarget
479
- });
480
- }, onClick = (event) => {
481
- if (!nodeId() || !store.clickConnectStartHandle && !local.isConnectableStart) return;
482
- if (!store.clickConnectStartHandle) {
483
- store.onClickConnectStart?.(event, {
484
- nodeId: nodeId(),
485
- handleId: handleId(),
486
- handleType: local.type
487
- }), actions.setClickConnectStartHandle({
488
- nodeId: nodeId(),
489
- type: local.type,
490
- id: handleId()
491
- });
492
- return;
493
- }
494
- let doc = getHostForElement(event.target), isValidConnectionHandler = local.isValidConnection ?? store.isValidConnection, { connection, isValid } = XYHandle.isValid(event, {
495
- handle: {
496
- nodeId: nodeId(),
497
- id: handleId(),
498
- type: local.type
499
- },
500
- connectionMode: store.connectionMode,
501
- fromNodeId: store.clickConnectStartHandle.nodeId,
502
- fromHandleId: store.clickConnectStartHandle.id ?? null,
503
- fromType: store.clickConnectStartHandle.type,
504
- isValidConnection: isValidConnectionHandler,
505
- flowId: store.id,
506
- doc,
507
- lib: store.lib,
508
- nodeLookup
509
- });
510
- isValid && connection && onConnectExtended(connection);
511
- let connectionClone = structuredClone(unwrap(store.connection));
512
- delete connectionClone.inProgress, connectionClone.toPosition = connectionClone.toHandle ? connectionClone.toHandle.position : null, store.onClickConnectEnd?.(event, connectionClone), actions.setClickConnectStartHandle(void 0);
513
- }, connectionIndicator = () => connectable() && (!connectionInProcess() || isPossibleTargetHandle()) && (connectionInProcess() || store.clickConnectStartHandle ? local.isConnectableEnd : local.isConnectableStart);
514
- return <div role="button" aria-label={store.ariaLabelConfig["handle.ariaLabel"]} tabIndex={-1} data-handleid={handleId()} data-nodeid={nodeId()} data-handlepos={local.position} data-id={`${store.id}-${nodeId()}-${local.id || null}-${local.type}`} onClick={store.clickConnect ? onClick : void 0} onPointerDown={onPointerDown} style={local.style} class={clsx$1("solid-flow__handle", `solid-flow__handle-${local.position}`, store.noDragClass, store.noPanClass, local.class, {
515
- valid: valid(),
516
- connectingto: connectingTo(),
517
- connectingfrom: connectingFrom(),
518
- source: !isTarget(),
519
- target: isTarget(),
520
- connectablestart: _props.isConnectableStart,
521
- connectableend: _props.isConnectableEnd,
522
- connectable: connectable(),
523
- connectionindicator: connectionIndicator()
524
- })} {...rest}>
525
- {local.children}
526
- </div>;
527
- }, DefaultNode = (props) => {
528
- let _props = mergeProps({
529
- targetPosition: "top",
530
- sourcePosition: "bottom"
531
- }, props);
532
- return <>
533
- <Handle type="target" position={_props.targetPosition} isConnectable={_props.isConnectable} />
534
- {props.data.label}
535
- <Handle type="source" position={_props.sourcePosition} isConnectable={_props.isConnectable} />
536
- </>;
537
- }, GroupNode = (props) => <div style={{
538
- position: "absolute",
539
- left: 0,
540
- top: 0,
541
- width: toPxString(props.width),
542
- height: toPxString(props.height)
543
- }} />, InputNode = (props) => {
544
- let _props = mergeProps({ sourcePosition: "bottom" }, props);
545
- return <>
546
- {props.data?.label}
547
- <Handle type="source" position={_props.sourcePosition} isConnectable={_props.isConnectable} />
548
- </>;
549
- }, createDraggable = (elem, params) => {
550
- let { store, nodeLookup, actions } = useInternalSolidFlow(), [dragging, setDragging] = createSignal(!1);
551
- return onMount(() => {
552
- let { onDrag, onDragStart, onDragStop, onNodeMouseDown } = params(), dragInstance = XYDrag({
553
- onDrag,
554
- onDragStart: (event, dragItems, node, nodes) => {
555
- setDragging(!0), onDragStart?.(event, dragItems, node, nodes);
556
- },
557
- onDragStop: (event, dragItems, node, nodes) => {
558
- setDragging(!1), onDragStop?.(event, dragItems, node, nodes);
559
- },
560
- onNodeMouseDown,
561
- getStoreItems: () => ({
562
- nodes: store.nodes,
563
- nodeLookup,
564
- edges: store.edges,
565
- nodeExtent: store.nodeExtent,
566
- snapGrid: store.snapGrid ?? [0, 0],
567
- snapToGrid: !!store.snapGrid,
568
- autoPanSpeed: store.autoPanSpeed,
569
- nodeOrigin: store.nodeOrigin,
570
- multiSelectionActive: store.multiselectionKeyPressed,
571
- domNode: store.domNode,
572
- transform: store.transform,
573
- autoPanOnNodeDrag: store.autoPanOnNodeDrag,
574
- nodesDraggable: store.nodesDraggable,
575
- selectNodesOnDrag: store.selectNodesOnDrag,
576
- nodeDragThreshold: store.nodeDragThreshold,
577
- unselectNodesAndEdges: actions.unselectNodesAndEdges,
578
- updateNodePositions: actions.updateNodePositions,
579
- panBy: actions.panBy
580
- })
581
- });
582
- function updateDrag(elem, params) {
583
- if (params.disabled) {
584
- dragInstance.destroy();
585
- return;
407
+ //#region src/core/facades.ts
408
+ /**
409
+ * A read-only `Map` view over an id-keyed record projection, for
410
+ * @xyflow/system interop: system helpers take `Map`s, our reactive lookups
411
+ * are records. Every read passes through to the record, so reads inside
412
+ * tracked scopes subscribe normally — `get`/`has` check with `in` first,
413
+ * which also subscribes while the key is still absent (the projection
414
+ * absent-key footgun), and `size`/iteration read the key set structurally.
415
+ *
416
+ * The mutating `Map` methods throw: writes belong to the roots the
417
+ * projection derives from.
418
+ */
419
+ var RecordMapFacade = class {
420
+ #record;
421
+ constructor(record) {
422
+ this.#record = record;
423
+ }
424
+ get(key) {
425
+ return key in this.#record ? this.#record[key] : void 0;
426
+ }
427
+ has(key) {
428
+ return key in this.#record;
429
+ }
430
+ get size() {
431
+ return Object.keys(this.#record).length;
432
+ }
433
+ *keys() {
434
+ yield* Object.keys(this.#record);
435
+ }
436
+ *values() {
437
+ for (let key of Object.keys(this.#record)) yield this.#record[key];
438
+ }
439
+ *entries() {
440
+ for (let key of Object.keys(this.#record)) yield [key, this.#record[key]];
441
+ }
442
+ [Symbol.iterator]() {
443
+ return this.entries();
444
+ }
445
+ forEach(callback, thisArg) {
446
+ for (let [key, value] of this.entries()) callback.call(thisArg, value, key, this);
447
+ }
448
+ [Symbol.toStringTag] = "RecordMapFacade";
449
+ set() {
450
+ throw Error("RecordMapFacade is read-only; write to the projection's source roots");
451
+ }
452
+ getOrInsert() {
453
+ throw Error("RecordMapFacade is read-only; write to the projection's source roots");
454
+ }
455
+ getOrInsertComputed() {
456
+ throw Error("RecordMapFacade is read-only; write to the projection's source roots");
457
+ }
458
+ delete() {
459
+ throw Error("RecordMapFacade is read-only; write to the projection's source roots");
460
+ }
461
+ clear() {
462
+ throw Error("RecordMapFacade is read-only; write to the projection's source roots");
463
+ }
464
+ };
465
+ //#endregion
466
+ //#region src/core/projections/connections.ts
467
+ /**
468
+ * Lookup keys for the connection index. Each edge is registered under six
469
+ * keys — for both of its endpoints: the node, the node+handle-type, and (when
470
+ * a handle id is present) the node+type+handle:
471
+ * `${nodeId}` · `${nodeId}-${type}` · `${nodeId}-${type}-${handleId}`
472
+ */
473
+ const connectionKey = (nodeId, type, handleId) => `${nodeId}${type ? handleId ? `-${type}-${handleId}` : `-${type}` : ""}`, pairKey = (aNode, aHandle, bNode, bHandle) => `${aNode}-${aHandle}--${bNode}-${bHandle}`, createConnections = (source) => createProjection(() => {
474
+ let out = {}, add = (key, entry, connection) => {
475
+ (out[key] ??= {})[entry] = connection;
476
+ };
477
+ for (let edge of source.edges) {
478
+ let sourceHandle = edge.sourceHandle ?? null, targetHandle = edge.targetHandle ?? null, connection = {
479
+ edgeId: edge.id,
480
+ source: edge.source,
481
+ target: edge.target,
482
+ sourceHandle,
483
+ targetHandle
484
+ }, sourceKey = pairKey(edge.source, sourceHandle, edge.target, targetHandle), targetKey = pairKey(edge.target, targetHandle, edge.source, sourceHandle);
485
+ add(edge.source, targetKey, connection), add(connectionKey(edge.source, "source"), targetKey, connection), sourceHandle && add(connectionKey(edge.source, "source", sourceHandle), targetKey, connection), add(edge.target, sourceKey, connection), add(connectionKey(edge.target, "target"), sourceKey, connection), targetHandle && add(connectionKey(edge.target, "target", targetHandle), sourceKey, connection);
486
+ }
487
+ return out;
488
+ }, {}, { key: "id" }), createEdgeLookup = (source) => createProjection(() => {
489
+ let out = {};
490
+ for (let edge of source.edges) out[edge.id] = edge;
491
+ return out;
492
+ }, {}, { key: "id" });
493
+ function isManualZIndexMode(zIndexMode) {
494
+ return zIndexMode === "manual";
495
+ }
496
+ function calculateZ(node, selectedNodeZ, zIndexMode) {
497
+ let zIndex = isNumeric(node.zIndex) ? node.zIndex : 0;
498
+ return isManualZIndexMode(zIndexMode) ? zIndex : zIndex + (node.selected ? selectedNodeZ : 0);
499
+ }
500
+ const EMPTY_AUTO_INDEX = /* @__PURE__ */ new Map(), createInternalNodes = (source) => {
501
+ let autoIndex = createMemo(() => {
502
+ if (source.zIndexMode !== "auto") return EMPTY_AUTO_INDEX;
503
+ let index = /* @__PURE__ */ new Map(), rootIds = /* @__PURE__ */ new Set();
504
+ for (let node of source.nodes) node.parentId ? rootIds.has(node.parentId) && !index.has(node.parentId) && index.set(node.parentId, index.size + 1) : rootIds.add(node.id);
505
+ return index;
506
+ }), entryById = /* @__PURE__ */ new Map(), rowStores = mapArray(() => source.nodes, (userNodeAccessor, index) => {
507
+ let id = userNodeAccessor().id, store = createProjection(() => {
508
+ let userNode = userNodeAccessor(), { nodeOrigin, nodeExtent, zIndexMode } = source, selectedNodeZ = source.elevateNodesOnSelect && !isManualZIndexMode(zIndexMode) ? 1e3 : 0, measurement = userNode.id in source.measurements ? source.measurements[userNode.id] : void 0, measured = {
509
+ width: userNode.measured?.width ?? measurement?.measured.width,
510
+ height: userNode.measured?.height ?? measurement?.measured.height
511
+ }, dimensions = getNodeDimensions({
512
+ measured,
513
+ width: userNode.width,
514
+ height: userNode.height,
515
+ initialWidth: userNode.initialWidth,
516
+ initialHeight: userNode.initialHeight
517
+ }), rootParentIndex = autoIndex().get(userNode.id), row = {
518
+ ...userNode,
519
+ measured,
520
+ internals: {
521
+ positionAbsolute: clampPosition(getNodePositionWithOrigin(userNode, nodeOrigin), isCoordinateExtent(userNode.extent) ? userNode.extent : nodeExtent, dimensions),
522
+ handleBounds: measurement?.handleBounds,
523
+ z: calculateZ(userNode, selectedNodeZ, zIndexMode) + (rootParentIndex === void 0 ? 0 : rootParentIndex * 10),
524
+ ...rootParentIndex === void 0 ? {} : { rootParentIndex },
525
+ userNode
526
+ }
527
+ };
528
+ if (userNode.parentId) {
529
+ let parentEntry = entryById.get(userNode.parentId);
530
+ if (parentEntry && parentEntry.index() < index()) {
531
+ let { x, y, z } = calculateChildXYZ(row, parentEntry.store.row, nodeOrigin, nodeExtent, selectedNodeZ, zIndexMode);
532
+ row.internals.positionAbsolute = {
533
+ x,
534
+ y
535
+ }, row.internals.z = z;
536
+ } else source.nodes.length;
586
537
  }
587
- dragInstance.update({
588
- domNode: elem,
589
- nodeId: params.nodeId,
590
- noDragClassName: params.noDragClass,
591
- handleSelector: params.handleSelector,
592
- isSelectable: params.isSelectable,
593
- nodeClickDistance: params.nodeClickDistance
594
- });
538
+ if (userNode.parentId) {
539
+ let parent = entryById.get(userNode.parentId);
540
+ parent?.store.row.internals.positionAbsolute.x, parent?.store.row.internals.z;
541
+ }
542
+ return { row };
543
+ }, {}, { key: "id" }), entry = {
544
+ store,
545
+ index
546
+ };
547
+ return entryById.set(id, entry), onCleanup(() => {
548
+ entryById.get(id) === entry && entryById.delete(id);
549
+ }), {
550
+ id,
551
+ store
552
+ };
553
+ }, { keyed: (userNode) => userNode.id }), assigned = /* @__PURE__ */ new Map();
554
+ return createProjection((draft) => {
555
+ let seen = /* @__PURE__ */ new Set();
556
+ for (let { id, store } of rowStores()) {
557
+ seen.add(id);
558
+ let row = store.row;
559
+ assigned.get(id) !== row && (assigned.set(id, row), draft[id] = row);
595
560
  }
596
- createEffect(() => {
597
- updateDrag(elem(), params());
598
- }), onCleanup(() => {
599
- dragInstance.destroy();
600
- });
601
- }), dragging;
602
- }, NodeIdContext = createContext();
603
- function useNodeId() {
604
- let ctx = useContext(NodeIdContext);
605
- if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeId");
606
- return ctx;
561
+ for (let id of assigned.keys()) seen.has(id) || (assigned.delete(id), delete draft[id]);
562
+ }, {}, {
563
+ key: null,
564
+ shallow: !0
565
+ });
566
+ };
567
+ function calculateChildXYZ(childNode, parentNode, nodeOrigin, nodeExtent, selectedNodeZ, zIndexMode) {
568
+ let { x: parentX, y: parentY } = parentNode.internals.positionAbsolute, childDimensions = getNodeDimensions(childNode), positionWithOrigin = getNodePositionWithOrigin(childNode, nodeOrigin), clampedPosition = isCoordinateExtent(childNode.extent) ? clampPosition(positionWithOrigin, childNode.extent, childDimensions) : positionWithOrigin, absolutePosition = clampPosition({
569
+ x: parentX + clampedPosition.x,
570
+ y: parentY + clampedPosition.y
571
+ }, nodeExtent, childDimensions);
572
+ childNode.extent === "parent" && (absolutePosition = clampPositionToParent(absolutePosition, childDimensions, parentNode));
573
+ let childZ = calculateZ(childNode, selectedNodeZ, zIndexMode), parentZ = parentNode.internals.z ?? 0;
574
+ return {
575
+ x: absolutePosition.x,
576
+ y: absolutePosition.y,
577
+ z: parentZ >= childZ ? parentZ + 1 : childZ
578
+ };
607
579
  }
608
580
  //#endregion
609
- //#region src/components/graph/node/NodeWrapper.tsx
610
- const NodeWrapper = (props) => {
611
- let { store, nodeLookup, parentLookup, actions } = useInternalSolidFlow(), [nodeRef, setNodeRef] = createSignal(), node = () => nodeLookup.get(props.nodeId), nodeId = () => node().id, nodeType = () => node().type ?? "default", deletable = () => node().deletable ?? !0, selectable = () => node().selectable ?? store.elementsSelectable, focusable = () => node().focusable ?? store.nodesFocusable, draggable = () => node().draggable ?? store.nodesDraggable, connectable = () => node().connectable ?? store.nodesConnectable, userNode = () => node().internals.userNode, nodeTypeValid = () => nodeType() in store.nodeTypes, nodeComponent = () => store.nodeTypes[nodeType()], isParentNode = () => parentLookup.has(node().id), transform = () => {
612
- let { x, y } = node().internals.positionAbsolute;
613
- return `translate(${x}px, ${y}px)`;
614
- }, sizeStyle = () => {
615
- let w = node().width ?? node().initialWidth, h = node().height ?? node().initialHeight;
616
- return {
617
- ...node().style,
618
- ...w ? { width: toPxString(w) } : {},
619
- ...h ? { height: toPxString(h) } : {}
620
- };
621
- }, style = () => ({
622
- "z-index": node().internals.z,
623
- transform: transform(),
624
- visibility: nodeHasDimensions(node()) ? "visible" : "hidden",
625
- ...sizeStyle(),
626
- ...node().style ?? {}
627
- });
628
- createEffect(() => {
629
- nodeTypeValid();
630
- }), createEffect((prev) => prev && prev.sourcePosition === node().sourcePosition && prev.targetPosition === node().targetPosition && prev.nodeType === nodeType() ? prev : (actions.requestUpdateNodeInternals([[node().id, {
631
- id: node().id,
632
- nodeElement: nodeRef(),
633
- force: !0
634
- }]]), {
635
- nodeType: nodeType(),
636
- sourcePosition: node().sourcePosition,
637
- targetPosition: node().targetPosition
638
- })), createEffect((prevNodeRef) => {
639
- let currentNodeRef = nodeRef();
640
- return currentNodeRef === prevNodeRef && nodeHasDimensions(node()) ? prevNodeRef : (prevNodeRef && props.resizeObserver?.unobserve(prevNodeRef), currentNodeRef && props.resizeObserver?.observe(currentNodeRef), currentNodeRef);
641
- });
642
- let onSelectNodeHandler = (event) => {
643
- selectable() && (!store.selectNodesOnDrag || !draggable() || store.nodeDragThreshold > 0) && actions.handleNodeSelection(node().id), props.onNodeClick?.({
644
- node: userNode(),
645
- event
646
- });
647
- }, onKeyDown = (event) => {
648
- if (isInputDOMNode(event) || store.disableKeyboardA11y) return;
649
- if (elementSelectionKeys.includes(event.key) && selectable()) {
650
- actions.handleNodeSelection(node().id, event.key === "Escape", nodeRef());
651
- return;
652
- }
653
- let arrowKeyDiff = ARROW_KEY_DIFFS[event.key];
654
- draggable() && node().selected && arrowKeyDiff && batch(() => {
655
- event.preventDefault(), actions.setAriaLiveMessage(store.ariaLabelConfig["node.a11yDescription.ariaLiveMessage"]({
656
- direction: event.key.replace("Arrow", "").toLowerCase(),
657
- x: ~~node().internals.positionAbsolute.x,
658
- y: ~~node().internals.positionAbsolute.y
659
- })), actions.moveSelectedNodes(arrowKeyDiff, event.shiftKey ? 4 : 1);
660
- });
661
- }, onFocus = () => {
662
- if (store.disableKeyboardA11y || !store.autoPanOnNodeFocus || !nodeRef()?.matches(":focus-visible")) return;
663
- let { width, height, viewport } = store;
664
- getNodesInside(/* @__PURE__ */ new Map([[node().id, node()]]), {
665
- x: 0,
666
- y: 0,
667
- width,
668
- height
669
- }, [
670
- viewport.x,
671
- viewport.y,
672
- viewport.zoom
673
- ], !0).length > 0 || actions.setCenter(node().position.x + (node().measured.width ?? 0) / 2, node().position.y + (node().measured.height ?? 0) / 2, { zoom: viewport.zoom });
674
- }, dragging = createDraggable(nodeRef, () => ({
675
- nodeId: node().id,
676
- isSelectable: selectable(),
677
- disabled: !draggable(),
678
- handleSelector: node().dragHandle,
679
- noDragClass: store.noDragClass,
680
- nodeClickDistance: props.nodeClickDistance,
681
- onNodeMouseDown: actions.handleNodeSelection,
682
- onDrag: (event, _, targetNode, nodes) => {
683
- props.onNodeDrag?.({
684
- event,
685
- targetNode,
686
- nodes
687
- });
688
- },
689
- onDragStart: (event, _, targetNode, nodes) => {
690
- props.onNodeDragStart?.({
691
- event,
692
- targetNode,
693
- nodes
694
- });
695
- },
696
- onDragStop: (event, _, targetNode, nodes) => {
697
- props.onNodeDragStop?.({
698
- event,
699
- targetNode,
700
- nodes
701
- });
702
- }
703
- }));
704
- return <Show when={!node().hidden}>
705
- <div ref={setNodeRef} data-id={node().id} class={clsx$1("solid-flow__node", `solid-flow__node-${nodeType()}`, {
706
- connectable: connectable(),
707
- draggable: draggable(),
708
- dragging: dragging(),
709
- nopan: draggable(),
710
- parent: isParentNode(),
711
- selectable: selectable(),
712
- selected: node().selected
713
- }, node().class)} style={style()} onClick={onSelectNodeHandler} onPointerEnter={(event) => props.onNodePointerEnter?.({
714
- node: userNode(),
715
- event
716
- })} onPointerLeave={(event) => props.onNodePointerLeave?.({
717
- node: userNode(),
718
- event
719
- })} onPointerMove={(event) => props.onNodePointerMove?.({
720
- node: userNode(),
721
- event
722
- })} onContextMenu={(event) => props.onNodeContextMenu?.({
723
- node: userNode(),
724
- event
725
- })} onKeyDown={(e) => focusable() && onKeyDown(e)} onFocus={() => focusable() && onFocus()} tabIndex={focusable() ? 0 : void 0} role={node().ariaRole ?? (focusable() ? "group" : void 0)} aria-roledescription="node" aria-describedby={store.disableKeyboardA11y ? void 0 : `${ARIA_NODE_DESC_KEY}-${store.id}`} {...node().domAttributes}>
726
- <NodeIdContext.Provider value={nodeId}>
727
- <NodeConnectableContext.Provider value={connectable}>
728
- <Dynamic component={nodeComponent()} data={node().data} id={node().id} selected={!!node().selected} selectable={selectable()} deletable={deletable()} sourcePosition={node().sourcePosition} targetPosition={node().targetPosition} zIndex={node().internals.z} dragging={dragging()} draggable={draggable()} dragHandle={node().dragHandle} parentId={node().parentId} type={nodeType()} isConnectable={connectable()} positionAbsoluteX={node().internals.positionAbsolute.x} positionAbsoluteY={node().internals.positionAbsolute.y} width={node().width} height={node().height} />
729
- </NodeConnectableContext.Provider>
730
- </NodeIdContext.Provider>
731
- </div>
732
- </Show>;
733
- }, OutputNode = (props) => {
734
- let _props = mergeProps({ targetPosition: "top" }, props);
735
- return <>
736
- <Handle type="target" position={_props.targetPosition} isConnectable={_props.isConnectable} />
737
- {props.data?.label}
738
- </>;
739
- }, getDefaultFlowStateProps = () => ({
740
- id: "1",
741
- nodes: [],
742
- edges: [],
743
- nodeOrigin: [0, 0],
744
- nodeExtent: infiniteExtent,
745
- defaultEdgeOptions: {},
746
- colorMode: "system",
747
- colorModeSSR: "light",
748
- connectionMode: "strict",
749
- connectionLineType: "default",
750
- connectionRadius: 20,
751
- nodeDragThreshold: 1,
752
- minZoom: .5,
753
- maxZoom: 2,
754
- selectionMode: "partial",
755
- fitView: !1,
756
- noPanClass: "nopan",
757
- noDragClass: "nodrag",
758
- noWheelClass: "nowheel",
759
- autoPanOnNodeDrag: !0,
760
- autoPanOnConnect: !0,
761
- autoPanOnNodeFocus: !0,
762
- autoPanOnSelection: !0,
763
- autoPanSpeed: 15,
764
- elevateEdgesOnSelect: !0,
765
- nodesDraggable: !0,
766
- nodesConnectable: !0,
767
- nodesFocusable: !0,
768
- edgesFocusable: !0,
769
- elementsSelectable: !0,
770
- selectNodesOnDrag: !0,
771
- elevateNodesOnSelect: !0,
772
- zIndexMode: "basic",
773
- onlyRenderVisibleElements: !1,
774
- disableKeyboardA11y: !1,
775
- defaultMarkerColor: "#b1b1b7",
776
- ariaLiveMessage: "",
777
- style: {},
778
- isValidConnection: (() => !0),
779
- onFlowError: createDevWarn("Solid Flow", "https://solidflow.dev/")
780
- }), SELECTED_NODE_Z = 1e3, defaultOptions = {
781
- nodeOrigin: [0, 0],
782
- nodeExtent: infiniteExtent,
783
- elevateNodesOnSelect: !0,
784
- defaults: {},
785
- zIndexMode: "basic"
786
- };
787
- function isManualZIndexMode(zIndexMode) {
788
- return zIndexMode === "manual";
789
- }
790
- const adoptUserNodesDefaultOptions = {
791
- ...defaultOptions,
792
- checkEquality: !0
793
- };
794
- function mergeObjects(base, incoming) {
795
- let result = { ...base };
796
- for (let key in incoming) incoming[key] !== void 0 && (result[key] = incoming[key]);
797
- return result;
798
- }
799
- function adoptUserNodes(nodes, nodeLookup, parentLookup, options) {
800
- let _options = mergeObjects(adoptUserNodesDefaultOptions, options), nodesInitialized = nodes.length > 0, rootParentIndex = { i: 0 }, tmpLookup = new Map(nodeLookup), selectedNodeZ = _options?.elevateNodesOnSelect && !isManualZIndexMode(_options.zIndexMode) ? SELECTED_NODE_Z : 0;
801
- nodeLookup.clear(), parentLookup.clear();
802
- for (let userNode of nodes) {
803
- let internalNode = tmpLookup.get(userNode.id);
804
- if (_options.checkEquality && userNode === internalNode?.internals.userNode) nodeLookup.set(userNode.id, internalNode);
805
- else {
806
- let positionWithOrigin = getNodePositionWithOrigin(userNode, _options.nodeOrigin), extent = isCoordinateExtent(userNode.extent) ? userNode.extent : _options.nodeExtent, clampedPosition = clampPosition(positionWithOrigin, extent, getNodeDimensions(userNode));
807
- internalNode = {
808
- ..._options.defaults,
809
- ...userNode,
810
- measured: {
811
- width: userNode.measured?.width,
812
- height: userNode.measured?.height
813
- },
814
- internals: {
815
- positionAbsolute: clampedPosition,
816
- handleBounds: userNode.measured ? internalNode?.internals.handleBounds : void 0,
817
- z: calculateZ(userNode, selectedNodeZ, _options.zIndexMode),
818
- userNode
819
- }
820
- }, nodeLookup.set(userNode.id, internalNode);
821
- }
822
- (internalNode.measured === void 0 || internalNode.measured.width === void 0 || internalNode.measured.height === void 0) && !internalNode.hidden && (nodesInitialized = !1), userNode.parentId && updateChildNode(internalNode, nodeLookup, parentLookup, options, rootParentIndex);
823
- }
824
- return nodesInitialized;
825
- }
826
- function updateParentLookup(node, parentLookup) {
827
- if (!node.parentId) return;
828
- let childNodes = parentLookup.get(node.parentId);
829
- childNodes ? childNodes.set(node.id, node) : parentLookup.set(node.parentId, /* @__PURE__ */ new Map([[node.id, node]]));
830
- }
581
+ //#region src/core/projections/layoutedEdges.ts
831
582
  /**
832
- * Updates positionAbsolute and zIndex of a child node and the parentLookup.
583
+ * Edge layout join: user edges × internal nodes screen-space edge geometry,
584
+ * decomposed into SUB-STORES (spike 13): each edge is its own keyed
585
+ * projection holding `{ row }` — the layouted row, or null while the edge
586
+ * produces none (missing/unready endpoints, culled) — and the public record
587
+ * is a SHALLOW projection holding the PRESENT rows' proxies by reference.
588
+ *
589
+ * Reads chain: `record[id].sourceX` goes through the shallow slot into the
590
+ * edge's own store, so every materialized leaf signal hangs off its EDGE's
591
+ * computed (defeating rc.1's per-update companion walk — see
592
+ * internalNodes.ts). The edge projection tracks exactly what the join reads —
593
+ * the edge's props and its endpoints' geometry leaves through nodeLookup
594
+ * (which chains into the node row stores) — so one node move re-runs only
595
+ * the adjacent edges' projections; the record computed re-runs only when
596
+ * membership or presence changes.
597
+ *
598
+ * The viewport participates only while onlyRenderVisibleElements is active —
599
+ * panning must not touch edge rows otherwise.
600
+ *
601
+ * Rows whose endpoints are missing or unmeasured simply drop out of the
602
+ * record — the same "no entry" contract the ReactiveMap pipeline had.
833
603
  */
834
- function updateChildNode(node, nodeLookup, parentLookup, options, rootParentIndex) {
835
- let { elevateNodesOnSelect, nodeOrigin, nodeExtent, zIndexMode } = mergeObjects(defaultOptions, options), parentId = node.parentId, parentNode = nodeLookup.get(parentId);
836
- if (!parentNode) return;
837
- updateParentLookup(node, parentLookup), rootParentIndex && !parentNode.parentId && parentNode.internals.rootParentIndex === void 0 && zIndexMode === "auto" && (parentNode.internals.rootParentIndex = ++rootParentIndex.i, parentNode.internals.z = parentNode.internals.z + rootParentIndex.i * 10), rootParentIndex && parentNode.internals.rootParentIndex !== void 0 && (rootParentIndex.i = parentNode.internals.rootParentIndex);
838
- let { x, y, z } = calculateChildXYZ(node, parentNode, nodeOrigin, nodeExtent, elevateNodesOnSelect && !isManualZIndexMode(zIndexMode) ? SELECTED_NODE_Z : 0, zIndexMode), { positionAbsolute } = node.internals, positionChanged = x !== positionAbsolute.x || y !== positionAbsolute.y;
839
- (positionChanged || z !== node.internals.z) && nodeLookup.set(node.id, {
840
- ...node,
841
- internals: {
842
- ...node.internals,
843
- positionAbsolute: positionChanged ? {
844
- x,
845
- y
846
- } : positionAbsolute,
847
- z
604
+ const createLayoutedEdges = (source) => {
605
+ let rowStores = mapArray(() => source.edges, (edgeAccessor) => ({
606
+ id: edgeAccessor().id,
607
+ store: createProjection(() => {
608
+ let edge = edgeAccessor(), row = buildRow(source, edge);
609
+ return source.nodeLookup.get(edge.source)?.internals.handleBounds, source.nodeLookup.get(edge.target)?.internals.handleBounds, { row };
610
+ }, { row: null }, { key: "id" })
611
+ }), { keyed: (edge) => edge.id }), assigned = /* @__PURE__ */ new Map();
612
+ return createProjection((draft) => {
613
+ let seen = /* @__PURE__ */ new Set();
614
+ for (let { id, store } of rowStores()) {
615
+ let row = store.row;
616
+ row && (seen.add(id), assigned.get(id) !== row && (assigned.set(id, row), draft[id] = row));
848
617
  }
618
+ for (let id of assigned.keys()) seen.has(id) || (assigned.delete(id), delete draft[id]);
619
+ }, {}, {
620
+ key: null,
621
+ shallow: !0
849
622
  });
850
- }
851
- function calculateZ(node, selectedNodeZ, zIndexMode) {
852
- let zIndex = isNumeric(node.zIndex) ? node.zIndex : 0;
853
- return isManualZIndexMode(zIndexMode) ? zIndex : zIndex + (node.selected ? selectedNodeZ : 0);
854
- }
855
- function calculateChildXYZ(childNode, parentNode, nodeOrigin, nodeExtent, selectedNodeZ, zIndexMode) {
856
- let { x: parentX, y: parentY } = parentNode.internals.positionAbsolute, childDimensions = getNodeDimensions(childNode), positionWithOrigin = getNodePositionWithOrigin(childNode, nodeOrigin), clampedPosition = isCoordinateExtent(childNode.extent) ? clampPosition(positionWithOrigin, childNode.extent, childDimensions) : positionWithOrigin, absolutePosition = clampPosition({
857
- x: parentX + clampedPosition.x,
858
- y: parentY + clampedPosition.y
859
- }, nodeExtent, childDimensions);
860
- childNode.extent === "parent" && (absolutePosition = clampPositionToParent(absolutePosition, childDimensions, parentNode));
861
- let childZ = calculateZ(childNode, selectedNodeZ, zIndexMode), parentZ = parentNode.internals.z ?? 0;
862
- return {
863
- x: absolutePosition.x,
864
- y: absolutePosition.y,
865
- z: parentZ >= childZ ? parentZ + 1 : childZ
866
- };
867
- }
868
- /**
869
- * this function adds the connection to the connectionLookup
870
- * at the following keys: nodeId-type-handleId, nodeId-type and nodeId
871
- * @param type type of the connection
872
- * @param connection connection that should be added to the lookup
873
- * @param connectionKey at which key the connection should be added
874
- * @param connectionLookup reference to the connection lookup
875
- * @param nodeId nodeId of the connection
876
- * @param handleId handleId of the conneciton
877
- */
878
- function addConnectionToLookup(type, connection, connectionKey, connectionLookup, nodeId, handleId) {
879
- let key = nodeId, nodeMap = connectionLookup.get(key) || /* @__PURE__ */ new Map();
880
- connectionLookup.set(key, nodeMap.set(connectionKey, connection)), key = `${nodeId}-${type}`;
881
- let typeMap = connectionLookup.get(key) || /* @__PURE__ */ new Map();
882
- if (connectionLookup.set(key, typeMap.set(connectionKey, connection)), handleId) {
883
- key = `${nodeId}-${type}-${handleId}`;
884
- let handleMap = connectionLookup.get(key) || /* @__PURE__ */ new Map();
885
- connectionLookup.set(key, handleMap.set(connectionKey, connection));
886
- }
887
- }
888
- function removeConnectionFromLookup(type, connectionKey, connectionLookup, nodeId, handleId) {
889
- let key = nodeId, nodeMap = connectionLookup.get(key);
890
- nodeMap && (nodeMap.delete(connectionKey), nodeMap.size === 0 && connectionLookup.delete(key)), key = `${nodeId}-${type}`;
891
- let typeMap = connectionLookup.get(key);
892
- if (typeMap && (typeMap.delete(connectionKey), typeMap.size === 0 && connectionLookup.delete(key)), handleId) {
893
- key = `${nodeId}-${type}-${handleId}`;
894
- let handleMap = connectionLookup.get(key);
895
- handleMap && (handleMap.delete(connectionKey), handleMap.size === 0 && connectionLookup.delete(key));
896
- }
897
- }
898
- //#endregion
899
- //#region src/data/createSolidFlow.tsx
900
- const InitialNodeTypesMap = {
901
- input: InputNode,
902
- output: OutputNode,
903
- default: DefaultNode,
904
- group: GroupNode
905
- }, InitialEdgeTypesMap = {
906
- straight: StraightEdgeInternal,
907
- smoothstep: SmoothStepEdgeInternal,
908
- default: BezierEdgeInternal,
909
- step: StepEdgeInternal
910
- }, getInitialViewport = (_nodesInitialized, fitView, initialViewport, width, height, nodeLookup) => {
623
+ }, buildRow = (source, edge) => {
624
+ let sourceNode = source.nodeLookup.get(edge.source), targetNode = source.nodeLookup.get(edge.target);
625
+ if (!sourceNode || !targetNode) return source.nodeLookup.size, null;
626
+ if (source.onlyRenderVisibleElements && !isEdgeVisible({
627
+ sourceNode,
628
+ targetNode,
629
+ width: source.width ?? 0,
630
+ height: source.height ?? 0,
631
+ transform: source.transform
632
+ })) return null;
633
+ let edgePosition = getEdgePosition({
634
+ id: edge.id,
635
+ sourceNode,
636
+ targetNode,
637
+ sourceHandle: edge.sourceHandle || null,
638
+ targetHandle: edge.targetHandle || null,
639
+ connectionMode: source.connectionMode,
640
+ onError: source.onError
641
+ });
642
+ return edgePosition ? {
643
+ ...source.defaultEdgeOptions,
644
+ ...edge,
645
+ ...edgePosition,
646
+ zIndex: getElevatedEdgeZIndex({
647
+ selected: edge.selected,
648
+ zIndex: edge.zIndex ?? source.defaultEdgeOptions.zIndex,
649
+ sourceNode,
650
+ targetNode,
651
+ elevateOnSelect: source.elevateEdgesOnSelect,
652
+ zIndexMode: source.zIndexMode
653
+ }),
654
+ sourceNode,
655
+ targetNode,
656
+ edge
657
+ } : null;
658
+ }, createParentIds = (source) => createProjection(() => {
659
+ let out = {};
660
+ for (let node of source.nodes) node.parentId && (out[node.parentId] = !0);
661
+ return out;
662
+ }, {}, { key: "id" }), getInitialViewport = (fitView, initialViewport, width, height, nodeLookup) => {
911
663
  if (fitView && !initialViewport && width && height) {
912
664
  let bounds = getInternalNodesBounds(nodeLookup, { filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) });
913
665
  return getViewportForBounds$1(bounds, width, height, .5, 2, .1);
@@ -917,23 +669,44 @@ const InitialNodeTypesMap = {
917
669
  y: 0,
918
670
  zoom: 1
919
671
  };
920
- }, createSolidFlow = (props) => {
921
- let _props = mergeProps(getDefaultFlowStateProps(), props), nodeLookup = new ReactiveMap(), parentLookup = new ReactiveMap(), edgeLookup = new ReactiveMap(), connectionLookup = new ReactiveMap(), layoutedEdgesMap = new ReactiveMap(), startNodesInitialized = batch(() => adoptUserNodes(_props.nodes, nodeLookup, parentLookup, {
922
- nodeExtent: _props.nodeExtent,
923
- nodeOrigin: _props.nodeOrigin,
924
- elevateNodesOnSelect: _props.elevateNodesOnSelect,
925
- zIndexMode: _props.zIndexMode,
926
- checkEquality: !0
927
- })), initialViewport = getInitialViewport(startNodesInitialized, _props.fitView, _props.initialViewport, _props.width ?? 0, _props.height ?? 0, nodeLookup), mediaPrefersDark = createMediaQuery("(prefers-color-scheme: dark)", _props.colorModeSSR === "dark"), [config, setConfig] = createSignal(_props), [ariaLabelConfig, setAriaLabelConfig] = createWritable(() => mergeAriaLabelConfig(config().ariaLabelConfig)), [ariaLiveMessage, setAriaLiveMessage] = createWritable(() => config().ariaLiveMessage), [clickConnectStartHandle, setClickConnectStartHandle] = createSignal(void 0), [connection, setConnection] = createSignal(initialConnection), [domNode, setDomNode] = createSignal(null), [dragging, setDragging] = createSignal(!1), [elementsSelectable, setElementsSelectable] = createWritable(() => config().elementsSelectable), [height, setHeight] = createWritable(() => config().height), [minZoom, _setMinZoom] = createWritable(() => config().minZoom), [maxZoom, _setMaxZoom] = createWritable(() => config().maxZoom), [nodesConnectable, setNodesConnectable] = createWritable(() => config().nodesDraggable), [nodesDraggable, setNodesDraggable] = createWritable(() => config().nodesDraggable), [panZoom, setPanZoom] = createSignal(null), [selectionRect, setSelectionRect] = createSignal(), [selectionRectMode, setSelectionRectMode] = createSignal(), [snapGrid, setSnapGrid] = createWritable(() => config().snapGrid), [translateExtent, _setTranslateExtent] = createWritable(() => config().translateExtent ?? infiniteExtent), [width, setWidth] = createWritable(() => config().width), [selectionKeyPressed, setSelectionKeyPressed] = createSignal(!1), [multiselectionKeyPressed, setMultiselectionKeyPressed] = createSignal(!1), [deleteKeyPressed, setDeleteKeyPressed] = createSignal(!1), [panActivationKeyPressed, setPanActivationKeyPressed] = createSignal(!1), [zoomActivationKeyPressed, setZoomActivationKeyPressed] = createSignal(!1), nodesMemo = createWritableStore(() => config().nodes), edgesMemo = createWritableStore(() => config().edges), viewportMemo = createWritableStore(() => config().viewport ?? initialViewport), transform = createMemo(() => [
928
- viewportMemo.get().x,
929
- viewportMemo.get().y,
930
- viewportMemo.get().zoom
672
+ }, createFlowState = (props, injections = {}) => {
673
+ let _props = merge(getDefaultFlowStateProps(), props), initialNodeTypes = injections.initialNodeTypes ?? {}, initialEdgeTypes = injections.initialEdgeTypes ?? {}, prefersDark = injections.prefersDark ?? (() => _props.colorModeSSR === "dark"), [config, setConfig] = createSignal(_props), [ariaLabelConfig, setAriaLabelConfig] = createSignal(() => mergeAriaLabelConfig(config().ariaLabelConfig)), [ariaLiveMessage, setAriaLiveMessage] = createSignal(() => config().ariaLiveMessage), [clickConnectStartHandle, setClickConnectStartHandle] = createSignal(void 0), [connection, setConnection] = createSignal(initialConnection), [domNode, setDomNode] = createSignal(null), [dragging, setDragging] = createSignal(!1), [elementsSelectable, setElementsSelectable] = createSignal(() => config().elementsSelectable), [height, setHeight] = createSignal(() => config().height), [minZoom, _setMinZoom] = createSignal(() => config().minZoom), [maxZoom, _setMaxZoom] = createSignal(() => config().maxZoom), [nodesConnectable, setNodesConnectable] = createSignal(() => config().nodesDraggable), [nodesDraggable, setNodesDraggable] = createSignal(() => config().nodesDraggable), [panZoom, setPanZoom] = createSignal(null), [selectionRect, setSelectionRect] = createSignal(), [selectionRectMode, setSelectionRectMode] = createSignal(), [snapGrid, setSnapGrid] = createSignal(() => config().snapGrid), [translateExtent, _setTranslateExtent] = createSignal(() => config().translateExtent ?? infiniteExtent), [width, setWidth] = createSignal(() => config().width), [selectionKeyPressed, setSelectionKeyPressed] = createSignal(!1), [multiselectionKeyPressed, setMultiselectionKeyPressed] = createSignal(!1), [deleteKeyPressed, setDeleteKeyPressed] = createSignal(!1), [panActivationKeyPressed, setPanActivationKeyPressed] = createSignal(!1), [zoomActivationKeyPressed, setZoomActivationKeyPressed] = createSignal(!1), [nodesStore, setNodesStore] = createStore(_props.nodes), [edgesStore, setEdgesStore] = createStore(_props.edges), [measurementsStore, setMeasurementsStore] = createStore({}), internalNodes = createInternalNodes({
674
+ get nodes() {
675
+ return nodesStore;
676
+ },
677
+ get measurements() {
678
+ return measurementsStore;
679
+ },
680
+ get nodeOrigin() {
681
+ return config().nodeOrigin;
682
+ },
683
+ get nodeExtent() {
684
+ return config().nodeExtent;
685
+ },
686
+ get elevateNodesOnSelect() {
687
+ return config().elevateNodesOnSelect;
688
+ },
689
+ get zIndexMode() {
690
+ return config().zIndexMode;
691
+ }
692
+ }), nodeLookup = new RecordMapFacade(internalNodes), initialViewport = getInitialViewport(_props.fitView, _props.initialViewport, _props.width ?? 0, _props.height ?? 0, nodeLookup), [viewportStore, setViewportStore] = createStore(_props.viewport ?? initialViewport);
693
+ createEffect(() => config().nodes, (next) => {
694
+ setNodesStore(() => next);
695
+ }, { defer: !0 }), createEffect(() => config().edges, (next) => {
696
+ setEdgesStore(() => next);
697
+ }, { defer: !0 }), createEffect(() => config().viewport, (next) => {
698
+ next && setViewportStore(() => next);
699
+ }, { defer: !0 });
700
+ let transform = createMemo(() => [
701
+ viewportStore.x,
702
+ viewportStore.y,
703
+ viewportStore.zoom
931
704
  ]), nodesInitialized = createMemo(() => {
932
- let nodes = nodesMemo.get();
705
+ let nodes = nodesStore;
933
706
  if (nodes.length === 0) return !1;
934
707
  for (let node of nodes) if (!node.hidden && (node.measured?.width === void 0 || node.measured?.height === void 0)) return !1;
935
708
  return !0;
936
- }), store = mergeProps({
709
+ }), store = merge({
937
710
  width: 0,
938
711
  height: 0
939
712
  }, config, {
@@ -962,7 +735,7 @@ const InitialNodeTypesMap = {
962
735
  return clickConnectStartHandle();
963
736
  },
964
737
  get colorMode() {
965
- return this._colorMode === "system" ? mediaPrefersDark() ? "dark" : "light" : this._colorMode;
738
+ return this._colorMode === "system" ? prefersDark() ? "dark" : "light" : this._colorMode;
966
739
  },
967
740
  get connection() {
968
741
  let state = connection();
@@ -979,7 +752,7 @@ const InitialNodeTypesMap = {
979
752
  },
980
753
  get edgeTypes() {
981
754
  return {
982
- ...InitialEdgeTypesMap,
755
+ ...initialEdgeTypes,
983
756
  ...this._edgeTypes
984
757
  };
985
758
  },
@@ -1001,8 +774,11 @@ const InitialNodeTypesMap = {
1001
774
  get minZoom() {
1002
775
  return minZoom();
1003
776
  },
777
+ get edges() {
778
+ return edgesStore;
779
+ },
1004
780
  get nodes() {
1005
- return nodesMemo.get();
781
+ return nodesStore;
1006
782
  },
1007
783
  get nodesConnectable() {
1008
784
  return nodesConnectable();
@@ -1012,7 +788,7 @@ const InitialNodeTypesMap = {
1012
788
  },
1013
789
  get nodeTypes() {
1014
790
  return {
1015
- ...InitialNodeTypesMap,
791
+ ...initialNodeTypes,
1016
792
  ...this._nodeTypes
1017
793
  };
1018
794
  },
@@ -1020,10 +796,10 @@ const InitialNodeTypesMap = {
1020
796
  return panZoom();
1021
797
  },
1022
798
  get selectedNodes() {
1023
- return nodesMemo.get().filter((node) => node.selected);
799
+ return nodesStore.filter((node) => node.selected);
1024
800
  },
1025
801
  get selectedEdges() {
1026
- return edgesMemo.get().filter((edge) => edge.selected);
802
+ return edgesStore.filter((edge) => edge.selected);
1027
803
  },
1028
804
  get selectionRect() {
1029
805
  return selectionRect();
@@ -1035,7 +811,7 @@ const InitialNodeTypesMap = {
1035
811
  return snapGrid();
1036
812
  },
1037
813
  get viewport() {
1038
- return viewportMemo.get();
814
+ return viewportStore;
1039
815
  },
1040
816
  get viewportInitialized() {
1041
817
  return panZoom() !== null;
@@ -1076,7 +852,45 @@ const InitialNodeTypesMap = {
1076
852
  get zoomActivationKeyPressed() {
1077
853
  return zoomActivationKeyPressed();
1078
854
  }
1079
- }), visibleNodesMap = createMemo(() => nodeLookup), visibleNodeIds = createMemo(() => Array.from(visibleNodesMap().values()).map((edge) => edge.id)), visibleEdgeIds = createMemo(() => Array.from(layoutedEdgesMap.values()).map((edge) => edge.id)), getEdge = (id) => layoutedEdgesMap.get(id), fitView = async (options) => store.panZoom ? await fitViewport({
855
+ }), visibleNodesMap = createMemo(() => nodeLookup), visibleNodeIds = createMemo(() => Object.keys(internalNodes)), parentIds = createParentIds({ get nodes() {
856
+ return store.nodes;
857
+ } }), edgeLookup = createEdgeLookup({ get edges() {
858
+ return store.edges;
859
+ } }), connections = createConnections({ get edges() {
860
+ return store.edges;
861
+ } }), layoutedEdges = createLayoutedEdges({
862
+ get edges() {
863
+ return store.edges;
864
+ },
865
+ get connectionMode() {
866
+ return store.connectionMode;
867
+ },
868
+ get defaultEdgeOptions() {
869
+ return store.defaultEdgeOptions;
870
+ },
871
+ get elevateEdgesOnSelect() {
872
+ return store.elevateEdgesOnSelect;
873
+ },
874
+ get zIndexMode() {
875
+ return store.zIndexMode;
876
+ },
877
+ get onlyRenderVisibleElements() {
878
+ return store.onlyRenderVisibleElements;
879
+ },
880
+ get width() {
881
+ return store.width;
882
+ },
883
+ get height() {
884
+ return store.height;
885
+ },
886
+ get transform() {
887
+ return store.transform;
888
+ },
889
+ get onError() {
890
+ return store.onError;
891
+ },
892
+ nodeLookup
893
+ }), visibleEdgeIds = createMemo(() => Object.keys(layoutedEdges)), getEdge = (id) => layoutedEdges[id], fitView = async (options) => store.panZoom ? await fitViewport({
1080
894
  nodes: nodeLookup,
1081
895
  width: store.width,
1082
896
  height: store.height,
@@ -1084,55 +898,54 @@ const InitialNodeTypesMap = {
1084
898
  minZoom: store.minZoom,
1085
899
  maxZoom: store.maxZoom
1086
900
  }, options ?? config().fitViewOptions) : !1, resetStoreValues = () => {
1087
- setDragging(!1), setSelectionRect(void 0), setSelectionRectMode(void 0), setSelectionKeyPressed(!1), setMultiselectionKeyPressed(!1), setDeleteKeyPressed(!1), setPanActivationKeyPressed(!1), setZoomActivationKeyPressed(!1), setConnection({ ...initialConnection }), setClickConnectStartHandle(void 0), viewportMemo.set(config().initialViewport ?? {
901
+ setDragging(!1), setSelectionRect(void 0), setSelectionRectMode(void 0), setSelectionKeyPressed(!1), setMultiselectionKeyPressed(!1), setDeleteKeyPressed(!1), setPanActivationKeyPressed(!1), setZoomActivationKeyPressed(!1), setConnection({ ...initialConnection }), setClickConnectStartHandle(void 0), setViewportStore(() => config().initialViewport ?? {
1088
902
  x: 0,
1089
903
  y: 0,
1090
904
  zoom: 1
1091
905
  }), setAriaLiveMessage(""), setSnapGrid(void 0);
1092
906
  }, addEdge = (edgeParams) => {
1093
- edgesMemo.set((edges) => addEdge$1(edgeParams, edges));
907
+ setEdgesStore((edges) => {
908
+ let next = addEdge$1(edgeParams, edges);
909
+ next !== edges && edges.push(next[next.length - 1]);
910
+ });
1094
911
  }, initialFitViewApplied = !1, initialNodesMeasured = !1, applyInitialFitView = (initialFitView) => {
1095
912
  initialFitViewApplied = !initialFitView;
1096
913
  }, tryInitialFitView = () => {
1097
914
  initialFitViewApplied || !initialNodesMeasured || untrack(() => store.panZoom && store.width && store.height) && (initialFitViewApplied = !0, untrack(() => fitView()));
1098
915
  }, updateNodePositions = (nodeDragItems, dragging = !1) => {
1099
- nodesMemo.set((node) => nodeDragItems.has(node.id), produce((node) => {
1100
- node.dragging = dragging, node.position = nodeDragItems.get(node.id).position;
1101
- }));
1102
- }, pendingEntries, requestUpdateNodeInternals = (updateEntries) => {
1103
- if (pendingEntries) {
1104
- pendingEntries.push(...updateEntries);
1105
- return;
1106
- }
1107
- pendingEntries = updateEntries, scheduleIdleCallback(() => {
1108
- batch(() => {
1109
- let updates = new Map(pendingEntries);
1110
- pendingEntries = void 0;
1111
- let { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, store.domNode, store.nodeOrigin);
1112
- if (!updatedInternals) return;
1113
- updateAbsolutePositions(nodeLookup, parentLookup, {
1114
- nodeOrigin: store.nodeOrigin,
1115
- nodeExtent: store.nodeExtent,
1116
- zIndexMode: store.zIndexMode
1117
- });
1118
- let nodeToChange = changes.reduce((acc, change) => {
1119
- let node = nodeLookup.get(change.id)?.internals.userNode;
1120
- return node && acc.set(node.id, change), acc;
1121
- }, /* @__PURE__ */ new Map());
1122
- nodesMemo.set((node) => nodeToChange.has(node.id), produce((node) => {
1123
- let change = nodeToChange.get(node.id);
1124
- switch (change.type) {
1125
- case "dimensions":
1126
- change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
1127
- ...node.measured,
1128
- ...change.dimensions
1129
- };
1130
- break;
1131
- case "position": node.position = change.position ?? node.position;
1132
- }
1133
- })), initialNodesMeasured = !0, tryInitialFitView();
1134
- });
916
+ setNodesStore((nodes) => {
917
+ for (let node of nodes) nodeDragItems.has(node.id) && (node.dragging = dragging, node.position = nodeDragItems.get(node.id).position);
918
+ });
919
+ }, applyMeasurementWrites = (writes) => {
920
+ setMeasurementsStore((draft) => {
921
+ for (let write of writes) if (write.hidden) {
922
+ let entry = draft[write.id];
923
+ entry && (entry.handleBounds = void 0);
924
+ } else draft[write.id] = {
925
+ measured: write.measured,
926
+ handleBounds: write.handleBounds
927
+ };
1135
928
  });
929
+ }, applyNodeChanges = (changes) => {
930
+ changes.length !== 0 && setNodesStore((nodes) => {
931
+ let nodeById = new Map(nodes.map((node) => [node.id, node]));
932
+ for (let change of changes) {
933
+ let node = nodeById.get(change.id);
934
+ if (node) switch (change.type) {
935
+ case "dimensions":
936
+ change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
937
+ ...node.measured,
938
+ ...change.dimensions
939
+ };
940
+ break;
941
+ case "position": node.position = change.position ?? node.position;
942
+ }
943
+ }
944
+ });
945
+ }, markInitialNodesMeasured = () => {
946
+ initialNodesMeasured = !0, tryInitialFitView();
947
+ }, requestMeasure = () => {}, setMeasureRequester = (fn) => {
948
+ requestMeasure = fn;
1136
949
  }, zoomBy = async (factor, options) => store.panZoom ? store.panZoom.scaleBy(factor, options) : !1, zoomIn = (options) => zoomBy(1.2, options), zoomOut = (options) => zoomBy(1 / 1.2, options), setCenter = async (x, y, options) => {
1137
950
  let nextZoom = options?.zoom === void 0 ? store.maxZoom : options.zoom, currentPanZoom = store.panZoom;
1138
951
  return currentPanZoom ? (await currentPanZoom.setViewport({
@@ -1148,232 +961,876 @@ const InitialNodeTypesMap = {
1148
961
  store.panZoom?.setClickDistance(distance);
1149
962
  }, unselectNodesAndEdges = ({ nodes: _nodes, edges } = {}) => {
1150
963
  let nodesToUnselect = new Set((_nodes || store.nodes).map(({ id }) => id));
1151
- nodesToUnselect.size && nodesMemo.set((node) => nodesToUnselect.has(node.id), produce((node) => {
1152
- node.selected = !1;
1153
- }));
1154
- let edgesToUnselect = new Set((edges || store.edges).map(({ id }) => id));
1155
- edgesToUnselect.size && edgesMemo.set((edge) => edgesToUnselect.has(edge.id), produce((edge) => {
1156
- edge.selected = !1;
1157
- }));
964
+ nodesToUnselect.size && setNodesStore((nodes) => {
965
+ for (let node of nodes) nodesToUnselect.has(node.id) && (node.selected = !1);
966
+ });
967
+ let edgesToUnselect = new Set((edges ?? store.edges).map(({ id }) => id));
968
+ edgesToUnselect.size && setEdgesStore((edges) => {
969
+ for (let edge of edges) edgesToUnselect.has(edge.id) && (edge.selected = !1);
970
+ }), flush();
1158
971
  }, addSelectedNodes = (ids) => {
1159
- let isMultiSelection = store.multiselectionKeyPressed, selectState = /* @__PURE__ */ new Map();
1160
- nodesMemo.set((node) => {
1161
- let nodeWillBeSelected = ids.includes(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
1162
- return selectState.set(node.id, selected), node.selected !== selected;
1163
- }, produce((node) => {
1164
- let internalNode = nodeLookup.get(node.id);
1165
- internalNode && (internalNode.selected = selectState.get(node.id)), node.selected = selectState.get(node.id);
1166
- })), isMultiSelection || unselectNodesAndEdges({ nodes: [] });
972
+ let isMultiSelection = store.multiselectionKeyPressed;
973
+ setNodesStore((nodes) => {
974
+ for (let node of nodes) {
975
+ let nodeWillBeSelected = ids.includes(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
976
+ node.selected !== selected && (node.selected = selected);
977
+ }
978
+ }), isMultiSelection || unselectNodesAndEdges({ nodes: [] }), flush();
1167
979
  }, addSelectedEdges = (ids) => {
1168
- let isMultiSelection = store.multiselectionKeyPressed, edgeSelectState = /* @__PURE__ */ new Map();
1169
- edgesMemo.set((edge) => {
1170
- let edgeWillBeSelected = ids.includes(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
1171
- return edgeSelectState.set(edge.id, selected), edge.selected !== selected;
1172
- }, produce((edge) => {
1173
- edge.selected = edgeSelectState.get(edge.id);
1174
- })), isMultiSelection || unselectNodesAndEdges({ edges: [] });
1175
- };
1176
- return createEffect(() => {
1177
- width() && height() && panZoom() && tryInitialFitView();
1178
- }), createEffect(() => {
1179
- store.panZoom?.syncViewport(store.viewport);
1180
- }), createEffect(() => {
1181
- let _panZoom = panZoom();
1182
- _panZoom && (createEffect(() => {
1183
- _panZoom.setScaleExtent([store.minZoom, store.maxZoom]);
1184
- }), createEffect(() => {
1185
- _panZoom.setTranslateExtent(store.translateExtent);
1186
- }));
1187
- }), createComputed(mapArray(() => store.nodes, (userNode) => {
1188
- createComputed(() => {
1189
- let internalNode = untrack(() => nodeLookup.get(userNode.id)), selectedNodeZ = store.elevateNodesOnSelect && store.zIndexMode !== "manual" ? 1e3 : 0, clampedPosition = clampPosition(getNodePositionWithOrigin(userNode, store.nodeOrigin), isCoordinateExtent(userNode.extent) ? userNode.extent : store.nodeExtent, getNodeDimensions(userNode)), preservedMeasured = {
1190
- width: userNode.measured?.width ?? internalNode?.measured?.width,
1191
- height: userNode.measured?.height ?? internalNode?.measured?.height
1192
- }, updatedNodeInternals = {
1193
- ...userNode,
1194
- measured: preservedMeasured,
1195
- internals: {
1196
- positionAbsolute: clampedPosition,
1197
- handleBounds: !userNode.measured && !internalNode?.measured ? void 0 : internalNode?.internals.handleBounds,
1198
- z: calculateZ(userNode, selectedNodeZ, store.zIndexMode),
1199
- userNode
980
+ let isMultiSelection = store.multiselectionKeyPressed;
981
+ setEdgesStore((edges) => {
982
+ for (let edge of edges) {
983
+ let edgeWillBeSelected = ids.includes(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
984
+ edge.selected !== selected && (edge.selected = selected);
985
+ }
986
+ }), isMultiSelection || unselectNodesAndEdges({ edges: [] }), flush();
987
+ }, handleNodeSelection = (id, unselect, nodeRef) => {
988
+ let node = store.nodes.find((n) => n.id === id);
989
+ node && (setSelectionRect(void 0), setSelectionRectMode(void 0), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
990
+ nodes: [node],
991
+ edges: []
992
+ }), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]));
993
+ }, handleEdgeSelection = (id) => {
994
+ let edge = edgeLookup[id];
995
+ edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (setSelectionRect(void 0), setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
996
+ nodes: [],
997
+ edges: [edge]
998
+ }) : addSelectedEdges([id]));
999
+ }, moveSelectedNodes = (direction, factor) => {
1000
+ let nodeUpdates = /* @__PURE__ */ new Map(), xVelo = store.snapGrid?.[0] ?? 5, yVelo = store.snapGrid?.[1] ?? 5, xDiff = direction.x * xVelo * factor, yDiff = direction.y * yVelo * factor;
1001
+ for (let node of nodeLookup.values()) {
1002
+ if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
1003
+ let nextPosition = {
1004
+ x: node.internals.positionAbsolute.x + xDiff,
1005
+ y: node.internals.positionAbsolute.y + yDiff
1006
+ };
1007
+ store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
1008
+ let { position } = calculateNodePosition({
1009
+ nodeId: node.id,
1010
+ nextPosition,
1011
+ nodeLookup,
1012
+ nodeExtent: store.nodeExtent,
1013
+ nodeOrigin: store.nodeOrigin,
1014
+ onError: store.onError
1015
+ });
1016
+ nodeUpdates.set(node.id, { position });
1017
+ }
1018
+ updateNodePositions(nodeUpdates);
1019
+ }, panBy$1 = (delta) => panBy({
1020
+ delta,
1021
+ panZoom: store.panZoom,
1022
+ transform: store.transform,
1023
+ translateExtent: store.translateExtent,
1024
+ width: store.width,
1025
+ height: store.height
1026
+ }), cancelConnection = () => {
1027
+ setConnection({ ...initialConnection });
1028
+ }, reset = () => {
1029
+ resetStoreValues(), unselectNodesAndEdges();
1030
+ }, flow = {
1031
+ get nodes() {
1032
+ return store.nodes;
1033
+ },
1034
+ get edges() {
1035
+ return store.edges;
1036
+ },
1037
+ get internalNodes() {
1038
+ return internalNodes;
1039
+ },
1040
+ get layoutedEdges() {
1041
+ return layoutedEdges;
1042
+ },
1043
+ get connections() {
1044
+ return connections;
1045
+ },
1046
+ selection: {
1047
+ get nodes() {
1048
+ return store.selectedNodes;
1049
+ },
1050
+ get edges() {
1051
+ return store.selectedEdges;
1052
+ }
1053
+ },
1054
+ get nodesInitialized() {
1055
+ return store.nodesInitialized;
1056
+ },
1057
+ get viewportInitialized() {
1058
+ return store.viewportInitialized;
1059
+ },
1060
+ get viewport() {
1061
+ return store.viewport;
1062
+ },
1063
+ get width() {
1064
+ return store.width;
1065
+ },
1066
+ get height() {
1067
+ return store.height;
1068
+ },
1069
+ get colorMode() {
1070
+ return store.colorMode;
1071
+ },
1072
+ get connection() {
1073
+ return store.connection;
1074
+ },
1075
+ get dragging() {
1076
+ return store.dragging;
1077
+ },
1078
+ get minZoom() {
1079
+ return store.minZoom;
1080
+ },
1081
+ get maxZoom() {
1082
+ return store.maxZoom;
1083
+ },
1084
+ get nodesDraggable() {
1085
+ return store.nodesDraggable;
1086
+ },
1087
+ get nodesConnectable() {
1088
+ return store.nodesConnectable;
1089
+ },
1090
+ get elementsSelectable() {
1091
+ return store.elementsSelectable;
1092
+ },
1093
+ get snapGrid() {
1094
+ return store.snapGrid;
1095
+ }
1096
+ }, getNodeRect = (node) => {
1097
+ let nodeToUse = isNode(node) ? node : nodeLookup.get(node.id), position = nodeToUse.parentId ? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, nodeLookup, store.nodeOrigin) : nodeToUse.position, nodeWithPosition = {
1098
+ ...nodeToUse,
1099
+ position,
1100
+ width: nodeToUse.measured?.width ?? nodeToUse.width,
1101
+ height: nodeToUse.measured?.height ?? nodeToUse.height
1102
+ };
1103
+ return nodeToRect(nodeWithPosition);
1104
+ }, updateNode = (id, nodeUpdate, options = { replace: !1 }) => {
1105
+ setNodesStore((nodes) => {
1106
+ let index = nodes.findIndex((node) => node.id === id);
1107
+ if (index === -1) return;
1108
+ let node = nodes[index], nextNode = typeof nodeUpdate == "function" ? nodeUpdate(node) : nodeUpdate;
1109
+ nodes[index] = options?.replace && isNode(nextNode) ? nextNode : {
1110
+ ...node,
1111
+ ...nextNode
1112
+ };
1113
+ });
1114
+ }, commands = {
1115
+ fitView,
1116
+ fitBounds: async (bounds, options) => {
1117
+ if (!store.panZoom) return !1;
1118
+ let viewport = getViewportForBounds$1(bounds, store.width, store.height, store.minZoom, store.maxZoom, options?.padding ?? .1);
1119
+ return await store.panZoom.setViewport(viewport, {
1120
+ duration: options?.duration,
1121
+ ease: options?.ease,
1122
+ interpolate: options?.interpolate
1123
+ }), !0;
1124
+ },
1125
+ zoomIn,
1126
+ zoomOut,
1127
+ setZoom: (zoomLevel, options) => {
1128
+ let currentPanZoom = store.panZoom;
1129
+ return currentPanZoom ? currentPanZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(!1);
1130
+ },
1131
+ setCenter,
1132
+ setViewport: async (nextViewport, options) => {
1133
+ let currentViewport = store.viewport;
1134
+ return store.panZoom ? (await store.panZoom.setViewport({
1135
+ x: nextViewport.x ?? currentViewport.x,
1136
+ y: nextViewport.y ?? currentViewport.y,
1137
+ zoom: nextViewport.zoom ?? currentViewport.zoom
1138
+ }, options), !0) : !1;
1139
+ },
1140
+ panBy: panBy$1,
1141
+ screenToFlowPosition: (position, options = { snapToGrid: !0 }) => {
1142
+ if (!store.domNode) return position;
1143
+ let _snapGrid = options.snapToGrid ? store.snapGrid : !1, { x, y, zoom } = store.viewport, { x: domX, y: domY } = store.domNode.getBoundingClientRect(), correctedPosition = {
1144
+ x: position.x - domX,
1145
+ y: position.y - domY
1146
+ };
1147
+ return pointToRendererPoint(correctedPosition, [
1148
+ x,
1149
+ y,
1150
+ zoom
1151
+ ], _snapGrid !== null, _snapGrid || [1, 1]);
1152
+ },
1153
+ flowToScreenPosition: (position) => {
1154
+ if (!store.domNode) return position;
1155
+ let { x, y, zoom } = store.viewport, { x: domX, y: domY } = store.domNode.getBoundingClientRect(), rendererPosition = rendererPointToPoint(position, [
1156
+ x,
1157
+ y,
1158
+ zoom
1159
+ ]);
1160
+ return {
1161
+ x: rendererPosition.x + domX,
1162
+ y: rendererPosition.y + domY
1163
+ };
1164
+ },
1165
+ addNodes: (payload) => {
1166
+ let newNodes = Array.isArray(payload) ? payload : [payload];
1167
+ setNodesStore((nodes) => [...nodes, ...newNodes]);
1168
+ },
1169
+ addEdges: (payload) => {
1170
+ let newEdges = Array.isArray(payload) ? payload : [payload];
1171
+ setEdgesStore((edges) => [...edges, ...newEdges]);
1172
+ },
1173
+ setNodes: setNodesStore,
1174
+ setEdges: setEdgesStore,
1175
+ updateNode,
1176
+ updateNodeData: (id, dataUpdate, options) => {
1177
+ let node = nodeLookup.get(id)?.internals.userNode;
1178
+ if (!node) return;
1179
+ let nextData = typeof dataUpdate == "function" ? dataUpdate(node) : dataUpdate;
1180
+ updateNode(id, (current) => ({
1181
+ ...current,
1182
+ data: options?.replace ? nextData : {
1183
+ ...current.data,
1184
+ ...nextData
1185
+ }
1186
+ }));
1187
+ },
1188
+ updateEdge: (id, edgeUpdate, options = { replace: !1 }) => {
1189
+ setEdgesStore((edges) => {
1190
+ let index = edges.findIndex((edge) => edge.id === id);
1191
+ if (index === -1) return;
1192
+ let edge = edges[index], nextEdge = typeof edgeUpdate == "function" ? edgeUpdate(edge) : edgeUpdate;
1193
+ edges[index] = options.replace && isEdge(nextEdge) ? nextEdge : {
1194
+ ...edge,
1195
+ ...nextEdge
1196
+ };
1197
+ });
1198
+ },
1199
+ deleteElements: async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => {
1200
+ let { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
1201
+ nodesToRemove,
1202
+ edgesToRemove,
1203
+ nodes: store.nodes,
1204
+ edges: store.edges,
1205
+ onBeforeDelete: store.onBeforeDelete
1206
+ });
1207
+ if (matchingEdges) {
1208
+ let remainingEdges = store.edges.filter((edge) => !matchingEdges.some(({ id }) => id === edge.id));
1209
+ store.onEdgesDelete?.(matchingEdges), setEdgesStore(() => remainingEdges);
1210
+ }
1211
+ if (matchingNodes) {
1212
+ let remainingNodes = store.nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id));
1213
+ store.onNodesDelete?.(matchingNodes), setNodesStore(() => remainingNodes);
1214
+ }
1215
+ return {
1216
+ deletedNodes: matchingNodes,
1217
+ deletedEdges: matchingEdges
1218
+ };
1219
+ },
1220
+ getIntersectingNodes: (nodeOrRect, partially = !0, nodesToIntersect) => {
1221
+ let isRect = isRectObject(nodeOrRect), nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
1222
+ return nodeRect ? (nodesToIntersect || store.nodes).filter((n) => {
1223
+ let internalNode = nodeLookup.get(n.id);
1224
+ if (!internalNode || !isRect && n.id === nodeOrRect.id) return !1;
1225
+ let currNodeRect = nodeToRect(internalNode), overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
1226
+ return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
1227
+ }) : [];
1228
+ },
1229
+ isNodeIntersecting: (nodeOrRect, area, partially = !0) => {
1230
+ let nodeRect = isRectObject(nodeOrRect) ? nodeOrRect : getNodeRect(nodeOrRect);
1231
+ if (!nodeRect) return !1;
1232
+ let overlappingArea = getOverlappingArea(nodeRect, area);
1233
+ return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
1234
+ },
1235
+ getNodesBounds: (nodesToMeasure) => getNodesBounds$1(nodesToMeasure, {
1236
+ nodeLookup,
1237
+ nodeOrigin: store.nodeOrigin
1238
+ }),
1239
+ updateNodeInternals: (id) => {
1240
+ let updateIds = Array.isArray(id) ? id : [id], updates = [];
1241
+ for (let updateId of updateIds) {
1242
+ let nodeElement = store.domNode?.querySelector(`.solid-flow__node[data-id="${updateId}"]`);
1243
+ nodeElement && updates.push([updateId, {
1244
+ id: updateId,
1245
+ nodeElement,
1246
+ force: !0
1247
+ }]);
1248
+ }
1249
+ requestMeasure(updates);
1250
+ },
1251
+ toObject: () => structuredClone({
1252
+ nodes: [...snapshot(store.nodes)],
1253
+ edges: [...snapshot(store.edges)],
1254
+ viewport: { ...snapshot(store.viewport) }
1255
+ })
1256
+ };
1257
+ return createEffect(() => !!(width() && height() && panZoom()), (ready) => {
1258
+ ready && tryInitialFitView();
1259
+ }), createEffect(() => ({
1260
+ panZoom: store.panZoom,
1261
+ viewport: {
1262
+ x: store.viewport.x,
1263
+ y: store.viewport.y,
1264
+ zoom: store.viewport.zoom
1265
+ }
1266
+ }), ({ panZoom, viewport }) => {
1267
+ panZoom?.syncViewport(viewport);
1268
+ }), createEffect(() => ({
1269
+ panZoom: panZoom(),
1270
+ extent: [store.minZoom, store.maxZoom]
1271
+ }), ({ panZoom, extent }) => {
1272
+ panZoom?.setScaleExtent(extent);
1273
+ }), createEffect(() => ({
1274
+ panZoom: panZoom(),
1275
+ extent: store.translateExtent
1276
+ }), ({ panZoom, extent }) => {
1277
+ panZoom?.setTranslateExtent(extent);
1278
+ }), createEffect(() => new Set(store.nodes.map((n) => n.id)), (currentIds) => {
1279
+ setMeasurementsStore((draft) => {
1280
+ for (let id of Object.keys(draft)) currentIds.has(id) || delete draft[id];
1281
+ });
1282
+ }), {
1283
+ store,
1284
+ flow,
1285
+ commands,
1286
+ internalNodes,
1287
+ layoutedEdges,
1288
+ nodeLookup,
1289
+ edgeLookup,
1290
+ parentIds,
1291
+ connections,
1292
+ actions: {
1293
+ getEdge,
1294
+ applyInitialFitView,
1295
+ applyMeasurementWrites,
1296
+ applyNodeChanges,
1297
+ markInitialNodesMeasured,
1298
+ setMeasureRequester,
1299
+ resetStoreValues,
1300
+ setAriaLabelConfig,
1301
+ setAriaLiveMessage,
1302
+ setClickConnectStartHandle,
1303
+ setConfig,
1304
+ setConnection,
1305
+ setDeleteKeyPressed,
1306
+ setDomNode,
1307
+ setDragging,
1308
+ get setEdges() {
1309
+ return setEdgesStore;
1310
+ },
1311
+ setElementsSelectable,
1312
+ setHeight,
1313
+ setMultiselectionKeyPressed,
1314
+ get setNodes() {
1315
+ return setNodesStore;
1316
+ },
1317
+ setNodesConnectable,
1318
+ setNodesDraggable,
1319
+ setPanActivationKeyPressed,
1320
+ setPanZoom,
1321
+ setSelectionKeyPressed,
1322
+ setSelectionRect,
1323
+ setSelectionRectMode,
1324
+ get setViewport() {
1325
+ return (viewport) => setViewportStore(() => viewport);
1326
+ },
1327
+ setWidth,
1328
+ setZoomActivationKeyPressed,
1329
+ addEdge,
1330
+ updateNodePositions,
1331
+ zoomIn,
1332
+ zoomOut,
1333
+ fitView,
1334
+ setCenter,
1335
+ setPaneClickDistance,
1336
+ unselectNodesAndEdges,
1337
+ addSelectedNodes,
1338
+ addSelectedEdges,
1339
+ handleNodeSelection,
1340
+ handleEdgeSelection,
1341
+ moveSelectedNodes,
1342
+ panBy: panBy$1,
1343
+ cancelConnection,
1344
+ reset
1345
+ }
1346
+ };
1347
+ }, getEdgeId = (connection) => {
1348
+ let { source, sourceHandle, target, targetHandle } = connection;
1349
+ return `xy-edge__${source}${sourceHandle || ""}-${target}${targetHandle || ""}`;
1350
+ }, NodeConnectableContext = createContext(null);
1351
+ function useNodeConnectable() {
1352
+ let ctx = useContext(NodeConnectableContext);
1353
+ if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeConnectable");
1354
+ return ctx;
1355
+ }
1356
+ //#endregion
1357
+ //#region src/components/graph/handle/Handle.tsx
1358
+ const Handle = (props) => {
1359
+ let _props = propDefaults(props, {
1360
+ type: "source",
1361
+ position: "top",
1362
+ isConnectableStart: !0,
1363
+ isConnectableEnd: !0
1364
+ }), { store, nodeLookup, connections, actions } = useInternalSolidFlow(), rest = omit(_props, "id", "type", "position", "isConnectable", "isConnectableStart", "isConnectableEnd", "isValidConnection", "onConnect", "onDisconnect", "children", "class", "style"), nodeId = useNodeId(), nodeConnectable = useNodeConnectable(), connectable = () => _props.isConnectable ?? nodeConnectable(), isTarget = () => _props.type === "target", handleId = () => _props.id ?? null, connectionInProcess = () => !!store.connection.fromHandle, connectingFrom = () => store.connection.fromHandle && store.connection.fromHandle.nodeId === nodeId() && store.connection.fromHandle.type === _props.type && store.connection.fromHandle.id === handleId(), connectingTo = () => store.connection.toHandle && store.connection.toHandle.nodeId === nodeId() && store.connection.toHandle.type === _props.type && store.connection.toHandle.id === handleId(), isPossibleTargetHandle = () => store.connectionMode === "strict" ? store.connection.fromHandle?.type !== _props.type : nodeId() !== store.connection.fromHandle?.nodeId || handleId() !== store.connection.fromHandle?.id, valid = () => !!(connectingTo() && store.connection.isValid), prevConnections = null;
1365
+ createEffect(() => {
1366
+ if (!_props.onConnect && !_props.onDisconnect) return null;
1367
+ let rec = connections[connectionKey(nodeId(), _props.type, _props.id)], map = /* @__PURE__ */ new Map();
1368
+ for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
1369
+ return { connections: map };
1370
+ }, (current) => {
1371
+ if (!current) return;
1372
+ let { connections: next } = current;
1373
+ prevConnections && !areConnectionMapsEqual(next, prevConnections) && (handleConnectionChange(prevConnections, next, props.onDisconnect), handleConnectionChange(next, prevConnections, props.onConnect)), prevConnections = next;
1374
+ });
1375
+ let onConnectExtended = (connection) => {
1376
+ let handleConnection = {
1377
+ ...connection,
1378
+ id: getEdgeId(connection)
1379
+ }, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
1380
+ actions.addEdge(edge), store.onConnect?.(handleConnection);
1381
+ }, onPointerDown = (event) => {
1382
+ XYHandle.onPointerDown(event, {
1383
+ handleId: handleId(),
1384
+ nodeId: nodeId(),
1385
+ isTarget: isTarget(),
1386
+ connectionRadius: store.connectionRadius,
1387
+ domNode: store.domNode,
1388
+ nodeLookup,
1389
+ connectionMode: store.connectionMode,
1390
+ lib: store.lib,
1391
+ autoPanOnConnect: store.autoPanOnConnect,
1392
+ flowId: store.id,
1393
+ isValidConnection: _props.isValidConnection ?? store.isValidConnection,
1394
+ updateConnection: ((connection) => {
1395
+ actions.setConnection(connection), flush();
1396
+ }),
1397
+ cancelConnection: actions.cancelConnection,
1398
+ panBy: actions.panBy,
1399
+ onConnect: onConnectExtended,
1400
+ onConnectStart: (event, startParams) => {
1401
+ store.onConnectStart?.(event, {
1402
+ nodeId: startParams.nodeId,
1403
+ handleId: startParams.handleId,
1404
+ handleType: startParams.handleType
1405
+ });
1406
+ },
1407
+ onConnectEnd: store.onConnectEnd,
1408
+ getTransform: () => store.transform,
1409
+ getFromHandle: () => store.connection.fromHandle,
1410
+ autoPanSpeed: store.autoPanSpeed,
1411
+ dragThreshold: store.connectionDragThreshold,
1412
+ handleDomNode: event.currentTarget
1413
+ });
1414
+ }, onClick = (event) => {
1415
+ if (!nodeId() || !store.clickConnectStartHandle && !_props.isConnectableStart) return;
1416
+ if (!store.clickConnectStartHandle) {
1417
+ store.onClickConnectStart?.(event, {
1418
+ nodeId: nodeId(),
1419
+ handleId: handleId(),
1420
+ handleType: _props.type
1421
+ }), actions.setClickConnectStartHandle({
1422
+ nodeId: nodeId(),
1423
+ type: _props.type,
1424
+ id: handleId()
1425
+ });
1426
+ return;
1427
+ }
1428
+ let doc = getHostForElement(event.target), isValidConnectionHandler = _props.isValidConnection ?? store.isValidConnection, { connection, isValid } = XYHandle.isValid(event, {
1429
+ handle: {
1430
+ nodeId: nodeId(),
1431
+ id: handleId(),
1432
+ type: _props.type
1433
+ },
1434
+ connectionMode: store.connectionMode,
1435
+ fromNodeId: store.clickConnectStartHandle.nodeId,
1436
+ fromHandleId: store.clickConnectStartHandle.id ?? null,
1437
+ fromType: store.clickConnectStartHandle.type,
1438
+ isValidConnection: isValidConnectionHandler,
1439
+ flowId: store.id,
1440
+ doc,
1441
+ lib: store.lib,
1442
+ nodeLookup
1443
+ });
1444
+ isValid && connection && onConnectExtended(connection);
1445
+ let connectionClone = structuredClone(snapshot(store.connection));
1446
+ delete connectionClone.inProgress, connectionClone.toPosition = connectionClone.toHandle ? connectionClone.toHandle.position : null, store.onClickConnectEnd?.(event, connectionClone), actions.setClickConnectStartHandle(void 0);
1447
+ }, connectionIndicator = () => connectable() && (!connectionInProcess() || isPossibleTargetHandle()) && (connectionInProcess() || store.clickConnectStartHandle ? _props.isConnectableEnd : _props.isConnectableStart);
1448
+ return <div {...rest} role="button" aria-label={store.ariaLabelConfig["handle.ariaLabel"]} tabindex={-1} data-handleid={handleId()} data-nodeid={nodeId()} data-handlepos={_props.position} data-id={`${store.id}-${nodeId()}-${_props.id || null}-${_props.type}`} onClick={store.clickConnect ? onClick : void 0} onPointerDown={onPointerDown} style={_props.style} class={[
1449
+ "solid-flow__handle",
1450
+ `solid-flow__handle-${_props.position}`,
1451
+ store.noDragClass,
1452
+ store.noPanClass,
1453
+ _props.class,
1454
+ {
1455
+ valid: valid(),
1456
+ connectingto: !!connectingTo(),
1457
+ connectingfrom: !!connectingFrom(),
1458
+ source: !isTarget(),
1459
+ target: isTarget(),
1460
+ connectablestart: _props.isConnectableStart,
1461
+ connectableend: _props.isConnectableEnd,
1462
+ connectable: !!connectable(),
1463
+ connectionindicator: connectionIndicator()
1464
+ }
1465
+ ]}>
1466
+ {_props.children}
1467
+ </div>;
1468
+ }, DefaultNode = (props) => {
1469
+ let _props = propDefaults(props, {
1470
+ targetPosition: Position$1.Top,
1471
+ sourcePosition: Position$1.Bottom
1472
+ });
1473
+ return <>
1474
+ <Handle type="target" position={_props.targetPosition} isConnectable={_props.isConnectable} />
1475
+ {props.data.label}
1476
+ <Handle type="source" position={_props.sourcePosition} isConnectable={_props.isConnectable} />
1477
+ </>;
1478
+ }, GroupNode = (props) => <div style={{
1479
+ position: "absolute",
1480
+ left: 0,
1481
+ top: 0,
1482
+ width: toPxString(props.width),
1483
+ height: toPxString(props.height)
1484
+ }} />, InputNode = (props) => {
1485
+ let _props = propDefaults(props, { sourcePosition: Position$1.Bottom });
1486
+ return <>
1487
+ {props.data?.label}
1488
+ <Handle type="source" position={_props.sourcePosition} isConnectable={_props.isConnectable} />
1489
+ </>;
1490
+ }, createDraggable = (elem, params) => {
1491
+ let { store, nodeLookup, actions } = useInternalSolidFlow(), [dragging, setDragging] = createSignal(!1);
1492
+ return createEffect(() => ({
1493
+ el: elem(),
1494
+ current: params()
1495
+ }), ({ el, current }) => {
1496
+ if (!el || current.disabled) return;
1497
+ let { onDrag, onDragStart, onDragStop, onNodeMouseDown } = current, dragInstance = XYDrag({
1498
+ onDrag,
1499
+ onDragStart: (event, dragItems, node, nodes) => {
1500
+ setDragging(!0), onDragStart?.(event, dragItems, node, nodes);
1501
+ },
1502
+ onDragStop: (event, dragItems, node, nodes) => {
1503
+ setDragging(!1), onDragStop?.(event, dragItems, node, nodes);
1504
+ },
1505
+ onNodeMouseDown,
1506
+ getStoreItems: () => ({
1507
+ nodes: store.nodes,
1508
+ nodeLookup,
1509
+ edges: store.edges,
1510
+ nodeExtent: store.nodeExtent,
1511
+ snapGrid: store.snapGrid ?? [0, 0],
1512
+ snapToGrid: !!store.snapGrid,
1513
+ autoPanSpeed: store.autoPanSpeed,
1514
+ nodeOrigin: store.nodeOrigin,
1515
+ multiSelectionActive: store.multiselectionKeyPressed,
1516
+ domNode: store.domNode,
1517
+ transform: store.transform,
1518
+ autoPanOnNodeDrag: store.autoPanOnNodeDrag,
1519
+ nodesDraggable: store.nodesDraggable,
1520
+ selectNodesOnDrag: store.selectNodesOnDrag,
1521
+ nodeDragThreshold: store.nodeDragThreshold,
1522
+ unselectNodesAndEdges: actions.unselectNodesAndEdges,
1523
+ updateNodePositions: actions.updateNodePositions,
1524
+ panBy: actions.panBy
1525
+ })
1526
+ });
1527
+ return dragInstance.update({
1528
+ domNode: el,
1529
+ nodeId: current.nodeId,
1530
+ noDragClassName: current.noDragClass,
1531
+ handleSelector: current.handleSelector,
1532
+ isSelectable: current.isSelectable,
1533
+ nodeClickDistance: current.nodeClickDistance
1534
+ }), () => {
1535
+ dragInstance.destroy();
1536
+ };
1537
+ }), dragging;
1538
+ }, NodeIdContext = createContext(null);
1539
+ function useNodeId() {
1540
+ let ctx = useContext(NodeIdContext);
1541
+ if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeId");
1542
+ return ctx;
1543
+ }
1544
+ //#endregion
1545
+ //#region src/components/graph/node/NodeWrapper.tsx
1546
+ const NodeWrapper = (props) => {
1547
+ let { store, nodeLookup, parentIds, actions } = useInternalSolidFlow(), [nodeRef, setNodeRef] = createSignal(), node = () => nodeLookup.get(props.nodeId), nodeId = () => node().id, nodeType = () => node().type ?? "default", deletable = () => node().deletable ?? !0, selectable = () => node().selectable ?? store.elementsSelectable, focusable = () => node().focusable ?? store.nodesFocusable, draggable = () => node().draggable ?? store.nodesDraggable, connectable = () => node().connectable ?? store.nodesConnectable, userNode = () => node().internals.userNode, nodeTypeValid = () => nodeType() in store.nodeTypes, nodeComponent = () => store.nodeTypes[nodeType()], isParentNode = () => !!parentIds[node().id], transform = () => {
1548
+ let { x, y } = node().internals.positionAbsolute;
1549
+ return `translate(${x}px, ${y}px)`;
1550
+ }, sizeStyle = () => {
1551
+ let w = node().width ?? node().initialWidth, h = node().height ?? node().initialHeight;
1552
+ return {
1553
+ ...node().style,
1554
+ ...w ? { width: toPxString(w) } : {},
1555
+ ...h ? { height: toPxString(h) } : {}
1556
+ };
1557
+ }, style = () => ({
1558
+ "z-index": node().internals.z,
1559
+ transform: transform(),
1560
+ visibility: nodeHasDimensions(node()) ? "visible" : "hidden",
1561
+ ...sizeStyle(),
1562
+ ...node().style ?? {}
1563
+ });
1564
+ createEffect(() => ({
1565
+ valid: nodeTypeValid(),
1566
+ nodeType: nodeType()
1567
+ }), ({ valid, nodeType }) => {}), createEffect(() => ({
1568
+ id: node().id,
1569
+ nodeElement: nodeRef(),
1570
+ nodeType: nodeType(),
1571
+ sourcePosition: node().sourcePosition,
1572
+ targetPosition: node().targetPosition
1573
+ }), (current, prev) => {
1574
+ current.nodeElement && (prev && prev.nodeElement === current.nodeElement && prev.sourcePosition === current.sourcePosition && prev.targetPosition === current.targetPosition && prev.nodeType === current.nodeType || actions.requestUpdateNodeInternals([[current.id, {
1575
+ id: current.id,
1576
+ nodeElement: current.nodeElement,
1577
+ force: !0
1578
+ }]]));
1579
+ }), createEffect(() => ({
1580
+ nodeElement: nodeRef(),
1581
+ hasDimensions: nodeHasDimensions(node()),
1582
+ resizeObserver: props.resizeObserver
1583
+ }), (current, prev) => {
1584
+ current.nodeElement === prev?.nodeElement && current.resizeObserver === prev?.resizeObserver && current.hasDimensions || (prev?.nodeElement && prev.resizeObserver?.unobserve(prev.nodeElement), current.nodeElement && current.resizeObserver?.observe(current.nodeElement));
1585
+ });
1586
+ let onSelectNodeHandler = (event) => {
1587
+ selectable() && (!store.selectNodesOnDrag || !draggable() || store.nodeDragThreshold > 0) && actions.handleNodeSelection(node().id), props.onNodeClick?.({
1588
+ node: userNode(),
1589
+ event
1590
+ });
1591
+ }, onKeyDown = (event) => {
1592
+ if (isInputDOMNode(event) || store.disableKeyboardA11y) return;
1593
+ if (elementSelectionKeys.includes(event.key) && selectable()) {
1594
+ actions.handleNodeSelection(node().id, event.key === "Escape", nodeRef());
1595
+ return;
1596
+ }
1597
+ let arrowKeyDiff = ARROW_KEY_DIFFS[event.key];
1598
+ draggable() && node().selected && arrowKeyDiff && (event.preventDefault(), actions.setAriaLiveMessage(store.ariaLabelConfig["node.a11yDescription.ariaLiveMessage"]({
1599
+ direction: event.key.replace("Arrow", "").toLowerCase(),
1600
+ x: ~~node().internals.positionAbsolute.x,
1601
+ y: ~~node().internals.positionAbsolute.y
1602
+ })), actions.moveSelectedNodes(arrowKeyDiff, event.shiftKey ? 4 : 1));
1603
+ }, onFocus = () => {
1604
+ if (store.disableKeyboardA11y || !store.autoPanOnNodeFocus || !nodeRef()?.matches(":focus-visible")) return;
1605
+ let { width, height, viewport } = store;
1606
+ getNodesInside(/* @__PURE__ */ new Map([[node().id, node()]]), {
1607
+ x: 0,
1608
+ y: 0,
1609
+ width,
1610
+ height
1611
+ }, [
1612
+ viewport.x,
1613
+ viewport.y,
1614
+ viewport.zoom
1615
+ ], !0).length > 0 || actions.setCenter(node().position.x + (node().measured.width ?? 0) / 2, node().position.y + (node().measured.height ?? 0) / 2, { zoom: viewport.zoom });
1616
+ }, dragging = createDraggable(nodeRef, () => ({
1617
+ nodeId: node().id,
1618
+ isSelectable: selectable(),
1619
+ disabled: !draggable(),
1620
+ handleSelector: node().dragHandle,
1621
+ noDragClass: store.noDragClass,
1622
+ nodeClickDistance: props.nodeClickDistance,
1623
+ onNodeMouseDown: actions.handleNodeSelection,
1624
+ onDrag: (event, _, targetNode, nodes) => {
1625
+ props.onNodeDrag?.({
1626
+ event,
1627
+ targetNode,
1628
+ nodes
1629
+ });
1630
+ },
1631
+ onDragStart: (event, _, targetNode, nodes) => {
1632
+ props.onNodeDragStart?.({
1633
+ event,
1634
+ targetNode,
1635
+ nodes
1636
+ });
1637
+ },
1638
+ onDragStop: (event, _, targetNode, nodes) => {
1639
+ props.onNodeDragStop?.({
1640
+ event,
1641
+ targetNode,
1642
+ nodes
1643
+ });
1644
+ }
1645
+ }));
1646
+ return <Show when={!node().hidden}>
1647
+ <div ref={setNodeRef} data-id={node().id} class={[
1648
+ "solid-flow__node",
1649
+ `solid-flow__node-${nodeType()}`,
1650
+ {
1651
+ connectable: !!connectable(),
1652
+ draggable: !!draggable(),
1653
+ dragging: dragging(),
1654
+ nopan: !!draggable(),
1655
+ parent: isParentNode(),
1656
+ selectable: !!selectable(),
1657
+ selected: !!node().selected
1658
+ },
1659
+ node().class
1660
+ ]} style={style()} onClick={onSelectNodeHandler} onPointerEnter={(event) => props.onNodePointerEnter?.({
1661
+ node: userNode(),
1662
+ event
1663
+ })} onPointerLeave={(event) => props.onNodePointerLeave?.({
1664
+ node: userNode(),
1665
+ event
1666
+ })} onPointerMove={(event) => props.onNodePointerMove?.({
1667
+ node: userNode(),
1668
+ event
1669
+ })} onContextMenu={(event) => props.onNodeContextMenu?.({
1670
+ node: userNode(),
1671
+ event
1672
+ })} onKeyDown={(e) => focusable() && onKeyDown(e)} onFocus={() => focusable() && onFocus()} tabindex={focusable() ? 0 : void 0} role={node().ariaRole ?? (focusable() ? "group" : void 0)} aria-roledescription="node" aria-describedby={store.disableKeyboardA11y ? void 0 : `${ARIA_NODE_DESC_KEY}-${store.id}`} {...node().domAttributes}>
1673
+ <NodeIdContext value={nodeId}>
1674
+ <NodeConnectableContext value={connectable}>
1675
+ <Dynamic component={nodeComponent()} data={node().data} id={node().id} selected={!!node().selected} selectable={selectable()} deletable={deletable()} sourcePosition={node().sourcePosition} targetPosition={node().targetPosition} zIndex={node().internals.z} dragging={dragging()} draggable={draggable()} dragHandle={node().dragHandle} parentId={node().parentId} type={nodeType()} isConnectable={connectable()} positionAbsoluteX={node().internals.positionAbsolute.x} positionAbsoluteY={node().internals.positionAbsolute.y} width={node().width} height={node().height} />
1676
+ </NodeConnectableContext>
1677
+ </NodeIdContext>
1678
+ </div>
1679
+ </Show>;
1680
+ }, OutputNode = (props) => {
1681
+ let _props = propDefaults(props, { targetPosition: Position$1.Top });
1682
+ return <>
1683
+ <Handle type="target" position={_props.targetPosition} isConnectable={_props.isConnectable} />
1684
+ {props.data?.label}
1685
+ </>;
1686
+ };
1687
+ //#endregion
1688
+ //#region src/data/xyflow.ts
1689
+ /**
1690
+ * The DOM side of the measurement pipeline (fork of @xyflow/system's
1691
+ * updateNodeInternals): reads each updated node element's dimensions and
1692
+ * handle bounds from the DOM and reports them as data — measurement writes
1693
+ * for the measurements root plus user-facing dimension/position changes —
1694
+ * instead of writing into a node lookup. The internalNodes projection turns
1695
+ * the measurement writes back into internal-node state.
1696
+ *
1697
+ * `parentExpandChildren` must be turned into changes via
1698
+ * {@link handleExpandParent} only AFTER the measurement writes have been
1699
+ * flushed, so parent geometry reflects this measuring pass.
1700
+ */
1701
+ function measureNodeInternals(updates, nodeLookup, domNode, nodeExtent) {
1702
+ let measurementWrites = [], changes = [], parentExpandChildren = [], updatedInternals = !1, viewportNode = domNode?.querySelector(".xyflow__viewport");
1703
+ if (!viewportNode) return {
1704
+ updatedInternals,
1705
+ measurementWrites,
1706
+ changes,
1707
+ parentExpandChildren
1708
+ };
1709
+ let style = window.getComputedStyle(viewportNode), { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
1710
+ for (let update of updates.values()) {
1711
+ let node = nodeLookup.get(update.id);
1712
+ if (!node) continue;
1713
+ if (node.hidden) {
1714
+ measurementWrites.push({
1715
+ id: node.id,
1716
+ hidden: !0
1717
+ }), updatedInternals = !0;
1718
+ continue;
1719
+ }
1720
+ let dimensions = getDimensions(update.nodeElement), dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height;
1721
+ if (dimensions.width && dimensions.height && (dimensionChanged || !node.internals.handleBounds || update.force)) {
1722
+ let nodeBounds = update.nodeElement.getBoundingClientRect(), extent = isCoordinateExtent(node.extent) ? node.extent : nodeExtent, { positionAbsolute } = node.internals;
1723
+ node.parentId && node.extent === "parent" ? positionAbsolute = clampPositionToParent(positionAbsolute, dimensions, nodeLookup.get(node.parentId)) : extent && (positionAbsolute = clampPosition(positionAbsolute, extent, dimensions)), measurementWrites.push({
1724
+ id: node.id,
1725
+ measured: dimensions,
1726
+ handleBounds: {
1727
+ source: getHandleBounds("source", update.nodeElement, nodeBounds, zoom, node.id),
1728
+ target: getHandleBounds("target", update.nodeElement, nodeBounds, zoom, node.id)
1729
+ }
1730
+ }), updatedInternals = !0, dimensionChanged && (changes.push({
1731
+ id: node.id,
1732
+ type: "dimensions",
1733
+ dimensions
1734
+ }), node.expandParent && node.parentId && parentExpandChildren.push({
1735
+ id: node.id,
1736
+ parentId: node.parentId,
1737
+ rect: nodeToRect({
1738
+ ...node,
1739
+ measured: dimensions,
1740
+ internals: {
1741
+ ...node.internals,
1742
+ positionAbsolute
1743
+ }
1744
+ })
1745
+ }));
1746
+ }
1747
+ }
1748
+ return {
1749
+ updatedInternals,
1750
+ measurementWrites,
1751
+ changes,
1752
+ parentExpandChildren
1753
+ };
1754
+ }
1755
+ function handleExpandParent(children, nodeLookup, getChildNodes, nodeOrigin = [0, 0]) {
1756
+ let changes = [], parentExpansions = /* @__PURE__ */ new Map();
1757
+ for (let child of children) {
1758
+ let parent = nodeLookup.get(child.parentId);
1759
+ if (!parent) continue;
1760
+ let parentRect = parentExpansions.get(child.parentId)?.expandedRect ?? nodeToRect(parent), expandedRect = getBoundsOfRects(parentRect, child.rect);
1761
+ parentExpansions.set(child.parentId, {
1762
+ expandedRect,
1763
+ parent
1764
+ });
1765
+ }
1766
+ return parentExpansions.size > 0 && parentExpansions.forEach(({ expandedRect, parent }, parentId) => {
1767
+ let positionAbsolute = parent.internals.positionAbsolute, dimensions = getNodeDimensions(parent), origin = parent.origin ?? nodeOrigin, xChange = expandedRect.x < positionAbsolute.x ? Math.round(Math.abs(positionAbsolute.x - expandedRect.x)) : 0, yChange = expandedRect.y < positionAbsolute.y ? Math.round(Math.abs(positionAbsolute.y - expandedRect.y)) : 0, newWidth = Math.max(dimensions.width, Math.round(expandedRect.width)), newHeight = Math.max(dimensions.height, Math.round(expandedRect.height)), widthChange = (newWidth - dimensions.width) * origin[0], heightChange = (newHeight - dimensions.height) * origin[1];
1768
+ if (xChange > 0 || yChange > 0 || widthChange || heightChange) {
1769
+ changes.push({
1770
+ id: parentId,
1771
+ type: "position",
1772
+ position: {
1773
+ x: parent.position.x - xChange + widthChange,
1774
+ y: parent.position.y - yChange + heightChange
1200
1775
  }
1201
- };
1202
- nodeLookup.set(userNode.id, updatedNodeInternals), userNode.parentId && updateChildNode(updatedNodeInternals, nodeLookup, parentLookup, {
1203
- nodeOrigin: store.nodeOrigin,
1204
- nodeExtent: store.nodeExtent,
1205
- elevateNodesOnSelect: store.elevateNodesOnSelect,
1206
- zIndexMode: store.zIndexMode,
1207
- checkEquality: !0
1208
1776
  });
1209
- }), onCleanup(() => {});
1210
- })), createEffect(() => {
1211
- let currentIds = new Set(store.nodes.map((n) => n.id));
1212
- for (let id of Array.from(nodeLookup.keys())) currentIds.has(id) || nodeLookup.delete(id);
1213
- }), createComputed(mapArray(() => store.edges, (edge) => {
1214
- let { source: sourceNodeId, target: targetNodeId, sourceHandle = null, targetHandle = null } = edge, connection = {
1215
- edgeId: edge.id,
1216
- source: sourceNodeId,
1217
- target: targetNodeId,
1218
- sourceHandle,
1219
- targetHandle
1220
- }, sourceKey = `${sourceNodeId}-${sourceHandle}--${targetNodeId}-${targetHandle}`, targetKey = `${targetNodeId}-${targetHandle}--${sourceNodeId}-${sourceHandle}`;
1221
- createComputed(() => {
1222
- batch(() => {
1223
- addConnectionToLookup("source", connection, targetKey, connectionLookup, sourceNodeId, sourceHandle), addConnectionToLookup("target", connection, sourceKey, connectionLookup, targetNodeId, targetHandle), edgeLookup.set(edge.id, edge);
1777
+ for (let childNode of getChildNodes(parentId)) children.some((child) => child.id === childNode.id) || changes.push({
1778
+ id: childNode.id,
1779
+ type: "position",
1780
+ position: {
1781
+ x: childNode.position.x + xChange,
1782
+ y: childNode.position.y + yChange
1783
+ }
1224
1784
  });
1225
- }), createComputed(() => {
1226
- let sourceNode = nodeLookup.get(edge.source), targetNode = nodeLookup.get(edge.target);
1227
- if (!sourceNode || !targetNode) return;
1228
- if (store.onlyRenderVisibleElements) {
1229
- if (!isEdgeVisible({
1230
- sourceNode,
1231
- targetNode,
1232
- width: store.width ?? 0,
1233
- height: store.height ?? 0,
1234
- transform: store.transform
1235
- })) return;
1236
- store.visibleNodesMap.set(sourceNode.id, sourceNode), store.visibleNodesMap.set(targetNode.id, targetNode);
1785
+ }
1786
+ (dimensions.width < expandedRect.width || dimensions.height < expandedRect.height || xChange || yChange) && changes.push({
1787
+ id: parentId,
1788
+ type: "dimensions",
1789
+ setAttributes: !0,
1790
+ dimensions: {
1791
+ width: newWidth + (xChange ? origin[0] * xChange - widthChange : 0),
1792
+ height: newHeight + (yChange ? origin[1] * yChange - heightChange : 0)
1237
1793
  }
1238
- let edgePosition = getEdgePosition({
1239
- id: edge.id,
1240
- sourceNode,
1241
- targetNode,
1242
- sourceHandle: edge.sourceHandle || null,
1243
- targetHandle: edge.targetHandle || null,
1244
- connectionMode: store.connectionMode,
1245
- onError: store.onError
1246
- });
1247
- edgePosition && layoutedEdgesMap.set(edge.id, {
1248
- ...store.defaultEdgeOptions,
1249
- ...edge,
1250
- ...edgePosition,
1251
- zIndex: getElevatedEdgeZIndex({
1252
- selected: edge.selected,
1253
- zIndex: edge.zIndex ?? store.defaultEdgeOptions.zIndex,
1254
- sourceNode,
1255
- targetNode,
1256
- elevateOnSelect: store.elevateEdgesOnSelect,
1257
- zIndexMode: store.zIndexMode
1258
- }),
1259
- sourceNode,
1260
- targetNode,
1261
- edge
1262
- });
1263
- }), onCleanup(() => {
1264
- batch(() => {
1265
- edgeLookup.delete(edge.id), layoutedEdgesMap.delete(edge.id), removeConnectionFromLookup("source", targetKey, connectionLookup, sourceNodeId, sourceHandle), removeConnectionFromLookup("target", sourceKey, connectionLookup, targetNodeId, targetHandle);
1266
- });
1267
1794
  });
1268
- })), createEffect(() => {
1269
- let currentIds = new Set(store.edges.map((e) => e.id));
1270
- for (let id of Array.from(edgeLookup.keys())) currentIds.has(id) || (edgeLookup.delete(id), connectionLookup.delete(id), layoutedEdgesMap.delete(id));
1271
- }), {
1272
- store,
1273
- nodeLookup,
1274
- edgeLookup,
1275
- parentLookup,
1276
- connectionLookup,
1795
+ }), changes;
1796
+ }
1797
+ //#endregion
1798
+ //#region src/data/createSolidFlow.tsx
1799
+ const InitialNodeTypesMap = {
1800
+ input: InputNode,
1801
+ output: OutputNode,
1802
+ default: DefaultNode,
1803
+ group: GroupNode
1804
+ }, InitialEdgeTypesMap = {
1805
+ straight: StraightEdgeInternal,
1806
+ smoothstep: SmoothStepEdgeInternal,
1807
+ default: BezierEdgeInternal,
1808
+ step: StepEdgeInternal
1809
+ }, createSolidFlow = (props) => {
1810
+ let state = createFlowState(props, {
1811
+ prefersDark: createMediaQuery("(prefers-color-scheme: dark)", props.colorModeSSR === "dark"),
1812
+ initialNodeTypes: InitialNodeTypesMap,
1813
+ initialEdgeTypes: InitialEdgeTypesMap
1814
+ }), { store, nodeLookup, actions } = state, pendingEntries, requestUpdateNodeInternals = (updateEntries) => {
1815
+ if (pendingEntries) {
1816
+ pendingEntries.push(...updateEntries);
1817
+ return;
1818
+ }
1819
+ pendingEntries = updateEntries, scheduleIdleCallback(() => {
1820
+ let updates = new Map(pendingEntries);
1821
+ pendingEntries = void 0;
1822
+ let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode);
1823
+ updatedInternals && (actions.applyMeasurementWrites(measurementWrites), flush(), parentExpandChildren.length > 0 && changes.push(...handleExpandParent(parentExpandChildren, nodeLookup, (parentId) => store.nodes.filter((node) => node.parentId === parentId), store.nodeOrigin)), actions.applyNodeChanges(changes), flush(), actions.markInitialNodesMeasured());
1824
+ });
1825
+ };
1826
+ return actions.setMeasureRequester(requestUpdateNodeInternals), {
1827
+ ...state,
1277
1828
  actions: {
1278
- getEdge,
1279
- applyInitialFitView,
1280
- resetStoreValues,
1281
- requestUpdateNodeInternals,
1282
- setAriaLabelConfig,
1283
- setAriaLiveMessage,
1284
- setClickConnectStartHandle,
1285
- setConfig,
1286
- setConnection,
1287
- setDeleteKeyPressed,
1288
- setDomNode,
1289
- setDragging,
1290
- get setEdges() {
1291
- return edgesMemo.set;
1292
- },
1293
- setElementsSelectable,
1294
- setHeight,
1295
- setMultiselectionKeyPressed,
1296
- get setNodes() {
1297
- return nodesMemo.set;
1298
- },
1299
- setNodesConnectable,
1300
- setNodesDraggable,
1301
- setPanActivationKeyPressed,
1302
- setPanZoom,
1303
- setSelectionKeyPressed,
1304
- setSelectionRect,
1305
- setSelectionRectMode,
1306
- get setViewport() {
1307
- return viewportMemo.set;
1308
- },
1309
- setWidth,
1310
- setZoomActivationKeyPressed,
1311
- addEdge,
1312
- updateNodePositions,
1313
- zoomIn,
1314
- zoomOut,
1315
- fitView,
1316
- setCenter,
1317
- setPaneClickDistance,
1318
- unselectNodesAndEdges,
1319
- addSelectedNodes,
1320
- addSelectedEdges,
1321
- handleNodeSelection: (id, unselect, nodeRef) => {
1322
- let node = store.nodes.find((n) => n.id === id);
1323
- node && (batch(() => {
1324
- setSelectionRect(void 0), setSelectionRectMode(void 0);
1325
- }), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
1326
- nodes: [node],
1327
- edges: []
1328
- }), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]));
1329
- },
1330
- handleEdgeSelection: (id) => {
1331
- let edge = edgeLookup.get(id);
1332
- edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (batch(() => {
1333
- setSelectionRect(void 0), setSelectionRectMode(void 0);
1334
- }), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
1335
- nodes: [],
1336
- edges: [edge]
1337
- }) : addSelectedEdges([id]));
1338
- },
1339
- moveSelectedNodes: (direction, factor) => {
1340
- let nodeUpdates = /* @__PURE__ */ new Map(), xVelo = store.snapGrid?.[0] ?? 5, yVelo = store.snapGrid?.[1] ?? 5, xDiff = direction.x * xVelo * factor, yDiff = direction.y * yVelo * factor;
1341
- for (let node of nodeLookup.values()) {
1342
- if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
1343
- let nextPosition = {
1344
- x: node.internals.positionAbsolute.x + xDiff,
1345
- y: node.internals.positionAbsolute.y + yDiff
1346
- };
1347
- store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
1348
- let { position, positionAbsolute } = calculateNodePosition({
1349
- nodeId: node.id,
1350
- nextPosition,
1351
- nodeLookup,
1352
- nodeExtent: store.nodeExtent,
1353
- nodeOrigin: store.nodeOrigin,
1354
- onError: store.onError
1355
- });
1356
- node.position = position, node.internals.positionAbsolute = positionAbsolute, nodeUpdates.set(node.id, node);
1357
- }
1358
- updateNodePositions(nodeUpdates);
1359
- },
1360
- panBy: (delta) => panBy({
1361
- delta,
1362
- panZoom: store.panZoom,
1363
- transform: store.transform,
1364
- translateExtent: store.translateExtent,
1365
- width: store.width,
1366
- height: store.height
1367
- }),
1368
- cancelConnection: () => {
1369
- setConnection({ ...initialConnection });
1370
- },
1371
- reset: () => {
1372
- resetStoreValues(), unselectNodesAndEdges();
1373
- }
1829
+ ...actions,
1830
+ requestUpdateNodeInternals
1374
1831
  }
1375
1832
  };
1376
- }, SolidFlowContext = createContext();
1833
+ }, SolidFlowContext = createContext(null);
1377
1834
  function useInternalSolidFlow() {
1378
1835
  let ctx = useContext(SolidFlowContext);
1379
1836
  if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useInternalSolidFlow within your components");
@@ -1382,13 +1839,13 @@ function useInternalSolidFlow() {
1382
1839
  //#endregion
1383
1840
  //#region src/components/graph/marker/Marker.tsx
1384
1841
  const Marker = (props) => {
1385
- let _props = mergeProps({
1842
+ let _props = propDefaults(props, {
1386
1843
  markerUnits: "strokeWidth",
1387
1844
  orient: "auto-start-reverse",
1388
1845
  width: 12.5,
1389
1846
  height: 12.5,
1390
1847
  color: "none"
1391
- }, props), color = () => _props.color ?? "var(--xy-edge-stroke)";
1848
+ }), color = () => _props.color ?? "var(--xy-edge-stroke)";
1392
1849
  return <marker class="solid-flow__arrowhead" id={_props.id} markerWidth={_props.width} markerHeight={_props.height} viewBox="-10 -10 20 20" markerUnits={_props.markerUnits} orient={_props.orient} refX="0" refY="0">
1393
1850
  <Show when={_props.type === "arrow"} fallback={<polyline class="arrowclosed" stroke={color()} fill={color()} stroke-linecap="round" stroke-linejoin="round" stroke-width={_props.strokeWidth} points="-5,-4 0,0 -5,4 -5,-4" />}>
1394
1851
  <polyline class="arrow" stroke={color()} fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width={_props.strokeWidth} points="-5,-4 0,0 -5,4" />
@@ -1440,7 +1897,7 @@ const Marker = (props) => {
1440
1897
  for (let item of a) if (!b.has(item)) return !1;
1441
1898
  return !0;
1442
1899
  }, Pane = (props) => {
1443
- let { store, nodeLookup, edgeLookup, connectionLookup, actions } = useInternalSolidFlow(), container, containerBounds = null, connectionEndedOnPane = !1, selectionInProgress = !1, selectedNodeIds = /* @__PURE__ */ new Set(), selectedEdgeIds = /* @__PURE__ */ new Set(), autoPanId = 0, position = {
1900
+ let { store, nodeLookup, edgeLookup, connections, actions } = useInternalSolidFlow(), [containerRef, setContainerRef] = createSignal(), container, containerBounds = null, connectionEndedOnPane = !1, selectionInProgress = !1, selectedNodeIds = /* @__PURE__ */ new Set(), selectedEdgeIds = /* @__PURE__ */ new Set(), autoPanId = 0, position = {
1444
1901
  x: 0,
1445
1902
  y: 0
1446
1903
  }, autoPanStarted = !1, autoPanOnSelection = () => props.autoPanOnSelection ?? !0, paneClickDistance = () => props.paneClickDistance ?? 1, _panOnDrag = () => store.panActivationKeyPressed || props.panOnDrag, isSelecting = () => store.selectionKeyPressed || !!store.selectionRect || props.selectionOnDrag && _panOnDrag() !== !0, isSelectionEnabled = () => store.elementsSelectable && (isSelecting() || store.selectionRectMode === "user"), onClick = (event) => {
@@ -1449,9 +1906,7 @@ const Marker = (props) => {
1449
1906
  selectionInProgress = !1, connectionEndedOnPane = !1;
1450
1907
  return;
1451
1908
  }
1452
- props.onPaneClick?.({ event }), batch(() => {
1453
- actions.unselectNodesAndEdges(), actions.setSelectionRectMode(void 0), actions.setSelectionRect(void 0);
1454
- });
1909
+ props.onPaneClick?.({ event }), actions.unselectNodesAndEdges(), actions.setSelectionRectMode(void 0), actions.setSelectionRect(void 0);
1455
1910
  }
1456
1911
  }, onPointerDownCapture = (event) => {
1457
1912
  if (event.pointerType === "touch" && _panOnDrag() !== !1 && !store.selectionKeyPressed || (containerBounds = container?.getBoundingClientRect() ?? null, !containerBounds)) return;
@@ -1469,7 +1924,7 @@ const Marker = (props) => {
1469
1924
  startY: userSelectionFlowOrigin.y,
1470
1925
  x,
1471
1926
  y
1472
- }), eventTargetIsContainer || (event.stopPropagation(), event.preventDefault());
1927
+ }), flush(), eventTargetIsContainer || (event.stopPropagation(), event.preventDefault());
1473
1928
  }, commitUserSelectionRect = (mouseX, mouseY) => {
1474
1929
  let selectionRect = store.selectionRect;
1475
1930
  if (selectionRect?.startX === void 0 || selectionRect.startY === void 0) return;
@@ -1488,33 +1943,23 @@ const Marker = (props) => {
1488
1943
  let edgesSelectable = store.defaultEdgeOptions.selectable ?? !0;
1489
1944
  selectedEdgeIds = /* @__PURE__ */ new Set();
1490
1945
  for (let nodeId of selectedNodeIds) {
1491
- let connections = connectionLookup.get(nodeId);
1492
- if (connections) for (let { edgeId } of connections.values()) {
1493
- let edge = edgeLookup.get(edgeId);
1946
+ let nodeConnections = connections[nodeId];
1947
+ if (nodeConnections) for (let { edgeId } of Object.values(nodeConnections)) {
1948
+ let edge = edgeLookup[edgeId];
1494
1949
  edge && (edge.selectable ?? edgesSelectable) && selectedEdgeIds.add(edgeId);
1495
1950
  }
1496
1951
  }
1497
- batch(() => {
1498
- if (!isSetEqual(prevSelectedNodeIds, selectedNodeIds)) {
1499
- let selectionMap = /* @__PURE__ */ new Map();
1500
- actions.setNodes((node) => {
1501
- let isSelected = selectedNodeIds.has(node.id);
1502
- return selectionMap.set(node.id, isSelected), !!node.selected !== isSelected;
1503
- }, produce((node) => {
1504
- node.selected = selectionMap.get(node.id);
1505
- }));
1952
+ isSetEqual(prevSelectedNodeIds, selectedNodeIds) || actions.setNodes((nodes) => {
1953
+ for (let node of nodes) {
1954
+ let isSelected = selectedNodeIds.has(node.id);
1955
+ !!node.selected !== isSelected && (node.selected = isSelected);
1506
1956
  }
1507
- if (!isSetEqual(prevSelectedEdgeIds, selectedEdgeIds)) {
1508
- let selectionMap = /* @__PURE__ */ new Map();
1509
- actions.setEdges((edge) => {
1510
- let isSelected = selectedEdgeIds.has(edge.id);
1511
- return selectionMap.set(edge.id, isSelected), !!edge.selected !== isSelected;
1512
- }, produce((edge) => {
1513
- edge.selected = selectionMap.get(edge.id);
1514
- }));
1957
+ }), isSetEqual(prevSelectedEdgeIds, selectedEdgeIds) || actions.setEdges((edges) => {
1958
+ for (let edge of edges) {
1959
+ let isSelected = selectedEdgeIds.has(edge.id);
1960
+ !!edge.selected !== isSelected && (edge.selected = isSelected);
1515
1961
  }
1516
- actions.setSelectionRectMode("user"), actions.setSelectionRect(nextUserSelectRect);
1517
- });
1962
+ }), actions.setSelectionRectMode("user"), actions.setSelectionRect(nextUserSelectRect), flush();
1518
1963
  }, autoPan = () => {
1519
1964
  if (!autoPanOnSelection() || !containerBounds) return;
1520
1965
  let [x = 0, y = 0] = calcAutoPan(position, containerBounds, store.autoPanSpeed);
@@ -1556,14 +2001,18 @@ const Marker = (props) => {
1556
2001
  event.target === container && store.connection.inProgress && (connectionEndedOnPane = !0);
1557
2002
  return;
1558
2003
  }
1559
- event.button === 0 && (event.target?.releasePointerCapture?.(event.pointerId), !selectionInProgress && event.target === container && onClick(event), batch(() => {
1560
- actions.setSelectionRect(void 0), selectionInProgress && actions.setSelectionRectMode(selectedNodeIds.size > 0 ? "nodes" : void 0);
1561
- }), selectionInProgress && props.onSelectionEnd?.(event), cleanupAutoPan());
2004
+ event.button === 0 && (event.target?.releasePointerCapture?.(event.pointerId), !selectionInProgress && event.target === container && onClick(event), actions.setSelectionRect(void 0), selectionInProgress && actions.setSelectionRectMode(selectedNodeIds.size > 0 ? "nodes" : void 0), flush(), selectionInProgress && props.onSelectionEnd?.(event), cleanupAutoPan());
1562
2005
  }, onPointerCancel = (event) => {
1563
2006
  event.target?.releasePointerCapture?.(event.pointerId), cleanupAutoPan();
1564
2007
  }, onClickCapture = (event) => {
1565
2008
  selectionInProgress &&= (event.stopPropagation(), !1);
1566
- }, onContextMenu = (event) => {
2009
+ };
2010
+ createEventListener(containerRef, "pointerdown", (e) => {
2011
+ isSelectionEnabled() && onPointerDownCapture(e);
2012
+ }, { capture: !0 }), createEventListener(containerRef, "click", (e) => {
2013
+ isSelectionEnabled() && onClickCapture(e);
2014
+ }, { capture: !0 });
2015
+ let onContextMenu = (event) => {
1567
2016
  if (event.target !== container) return;
1568
2017
  let result = _panOnDrag();
1569
2018
  if (Array.isArray(result) && result.includes(2)) {
@@ -1573,40 +2022,28 @@ const Marker = (props) => {
1573
2022
  props.onPaneContextMenu?.({ event });
1574
2023
  };
1575
2024
  return <div ref={(el) => {
1576
- container = el, el.addEventListener("click", (e) => {
1577
- isSelectionEnabled() && onClickCapture(e);
1578
- }, { capture: !0 });
1579
- }} class={clsx$1("solid-flow__container solid-flow__pane", {
1580
- selection: isSelecting(),
2025
+ container = el, setContainerRef(el);
2026
+ }} class={["solid-flow__container solid-flow__pane", {
2027
+ selection: !!isSelecting(),
1581
2028
  dragging: store.dragging,
1582
2029
  draggable: props.panOnDrag === !0 || Array.isArray(props.panOnDrag) && props.panOnDrag.includes(0)
1583
- })} onClick={(e) => isSelectionEnabled() ? void 0 : onClick(e)} on:pointerdown={{
1584
- capture: !0,
1585
- handleEvent: (e) => {
1586
- isSelectionEnabled() && onPointerDownCapture(e);
1587
- }
1588
- }} onPointerMove={(e) => isSelectionEnabled() ? onPointerMove(e) : void 0} onPointerUp={onPointerUp} onPointerCancel={(e) => isSelectionEnabled() ? onPointerCancel(e) : void 0} onContextMenu={onContextMenu}>
2030
+ }]} onClick={(e) => isSelectionEnabled() ? void 0 : onClick(e)} onPointerMove={(e) => isSelectionEnabled() ? onPointerMove(e) : void 0} onPointerUp={onPointerUp} onPointerCancel={(e) => isSelectionEnabled() ? onPointerCancel(e) : void 0} onContextMenu={onContextMenu}>
1589
2031
  {props.children}
1590
2032
  </div>;
1591
2033
  }, Panel = (props) => {
1592
- let { store } = useInternalSolidFlow(), _props = mergeProps({
2034
+ let { store } = useInternalSolidFlow(), _props = propDefaults(props, {
1593
2035
  position: "top-right",
1594
2036
  style: {}
1595
- }, props), [local, rest] = splitProps(_props, [
1596
- "class",
1597
- "position",
1598
- "style",
1599
- "children"
1600
- ]);
1601
- return <div class={clsx$1([
2037
+ }), rest = omit(_props, "class", "position", "style", "children");
2038
+ return <div class={[
1602
2039
  "solid-flow__panel",
1603
- ...local.position.split("-"),
1604
- local.class
1605
- ])} style={{
2040
+ ..._props.position.split("-"),
2041
+ _props.class
2042
+ ]} style={{
1606
2043
  "pointer-events": store.selectionRectMode ? "none" : void 0,
1607
- ...local.style
2044
+ ..._props.style
1608
2045
  }} {...rest}>
1609
- {local.children}
2046
+ {_props.children}
1610
2047
  </div>;
1611
2048
  }, Viewport = (props) => {
1612
2049
  let { store } = useInternalSolidFlow();
@@ -1621,7 +2058,7 @@ const Marker = (props) => {
1621
2058
  </Portal>}
1622
2059
  </Show>;
1623
2060
  }, Zoom = (props) => {
1624
- let ref, { store, actions } = useInternalSolidFlow(), viewPort = () => props.initialViewport || {
2061
+ let [ref, setRef] = createSignal(), { store, actions } = useInternalSolidFlow(), viewPort = () => props.initialViewport || {
1625
2062
  x: 0,
1626
2063
  y: 0,
1627
2064
  zoom: 1
@@ -1633,9 +2070,10 @@ const Marker = (props) => {
1633
2070
  zoom
1634
2071
  });
1635
2072
  };
1636
- return onMount(() => {
1637
- let panZoomInstance = XYPanZoom({
1638
- domNode: ref,
2073
+ return createEffect(() => ref(), (el) => {
2074
+ if (!el) return;
2075
+ let panZoomInstance = untrack(() => XYPanZoom({
2076
+ domNode: el,
1639
2077
  minZoom: store.minZoom,
1640
2078
  maxZoom: store.maxZoom,
1641
2079
  translateExtent: store.translateExtent,
@@ -1644,43 +2082,46 @@ const Marker = (props) => {
1644
2082
  onPanZoomStart: props.onMoveStart,
1645
2083
  onPanZoom: props.onMove,
1646
2084
  onPanZoomEnd: props.onMoveEnd
1647
- }), vp = panZoomInstance.getViewport();
1648
- (viewPort().x !== vp.x || viewPort().y !== vp.y || viewPort().zoom !== vp.zoom) && onTransformChange([
2085
+ })), vp = panZoomInstance.getViewport(), initial = untrack(() => viewPort());
2086
+ (initial.x !== vp.x || initial.y !== vp.y || initial.zoom !== vp.zoom) && onTransformChange([
1649
2087
  vp.x,
1650
2088
  vp.y,
1651
2089
  vp.zoom
1652
- ]), batch(() => {
1653
- actions.setViewport(vp), actions.setPanZoom(panZoomInstance);
1654
- }), props.onViewportInitialized?.(), createEffect(() => {
1655
- panZoomInstance.update({
1656
- lib: store.lib,
1657
- panActivationKeyPressed: store.panActivationKeyPressed,
1658
- zoomActivationKeyPressed: store.zoomActivationKeyPressed,
1659
- noPanClassName: store.noPanClass,
1660
- noWheelClassName: store.noWheelClass,
1661
- userSelectionActive: !!store.selectionRect,
1662
- panOnScrollSpeed: props.panOnScrollSpeed,
1663
- panOnDrag: panOnDrag(),
1664
- panOnScroll: panOnScroll(),
1665
- zoomOnScroll: props.zoomOnScroll,
1666
- zoomOnDoubleClick: props.zoomOnDoubleClick,
1667
- zoomOnPinch: props.zoomOnPinch,
1668
- panOnScrollMode: props.panOnScrollMode,
1669
- preventScrolling: typeof props.preventScrolling != "boolean" || props.preventScrolling,
1670
- paneClickDistance: props.paneClickDistance,
1671
- selectionOnDrag: props.selectionOnDrag,
1672
- connectionInProgress: store.connection.inProgress,
1673
- onTransformChange
1674
- });
2090
+ ]), actions.setViewport(vp), actions.setPanZoom(panZoomInstance), props.onViewportInitialized?.();
2091
+ }), createEffect(() => ({
2092
+ panZoom: store.panZoom,
2093
+ options: {
2094
+ lib: store.lib,
2095
+ panActivationKeyPressed: store.panActivationKeyPressed,
2096
+ zoomActivationKeyPressed: store.zoomActivationKeyPressed,
2097
+ noPanClassName: store.noPanClass,
2098
+ noWheelClassName: store.noWheelClass,
2099
+ userSelectionActive: !!store.selectionRect,
2100
+ panOnScrollSpeed: props.panOnScrollSpeed,
2101
+ panOnDrag: panOnDrag(),
2102
+ panOnScroll: panOnScroll(),
2103
+ zoomOnScroll: props.zoomOnScroll,
2104
+ zoomOnDoubleClick: props.zoomOnDoubleClick,
2105
+ zoomOnPinch: props.zoomOnPinch,
2106
+ panOnScrollMode: props.panOnScrollMode,
2107
+ preventScrolling: typeof props.preventScrolling != "boolean" || props.preventScrolling,
2108
+ paneClickDistance: props.paneClickDistance,
2109
+ selectionOnDrag: props.selectionOnDrag,
2110
+ connectionInProgress: store.connection.inProgress
2111
+ }
2112
+ }), ({ panZoom, options }) => {
2113
+ panZoom?.update({
2114
+ ...options,
2115
+ onTransformChange
1675
2116
  });
1676
- }), <div ref={ref} class="solid-flow__container solid-flow__zoom">
2117
+ }), <div ref={setRef} class="solid-flow__container solid-flow__zoom">
1677
2118
  {props.children}
1678
2119
  </div>;
1679
2120
  }, ConnectionLine = (props) => {
1680
2121
  let { store } = useInternalSolidFlow(), connectionStatus = () => getConnectionStatus(store.connection.isValid);
1681
2122
  return <Show when={store.connection.inProgress}>
1682
2123
  <svg class="solid-flow__container solid-flow__connectionline" width={store.width} height={store.height} style={props.containerStyle}>
1683
- <g class={clsx$1(["solid-flow__connection", connectionStatus()])}>
2124
+ <g class={["solid-flow__connection", connectionStatus()]}>
1684
2125
  <Show when={props.component} fallback={<InternalConnectionLine style={props.style} />}>
1685
2126
  {(CustomComponent) => {
1686
2127
  let UserConnectionLine = CustomComponent();
@@ -1722,24 +2163,32 @@ const Marker = (props) => {
1722
2163
  };
1723
2164
  return <path class="solid-flow__connection-path" fill="none" style={props.style} d={path()} />;
1724
2165
  }, DotPattern = (props) => {
1725
- let _props = mergeProps({ radius: 5 }, props);
1726
- return <circle class={clsx$1("solid-flow__background-pattern", "dots", _props.class)} cx={_props.radius} cy={_props.radius} r={_props.radius} />;
2166
+ let _props = propDefaults(props, { radius: 5 });
2167
+ return <circle class={[
2168
+ "solid-flow__background-pattern",
2169
+ "dots",
2170
+ _props.class
2171
+ ]} cx={_props.radius} cy={_props.radius} r={_props.radius} />;
1727
2172
  }, LinePattern = (props) => {
1728
- let _props = mergeProps({ lineWidth: 1 }, props);
1729
- return <path stroke-width={_props.lineWidth} d={`M${_props.dimensions[0] / 2} 0 V${_props.dimensions[1]} M0 ${_props.dimensions[1] / 2} H${_props.dimensions[0]}`} class={clsx("solid-flow__background-pattern", _props.variant, _props.class)} />;
2173
+ let _props = propDefaults(props, { lineWidth: 1 });
2174
+ return <path stroke-width={_props.lineWidth} d={`M${_props.dimensions[0] / 2} 0 V${_props.dimensions[1]} M0 ${_props.dimensions[1] / 2} H${_props.dimensions[0]}`} class={[
2175
+ "solid-flow__background-pattern",
2176
+ _props.variant,
2177
+ _props.class
2178
+ ]} />;
1730
2179
  }, DEFAULT_SIZE = {
1731
2180
  dots: 1,
1732
2181
  lines: 1,
1733
2182
  cross: 6
1734
2183
  }, Background = (props) => {
1735
- let _props = mergeProps({
2184
+ let _props = propDefaults(props, {
1736
2185
  variant: "dots",
1737
2186
  gap: 20,
1738
2187
  size: 1,
1739
2188
  lineWidth: 1,
1740
2189
  style: {}
1741
- }, props), { store } = useInternalSolidFlow(), isDots = () => _props.variant === "dots", isCross = () => _props.variant === "cross", patternId = () => `background-pattern-${store.id}-${_props.id ?? ""}`, scaledSize = () => (_props.size ?? DEFAULT_SIZE[_props.variant]) * store.viewport.zoom, gapXY = () => Array.isArray(_props.gap) ? _props.gap : [_props.gap, _props.gap], scaledGap = () => [gapXY()[0] * store.viewport.zoom || 1, gapXY()[1] * store.viewport.zoom || 1], patternDimensions = () => isCross() ? [scaledSize(), scaledSize()] : scaledGap(), patternOffset = () => isDots() ? [scaledSize() / 2, scaledSize() / 2] : [patternDimensions()[0] / 2, patternDimensions()[1] / 2];
1742
- return <svg data-testid="solid-flow__background" class={clsx$1("solid-flow__container solid-flow__background", _props.class)} style={{
2190
+ }), { store } = useInternalSolidFlow(), isDots = () => _props.variant === "dots", isCross = () => _props.variant === "cross", patternId = () => `background-pattern-${store.id}-${_props.id ?? ""}`, scaledSize = () => (_props.size ?? DEFAULT_SIZE[_props.variant]) * store.viewport.zoom, gapXY = () => Array.isArray(_props.gap) ? _props.gap : [_props.gap, _props.gap], scaledGap = () => [gapXY()[0] * store.viewport.zoom || 1, gapXY()[1] * store.viewport.zoom || 1], patternDimensions = () => isCross() ? [scaledSize(), scaledSize()] : scaledGap(), patternOffset = () => isDots() ? [scaledSize() / 2, scaledSize() / 2] : [patternDimensions()[0] / 2, patternDimensions()[1] / 2];
2191
+ return <svg data-testid="solid-flow__background" class={["solid-flow__container solid-flow__background", _props.class]} style={{
1743
2192
  "--xy-background-color-props": _props.bgColor,
1744
2193
  "--xy-background-pattern-color-props": _props.patternColor,
1745
2194
  ..._props.style
@@ -1752,24 +2201,15 @@ const Marker = (props) => {
1752
2201
  <rect x="0" y="0" width="100%" height="100%" fill={`url(#${patternId()})`} />
1753
2202
  </svg>;
1754
2203
  }, ControlButton = (props) => {
1755
- let [local, rest] = splitProps(props, [
1756
- "class",
1757
- "bgColor",
1758
- "bgColorHover",
1759
- "color",
1760
- "colorHover",
1761
- "borderColor",
1762
- "onClick",
1763
- "children"
1764
- ]), style = () => Object.entries({
1765
- "--xy-controls-button-background-color-props": local.bgColor,
1766
- "--xy-controls-button-background-color-hover-props": local.bgColorHover,
1767
- "--xy-controls-button-color-props": local.color,
1768
- "--xy-controls-button-color-hover-props": local.colorHover,
1769
- "--xy-controls-button-border-color-props": local.borderColor
2204
+ let rest = omit(props, "class", "bgColor", "bgColorHover", "color", "colorHover", "borderColor", "onClick", "children"), style = () => Object.entries({
2205
+ "--xy-controls-button-background-color-props": props.bgColor,
2206
+ "--xy-controls-button-background-color-hover-props": props.bgColorHover,
2207
+ "--xy-controls-button-color-props": props.color,
2208
+ "--xy-controls-button-color-hover-props": props.colorHover,
2209
+ "--xy-controls-button-border-color-props": props.borderColor
1770
2210
  }).filter(([_, value]) => value !== void 0).reduce((acc, [key, value]) => (acc[key] = value, acc), {});
1771
- return <button type="button" class={clsx$1("solid-flow__controls-button", local.class)} onClick={(e) => local.onClick?.(e)} style={style()} {...rest}>
1772
- {local.children}
2211
+ return <button type="button" class={["solid-flow__controls-button", props.class]} onClick={(e) => props.onClick?.(e)} style={style()} {...rest}>
2212
+ {props.children}
1773
2213
  </button>;
1774
2214
  }, Fit = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30">
1775
2215
  <path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z" />
@@ -1782,54 +2222,35 @@ const Marker = (props) => {
1782
2222
  </svg>, Unlock = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
1783
2223
  <path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z" />
1784
2224
  </svg>, Controls = (props) => {
1785
- let { store, actions } = useInternalSolidFlow(), _props = mergeProps({
2225
+ let { store, actions } = useInternalSolidFlow(), _props = propDefaults(props, {
1786
2226
  position: "bottom-left",
1787
2227
  showZoom: !0,
1788
2228
  showFitView: !0,
1789
2229
  showLock: !0,
1790
2230
  orientation: "vertical"
1791
- }, props), [local, rest] = splitProps(_props, [
1792
- "class",
1793
- "position",
1794
- "showZoom",
1795
- "showFitView",
1796
- "showLock",
1797
- "orientation",
1798
- "fitViewOptions",
1799
- "beforeControls",
1800
- "afterControls",
1801
- "buttonBgColor",
1802
- "buttonBgColorHover",
1803
- "buttonColor",
1804
- "buttonColorHover",
1805
- "buttonBorderColor",
1806
- "style",
1807
- "children"
1808
- ]), getMinZoomReached = () => store.viewport.zoom <= store.minZoom, getMaxZoomReached = () => store.viewport.zoom >= store.maxZoom, getIsInteractive = () => store.nodesDraggable || store.nodesConnectable || store.elementsSelectable, onZoomInHandler = () => {
2231
+ }), rest = omit(_props, "class", "position", "showZoom", "showFitView", "showLock", "orientation", "fitViewOptions", "beforeControls", "afterControls", "buttonBgColor", "buttonBgColorHover", "buttonColor", "buttonColorHover", "buttonBorderColor", "style", "children"), getMinZoomReached = () => store.viewport.zoom <= store.minZoom, getMaxZoomReached = () => store.viewport.zoom >= store.maxZoom, getIsInteractive = () => store.nodesDraggable || store.nodesConnectable || store.elementsSelectable, onZoomInHandler = () => {
1809
2232
  actions.zoomIn();
1810
2233
  }, onZoomOutHandler = () => {
1811
2234
  actions.zoomOut();
1812
2235
  }, onFitViewHandler = () => {
1813
- actions.fitView(local.fitViewOptions);
2236
+ actions.fitView(_props.fitViewOptions);
1814
2237
  }, onToggleInteractivity = () => {
1815
2238
  let newValue = !getIsInteractive();
1816
- batch(() => {
1817
- actions.setNodesDraggable(newValue), actions.setNodesConnectable(newValue), actions.setElementsSelectable(newValue);
1818
- });
2239
+ actions.setNodesDraggable(newValue), actions.setNodesConnectable(newValue), actions.setElementsSelectable(newValue);
1819
2240
  }, buttonProps = () => ({
1820
- bgColor: local.buttonBgColor,
1821
- bgColorHover: local.buttonBgColorHover,
1822
- color: local.buttonColor,
1823
- colorHover: local.buttonColorHover,
1824
- borderColor: local.buttonBorderColor
2241
+ bgColor: _props.buttonBgColor,
2242
+ bgColorHover: _props.buttonBgColorHover,
2243
+ color: _props.buttonColor,
2244
+ colorHover: _props.buttonColorHover,
2245
+ borderColor: _props.buttonBorderColor
1825
2246
  });
1826
- return <Panel class={clsx$1([
2247
+ return <Panel class={[
1827
2248
  "solid-flow__controls",
1828
- local.orientation,
1829
- local.class
1830
- ])} position={local.position} data-testid="solid-flow__controls" aria-label={store.ariaLabelConfig["controls.ariaLabel"]} style={local.style} {...rest}>
1831
- {local.beforeControls}
1832
- <Show when={local.showZoom}>
2249
+ _props.orientation,
2250
+ _props.class
2251
+ ]} position={_props.position} data-testid="solid-flow__controls" aria-label={store.ariaLabelConfig["controls.ariaLabel"]} style={_props.style} {...rest}>
2252
+ {_props.beforeControls}
2253
+ <Show when={_props.showZoom}>
1833
2254
  <>
1834
2255
  <ControlButton onClick={onZoomInHandler} class="solid-flow__controls-zoomin" title={store.ariaLabelConfig["controls.zoomIn.ariaLabel"]} aria-label={store.ariaLabelConfig["controls.zoomIn.ariaLabel"]} disabled={getMaxZoomReached()} {...buttonProps()}>
1835
2256
  <Plus />
@@ -1839,56 +2260,51 @@ const Marker = (props) => {
1839
2260
  </ControlButton>
1840
2261
  </>
1841
2262
  </Show>
1842
- <Show when={local.showFitView}>
2263
+ <Show when={_props.showFitView}>
1843
2264
  <ControlButton class="solid-flow__controls-fitview" onClick={onFitViewHandler} title={store.ariaLabelConfig["controls.fitView.ariaLabel"]} aria-label={store.ariaLabelConfig["controls.fitView.ariaLabel"]} {...buttonProps()}>
1844
2265
  <Fit />
1845
2266
  </ControlButton>
1846
2267
  </Show>
1847
- <Show when={local.showLock}>
2268
+ <Show when={_props.showLock}>
1848
2269
  <ControlButton class="solid-flow__controls-interactive" onClick={onToggleInteractivity} title={store.ariaLabelConfig["controls.interactive.ariaLabel"]} aria-label={store.ariaLabelConfig["controls.interactive.ariaLabel"]} {...buttonProps()}>
1849
2270
  <Show when={getIsInteractive()} fallback={<Lock />}>
1850
2271
  <Unlock />
1851
2272
  </Show>
1852
2273
  </ControlButton>
1853
2274
  </Show>
1854
- {local.children}
1855
- {local.afterControls}
2275
+ {_props.children}
2276
+ {_props.afterControls}
1856
2277
  </Panel>;
1857
2278
  }, EdgeToolbar = (props) => {
1858
- let [local, rest] = splitProps(props, [
1859
- "x",
1860
- "y",
1861
- "alignX",
1862
- "alignY",
1863
- "isVisible",
1864
- "selectEdgeOnClick",
1865
- "class",
1866
- "children"
1867
- ]), { store, edgeLookup } = useInternalSolidFlow(), edgeId = useEdgeId(), isActive = () => typeof local.isVisible == "boolean" ? local.isVisible : !!edgeLookup.get(edgeId())?.selected, transform = () => getEdgeToolbarTransform(local.x, local.y, store.viewport.zoom, local.alignX ?? "center", local.alignY ?? "center");
2279
+ let rest = omit(props, "x", "y", "alignX", "alignY", "isVisible", "selectEdgeOnClick", "class", "children"), { store, edgeLookup } = useInternalSolidFlow(), edgeId = useEdgeId(), isActive = () => typeof props.isVisible == "boolean" ? props.isVisible : !!edgeLookup[edgeId()]?.selected, transform = () => getEdgeToolbarTransform(props.x, props.y, store.viewport.zoom, props.alignX ?? "center", props.alignY ?? "center");
1868
2280
  return <Show when={isActive()}>
1869
- <EdgeLabel selectEdgeOnClick={local.selectEdgeOnClick} transparent>
1870
- <div class={clsx$1("solid-flow__edge-toolbar", local.class)} style={{
2281
+ <EdgeLabel selectEdgeOnClick={props.selectEdgeOnClick} transparent>
2282
+ <div class={["solid-flow__edge-toolbar", props.class]} style={{
1871
2283
  position: "absolute",
1872
2284
  transform: transform(),
1873
2285
  "transform-origin": "0 0"
1874
2286
  }} data-id={edgeId()} {...rest}>
1875
- {local.children}
2287
+ {props.children}
1876
2288
  </div>
1877
2289
  </EdgeLabel>
1878
2290
  </Show>;
1879
2291
  }, MiniMapNode = (props) => {
1880
- let _props = mergeProps({
2292
+ let _props = propDefaults(props, {
1881
2293
  borderRadius: 5,
1882
2294
  width: 0,
1883
2295
  height: 0
1884
- }, props), style = () => Object.entries({
2296
+ }), style = () => Object.entries({
1885
2297
  fill: _props.color,
1886
2298
  stroke: _props.strokeColor,
1887
2299
  "stroke-width": _props.strokeWidth
1888
2300
  }).filter(([_, value]) => value !== void 0).reduce((acc, [key, value]) => (acc[key] = value, acc), {});
1889
- return <rect class={clsx$1("solid-flow__minimap-node", { selected: _props.selected }, _props.class)} x={_props.x} y={_props.y} rx={_props.borderRadius} ry={_props.borderRadius} width={_props.width} height={_props.height} shape-rendering={_props.shapeRendering} style={style()} />;
2301
+ return <rect class={[
2302
+ "solid-flow__minimap-node",
2303
+ { selected: !!_props.selected },
2304
+ _props.class
2305
+ ]} x={_props.x} y={_props.y} rx={_props.borderRadius} ry={_props.borderRadius} width={_props.width} height={_props.height} shape-rendering={_props.shapeRendering} style={style()} />;
1890
2306
  }, getAttrFunction = (func) => func instanceof Function ? func : () => func, MiniMap = (props) => {
1891
- let { store, nodeLookup } = useInternalSolidFlow(), _props = mergeProps({
2307
+ let { store, nodeLookup } = useInternalSolidFlow(), _props = propDefaults(props, {
1892
2308
  position: "bottom-right",
1893
2309
  nodeClass: "",
1894
2310
  nodeStrokeColor: "transparent",
@@ -1899,26 +2315,7 @@ const Marker = (props) => {
1899
2315
  nodeBorderRadius: 5,
1900
2316
  nodeStrokeWidth: 2,
1901
2317
  style: {}
1902
- }, props), [local, paneProps] = splitProps(_props, [
1903
- "class",
1904
- "style",
1905
- "position",
1906
- "nodeClass",
1907
- "nodeStrokeColor",
1908
- "nodeColor",
1909
- "pannable",
1910
- "zoomable",
1911
- "inversePan",
1912
- "zoomStep",
1913
- "bgColor",
1914
- "width",
1915
- "height",
1916
- "maskColor",
1917
- "maskStrokeColor",
1918
- "maskStrokeWidth",
1919
- "nodeBorderRadius",
1920
- "nodeStrokeWidth"
1921
- ]), nodeColorFunc = () => local.nodeColor === void 0 ? void 0 : getAttrFunction(local.nodeColor), nodeStrokeColorFunc = () => getAttrFunction(local.nodeStrokeColor), nodeClassFunc = () => getAttrFunction(local.nodeClass), shapeRendering = () => typeof window > "u" || window.chrome ? "crispEdges" : "geometricPrecision", labelledBy = createMemo(() => `solid-flow__minimap-desc-${store.id}`), getViewBB = () => ({
2318
+ }), paneProps = omit(_props, "class", "style", "position", "nodeClass", "nodeStrokeColor", "nodeColor", "pannable", "zoomable", "inversePan", "zoomStep", "bgColor", "width", "height", "maskColor", "maskStrokeColor", "maskStrokeWidth", "nodeBorderRadius", "nodeStrokeWidth"), nodeColorFunc = () => _props.nodeColor === void 0 ? void 0 : getAttrFunction(_props.nodeColor), nodeStrokeColorFunc = () => getAttrFunction(_props.nodeStrokeColor), nodeClassFunc = () => getAttrFunction(_props.nodeClass), shapeRendering = () => typeof window > "u" || window.chrome ? "crispEdges" : "geometricPrecision", labelledBy = createMemo(() => `solid-flow__minimap-desc-${store.id}`), getViewBB = () => ({
1922
2319
  x: -store.viewport.x / store.viewport.zoom,
1923
2320
  y: -store.viewport.y / store.viewport.zoom,
1924
2321
  width: store.width / store.viewport.zoom,
@@ -1926,57 +2323,64 @@ const Marker = (props) => {
1926
2323
  }), getBoundingRect = () => {
1927
2324
  let viewBB = getViewBB();
1928
2325
  return nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(nodeLookup), viewBB) : viewBB;
1929
- }, getScaledWidth = () => getBoundingRect().width / local.width, getScaledHeight = () => getBoundingRect().height / local.height, getViewScale = () => Math.max(getScaledWidth(), getScaledHeight()), getViewWidth = () => getViewScale() * local.width, getViewHeight = () => getViewScale() * local.height, getOffset = () => 5 * getViewScale(), getX = () => {
2326
+ }, getScaledWidth = () => getBoundingRect().width / _props.width, getScaledHeight = () => getBoundingRect().height / _props.height, getViewScale = () => Math.max(getScaledWidth(), getScaledHeight()), getViewWidth = () => getViewScale() * _props.width, getViewHeight = () => getViewScale() * _props.height, getOffset = () => 5 * getViewScale(), getX = () => {
1930
2327
  let boundingRect = getBoundingRect();
1931
2328
  return boundingRect.x - (getViewWidth() - boundingRect.width) / 2 - getOffset();
1932
2329
  }, getY = () => {
1933
2330
  let boundingRect = getBoundingRect();
1934
2331
  return boundingRect.y - (getViewHeight() - boundingRect.height) / 2 - getOffset();
1935
- }, getViewboxWidth = () => getViewWidth() + getOffset() * 2, getViewboxHeight = () => getViewHeight() + getOffset() * 2, strokeWidth = () => local.maskStrokeWidth ? local.maskStrokeWidth * getViewScale() : void 0, prevNodeIds = [], nodeIds = () => {
2332
+ }, getViewboxWidth = () => getViewWidth() + getOffset() * 2, getViewboxHeight = () => getViewHeight() + getOffset() * 2, strokeWidth = () => _props.maskStrokeWidth ? _props.maskStrokeWidth * getViewScale() : void 0, prevNodeIds = [], nodeIds = () => {
1936
2333
  let currentNodeIds = store.nodes.map((node) => node.id), currentSet = new Set(currentNodeIds);
1937
2334
  return (prevNodeIds.length !== currentNodeIds.length || !prevNodeIds.every((id) => currentSet.has(id))) && (prevNodeIds = currentNodeIds), prevNodeIds;
1938
2335
  };
1939
- return <Panel position={local.position} data-testid="solid-flow__minimap" class={clsx$1(["solid-flow__minimap", local.class])} style={{
1940
- "--xy-minimap-background-color-props": local.bgColor,
1941
- ...local.style
2336
+ return <Panel position={_props.position} data-testid="solid-flow__minimap" class={["solid-flow__minimap", _props.class]} style={{
2337
+ "--xy-minimap-background-color-props": _props.bgColor,
2338
+ ..._props.style
1942
2339
  }} {...paneProps}>
1943
2340
  <Show when={store.panZoom}>
1944
2341
  {(panZoom) => {
1945
- let [ref, setRef] = createSignal();
1946
- return onMount(() => {
1947
- let minimap = XYMinimap({
1948
- domNode: ref(),
1949
- panZoom: panZoom(),
2342
+ let [ref, setRef] = createSignal(), [minimap, setMinimap] = createSignal();
2343
+ return createEffect(() => ({
2344
+ el: ref(),
2345
+ panZoom: panZoom()
2346
+ }), ({ el, panZoom }) => {
2347
+ if (!el) return;
2348
+ let instance = XYMinimap({
2349
+ domNode: el,
2350
+ panZoom,
1950
2351
  getTransform: () => store.transform,
1951
2352
  getViewScale
1952
2353
  });
1953
- createEffect(() => {
1954
- minimap.update({
1955
- translateExtent: store.translateExtent,
1956
- width: store.width,
1957
- height: store.height,
1958
- inversePan: local.inversePan,
1959
- zoomStep: local.zoomStep,
1960
- pannable: local.pannable,
1961
- zoomable: local.zoomable
1962
- });
1963
- }), onCleanup(() => {
1964
- minimap.destroy();
1965
- });
1966
- }), <svg ref={setRef} width={local.width} height={local.height} viewBox={`${getX()} ${getY()} ${getViewboxWidth()} ${getViewboxHeight()}`} class="solid-flow__minimap-svg" role="img" aria-labelledby={labelledBy()} style={{
1967
- "--xy-minimap-mask-background-color-props": local.maskColor,
1968
- "--xy-minimap-mask-stroke-color-props": local.maskStrokeColor,
2354
+ return setMinimap(instance), () => {
2355
+ instance.destroy();
2356
+ };
2357
+ }), createEffect(() => ({
2358
+ instance: minimap(),
2359
+ options: {
2360
+ translateExtent: store.translateExtent,
2361
+ width: store.width,
2362
+ height: store.height,
2363
+ inversePan: _props.inversePan,
2364
+ zoomStep: _props.zoomStep,
2365
+ pannable: _props.pannable,
2366
+ zoomable: _props.zoomable
2367
+ }
2368
+ }), ({ instance, options }) => {
2369
+ instance?.update(options);
2370
+ }), <svg ref={setRef} width={_props.width} height={_props.height} viewBox={`${getX()} ${getY()} ${getViewboxWidth()} ${getViewboxHeight()}`} class="solid-flow__minimap-svg" role="img" aria-labelledby={labelledBy()} style={{
2371
+ "--xy-minimap-mask-background-color-props": _props.maskColor,
2372
+ "--xy-minimap-mask-stroke-color-props": _props.maskStrokeColor,
1969
2373
  "--xy-minimap-mask-stroke-width-props": strokeWidth()
1970
2374
  }}>
1971
2375
  <title id={labelledBy()}>{store.ariaLabelConfig["minimap.ariaLabel"]}</title>
1972
- <Index each={nodeIds()}>
2376
+ <For keyed={!1} each={nodeIds()}>
1973
2377
  {(nodeId) => {
1974
2378
  let node = createMemo(() => nodeLookup.get(nodeId())), nodeVisible = () => !!(node() && nodeHasDimensions(node()) && !node().hidden);
1975
2379
  return <Show when={nodeVisible() && getNodeDimensions(node())}>
1976
- {(nodeDimensions) => <MiniMapNode x={node().internals.positionAbsolute.x} y={node().internals.positionAbsolute.y} borderRadius={local.nodeBorderRadius} strokeWidth={local.nodeStrokeWidth} shapeRendering={shapeRendering()} width={nodeDimensions().width} height={nodeDimensions().height} selected={node().selected} color={nodeColorFunc()?.call(null, node())} strokeColor={nodeStrokeColorFunc().call(null, node())} class={nodeClassFunc().call(null, node())} />}
2380
+ {(nodeDimensions) => <MiniMapNode x={node().internals.positionAbsolute.x} y={node().internals.positionAbsolute.y} borderRadius={_props.nodeBorderRadius} strokeWidth={_props.nodeStrokeWidth} shapeRendering={shapeRendering()} width={nodeDimensions().width} height={nodeDimensions().height} selected={node().selected} color={nodeColorFunc()?.call(null, node())} strokeColor={nodeStrokeColorFunc().call(null, node())} class={nodeClassFunc().call(null, node())} />}
1977
2381
  </Show>;
1978
2382
  }}
1979
- </Index>
2383
+ </For>
1980
2384
  <path class="solid-flow__minimap-mask" d={`M${getX() - getOffset()},${getY() - getOffset()}h${getViewboxWidth() + getOffset() * 2}v${getViewboxHeight() + getOffset() * 2}h${-getViewboxWidth() - getOffset() * 2}z
1981
2385
  M${getViewBB().x},${getViewBB().y}h${getViewBB().width}v${getViewBB().height}h${-getViewBB().width}z`} fill-rule="evenodd" pointer-events="none" />
1982
2386
  </svg>;
@@ -1984,7 +2388,7 @@ const Marker = (props) => {
1984
2388
  </Show>
1985
2389
  </Panel>;
1986
2390
  }, ResizeControl = (props) => {
1987
- let _props = mergeProps({
2391
+ let _props = propDefaults(props, {
1988
2392
  variant: "handle",
1989
2393
  minWidth: 10,
1990
2394
  minHeight: 10,
@@ -1993,28 +2397,11 @@ const Marker = (props) => {
1993
2397
  keepAspectRatio: !1,
1994
2398
  autoScale: !0,
1995
2399
  style: {}
1996
- }, props), [local, rest] = splitProps(_props, [
1997
- "nodeId",
1998
- "variant",
1999
- "position",
2000
- "minWidth",
2001
- "minHeight",
2002
- "maxWidth",
2003
- "maxHeight",
2004
- "keepAspectRatio",
2005
- "autoScale",
2006
- "onResizeStart",
2007
- "onResize",
2008
- "onResizeEnd",
2009
- "shouldResize",
2010
- "class",
2011
- "children",
2012
- "color",
2013
- "style"
2014
- ]), resizeControlRef, { store, nodeLookup, actions } = useInternalSolidFlow(), ctxNodeId = useNodeId(), nodeId = () => local.nodeId ?? ctxNodeId(), isLineVariant = () => local.variant === "line", controlPosition = () => local.position ?? (isLineVariant() ? "right" : "bottom-right"), positionClassNames = () => controlPosition().split("-");
2015
- return onMount(() => {
2016
- let resizer = XYResizer({
2017
- domNode: resizeControlRef,
2400
+ }), rest = omit(_props, "nodeId", "variant", "position", "minWidth", "minHeight", "maxWidth", "maxHeight", "keepAspectRatio", "autoScale", "onResizeStart", "onResize", "onResizeEnd", "shouldResize", "class", "children", "color", "style"), [resizeControlRef, setResizeControlRef] = createSignal(), { store, nodeLookup, actions } = useInternalSolidFlow(), ctxNodeId = useNodeId(), nodeId = () => _props.nodeId ?? ctxNodeId(), isLineVariant = () => _props.variant === "line", controlPosition = () => _props.position ?? (isLineVariant() ? "right" : "bottom-right"), positionClassNames = () => controlPosition().split("-"), [resizer, setResizer] = createSignal();
2401
+ return createEffect(() => resizeControlRef(), (el) => {
2402
+ if (!el) return;
2403
+ let instance = XYResizer({
2404
+ domNode: el,
2018
2405
  nodeId: nodeId(),
2019
2406
  getStoreItems: () => ({
2020
2407
  nodeLookup,
@@ -2034,63 +2421,63 @@ const Marker = (props) => {
2034
2421
  position
2035
2422
  });
2036
2423
  for (let childChange of childChanges) changes.set(childChange.id, { position: childChange.position });
2037
- actions.setNodes((node) => changes.has(node.id), produce((node) => {
2038
- let nodeChange = changes.get(node.id);
2039
- node.width = nodeChange.width, node.height = nodeChange.height, node.position = {
2040
- x: nodeChange.position?.x ?? node.position.x,
2041
- y: nodeChange.position?.y ?? node.position.y
2042
- };
2043
- }));
2424
+ actions.setNodes((nodes) => {
2425
+ for (let node of nodes) {
2426
+ let nodeChange = changes.get(node.id);
2427
+ nodeChange && (node.width = nodeChange.width, node.height = nodeChange.height, node.position = {
2428
+ x: nodeChange.position?.x ?? node.position.x,
2429
+ y: nodeChange.position?.y ?? node.position.y
2430
+ });
2431
+ }
2432
+ });
2044
2433
  }
2045
2434
  });
2046
- createEffect(() => {
2047
- resizer.update({
2048
- controlPosition: controlPosition(),
2049
- boundaries: {
2050
- minWidth: local.minWidth,
2051
- minHeight: local.minHeight,
2052
- maxWidth: local.maxWidth,
2053
- maxHeight: local.maxHeight
2054
- },
2055
- keepAspectRatio: !!local.keepAspectRatio,
2056
- onResizeStart: local.onResizeStart,
2057
- onResize: local.onResize,
2058
- onResizeEnd: local.onResizeEnd,
2059
- shouldResize: local.shouldResize
2060
- });
2061
- }), onCleanup(() => {
2062
- resizer.destroy();
2063
- });
2064
- }), <div ref={resizeControlRef} class={clsx$1([
2435
+ return setResizer(instance), () => {
2436
+ instance.destroy();
2437
+ };
2438
+ }), createEffect(() => ({
2439
+ instance: resizer(),
2440
+ options: {
2441
+ controlPosition: controlPosition(),
2442
+ boundaries: {
2443
+ minWidth: _props.minWidth,
2444
+ minHeight: _props.minHeight,
2445
+ maxWidth: _props.maxWidth,
2446
+ maxHeight: _props.maxHeight
2447
+ },
2448
+ keepAspectRatio: !!_props.keepAspectRatio,
2449
+ onResizeStart: _props.onResizeStart,
2450
+ onResize: _props.onResize,
2451
+ onResizeEnd: _props.onResizeEnd,
2452
+ shouldResize: _props.shouldResize
2453
+ }
2454
+ }), ({ instance, options }) => {
2455
+ instance?.update(options);
2456
+ }), <div ref={setResizeControlRef} class={[
2065
2457
  "solid-flow__resize-control",
2066
- local.variant,
2458
+ _props.variant,
2067
2459
  store.noDragClass,
2068
2460
  ...positionClassNames(),
2069
- local.class
2070
- ])} style={{
2071
- "border-color": isLineVariant() ? local.color : void 0,
2072
- "background-color": isLineVariant() ? void 0 : local.color,
2073
- scale: isLineVariant() || !local.autoScale ? void 0 : Math.max(1 / store.viewport.zoom, 1),
2074
- ...local.style
2461
+ _props.class
2462
+ ]} style={{
2463
+ "border-color": isLineVariant() ? _props.color : void 0,
2464
+ "background-color": isLineVariant() ? void 0 : _props.color,
2465
+ scale: isLineVariant() || !_props.autoScale ? void 0 : Math.max(1 / store.viewport.zoom, 1),
2466
+ ..._props.style
2075
2467
  }} {...rest}>
2076
- {local.children}
2468
+ {_props.children}
2077
2469
  </div>;
2078
2470
  }, NodeResizer = (props) => {
2079
- let _props = mergeProps({
2471
+ let _props = propDefaults(props, {
2080
2472
  autoScale: !0,
2081
2473
  visible: !0
2082
- }, props), [local, rest] = splitProps(props, [
2083
- "handleClass",
2084
- "handleStyle",
2085
- "lineClass",
2086
- "lineStyle"
2087
- ]);
2474
+ }), rest = omit(props, "handleClass", "handleStyle", "lineClass", "lineStyle");
2088
2475
  return <Show when={_props.visible}>
2089
2476
  <For each={XY_RESIZER_LINE_POSITIONS}>
2090
- {(position) => <ResizeControl variant="line" position={position} class={local.lineClass} style={local.lineStyle} {...rest} />}
2477
+ {(position) => <ResizeControl variant="line" position={position} class={props.lineClass} style={props.lineStyle} {...rest} />}
2091
2478
  </For>
2092
2479
  <For each={XY_RESIZER_HANDLE_POSITIONS}>
2093
- {(position) => <ResizeControl position={position} class={local.handleClass} style={local.handleStyle} {...rest} />}
2480
+ {(position) => <ResizeControl position={position} class={props.handleClass} style={props.handleStyle} {...rest} />}
2094
2481
  </For>
2095
2482
  </Show>;
2096
2483
  };
@@ -2106,8 +2493,8 @@ const Marker = (props) => {
2106
2493
  * @returns an accessor for the current color mode class
2107
2494
  */
2108
2495
  function useColorMode() {
2109
- let { store } = useInternalSolidFlow();
2110
- return () => store.colorMode;
2496
+ let { flow } = useInternalSolidFlow();
2497
+ return () => flow.colorMode;
2111
2498
  }
2112
2499
  //#endregion
2113
2500
  //#region src/hooks/useConnection.tsx
@@ -2118,8 +2505,8 @@ function useColorMode() {
2118
2505
  * @returns current connection as a readable store
2119
2506
  */
2120
2507
  function useConnection() {
2121
- let { store } = useInternalSolidFlow();
2122
- return () => store.connection;
2508
+ let { flow } = useInternalSolidFlow();
2509
+ return () => flow.connection;
2123
2510
  }
2124
2511
  //#endregion
2125
2512
  //#region src/hooks/useGraph.ts
@@ -2150,21 +2537,19 @@ function useEdges() {
2150
2537
  * @returns store with the viewport object
2151
2538
  */
2152
2539
  function useViewport() {
2153
- let { store } = useInternalSolidFlow();
2154
- return () => store.viewport;
2540
+ let { flow } = useInternalSolidFlow();
2541
+ return () => flow.viewport;
2155
2542
  }
2156
2543
  //#endregion
2157
2544
  //#region src/hooks/useHandleEdgeSelect.tsx
2158
2545
  function useHandleEdgeSelect() {
2159
2546
  let { store, edgeLookup, actions } = useInternalSolidFlow();
2160
2547
  return (id) => {
2161
- let edge = edgeLookup.get(id);
2162
- edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && batch(() => {
2163
- actions.setSelectionRect(void 0), actions.setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && actions.unselectNodesAndEdges({
2164
- nodes: [],
2165
- edges: [edge]
2166
- }) : actions.addSelectedEdges([id]);
2167
- });
2548
+ let edge = edgeLookup[id];
2549
+ edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (actions.setSelectionRect(void 0), actions.setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && actions.unselectNodesAndEdges({
2550
+ nodes: [],
2551
+ edges: [edge]
2552
+ }) : actions.addSelectedEdges([id]));
2168
2553
  };
2169
2554
  }
2170
2555
  //#endregion
@@ -2179,8 +2564,8 @@ function useHandleEdgeSelect() {
2179
2564
  * @returns an accessor that indicates whether the nodes are initialized
2180
2565
  */
2181
2566
  function useNodesInitialized() {
2182
- let { store } = useInternalSolidFlow();
2183
- return () => store.nodesInitialized;
2567
+ let { flow } = useInternalSolidFlow();
2568
+ return () => flow.nodesInitialized;
2184
2569
  }
2185
2570
  /**
2186
2571
  * Hook for seeing if the viewport is initialized.
@@ -2191,8 +2576,8 @@ function useNodesInitialized() {
2191
2576
  * @returns an accessor that indicates whether the viewport is initialized
2192
2577
  */
2193
2578
  function useViewportInitialized() {
2194
- let { store } = useInternalSolidFlow();
2195
- return () => store.viewportInitialized;
2579
+ let { flow } = useInternalSolidFlow();
2580
+ return () => flow.viewportInitialized;
2196
2581
  }
2197
2582
  //#endregion
2198
2583
  //#region src/hooks/useInternalNode.tsx
@@ -2204,8 +2589,8 @@ function useViewportInitialized() {
2204
2589
  * @returns an accessor with an internal node or undefined
2205
2590
  */
2206
2591
  function useInternalNode(id) {
2207
- let { nodeLookup } = useInternalSolidFlow();
2208
- return () => nodeLookup.get(id());
2592
+ let { flow } = useInternalSolidFlow();
2593
+ return () => flow.internalNodes[id()];
2209
2594
  }
2210
2595
  //#endregion
2211
2596
  //#region src/hooks/useNodeConnections.ts
@@ -2221,25 +2606,28 @@ function useInternalNode(id) {
2221
2606
  * @returns an array with connections
2222
2607
  */
2223
2608
  const useNodeConnections = (params) => {
2224
- let { connectionLookup } = useInternalSolidFlow(), ctxNodeId = () => {
2609
+ let { flow } = useInternalSolidFlow(), ctxNodeId = () => {
2225
2610
  let id = useContext(NodeIdContext);
2226
2611
  return id ? id() : "";
2227
- }, id = () => params().handleId, type = () => params().handleType, nodeId = () => params().id ?? ctxNodeId(), [connections, setConnections] = createSignal([]), lookupKey = () => `${nodeId()}${type() ? id() ? `-${type()}-${id()}` : `-${type()}` : ""}`;
2228
- return createEffect((prevConnections) => {
2229
- let nextConnections = connectionLookup.get(lookupKey());
2230
- return areConnectionMapsEqual(nextConnections, prevConnections) || (prevConnections = nextConnections, setConnections(Array.from(prevConnections?.values() ?? []))), nextConnections;
2612
+ }, id = () => params().handleId, type = () => params().handleType, nodeId = () => params().id ?? ctxNodeId(), [connections, setConnections] = createSignal([]), prevConnections;
2613
+ return createEffect(() => {
2614
+ let rec = flow.connections[connectionKey(nodeId(), type(), id())], map = /* @__PURE__ */ new Map();
2615
+ for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
2616
+ return { connections: map };
2617
+ }, ({ connections: nextConnections }) => {
2618
+ areConnectionMapsEqual(nextConnections, prevConnections) || (prevConnections = nextConnections, setConnections(Array.from(nextConnections.values())));
2231
2619
  }), connections;
2232
2620
  };
2233
2621
  //#endregion
2234
2622
  //#region src/hooks/useNodesData.ts
2235
2623
  function useNodesData(nodeIds) {
2236
- let { nodeLookup } = useInternalSolidFlow(), prevNodesData = [];
2624
+ let { flow } = useInternalSolidFlow(), prevNodesData = [];
2237
2625
  return createMemo(() => {
2238
2626
  let nodesData = [], idValues = nodeIds();
2239
2627
  if (!idValues) return;
2240
2628
  let ids = Array.isArray(idValues) ? idValues : [idValues];
2241
2629
  for (let nodeId of ids) {
2242
- let node = nodeLookup.get(nodeId)?.internals.userNode;
2630
+ let node = flow.internalNodes[nodeId]?.internals.userNode;
2243
2631
  node && nodesData.push({
2244
2632
  id: node.id,
2245
2633
  type: node.type,
@@ -2252,235 +2640,69 @@ function useNodesData(nodeIds) {
2252
2640
  //#endregion
2253
2641
  //#region src/hooks/useSolidFlow.tsx
2254
2642
  /**
2255
- * Hook for accessing the SvelteFlow instance.
2643
+ * Hook for accessing the flow instance: `{ flow, commands }` plus the
2644
+ * commands spread at the top level for upstream familiarity.
2645
+ *
2646
+ * `flow` and `commands` are stable identities, so destructuring them is safe:
2647
+ * `const { flow, commands } = useSolidFlow()`.
2256
2648
  *
2257
2649
  * @public
2258
- * @returns A set of helper functions
2650
+ * @returns the flow's read struct, write surface, and deprecated aliases
2259
2651
  */
2260
2652
  function useSolidFlow() {
2261
- let { store, actions, nodeLookup, edgeLookup, connectionLookup } = useInternalSolidFlow(), getNodeRect = (node) => {
2262
- let nodeToUse = isNode(node) ? node : nodeLookup.get(node.id), position = nodeToUse.parentId ? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, nodeLookup, store.nodeOrigin) : nodeToUse.position, nodeWithPosition = {
2263
- ...nodeToUse,
2264
- position,
2265
- width: nodeToUse.measured?.width ?? nodeToUse.width,
2266
- height: nodeToUse.measured?.height ?? nodeToUse.height
2267
- };
2268
- return nodeToRect(nodeWithPosition);
2269
- }, updateNode = (id, nodeUpdate, options = { replace: !1 }) => {
2270
- actions.setNodes((node) => node.id === id, (node) => {
2271
- let nextNode = typeof nodeUpdate == "function" ? nodeUpdate(node) : nodeUpdate;
2272
- return options?.replace && isNode(nextNode) ? nextNode : {
2273
- ...node,
2274
- ...nextNode
2275
- };
2276
- });
2277
- }, updateEdge = (id, edgeUpdate, options = { replace: !1 }) => {
2278
- actions.setEdges((edge) => edge.id === id, (edge) => {
2279
- let nextEdge = typeof edgeUpdate == "function" ? edgeUpdate(edge) : edgeUpdate;
2280
- return options.replace && isEdge(nextEdge) ? nextEdge : {
2281
- ...edge,
2282
- ...nextEdge
2283
- };
2284
- });
2285
- }, getInternalNode = (id) => nodeLookup.get(id);
2653
+ let { flow, commands, store, edgeLookup } = useInternalSolidFlow(), getInternalNode = (id) => flow.internalNodes[id];
2286
2654
  return {
2287
- zoomIn: actions.zoomIn,
2288
- zoomOut: actions.zoomOut,
2655
+ ...commands,
2656
+ flow,
2657
+ commands,
2289
2658
  getInternalNode,
2290
- setCenter: actions.setCenter,
2291
- fitView: actions.fitView,
2292
2659
  getNode: (id) => getInternalNode(id)?.internals.userNode,
2293
- getNodes: (ids) => ids ? getElements(nodeLookup, ids) : store.nodes,
2294
- getEdge: (id) => edgeLookup.get(id),
2295
- getEdges: (ids) => ids ? getElements(edgeLookup, ids) : store.edges,
2296
- addNodes: (payload) => {
2297
- let newNodes = Array.isArray(payload) ? payload : [payload];
2298
- actions.setNodes((nodes) => [...nodes, ...newNodes]);
2299
- },
2300
- addEdges: (payload) => {
2301
- let newEdges = Array.isArray(payload) ? payload : [payload];
2302
- actions.setEdges((edges) => [...edges, ...newEdges]);
2303
- },
2304
- getViewport: () => unwrap(store.viewport),
2305
- setViewport: async (nextViewport, options) => {
2306
- let currentViewport = store.viewport;
2307
- return store.panZoom ? (await store.panZoom.setViewport({
2308
- x: nextViewport.x ?? currentViewport.x,
2309
- y: nextViewport.y ?? currentViewport.y,
2310
- zoom: nextViewport.zoom ?? currentViewport.zoom
2311
- }, options), Promise.resolve(!0)) : Promise.resolve(!1);
2312
- },
2313
- getZoom: () => store.viewport.zoom,
2314
- setZoom: (zoomLevel, options) => {
2315
- let panZoom = store.panZoom;
2316
- return panZoom ? panZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(!1);
2317
- },
2318
- fitBounds: async (bounds, options) => {
2319
- if (!store.panZoom) return Promise.resolve(!1);
2320
- let viewport = getViewportForBounds$1(bounds, store.width, store.height, store.minZoom, store.maxZoom, options?.padding ?? .1);
2321
- return await store.panZoom.setViewport(viewport, {
2322
- duration: options?.duration,
2323
- ease: options?.ease,
2324
- interpolate: options?.interpolate
2325
- }), Promise.resolve(!0);
2326
- },
2327
- getIntersectingNodes: (nodeOrRect, partially = !0, nodesToIntersect) => {
2328
- let isRect = isRectObject(nodeOrRect), nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
2329
- return nodeRect ? (nodesToIntersect || store.nodes).filter((n) => {
2330
- let internalNode = nodeLookup.get(n.id);
2331
- if (!internalNode || !isRect && n.id === nodeOrRect.id) return !1;
2332
- let currNodeRect = nodeToRect(internalNode), overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
2333
- return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
2334
- }) : [];
2335
- },
2336
- isNodeIntersecting: (nodeOrRect, area, partially = !0) => {
2337
- let nodeRect = isRectObject(nodeOrRect) ? nodeOrRect : getNodeRect(nodeOrRect);
2338
- if (!nodeRect) return !1;
2339
- let overlappingArea = getOverlappingArea(nodeRect, area);
2340
- return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
2341
- },
2342
- deleteElements: async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => {
2343
- let { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
2344
- nodesToRemove,
2345
- edgesToRemove,
2346
- nodes: store.nodes,
2347
- edges: store.edges,
2348
- onBeforeDelete: store.onBeforeDelete
2349
- });
2350
- return batch(() => {
2351
- if (matchingEdges) {
2352
- let remmainingEdges = store.edges.filter((edge) => !matchingEdges.some(({ id }) => id === edge.id));
2353
- store.onEdgesDelete?.(matchingEdges), actions.setEdges(remmainingEdges);
2354
- }
2355
- if (matchingNodes) {
2356
- let remmainingNodes = store.nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id));
2357
- store.onNodesDelete?.(matchingNodes), actions.setNodes(remmainingNodes);
2358
- }
2359
- }), {
2360
- deletedNodes: matchingNodes,
2361
- deletedEdges: matchingEdges
2362
- };
2363
- },
2364
- screenToFlowPosition: (position, options = { snapToGrid: !0 }) => {
2365
- if (!store.domNode) return position;
2366
- let _snapGrid = options.snapToGrid ? store.snapGrid : !1, { x, y, zoom } = store.viewport, { x: domX, y: domY } = store.domNode.getBoundingClientRect(), correctedPosition = {
2367
- x: position.x - domX,
2368
- y: position.y - domY
2369
- };
2370
- return pointToRendererPoint(correctedPosition, [
2371
- x,
2372
- y,
2373
- zoom
2374
- ], _snapGrid !== null, _snapGrid || [1, 1]);
2375
- },
2376
- /**
2377
- *
2378
- * @param position
2379
- * @returns
2380
- */
2381
- flowToScreenPosition: (position) => {
2382
- if (!store.domNode) return position;
2383
- let { x, y, zoom } = store.viewport, { x: domX, y: domY } = store.domNode.getBoundingClientRect(), rendererPosition = rendererPointToPoint(position, [
2384
- x,
2385
- y,
2386
- zoom
2387
- ]);
2388
- return {
2389
- x: rendererPosition.x + domX,
2390
- y: rendererPosition.y + domY
2391
- };
2392
- },
2393
- toObject: () => structuredClone({
2394
- nodes: [...unwrap(store.nodes)],
2395
- edges: [...unwrap(store.edges)],
2396
- viewport: { ...unwrap(store.viewport) }
2397
- }),
2398
- updateNode,
2399
- updateNodeData: (id, dataUpdate, options) => {
2400
- let node = nodeLookup.get(id)?.internals.userNode;
2401
- if (!node) return;
2402
- let nextData = typeof dataUpdate == "function" ? dataUpdate(node) : dataUpdate;
2403
- updateNode(id, (node) => ({
2404
- ...node,
2405
- data: options?.replace ? nextData : {
2406
- ...node.data,
2407
- ...nextData
2408
- }
2409
- }));
2410
- },
2411
- updateEdge,
2412
- getNodesBounds: (nodes) => getNodesBounds$1(nodes, {
2413
- nodeLookup,
2414
- nodeOrigin: store.nodeOrigin
2415
- }),
2416
- getHandleConnections: ({ type, id, nodeId }) => Array.from(connectionLookup.get(`${nodeId}-${type}-${id ?? null}`)?.values() ?? [])
2660
+ getNodes: (ids) => ids ? ids.flatMap((id) => {
2661
+ let userNode = flow.internalNodes[id]?.internals.userNode;
2662
+ return userNode ? [userNode] : [];
2663
+ }) : [...flow.nodes],
2664
+ getEdge: (id) => edgeLookup[id],
2665
+ getEdges: (ids) => ids ? ids.flatMap((id) => edgeLookup[id] ? [edgeLookup[id]] : []) : [...flow.edges],
2666
+ getViewport: () => snapshot(store.viewport),
2667
+ getZoom: () => flow.viewport.zoom,
2668
+ getHandleConnections: ({ type, id, nodeId }) => Object.values(flow.connections[connectionKey(nodeId, type, id ?? null)] ?? {})
2417
2669
  };
2418
2670
  }
2419
- function getElements(lookup, ids) {
2420
- let result = [];
2421
- for (let id of ids) {
2422
- let item = lookup.get(id);
2423
- if (item) {
2424
- let element = "internals" in item ? item.internals?.userNode : item;
2425
- result.push(element);
2426
- }
2427
- }
2428
- return result;
2429
- }
2430
2671
  //#endregion
2431
2672
  //#region src/hooks/useUpdateNodeInternals.ts
2432
2673
  /**
2433
- * Hook for updating node internals.
2674
+ * Hook for updating node internals. Sugar for `commands.updateNodeInternals`.
2434
2675
  *
2435
2676
  * @public
2436
2677
  * @returns function for updating node internals
2437
2678
  */
2438
2679
  function useUpdateNodeInternals() {
2439
- let { store, actions } = useInternalSolidFlow();
2440
- return (id) => {
2441
- let updateIds = Array.isArray(id) ? id : [id], updates = [];
2442
- for (let updateId of updateIds) {
2443
- let nodeElement = store.domNode?.querySelector(`.solid-flow__node[data-id="${updateId}"]`);
2444
- nodeElement && updates.push([updateId, {
2445
- id: updateId,
2446
- nodeElement,
2447
- force: !0
2448
- }]);
2449
- }
2450
- actions.requestUpdateNodeInternals(updates);
2451
- };
2680
+ let { commands } = useInternalSolidFlow();
2681
+ return commands.updateNodeInternals;
2452
2682
  }
2453
2683
  //#endregion
2454
2684
  //#region src/components/graph/plugins/NodeToolbar.tsx
2455
2685
  const NodeToolbar = (props) => {
2456
- let { store } = useInternalSolidFlow(), { getNodes, getNodesBounds, getInternalNode } = useSolidFlow(), _props = mergeProps({
2686
+ let { store } = useInternalSolidFlow(), { getNodes, getNodesBounds, getInternalNode } = useSolidFlow(), _props = propDefaults(props, {
2457
2687
  offset: 10,
2458
2688
  position: "top",
2459
2689
  align: "center",
2460
2690
  style: {}
2461
- }, props), [local, divProps] = splitProps(_props, [
2462
- "nodeId",
2463
- "position",
2464
- "align",
2465
- "offset",
2466
- "isVisible",
2467
- "style",
2468
- "children"
2469
- ]), ctxNodeId = () => {
2691
+ }), divProps = omit(_props, "nodeId", "position", "align", "offset", "isVisible", "style", "children"), ctxNodeId = () => {
2470
2692
  let id = useContext(NodeIdContext);
2471
2693
  return id ? id() : "";
2472
- }, toolbarNodes = () => (Array.isArray(local.nodeId) ? local.nodeId : [local.nodeId ?? ctxNodeId()]).reduce((res, nodeId) => {
2694
+ }, toolbarNodes = () => (Array.isArray(_props.nodeId) ? _props.nodeId : [_props.nodeId ?? ctxNodeId()]).reduce((res, nodeId) => {
2473
2695
  let node = getInternalNode(nodeId);
2474
2696
  return node && res.push(node), res;
2475
2697
  }, []), transform = () => {
2476
2698
  let nodeRect = getNodesBounds(toolbarNodes());
2477
- return nodeRect ? getNodeToolbarTransform(nodeRect, store.viewport, local.position, local.offset, local.align) : "";
2699
+ return nodeRect ? getNodeToolbarTransform(nodeRect, store.viewport, _props.position, _props.offset, _props.align) : "";
2478
2700
  }, zIndex = () => {
2479
2701
  let nodes = toolbarNodes();
2480
2702
  return nodes.length === 0 ? 1 : Math.max(...nodes.map((node) => (node.internals.z || 5) + 1));
2481
2703
  }, selectedNodesCount = () => getNodes().filter((node) => node.selected).length, isActive = () => {
2482
2704
  let nodes = toolbarNodes();
2483
- return typeof local.isVisible == "boolean" ? local.isVisible : nodes.length === 1 && !!nodes[0].selected && selectedNodesCount() === 1;
2705
+ return typeof _props.isVisible == "boolean" ? _props.isVisible : nodes.length === 1 && !!nodes[0].selected && selectedNodesCount() === 1;
2484
2706
  }, showPortal = () => !!(store.domNode && isActive() && toolbarNodes().length > 0);
2485
2707
  return <Show when={showPortal()}>
2486
2708
  <Portal mount={store.domNode}>
@@ -2488,14 +2710,14 @@ const NodeToolbar = (props) => {
2488
2710
  position: "absolute",
2489
2711
  transform: transform(),
2490
2712
  "z-index": zIndex(),
2491
- ...local.style
2713
+ ..._props.style
2492
2714
  }} {...divProps}>
2493
- {local.children}
2715
+ {_props.children}
2494
2716
  </div>
2495
2717
  </Portal>
2496
2718
  </Show>;
2497
2719
  }, Selection = (props) => {
2498
- let _props = mergeProps({ isVisible: !0 }, props), styles = () => ({
2720
+ let _props = propDefaults(props, { isVisible: !0 }), styles = () => ({
2499
2721
  ..._props.width != null && { width: typeof _props.width == "string" ? _props.width : toPxString(_props.width) },
2500
2722
  ..._props.height != null && { height: typeof _props.height == "string" ? _props.height : toPxString(_props.height) },
2501
2723
  ..._props.x != null && _props.y != null && { transform: `translate(${props.x}px, ${props.y}px)` }
@@ -2505,8 +2727,11 @@ const NodeToolbar = (props) => {
2505
2727
  </Show>;
2506
2728
  }, NodeSelection = (props) => {
2507
2729
  let { store, nodeLookup, actions } = useInternalSolidFlow(), [ref, setRef] = createSignal(), bounds = () => store.selectionRectMode === "nodes" ? getInternalNodesBounds(nodeLookup, { filter: (node) => !!node.selected }) : null;
2508
- createEffect(() => {
2509
- store.disableKeyboardA11y || ref()?.focus({ preventScroll: !0 });
2730
+ createEffect(() => ({
2731
+ el: ref(),
2732
+ focusable: !store.disableKeyboardA11y
2733
+ }), ({ el, focusable }) => {
2734
+ focusable && el?.focus({ preventScroll: !0 });
2510
2735
  });
2511
2736
  let onContextMenu = (event) => {
2512
2737
  let selectedNodes = store.nodes.filter((n) => n.selected);
@@ -2550,11 +2775,11 @@ const NodeToolbar = (props) => {
2550
2775
  diff && (event.preventDefault(), actions.moveSelectedNodes(diff, event.shiftKey ? 4 : 1));
2551
2776
  };
2552
2777
  return <Show when={store.selectionRectMode === "nodes" && bounds() && isNumeric(bounds()?.x) && isNumeric(bounds()?.y)}>
2553
- <div ref={setRef} class={clsx$1("solid-flow__selection-wrapper", store.noPanClass)} style={{
2778
+ <div ref={setRef} class={["solid-flow__selection-wrapper", store.noPanClass]} style={{
2554
2779
  width: toPxString(bounds()?.width),
2555
2780
  height: toPxString(bounds()?.height),
2556
2781
  transform: `translate(${bounds()?.x}px, ${bounds()?.y}px)`
2557
- }} onClick={onClick} onContextMenu={onContextMenu} role={store.disableKeyboardA11y ? void 0 : "button"} tabIndex={store.disableKeyboardA11y ? void 0 : -1} onKeyDown={store.disableKeyboardA11y ? void 0 : onKeyDown}>
2782
+ }} onClick={onClick} onContextMenu={onContextMenu} role={store.disableKeyboardA11y ? void 0 : "button"} tabindex={store.disableKeyboardA11y ? void 0 : -1} onKeyDown={store.disableKeyboardA11y ? void 0 : onKeyDown}>
2558
2783
  <Selection width="100%" height="100%" x={0} y={0} />
2559
2784
  </div>
2560
2785
  </Show>;
@@ -2599,16 +2824,24 @@ function matchesKeyArray(event, keyDefs) {
2599
2824
  return keyDefs ? (Array.isArray(keyDefs) ? keyDefs : [keyDefs]).some((keyDef) => matchesKey(event, keyDef)) : !1;
2600
2825
  }
2601
2826
  const KeyHandler = (props) => {
2602
- let { store, actions } = useInternalSolidFlow(), { deleteElements } = useSolidFlow(), _props = mergeProps({
2603
- selectionKey: "Shift",
2604
- multiSelectionKey: isMacOs() ? "Meta" : "Control",
2605
- deleteKey: "Backspace",
2606
- panActivationKey: " ",
2607
- zoomActivationKey: isMacOs() ? "Meta" : "Control"
2608
- }, props), resetKeysAndSelection = () => {
2609
- batch(() => {
2610
- actions.setSelectionRect(void 0), actions.setSelectionKeyPressed(!1), actions.setMultiselectionKeyPressed(!1), actions.setDeleteKeyPressed(!1), actions.setPanActivationKeyPressed(!1), actions.setZoomActivationKeyPressed(!1);
2611
- });
2827
+ let { store, actions } = useInternalSolidFlow(), { deleteElements } = useSolidFlow(), _props = {
2828
+ get selectionKey() {
2829
+ return props.selectionKey ?? "Shift";
2830
+ },
2831
+ get multiSelectionKey() {
2832
+ return props.multiSelectionKey ?? (isMacOs() ? "Meta" : "Control");
2833
+ },
2834
+ get deleteKey() {
2835
+ return props.deleteKey ?? "Backspace";
2836
+ },
2837
+ get panActivationKey() {
2838
+ return props.panActivationKey ?? " ";
2839
+ },
2840
+ get zoomActivationKey() {
2841
+ return props.zoomActivationKey ?? (isMacOs() ? "Meta" : "Control");
2842
+ }
2843
+ }, resetKeysAndSelection = () => {
2844
+ actions.setSelectionRect(void 0), actions.setSelectionKeyPressed(!1), actions.setMultiselectionKeyPressed(!1), actions.setDeleteKeyPressed(!1), actions.setPanActivationKeyPressed(!1), actions.setZoomActivationKeyPressed(!1);
2612
2845
  }, handleDelete = async () => {
2613
2846
  let selectedNodes = store.nodes.filter((node) => node.selected), selectedEdges = store.edges.filter((edge) => edge.selected), { deletedNodes, deletedEdges } = await deleteElements({
2614
2847
  nodes: selectedNodes,
@@ -2618,22 +2851,19 @@ const KeyHandler = (props) => {
2618
2851
  nodes: deletedNodes,
2619
2852
  edges: deletedEdges
2620
2853
  });
2621
- }, handleKeyDown = (event) => {
2622
- batch(() => {
2623
- matchesKeyArray(event, _props.selectionKey) && actions.setSelectionKeyPressed(!0), matchesKeyArray(event, _props.multiSelectionKey) && actions.setMultiselectionKeyPressed(!0), matchesKeyArray(event, _props.deleteKey) && !isInputDOMNode(event) && (event.ctrlKey || event.metaKey || event.shiftKey || (actions.setDeleteKeyPressed(!0), handleDelete())), matchesKeyArray(event, _props.panActivationKey) && actions.setPanActivationKeyPressed(!0), matchesKeyArray(event, _props.zoomActivationKey) && actions.setZoomActivationKeyPressed(!0);
2624
- });
2625
- }, handleKeyUp = (event) => {
2626
- batch(() => {
2627
- matchesKeyArray(event, _props.selectionKey) && actions.setSelectionKeyPressed(!1), matchesKeyArray(event, _props.multiSelectionKey) && actions.setMultiselectionKeyPressed(!1), matchesKeyArray(event, _props.deleteKey) && actions.setDeleteKeyPressed(!1), matchesKeyArray(event, _props.panActivationKey) && actions.setPanActivationKeyPressed(!1), matchesKeyArray(event, _props.zoomActivationKey) && actions.setZoomActivationKeyPressed(!1);
2628
- });
2629
2854
  };
2630
- return onMount(() => {
2631
- window.addEventListener("keydown", handleKeyDown), window.addEventListener("keyup", handleKeyUp), window.addEventListener("blur", resetKeysAndSelection), window.addEventListener("contextmenu", resetKeysAndSelection), onCleanup(() => {
2632
- window.removeEventListener("keydown", handleKeyDown), window.removeEventListener("keyup", handleKeyUp), window.removeEventListener("blur", resetKeysAndSelection), window.removeEventListener("contextmenu", resetKeysAndSelection);
2633
- });
2855
+ return isServer || createEventListenerMap(window, {
2856
+ keydown: (event) => {
2857
+ matchesKeyArray(event, _props.selectionKey) && actions.setSelectionKeyPressed(!0), matchesKeyArray(event, _props.multiSelectionKey) && actions.setMultiselectionKeyPressed(!0), matchesKeyArray(event, _props.deleteKey) && !isInputDOMNode(event) && (event.ctrlKey || event.metaKey || event.shiftKey || (actions.setDeleteKeyPressed(!0), handleDelete())), matchesKeyArray(event, _props.panActivationKey) && actions.setPanActivationKeyPressed(!0), matchesKeyArray(event, _props.zoomActivationKey) && actions.setZoomActivationKeyPressed(!0), flush();
2858
+ },
2859
+ keyup: (event) => {
2860
+ matchesKeyArray(event, _props.selectionKey) && actions.setSelectionKeyPressed(!1), matchesKeyArray(event, _props.multiSelectionKey) && actions.setMultiselectionKeyPressed(!1), matchesKeyArray(event, _props.deleteKey) && actions.setDeleteKeyPressed(!1), matchesKeyArray(event, _props.panActivationKey) && actions.setPanActivationKeyPressed(!1), matchesKeyArray(event, _props.zoomActivationKey) && actions.setZoomActivationKeyPressed(!1), flush();
2861
+ },
2862
+ blur: resetKeysAndSelection,
2863
+ contextmenu: resetKeysAndSelection
2634
2864
  }), null;
2635
2865
  }, SolidFlow = (props) => {
2636
- let domNode, _props = mergeProps({
2866
+ let [domNodeRef, setDomNodeRef] = createSignal(), domNode, _props = merge({
2637
2867
  ...getDefaultFlowStateProps(),
2638
2868
  colorMode: "light",
2639
2869
  deleteKeyCode: "Backspace",
@@ -2659,69 +2889,73 @@ const KeyHandler = (props) => {
2659
2889
  zoomOnPinch: !0,
2660
2890
  zoomOnDoubleClick: !0,
2661
2891
  zoomOnScroll: !0
2662
- }, props), [flowProps, htmlProps] = splitProps(_props, /* @__PURE__ */ "nodes.edges.nodeTypes.edgeTypes.width.height.fitView.fitViewOptions.nodeOrigin.nodeDragThreshold.paneClickDistance.nodeClickDistance.minZoom.maxZoom.zIndexMode.initialViewport.viewport.translateExtent.nodeExtent.selectionKey.panActivationKey.deleteKey.multiSelectionKey.zoomActivationKey.panOnDrag.panOnScroll.panOnScrollMode.panOnScrollSpeed.selectionOnDrag.selectNodesOnDrag.preventScrolling.zoomOnScroll.zoomOnDoubleClick.zoomOnPinch.onlyRenderVisibleElements.autoPanOnConnect.autoPanOnNodeDrag.autoPanOnNodeFocus.autoPanOnSelection.autoPanSpeed.connectionRadius.connectionMode.connectionLineType.connectionLineComponent.connectionLineStyle.connectionLineContainerStyle.connectionDragThreshold.isValidConnection.clickConnect.reconnectRadius.selectionMode.elementsSelectable.nodesDraggable.nodesConnectable.nodesFocusable.edgesFocusable.disableKeyboardA11y.ariaLabelConfig.ariaLiveMessage.colorMode.colorModeSSR.class.style.snapGrid.defaultMarkerColor.defaultEdgeOptions.elevateNodesOnSelect.elevateEdgesOnSelect.noDragClass.noPanClass.noWheelClass.attributionPosition.proOptions.onInit.onMoveStart.onMove.onMoveEnd.onFlowError.onNodeClick.onNodeContextMenu.onNodeDrag.onNodeDragStart.onNodeDragStop.onNodePointerEnter.onNodePointerMove.onNodePointerLeave.onEdgeClick.onEdgeContextMenu.onEdgePointerEnter.onEdgePointerLeave.onPaneClick.onPaneContextMenu.onSelectionChange.onSelectionClick.onSelectionContextMenu.onSelectionDrag.onSelectionDragStart.onSelectionDragStop.onSelectionStart.onSelectionEnd.onConnect.onConnectStart.onConnectEnd.onReconnect.onReconnectStart.onReconnectEnd.onClickConnectStart.onClickConnectEnd.onBeforeConnect.onBeforeReconnect.onDelete.onBeforeDelete.deleteKeyCode.selectionKeyCode.panActivationKeyCode.multiSelectionKeyCode.zoomActivationKeyCode.children".split(".")), TypedSolidFlowContext = SolidFlowContext, solidFlow = useContext(TypedSolidFlowContext) ?? createSolidFlow(_props), { store, actions } = solidFlow;
2663
- onMount(() => {
2664
- actions.applyInitialFitView(flowProps.fitView), batch(() => {
2665
- actions.setConfig(_props), actions.setDomNode(domNode);
2666
- }), createResizeObserver(domNode, () => {
2667
- actions.setWidth(domNode.clientWidth), actions.setHeight(domNode.clientHeight);
2668
- }), createEffect(() => {
2669
- actions.setPaneClickDistance(flowProps.paneClickDistance);
2670
- }), onCleanup(() => {
2671
- actions.reset();
2892
+ }, props), htmlProps = omit(_props, "nodes", "edges", "nodeTypes", "edgeTypes", "width", "height", "fitView", "fitViewOptions", "nodeOrigin", "nodeDragThreshold", "paneClickDistance", "nodeClickDistance", "minZoom", "maxZoom", "zIndexMode", "initialViewport", "viewport", "translateExtent", "nodeExtent", "selectionKey", "panActivationKey", "deleteKey", "multiSelectionKey", "zoomActivationKey", "panOnDrag", "panOnScroll", "panOnScrollMode", "panOnScrollSpeed", "selectionOnDrag", "selectNodesOnDrag", "preventScrolling", "zoomOnScroll", "zoomOnDoubleClick", "zoomOnPinch", "onlyRenderVisibleElements", "autoPanOnConnect", "autoPanOnNodeDrag", "autoPanOnNodeFocus", "autoPanOnSelection", "autoPanSpeed", "connectionRadius", "connectionMode", "connectionLineType", "connectionLineComponent", "connectionLineStyle", "connectionLineContainerStyle", "connectionDragThreshold", "isValidConnection", "clickConnect", "reconnectRadius", "selectionMode", "elementsSelectable", "nodesDraggable", "nodesConnectable", "nodesFocusable", "edgesFocusable", "disableKeyboardA11y", "ariaLabelConfig", "ariaLiveMessage", "colorMode", "colorModeSSR", "class", "style", "snapGrid", "defaultMarkerColor", "defaultEdgeOptions", "elevateNodesOnSelect", "elevateEdgesOnSelect", "noDragClass", "noPanClass", "noWheelClass", "attributionPosition", "proOptions", "onInit", "onMoveStart", "onMove", "onMoveEnd", "onFlowError", "onNodeClick", "onNodeContextMenu", "onNodeDrag", "onNodeDragStart", "onNodeDragStop", "onNodePointerEnter", "onNodePointerMove", "onNodePointerLeave", "onEdgeClick", "onEdgeContextMenu", "onEdgePointerEnter", "onEdgePointerLeave", "onPaneClick", "onPaneContextMenu", "onSelectionChange", "onSelectionClick", "onSelectionContextMenu", "onSelectionDrag", "onSelectionDragStart", "onSelectionDragStop", "onSelectionStart", "onSelectionEnd", "onConnect", "onConnectStart", "onConnectEnd", "onReconnect", "onReconnectStart", "onReconnectEnd", "onClickConnectStart", "onClickConnectEnd", "onBeforeConnect", "onBeforeReconnect", "onDelete", "onBeforeDelete", "deleteKeyCode", "selectionKeyCode", "panActivationKeyCode", "multiSelectionKeyCode", "zoomActivationKeyCode", "children"), TypedSolidFlowContext = SolidFlowContext, solidFlow = useContext(TypedSolidFlowContext) ?? createSolidFlow(_props), { store, actions } = solidFlow;
2893
+ onSettled(() => (actions.applyInitialFitView(_props.fitView), actions.setConfig(_props), actions.setDomNode(domNode), () => {
2894
+ actions.reset();
2895
+ })), createEffect(() => domNodeRef(), (el) => {
2896
+ if (!el) return;
2897
+ let observer = new ResizeObserver(() => {
2898
+ actions.setWidth(el.clientWidth), actions.setHeight(el.clientHeight);
2672
2899
  });
2900
+ return observer.observe(el), () => observer.disconnect();
2901
+ }), createEffect(() => ({
2902
+ panZoom: store.panZoom,
2903
+ distance: _props.paneClickDistance
2904
+ }), ({ panZoom, distance }) => {
2905
+ panZoom?.setClickDistance(distance);
2673
2906
  });
2674
2907
  let selectedElements = createMemo(() => ({
2675
2908
  nodes: store.selectedNodes,
2676
2909
  edges: store.selectedEdges
2677
- }), void 0, { equals: (a, b) => a.nodes.length === b.nodes.length && a.edges.length === b.edges.length && a.nodes.every((node, i) => node.id === b.nodes[i].id) && a.edges.every((edge, i) => edge.id === b.edges[i].id) });
2678
- createEffect(() => {
2679
- let params = selectedElements();
2680
- untrack(() => flowProps.onSelectionChange)?.(params);
2910
+ }), { equals: (a, b) => a.nodes.length === b.nodes.length && a.edges.length === b.edges.length && a.nodes.every((node, i) => node.id === b.nodes[i].id) && a.edges.every((edge, i) => edge.id === b.edges[i].id) });
2911
+ createEffect(() => selectedElements(), (params) => {
2912
+ untrack(() => _props.onSelectionChange)?.(params);
2681
2913
  });
2682
2914
  let rootStyle = () => ({
2683
- width: toPxString(flowProps.width),
2684
- height: toPxString(flowProps.height),
2685
- ...flowProps.style
2915
+ width: toPxString(_props.width),
2916
+ height: toPxString(_props.height),
2917
+ ..._props.style
2686
2918
  });
2687
- return <div role="application" data-testid="solid-flow__wrapper" ref={domNode} class={clsx$1([
2919
+ return <div role="application" data-testid="solid-flow__wrapper" ref={(el) => {
2920
+ domNode = el, setDomNodeRef(el);
2921
+ }} class={[
2688
2922
  "solid-flow",
2689
2923
  "solid-flow__container",
2690
- flowProps.class,
2924
+ _props.class,
2691
2925
  store.colorMode
2692
- ])} style={rootStyle()} onScroll={(e) => {
2926
+ ]} style={rootStyle()} onScroll={(e) => {
2693
2927
  e.currentTarget.scrollTo({
2694
2928
  top: 0,
2695
2929
  left: 0,
2696
2930
  behavior: "auto"
2697
2931
  });
2698
2932
  }} {...htmlProps}>
2699
- <TypedSolidFlowContext.Provider value={solidFlow}>
2700
- <KeyHandler selectionKey={flowProps.selectionKey} deleteKey={flowProps.deleteKey} panActivationKey={flowProps.panActivationKey} multiSelectionKey={flowProps.multiSelectionKey} zoomActivationKey={flowProps.zoomActivationKey} />
2701
- <Zoom panOnScrollMode={flowProps.panOnScrollMode} preventScrolling={flowProps.preventScrolling} zoomOnScroll={flowProps.zoomOnScroll} zoomOnDoubleClick={flowProps.zoomOnDoubleClick} zoomOnPinch={flowProps.zoomOnPinch} panOnScroll={flowProps.panOnScroll} panOnScrollSpeed={flowProps.panOnScrollSpeed} panOnDrag={flowProps.panOnDrag} paneClickDistance={flowProps.paneClickDistance} selectionOnDrag={flowProps.selectionOnDrag} onMoveStart={flowProps.onMoveStart} onMove={flowProps.onMove} onMoveEnd={flowProps.onMoveEnd} onViewportInitialized={flowProps.onInit} initialViewport={flowProps.viewport || flowProps.initialViewport}>
2702
- <Pane onPaneClick={flowProps.onPaneClick} onPaneContextMenu={flowProps.onPaneContextMenu} onSelectionStart={flowProps.onSelectionStart} onSelectionEnd={flowProps.onSelectionEnd} panOnDrag={flowProps.panOnDrag} selectionOnDrag={flowProps.selectionOnDrag} paneClickDistance={flowProps.paneClickDistance} autoPanOnSelection={flowProps.autoPanOnSelection}>
2933
+ <TypedSolidFlowContext value={solidFlow}>
2934
+ <KeyHandler selectionKey={_props.selectionKey} deleteKey={_props.deleteKey} panActivationKey={_props.panActivationKey} multiSelectionKey={_props.multiSelectionKey} zoomActivationKey={_props.zoomActivationKey} />
2935
+ <Zoom panOnScrollMode={_props.panOnScrollMode} preventScrolling={_props.preventScrolling} zoomOnScroll={_props.zoomOnScroll} zoomOnDoubleClick={_props.zoomOnDoubleClick} zoomOnPinch={_props.zoomOnPinch} panOnScroll={_props.panOnScroll} panOnScrollSpeed={_props.panOnScrollSpeed} panOnDrag={_props.panOnDrag} paneClickDistance={_props.paneClickDistance} selectionOnDrag={_props.selectionOnDrag} onMoveStart={_props.onMoveStart} onMove={_props.onMove} onMoveEnd={_props.onMoveEnd} onViewportInitialized={_props.onInit} initialViewport={_props.viewport || _props.initialViewport}>
2936
+ <Pane onPaneClick={_props.onPaneClick} onPaneContextMenu={_props.onPaneContextMenu} onSelectionStart={_props.onSelectionStart} onSelectionEnd={_props.onSelectionEnd} panOnDrag={_props.panOnDrag} selectionOnDrag={_props.selectionOnDrag} paneClickDistance={_props.paneClickDistance} autoPanOnSelection={_props.autoPanOnSelection}>
2703
2937
  <Viewport>
2704
2938
  <div class="solid-flow__container solid-flow__viewport-back" />
2705
- <EdgeRenderer reconnectRadius={flowProps.reconnectRadius} onEdgeClick={flowProps.onEdgeClick} onEdgeContextMenu={flowProps.onEdgeContextMenu} onEdgePointerEnter={flowProps.onEdgePointerEnter} onEdgePointerLeave={flowProps.onEdgePointerLeave} defaultEdgeOptions={flowProps.defaultEdgeOptions} />
2939
+ <EdgeRenderer reconnectRadius={_props.reconnectRadius} onEdgeClick={_props.onEdgeClick} onEdgeContextMenu={_props.onEdgeContextMenu} onEdgePointerEnter={_props.onEdgePointerEnter} onEdgePointerLeave={_props.onEdgePointerLeave} defaultEdgeOptions={_props.defaultEdgeOptions} />
2706
2940
  <div class="solid-flow__container solid-flow__edge-labels" />
2707
- <ConnectionLine type={flowProps.connectionLineType} component={flowProps.connectionLineComponent} containerStyle={flowProps.connectionLineContainerStyle} style={flowProps.connectionLineStyle} />
2708
- <NodeRenderer nodeClickDistance={flowProps.nodeClickDistance} onNodeClick={flowProps.onNodeClick} onNodeContextMenu={flowProps.onNodeContextMenu} onNodePointerEnter={flowProps.onNodePointerEnter} onNodePointerMove={flowProps.onNodePointerMove} onNodePointerLeave={flowProps.onNodePointerLeave} onNodeDrag={flowProps.onNodeDrag} onNodeDragStart={flowProps.onNodeDragStart} onNodeDragStop={flowProps.onNodeDragStop} />
2709
- <NodeSelection onSelectionClick={flowProps.onSelectionClick} onSelectionContextMenu={flowProps.onSelectionContextMenu} onNodeDrag={flowProps.onNodeDrag} onNodeDragStart={flowProps.onNodeDragStart} onNodeDragStop={flowProps.onNodeDragStop} />
2941
+ <ConnectionLine type={_props.connectionLineType} component={_props.connectionLineComponent} containerStyle={_props.connectionLineContainerStyle} style={_props.connectionLineStyle} />
2942
+ <NodeRenderer nodeClickDistance={_props.nodeClickDistance} onNodeClick={_props.onNodeClick} onNodeContextMenu={_props.onNodeContextMenu} onNodePointerEnter={_props.onNodePointerEnter} onNodePointerMove={_props.onNodePointerMove} onNodePointerLeave={_props.onNodePointerLeave} onNodeDrag={_props.onNodeDrag} onNodeDragStart={_props.onNodeDragStart} onNodeDragStop={_props.onNodeDragStop} />
2943
+ <NodeSelection onSelectionClick={_props.onSelectionClick} onSelectionContextMenu={_props.onSelectionContextMenu} onNodeDrag={_props.onNodeDrag} onNodeDragStart={_props.onNodeDragStart} onNodeDragStop={_props.onNodeDragStop} />
2710
2944
  </Viewport>
2711
2945
  <Selection isVisible={!!store.selectionRect && store.selectionRectMode === "user"} width={store.selectionRect?.width} height={store.selectionRect?.height} x={store.selectionRect?.x} y={store.selectionRect?.y} />
2712
2946
  </Pane>
2713
2947
  </Zoom>
2714
- <Attribution proOptions={flowProps.proOptions} position={flowProps.attributionPosition} />
2948
+ <Attribution proOptions={_props.proOptions} position={_props.attributionPosition} />
2715
2949
  <A11yDescriptions />
2716
- {flowProps.children}
2717
- </TypedSolidFlowContext.Provider>
2950
+ {_props.children}
2951
+ </TypedSolidFlowContext>
2718
2952
  </div>;
2719
2953
  }, SolidFlowProvider = (props) => {
2720
- let _props = mergeProps(getDefaultFlowStateProps(), props), solidFlow = createSolidFlow(_props);
2954
+ let _props = merge(getDefaultFlowStateProps(), props), solidFlow = createSolidFlow(_props);
2721
2955
  onCleanup(() => {
2722
2956
  solidFlow.actions.reset();
2723
2957
  });
2724
- let ContextProvider = SolidFlowContext.Provider;
2958
+ let ContextProvider = SolidFlowContext;
2725
2959
  return <ContextProvider value={solidFlow}>{props.children}</ContextProvider>;
2726
2960
  }, createEdgeStore = (edges) => {
2727
2961
  let [store, setStore] = createStore(edges);
@@ -2731,4 +2965,4 @@ const KeyHandler = (props) => {
2731
2965
  return [store, setStore];
2732
2966
  };
2733
2967
  //#endregion
2734
- export { Background, BaseEdge, BezierEdge, BezierEdgeInternal, ConnectionLine, ConnectionLineType, ConnectionMode, ControlButton, Controls, DefaultNode, EdgeLabel, EdgeLabelRenderer, EdgeReconnectAnchor, EdgeRenderer, EdgeToolbar, EdgeWrapper, GroupNode, Handle, InputNode, Marker, MarkerDefinition, MarkerType, MiniMap, NodeRenderer, NodeResizer, NodeSelection, NodeToolbar, NodeWrapper, OutputNode, PanOnScrollMode, Pane, Panel, Position, ResizeControl, ResizeControlVariant, Selection, SelectionMode, SmoothStepEdge, SmoothStepEdgeInternal, SolidFlow, SolidFlowProvider, StepEdge, StepEdgeInternal, StraightEdge, StraightEdgeInternal, Viewport, ViewportPortal, Zoom, addEdge, createEdgeStore, createNodeStore, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, useColorMode, useConnection, useEdges, useHandleEdgeSelect, useInternalNode, useNodeConnections, useNodes, useNodesData, useNodesInitialized, useSolidFlow, useUpdateNodeInternals, useViewport, useViewportInitialized };
2968
+ export { Background, BaseEdge, BezierEdge, BezierEdgeInternal, ConnectionLine, ConnectionLineType, ConnectionMode, ControlButton, Controls, DefaultNode, EdgeLabel, EdgeLabelRenderer, EdgeReconnectAnchor, EdgeRenderer, EdgeToolbar, EdgeWrapper, GroupNode, Handle, InputNode, Marker, MarkerDefinition, MarkerType, MiniMap, NodeRenderer, NodeResizer, NodeSelection, NodeToolbar, NodeWrapper, OutputNode, PanOnScrollMode, Pane, Panel, Position, ResizeControl, ResizeControlVariant, Selection, SelectionMode, SmoothStepEdge, SmoothStepEdgeInternal, SolidFlow, SolidFlowProvider, StepEdge, StepEdgeInternal, StraightEdge, StraightEdgeInternal, Viewport, ViewportPortal, Zoom, addEdge, connectionKey, createEdgeStore, createNodeStore, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, useColorMode, useConnection, useEdges, useHandleEdgeSelect, useInternalNode, useNodeConnections, useNodes, useNodesData, useNodesInitialized, useSolidFlow, useUpdateNodeInternals, useViewport, useViewportInitialized };