@dschz/solid-flow 1.0.0-next.6 → 1.0.0-next.7

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.
@@ -65,7 +65,7 @@ function propDefaults(props, defaults) {
65
65
  const getEdgeId = (connection) => {
66
66
  let { source, sourceHandle, target, targetHandle } = connection;
67
67
  return `xy-edge__${source}${sourceHandle || ""}-${target}${targetHandle || ""}`;
68
- }, EdgeLabelRenderer = (props) => {
68
+ }, isEdgeSelectable = (edge, store) => edge.selectable ?? store.defaultEdgeOptions.selectable ?? store.elementsSelectable, EdgeLabelRenderer = (props) => {
69
69
  let { store } = useInternalSolidFlow(), labelNode = () => store.domNode?.querySelector(".solid-flow__edge-labels");
70
70
  return <Show when={labelNode()}>{(root) => <Portal mount={root()}>{props.children}</Portal>}</Show>;
71
71
  }, EdgeLabel = (props) => {
@@ -145,6 +145,181 @@ const getEdgeId = (connection) => {
145
145
  };
146
146
  };
147
147
  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} />;
148
+ };
149
+ //#endregion
150
+ //#region src/core/spatial/grid.ts
151
+ /**
152
+ * A uniform spatial hash over axis-aligned rects — the plain, NON-reactive
153
+ * building block behind the flow's spatial queries (RFC-4239 dossier:
154
+ * gesture-scoped snapshots and epoch-rebuilt indexes; deliberately never a
155
+ * live-maintained reactive structure, which would re-create the round-6
156
+ * central-collection anti-pattern).
157
+ *
158
+ * Every operation is O(cells touched); with cellSize on the order of the
159
+ * query radius or median node size, inserts and queries touch O(1) cells.
160
+ * No balancing, no extent-known-up-front requirement, no dependency —
161
+ * upstream's own bake-off (quadtree vs BVH vs rbush) is why: fixed-radius
162
+ * neighborhood and rect-vs-box queries are the textbook grid case.
163
+ */
164
+ var SpatialGrid = class {
165
+ cellSize;
166
+ cells = /* @__PURE__ */ new Map();
167
+ rects = /* @__PURE__ */ new Map();
168
+ constructor(cellSize) {
169
+ this.cellSize = cellSize;
170
+ }
171
+ cellRange(rect) {
172
+ let size = this.cellSize;
173
+ return {
174
+ minX: Math.floor(rect.x / size),
175
+ maxX: Math.floor((rect.x + rect.width) / size),
176
+ minY: Math.floor(rect.y / size),
177
+ maxY: Math.floor((rect.y + rect.height) / size)
178
+ };
179
+ }
180
+ insert(id, rect) {
181
+ this.rects.set(id, rect);
182
+ let { minX, maxX, minY, maxY } = this.cellRange(rect);
183
+ for (let cx = minX; cx <= maxX; cx++) for (let cy = minY; cy <= maxY; cy++) {
184
+ let key = `${cx}:${cy}`, bucket = this.cells.get(key);
185
+ bucket ? bucket.push(id) : this.cells.set(key, [id]);
186
+ }
187
+ }
188
+ /** Ids of entries whose rect overlaps the query rect (touching counts). */
189
+ queryRect(query) {
190
+ let { minX, maxX, minY, maxY } = this.cellRange(query), seen = /* @__PURE__ */ new Set(), result = [];
191
+ for (let cx = minX; cx <= maxX; cx++) for (let cy = minY; cy <= maxY; cy++) {
192
+ let bucket = this.cells.get(`${cx}:${cy}`);
193
+ if (bucket) for (let id of bucket) {
194
+ if (seen.has(id)) continue;
195
+ seen.add(id);
196
+ let rect = this.rects.get(id);
197
+ rect.x <= query.x + query.width && rect.x + rect.width >= query.x && rect.y <= query.y + query.height && rect.y + rect.height >= query.y && result.push(id);
198
+ }
199
+ }
200
+ return result;
201
+ }
202
+ get size() {
203
+ return this.rects.size;
204
+ }
205
+ }, GestureSpatialLookup = class {
206
+ #real;
207
+ #cellSize;
208
+ #grid = null;
209
+ #queryRect = null;
210
+ constructor(real, cellSize) {
211
+ this.#real = real, this.#cellSize = cellSize;
212
+ }
213
+ /** Snapshot the current geometry into the grid (gesture start). */
214
+ arm(rectOf) {
215
+ let grid = new SpatialGrid(this.#cellSize);
216
+ for (let [id, value] of this.#real.entries()) grid.insert(id, rectOf(value));
217
+ this.#grid = grid, this.#queryRect = null;
218
+ }
219
+ /** Focus iteration on the neighborhood of the pointer (per move). */
220
+ setQueryCenter(center, radius) {
221
+ this.#queryRect = {
222
+ x: center.x - radius,
223
+ y: center.y - radius,
224
+ width: radius * 2,
225
+ height: radius * 2
226
+ };
227
+ }
228
+ /** Focus iteration on an explicit rect (box-selection gestures). */
229
+ setQueryRect(rect) {
230
+ this.#queryRect = rect;
231
+ }
232
+ /** Back to plain pass-through (gesture end). */
233
+ disarm() {
234
+ this.#grid = null, this.#queryRect = null;
235
+ }
236
+ #candidateIds() {
237
+ return !this.#grid || !this.#queryRect ? null : this.#grid.queryRect(this.#queryRect);
238
+ }
239
+ get(key) {
240
+ return this.#real.get(key);
241
+ }
242
+ has(key) {
243
+ return this.#real.has(key);
244
+ }
245
+ get size() {
246
+ return this.#real.size;
247
+ }
248
+ *keys() {
249
+ let candidates = this.#candidateIds();
250
+ if (!candidates) {
251
+ yield* this.#real.keys();
252
+ return;
253
+ }
254
+ for (let id of candidates) this.#real.has(id) && (yield id);
255
+ }
256
+ *values() {
257
+ let candidates = this.#candidateIds();
258
+ if (!candidates) {
259
+ yield* this.#real.values();
260
+ return;
261
+ }
262
+ for (let id of candidates) {
263
+ let value = this.#real.get(id);
264
+ value !== void 0 && (yield value);
265
+ }
266
+ }
267
+ *entries() {
268
+ let candidates = this.#candidateIds();
269
+ if (!candidates) {
270
+ yield* this.#real.entries();
271
+ return;
272
+ }
273
+ for (let id of candidates) {
274
+ let value = this.#real.get(id);
275
+ value !== void 0 && (yield [id, value]);
276
+ }
277
+ }
278
+ [Symbol.iterator]() {
279
+ return this.entries();
280
+ }
281
+ forEach(callback, thisArg) {
282
+ for (let [key, value] of this.entries()) callback.call(thisArg, value, key, this);
283
+ }
284
+ [Symbol.toStringTag] = "GestureSpatialLookup";
285
+ set() {
286
+ throw Error("GestureSpatialLookup is read-only");
287
+ }
288
+ getOrInsert() {
289
+ throw Error("GestureSpatialLookup is read-only");
290
+ }
291
+ getOrInsertComputed() {
292
+ throw Error("GestureSpatialLookup is read-only");
293
+ }
294
+ delete() {
295
+ throw Error("GestureSpatialLookup is read-only");
296
+ }
297
+ clear() {
298
+ throw Error("GestureSpatialLookup is read-only");
299
+ }
300
+ };
301
+ //#endregion
302
+ //#region src/components/handle/connectionGestureLookup.ts
303
+ /**
304
+ * Upstream `getClosestHandle` prefilters nodes within
305
+ * `connectionRadius + ADDITIONAL_DISTANCE` of the pointer; ADDITIONAL_DISTANCE
306
+ * is hardcoded to 250 in @xyflow/system (xyhandle/utils.ts). Tracked here with
307
+ * a safety pad: a superset of candidates is always correct (their exact
308
+ * distance filter runs after), so the pad only costs a few extra candidates.
309
+ */
310
+ const armConnectionGestureLookup = (options) => {
311
+ let { event, real, domNode, getTransform, connectionRadius } = options, containerBounds = domNode?.getBoundingClientRect();
312
+ if (!containerBounds) return real;
313
+ let radius = connectionRadius + 250 + 50, lookup = new GestureSpatialLookup(real, radius);
314
+ lookup.arm((node) => nodeToRect(node));
315
+ let update = (moveEvent) => {
316
+ lookup.setQueryCenter(pointToRendererPoint(getEventPosition(moveEvent, containerBounds), getTransform(), !1, [1, 1]), radius);
317
+ };
318
+ update(event);
319
+ let doc = getHostForElement(event.target), dispose = () => {
320
+ doc.removeEventListener("mousemove", update, !0), doc.removeEventListener("touchmove", update, !0), doc.removeEventListener("mouseup", dispose, !0), doc.removeEventListener("touchend", dispose, !0), lookup.disarm();
321
+ };
322
+ return doc.addEventListener("mousemove", update, !0), doc.addEventListener("touchmove", update, !0), doc.addEventListener("mouseup", dispose, !0), doc.addEventListener("touchend", dispose, !0), lookup;
148
323
  }, EdgeReconnectAnchor = (props) => {
149
324
  let _props = propDefaults(props, {
150
325
  size: 25,
@@ -165,7 +340,13 @@ const getEdgeId = (connection) => {
165
340
  nodeId: edge().target,
166
341
  handleId: edge().targetHandle ?? null,
167
342
  type: "target"
168
- };
343
+ }, gestureLookup = armConnectionGestureLookup({
344
+ event,
345
+ real: nodeLookup,
346
+ domNode: store.domNode,
347
+ getTransform: () => store.transform,
348
+ connectionRadius: store.connectionRadius
349
+ });
169
350
  XYHandle.onPointerDown(event, {
170
351
  lib: store.lib,
171
352
  flowId: store.id,
@@ -175,7 +356,7 @@ const getEdgeId = (connection) => {
175
356
  autoPanOnConnect: store.autoPanOnConnect,
176
357
  connectionMode: store.connectionMode,
177
358
  connectionRadius: store.connectionRadius,
178
- nodeLookup,
359
+ nodeLookup: gestureLookup,
179
360
  isTarget: opposite.type === "target",
180
361
  edgeUpdaterType: opposite.type,
181
362
  cancelConnection: actions.cancelConnection,
@@ -236,9 +417,73 @@ const getEdgeId = (connection) => {
236
417
  </div>
237
418
  </Show>
238
419
  </>;
239
- }, createEdgeStore = (edges) => {
240
- let [store, setStore] = createStore(edges);
241
- return [store, setStore];
420
+ }, createSelectionCommands = ({ store, setNodesStore, setEdgesStore, setSelectionRect, setSelectionRectMode, nodeLookup, edgeLookup, updateNodePositions }) => {
421
+ let unselectNodesAndEdges = ({ nodes: _nodes, edges } = {}) => {
422
+ let nodesToUnselect = new Set((_nodes || store.nodes).map(({ id }) => id));
423
+ nodesToUnselect.size && setNodesStore((nodes) => {
424
+ for (let node of nodes) nodesToUnselect.has(node.id) && (node.selected = !1);
425
+ });
426
+ let edgesToUnselect = new Set((edges ?? store.edges).map(({ id }) => id));
427
+ edgesToUnselect.size && setEdgesStore((edges) => {
428
+ for (let edge of edges) edgesToUnselect.has(edge.id) && (edge.selected = !1);
429
+ }), flush();
430
+ }, addSelectedNodes = (ids) => {
431
+ let isMultiSelection = store.multiselectionKeyPressed, idSet = new Set(ids);
432
+ setNodesStore((nodes) => {
433
+ for (let node of nodes) {
434
+ let nodeWillBeSelected = idSet.has(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
435
+ node.selected !== selected && (node.selected = selected);
436
+ }
437
+ }), isMultiSelection || unselectNodesAndEdges({ nodes: [] }), flush();
438
+ }, addSelectedEdges = (ids) => {
439
+ let isMultiSelection = store.multiselectionKeyPressed, idSet = new Set(ids);
440
+ setEdgesStore((edges) => {
441
+ for (let edge of edges) {
442
+ let edgeWillBeSelected = idSet.has(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
443
+ edge.selected !== selected && (edge.selected = selected);
444
+ }
445
+ }), isMultiSelection || unselectNodesAndEdges({ edges: [] }), flush();
446
+ };
447
+ return {
448
+ unselectNodesAndEdges,
449
+ addSelectedNodes,
450
+ addSelectedEdges,
451
+ handleNodeSelection: (id, unselect, nodeRef) => {
452
+ let node = store.nodes.find((n) => n.id === id);
453
+ node && (setSelectionRect(void 0), setSelectionRectMode(void 0), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
454
+ nodes: [node],
455
+ edges: []
456
+ }), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]));
457
+ },
458
+ handleEdgeSelection: (id) => {
459
+ let edge = edgeLookup[id];
460
+ edge && isEdgeSelectable(edge, store) && (setSelectionRect(void 0), setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
461
+ nodes: [],
462
+ edges: [edge]
463
+ }) : addSelectedEdges([id]));
464
+ },
465
+ moveSelectedNodes: (direction, factor) => {
466
+ 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;
467
+ for (let node of nodeLookup.values()) {
468
+ if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
469
+ let nextPosition = {
470
+ x: node.internals.positionAbsolute.x + xDiff,
471
+ y: node.internals.positionAbsolute.y + yDiff
472
+ };
473
+ store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
474
+ let { position } = calculateNodePosition({
475
+ nodeId: node.id,
476
+ nextPosition,
477
+ nodeLookup,
478
+ nodeExtent: store.nodeExtent,
479
+ nodeOrigin: store.nodeOrigin,
480
+ onError: store.onError
481
+ });
482
+ nodeUpdates.set(node.id, { position });
483
+ }
484
+ updateNodePositions(nodeUpdates);
485
+ }
486
+ };
242
487
  }, STEP = .5 / 2, rectsEqual = (a, b) => a === b || !!a && !!b && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height, rectsOverlap = (a, b) => a.x <= b.x + b.width && a.x + a.width >= b.x && a.y <= b.y + b.height && a.y + a.height >= b.y, createCullingViewport = (source) => createMemo(() => {
243
488
  let { width, height } = source;
244
489
  if (!width || !height) return null;
@@ -369,14 +614,48 @@ var RecordMapFacade = class {
369
614
  }
370
615
  };
371
616
  //#endregion
372
- //#region src/core/projections/connections.ts
617
+ //#region src/core/measurementIngest.ts
373
618
  /**
374
- * Lookup keys for the connection index. Each edge is registered under six
375
- * keys for both of its endpoints: the node, the node+handle-type, and (when
376
- * a handle id is present) the node+type+handle:
377
- * `${nodeId}` · `${nodeId}-${type}` · `${nodeId}-${type}-${handleId}`
619
+ * The measurement ingest lifecycle (WP3): everything that flows FROM the DOM
620
+ * measuring pass INTO the data graph, plus the garbage collection that keeps
621
+ * the measurements root aligned with graph membership. The DOM side (resize
622
+ * observers, the idle-scheduled measuring pass) lives in createSolidFlow;
623
+ * headless usage never calls these.
378
624
  */
379
- const connectionKey = (nodeId, type, handleId) => `${nodeId}${type ? handleId ? `-${type}-${handleId}` : `-${type}` : ""}`, pairKey = (aNode, aHandle, bNode, bHandle) => `${aNode}-${aHandle}--${bNode}-${bHandle}`, createConnections = (source) => createProjection(() => {
625
+ const createMeasurementIngest = ({ setMeasurementsStore, setNodesStore, nodes }) => (createEffect(() => new Set(nodes().map((n) => n.id)), (currentIds) => {
626
+ setMeasurementsStore((draft) => {
627
+ for (let id of Object.keys(draft)) currentIds.has(id) || delete draft[id];
628
+ });
629
+ }), {
630
+ applyMeasurementWrites: (writes) => {
631
+ setMeasurementsStore((draft) => {
632
+ for (let write of writes) if (write.hidden) {
633
+ let entry = draft[write.id];
634
+ entry && (entry.handleBounds = void 0);
635
+ } else draft[write.id] = {
636
+ measured: write.measured,
637
+ handleBounds: write.handleBounds
638
+ };
639
+ });
640
+ },
641
+ applyNodeChanges: (changes) => {
642
+ changes.length !== 0 && setNodesStore((nodes) => {
643
+ let nodeById = new Map(nodes.map((node) => [node.id, node]));
644
+ for (let change of changes) {
645
+ let node = nodeById.get(change.id);
646
+ if (node) switch (change.type) {
647
+ case "dimensions":
648
+ change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
649
+ ...node.measured,
650
+ ...change.dimensions
651
+ };
652
+ break;
653
+ case "position": node.position = change.position ?? node.position;
654
+ }
655
+ }
656
+ });
657
+ }
658
+ }), connectionKey = (nodeId, type, handleId) => `${nodeId}${type ? handleId ? `-${type}-${handleId}` : `-${type}` : ""}`, pairKey = (aNode, aHandle, bNode, bHandle) => `${aNode}-${aHandle}--${bNode}-${bHandle}`, createConnections = (source) => createProjection(() => {
380
659
  let out = {}, add = (key, entry, connection) => {
381
660
  (out[key] ??= {})[entry] = connection;
382
661
  };
@@ -441,10 +720,6 @@ const EMPTY_AUTO_INDEX = /* @__PURE__ */ new Map(), createInternalNodes = (sourc
441
720
  }, row.internals.z = z;
442
721
  } else source.nodes.length;
443
722
  }
444
- if (userNode.parentId) {
445
- let parent = entryById.get(userNode.parentId);
446
- parent?.store.row.internals.positionAbsolute.x, parent?.store.row.internals.z;
447
- }
448
723
  return { row };
449
724
  }, {}, { key: "id" }), entry = {
450
725
  store,
@@ -513,8 +788,8 @@ const createLayoutedEdges = (source) => {
513
788
  let rowStores = mapArray(() => source.edges, (edgeAccessor) => ({
514
789
  id: edgeAccessor().id,
515
790
  store: createProjection(() => {
516
- let edge = edgeAccessor(), row = buildRow(source, edge);
517
- return source.nodeLookup.get(edge.source)?.internals.handleBounds, source.nodeLookup.get(edge.target)?.internals.handleBounds, { row };
791
+ let edge = edgeAccessor();
792
+ return { row: buildRow(source, edge) };
518
793
  }, { row: null }, { key: "id" })
519
794
  }), { keyed: (edge) => edge.id }), assigned = /* @__PURE__ */ new Map();
520
795
  return createProjection((draft) => {
@@ -530,7 +805,7 @@ const createLayoutedEdges = (source) => {
530
805
  });
531
806
  }, buildRow = (source, edge) => {
532
807
  let sourceNode = source.nodeLookup.get(edge.source), targetNode = source.nodeLookup.get(edge.target);
533
- if (!sourceNode || !targetNode) return source.nodeLookup.size, null;
808
+ if (!sourceNode || !targetNode) return null;
534
809
  let edgePosition = getEdgePosition({
535
810
  id: edge.id,
536
811
  sourceNode,
@@ -560,7 +835,33 @@ const createLayoutedEdges = (source) => {
560
835
  let out = {};
561
836
  for (let node of source.nodes) node.parentId && (out[node.parentId] = !0);
562
837
  return out;
563
- }, {}, { key: "id" }), getInitialViewport = (fitView, initialViewport, width, height, nodeLookup) => {
838
+ }, {}, { key: "id" }), createSeededGraphStores = (props, config) => {
839
+ props.nodes !== void 0 && props.defaultNodes, props.edges !== void 0 && props.defaultEdges;
840
+ let [nodesStore, setNodesStore] = createStore(props.nodes ?? [...props.defaultNodes ?? []]), [edgesStore, setEdgesStore] = createStore(props.edges ?? [...props.defaultEdges ?? []]), nodeSeedAdopted = props.nodes !== void 0 || props.defaultNodes !== void 0, edgeSeedAdopted = props.edges !== void 0 || props.defaultEdges !== void 0;
841
+ return createEffect(() => {
842
+ let next = config().nodes;
843
+ if (next) for (let node of next);
844
+ return { next };
845
+ }, ({ next }) => {
846
+ next && (nodeSeedAdopted = !0, setNodesStore(() => next));
847
+ }, { defer: !0 }), createEffect(() => {
848
+ let next = config().edges;
849
+ if (next) for (let edge of next);
850
+ return { next };
851
+ }, ({ next }) => {
852
+ next && (edgeSeedAdopted = !0, setEdgesStore(() => next));
853
+ }, { defer: !0 }), createEffect(() => ({
854
+ nodes: config().defaultNodes,
855
+ edges: config().defaultEdges
856
+ }), ({ nodes: defaultNodes, edges: defaultEdges }) => {
857
+ defaultNodes && !nodeSeedAdopted && config().nodes === void 0 && (nodeSeedAdopted = !0, setNodesStore(() => [...defaultNodes])), defaultEdges && !edgeSeedAdopted && config().edges === void 0 && (edgeSeedAdopted = !0, setEdgesStore(() => [...defaultEdges]));
858
+ }, { defer: !0 }), {
859
+ nodesStore,
860
+ setNodesStore,
861
+ edgesStore,
862
+ setEdgesStore
863
+ };
864
+ }, getInitialViewport = (fitView, initialViewport, width, height, nodeLookup) => {
564
865
  if (fitView && !initialViewport && width && height) {
565
866
  let bounds = getInternalNodesBounds(nodeLookup, { filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) });
566
867
  return getViewportForBounds$1(bounds, width, height, .5, 2, .1);
@@ -571,9 +872,7 @@ const createLayoutedEdges = (source) => {
571
872
  zoom: 1
572
873
  };
573
874
  }, createFlowState = (props, injections = {}) => {
574
- 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);
575
- props.nodes !== void 0 && props.defaultNodes, props.edges !== void 0 && props.defaultEdges;
576
- let [nodesStore, setNodesStore] = createStore(props.nodes ?? [...props.defaultNodes ?? []]), [edgesStore, setEdgesStore] = createStore(props.edges ?? [...props.defaultEdges ?? []]), nodeSeedAdopted = props.nodes !== void 0 || props.defaultNodes !== void 0, edgeSeedAdopted = props.edges !== void 0 || props.defaultEdges !== void 0, [measurementsStore, setMeasurementsStore] = createStore({}), internalNodes = createInternalNodes({
875
+ let _props = merge(getDefaultFlowStateProps(), props), initialNodeTypes = injections.initialNodeTypes ?? {}, initialEdgeTypes = injections.initialEdgeTypes ?? {}, prefersDark = injections.prefersDark ?? (() => _props.colorModeSSR === "dark"), [config, setConfig] = createSignal(_props), ariaLabelConfig = createMemo(() => 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 = createMemo(() => config().minZoom), maxZoom = createMemo(() => config().maxZoom), [nodesConnectable, setNodesConnectable] = createSignal(() => config().nodesConnectable), [nodesDraggable, setNodesDraggable] = createSignal(() => config().nodesDraggable), [panZoom, setPanZoom] = createSignal(null), [selectionRect, setSelectionRect] = createSignal(), [selectionRectMode, setSelectionRectMode] = createSignal(), [snapGrid, setSnapGrid] = createSignal(() => config().snapGrid), translateExtent = createMemo(() => 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, edgesStore, setEdgesStore } = createSeededGraphStores(props, config), [measurementsStore, setMeasurementsStore] = createStore({}), internalNodes = createInternalNodes({
577
876
  get nodes() {
578
877
  return nodesStore;
579
878
  },
@@ -593,24 +892,7 @@ const createLayoutedEdges = (source) => {
593
892
  return config().zIndexMode;
594
893
  }
595
894
  }), nodeLookup = new RecordMapFacade(internalNodes), initialViewport = getInitialViewport(_props.fitView, _props.initialViewport, _props.width ?? 0, _props.height ?? 0, nodeLookup), [viewportStore, setViewportStore] = createStore(_props.viewport ?? initialViewport);
596
- createEffect(() => {
597
- let next = config().nodes;
598
- if (next) for (let node of next);
599
- return { next };
600
- }, ({ next }) => {
601
- next && (nodeSeedAdopted = !0, setNodesStore(() => next));
602
- }, { defer: !0 }), createEffect(() => {
603
- let next = config().edges;
604
- if (next) for (let edge of next);
605
- return { next };
606
- }, ({ next }) => {
607
- next && (edgeSeedAdopted = !0, setEdgesStore(() => next));
608
- }, { defer: !0 }), createEffect(() => ({
609
- nodes: config().defaultNodes,
610
- edges: config().defaultEdges
611
- }), ({ nodes: defaultNodes, edges: defaultEdges }) => {
612
- defaultNodes && !nodeSeedAdopted && config().nodes === void 0 && (nodeSeedAdopted = !0, setNodesStore(() => [...defaultNodes])), defaultEdges && !edgeSeedAdopted && config().edges === void 0 && (edgeSeedAdopted = !0, setEdgesStore(() => [...defaultEdges]));
613
- }, { defer: !0 }), createEffect(() => config().viewport, (next) => {
895
+ createEffect(() => config().viewport, (next) => {
614
896
  next && setViewportStore(() => next);
615
897
  }, { defer: !0 });
616
898
  let transform = createMemo(() => [
@@ -622,25 +904,29 @@ const createLayoutedEdges = (source) => {
622
904
  if (nodes.length === 0) return !1;
623
905
  for (let node of nodes) if (!node.hidden && (node.measured?.width === void 0 || node.measured?.height === void 0)) return !1;
624
906
  return !0;
625
- }), store = merge({
907
+ }), resolvedColorMode = createMemo(() => {
908
+ let mode = config().colorMode;
909
+ return mode === "system" ? prefersDark() ? "dark" : "light" : mode;
910
+ }), projectedConnection = createMemo(() => {
911
+ let state = connection();
912
+ return state.inProgress ? {
913
+ ...state,
914
+ to: pointToRendererPoint(state.to, transform())
915
+ } : state;
916
+ }), connectionFromHandle = createMemo(() => connection().fromHandle ?? null, { equals: (a, b) => a === b || !!a && !!b && a.nodeId === b.nodeId && a.type === b.type && a.id === b.id }), connectionTargetByHandle = createProjection((draft) => {
917
+ let state = connection(), toHandle = state.inProgress ? state.toHandle : null, key = toHandle ? connectionKey(toHandle.nodeId, toHandle.type, toHandle.id ?? null) : null;
918
+ for (let existing of Object.keys(draft)) existing !== key && delete draft[existing];
919
+ key && (draft[key] = state.isValid ? "valid" : "invalid");
920
+ }, {}, { key: null }), mergedNodeTypes = createMemo(() => ({
921
+ ...initialNodeTypes,
922
+ ...config().nodeTypes
923
+ })), mergedEdgeTypes = createMemo(() => ({
924
+ ...initialEdgeTypes,
925
+ ...config().edgeTypes
926
+ })), selectedNodesView = createMemo(() => nodesStore.filter((node) => node.selected)), selectedEdgesView = createMemo(() => edgesStore.filter((edge) => edge.selected)), store = merge({
626
927
  width: 0,
627
928
  height: 0
628
929
  }, config, {
629
- get _colorMode() {
630
- return config().colorMode;
631
- },
632
- get _colorModeSSR() {
633
- return config().colorModeSSR;
634
- },
635
- get _connection() {
636
- return connection();
637
- },
638
- get _nodeTypes() {
639
- return config().nodeTypes;
640
- },
641
- get _edgeTypes() {
642
- return config().edgeTypes;
643
- },
644
930
  get ariaLabelConfig() {
645
931
  return ariaLabelConfig();
646
932
  },
@@ -651,14 +937,16 @@ const createLayoutedEdges = (source) => {
651
937
  return clickConnectStartHandle();
652
938
  },
653
939
  get colorMode() {
654
- return this._colorMode === "system" ? prefersDark() ? "dark" : "light" : this._colorMode;
940
+ return resolvedColorMode();
655
941
  },
656
942
  get connection() {
657
- let state = connection();
658
- return {
659
- ...state,
660
- to: state.inProgress ? pointToRendererPoint(state.to, this.transform) : state.to
661
- };
943
+ return projectedConnection();
944
+ },
945
+ get connectionFromHandle() {
946
+ return connectionFromHandle();
947
+ },
948
+ get connectionTargetByHandle() {
949
+ return connectionTargetByHandle;
662
950
  },
663
951
  get domNode() {
664
952
  return domNode();
@@ -667,10 +955,7 @@ const createLayoutedEdges = (source) => {
667
955
  return dragging();
668
956
  },
669
957
  get edgeTypes() {
670
- return {
671
- ...initialEdgeTypes,
672
- ...this._edgeTypes
673
- };
958
+ return mergedEdgeTypes();
674
959
  },
675
960
  get elementsSelectable() {
676
961
  return elementsSelectable();
@@ -703,19 +988,16 @@ const createLayoutedEdges = (source) => {
703
988
  return nodesDraggable();
704
989
  },
705
990
  get nodeTypes() {
706
- return {
707
- ...initialNodeTypes,
708
- ...this._nodeTypes
709
- };
991
+ return mergedNodeTypes();
710
992
  },
711
993
  get panZoom() {
712
994
  return panZoom();
713
995
  },
714
996
  get selectedNodes() {
715
- return nodesStore.filter((node) => node.selected);
997
+ return selectedNodesView();
716
998
  },
717
999
  get selectedEdges() {
718
- return edgesStore.filter((edge) => edge.selected);
1000
+ return selectedEdgesView();
719
1001
  },
720
1002
  get selectionRect() {
721
1003
  return selectionRect();
@@ -830,33 +1112,11 @@ const createLayoutedEdges = (source) => {
830
1112
  setNodesStore((nodes) => {
831
1113
  for (let node of nodes) nodeDragItems.has(node.id) && (node.dragging = dragging, node.position = nodeDragItems.get(node.id).position);
832
1114
  });
833
- }, applyMeasurementWrites = (writes) => {
834
- setMeasurementsStore((draft) => {
835
- for (let write of writes) if (write.hidden) {
836
- let entry = draft[write.id];
837
- entry && (entry.handleBounds = void 0);
838
- } else draft[write.id] = {
839
- measured: write.measured,
840
- handleBounds: write.handleBounds
841
- };
842
- });
843
- }, applyNodeChanges = (changes) => {
844
- changes.length !== 0 && setNodesStore((nodes) => {
845
- let nodeById = new Map(nodes.map((node) => [node.id, node]));
846
- for (let change of changes) {
847
- let node = nodeById.get(change.id);
848
- if (node) switch (change.type) {
849
- case "dimensions":
850
- change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
851
- ...node.measured,
852
- ...change.dimensions
853
- };
854
- break;
855
- case "position": node.position = change.position ?? node.position;
856
- }
857
- }
858
- });
859
- }, markInitialNodesMeasured = () => {
1115
+ }, { applyMeasurementWrites, applyNodeChanges } = createMeasurementIngest({
1116
+ setMeasurementsStore,
1117
+ setNodesStore,
1118
+ nodes: () => nodesStore
1119
+ }), markInitialNodesMeasured = () => {
860
1120
  initialNodesMeasured = !0, tryInitialFitView();
861
1121
  }, requestMeasure = () => {}, setMeasureRequester = (fn) => {
862
1122
  requestMeasure = fn;
@@ -871,66 +1131,16 @@ const createLayoutedEdges = (source) => {
871
1131
  ease: options?.ease,
872
1132
  interpolate: options?.interpolate
873
1133
  }), Promise.resolve(!0)) : Promise.resolve(!1);
874
- }, setPaneClickDistance = (distance) => {
875
- store.panZoom?.setClickDistance(distance);
876
- }, unselectNodesAndEdges = ({ nodes: _nodes, edges } = {}) => {
877
- let nodesToUnselect = new Set((_nodes || store.nodes).map(({ id }) => id));
878
- nodesToUnselect.size && setNodesStore((nodes) => {
879
- for (let node of nodes) nodesToUnselect.has(node.id) && (node.selected = !1);
880
- });
881
- let edgesToUnselect = new Set((edges ?? store.edges).map(({ id }) => id));
882
- edgesToUnselect.size && setEdgesStore((edges) => {
883
- for (let edge of edges) edgesToUnselect.has(edge.id) && (edge.selected = !1);
884
- }), flush();
885
- }, addSelectedNodes = (ids) => {
886
- let isMultiSelection = store.multiselectionKeyPressed;
887
- setNodesStore((nodes) => {
888
- for (let node of nodes) {
889
- let nodeWillBeSelected = ids.includes(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
890
- node.selected !== selected && (node.selected = selected);
891
- }
892
- }), isMultiSelection || unselectNodesAndEdges({ nodes: [] }), flush();
893
- }, addSelectedEdges = (ids) => {
894
- let isMultiSelection = store.multiselectionKeyPressed;
895
- setEdgesStore((edges) => {
896
- for (let edge of edges) {
897
- let edgeWillBeSelected = ids.includes(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
898
- edge.selected !== selected && (edge.selected = selected);
899
- }
900
- }), isMultiSelection || unselectNodesAndEdges({ edges: [] }), flush();
901
- }, handleNodeSelection = (id, unselect, nodeRef) => {
902
- let node = store.nodes.find((n) => n.id === id);
903
- node && (setSelectionRect(void 0), setSelectionRectMode(void 0), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
904
- nodes: [node],
905
- edges: []
906
- }), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]));
907
- }, handleEdgeSelection = (id) => {
908
- let edge = edgeLookup[id];
909
- edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (setSelectionRect(void 0), setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
910
- nodes: [],
911
- edges: [edge]
912
- }) : addSelectedEdges([id]));
913
- }, moveSelectedNodes = (direction, factor) => {
914
- 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;
915
- for (let node of nodeLookup.values()) {
916
- if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
917
- let nextPosition = {
918
- x: node.internals.positionAbsolute.x + xDiff,
919
- y: node.internals.positionAbsolute.y + yDiff
920
- };
921
- store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
922
- let { position } = calculateNodePosition({
923
- nodeId: node.id,
924
- nextPosition,
925
- nodeLookup,
926
- nodeExtent: store.nodeExtent,
927
- nodeOrigin: store.nodeOrigin,
928
- onError: store.onError
929
- });
930
- nodeUpdates.set(node.id, { position });
931
- }
932
- updateNodePositions(nodeUpdates);
933
- }, panBy$1 = (delta) => panBy({
1134
+ }, stableSetViewport = (viewport) => setViewportStore(() => viewport), { unselectNodesAndEdges, addSelectedNodes, addSelectedEdges, handleNodeSelection, handleEdgeSelection, moveSelectedNodes } = createSelectionCommands({
1135
+ store,
1136
+ setNodesStore,
1137
+ setEdgesStore,
1138
+ setSelectionRect,
1139
+ setSelectionRectMode,
1140
+ nodeLookup,
1141
+ edgeLookup,
1142
+ updateNodePositions
1143
+ }), panBy$1 = (delta) => panBy({
934
1144
  delta,
935
1145
  panZoom: store.panZoom,
936
1146
  transform: store.transform,
@@ -1007,8 +1217,27 @@ const createLayoutedEdges = (source) => {
1007
1217
  get snapGrid() {
1008
1218
  return store.snapGrid;
1009
1219
  }
1010
- }, getNodeRect = (node) => {
1011
- 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 = {
1220
+ }, intersectionGrid = null, intersectionRows = null, queryIntersectionCandidates = (rect) => untrack(() => {
1221
+ if (!intersectionGrid) {
1222
+ let grid = new SpatialGrid(300), rows = /* @__PURE__ */ new Map();
1223
+ for (let node of store.nodes) {
1224
+ let internalNode = nodeLookup.get(node.id);
1225
+ internalNode && (grid.insert(node.id, nodeToRect(internalNode)), rows.set(node.id, node));
1226
+ }
1227
+ intersectionGrid = grid, intersectionRows = rows, queueMicrotask(() => {
1228
+ intersectionGrid = null, intersectionRows = null;
1229
+ });
1230
+ }
1231
+ let rows = intersectionRows, result = [];
1232
+ for (let id of intersectionGrid.queryRect(rect)) {
1233
+ let row = rows.get(id);
1234
+ row && result.push(row);
1235
+ }
1236
+ return result;
1237
+ }), getNodeRect = (node) => {
1238
+ let nodeToUse = isNode(node) ? node : nodeLookup.get(node.id);
1239
+ if (!nodeToUse) return null;
1240
+ let position = nodeToUse.parentId ? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, nodeLookup, store.nodeOrigin) : nodeToUse.position, nodeWithPosition = {
1012
1241
  ...nodeToUse,
1013
1242
  position,
1014
1243
  width: nodeToUse.measured?.width ?? nodeToUse.width,
@@ -1062,7 +1291,7 @@ const createLayoutedEdges = (source) => {
1062
1291
  x,
1063
1292
  y,
1064
1293
  zoom
1065
- ], _snapGrid !== null, _snapGrid || [1, 1]);
1294
+ ], !!_snapGrid, _snapGrid || [1, 1]);
1066
1295
  },
1067
1296
  flowToScreenPosition: (position) => {
1068
1297
  if (!store.domNode) return position;
@@ -1126,14 +1355,18 @@ const createLayoutedEdges = (source) => {
1126
1355
  let remainingNodes = store.nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id));
1127
1356
  store.onNodesDelete?.(matchingNodes), setNodesStore(() => remainingNodes);
1128
1357
  }
1129
- return {
1358
+ let deletedNodes = matchingNodes ?? [], deletedEdges = matchingEdges ?? [];
1359
+ return (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
1360
+ nodes: deletedNodes,
1361
+ edges: deletedEdges
1362
+ }), {
1130
1363
  deletedNodes: matchingNodes,
1131
1364
  deletedEdges: matchingEdges
1132
1365
  };
1133
1366
  },
1134
1367
  getIntersectingNodes: (nodeOrRect, partially = !0, nodesToIntersect) => {
1135
1368
  let isRect = isRectObject(nodeOrRect), nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
1136
- return nodeRect ? (nodesToIntersect || store.nodes).filter((n) => {
1369
+ return nodeRect ? (nodesToIntersect ?? queryIntersectionCandidates(nodeRect)).filter((n) => {
1137
1370
  let internalNode = nodeLookup.get(n.id);
1138
1371
  if (!internalNode || !isRect && n.id === nodeOrRect.id) return !1;
1139
1372
  let currNodeRect = nodeToRect(internalNode), overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
@@ -1189,10 +1422,6 @@ const createLayoutedEdges = (source) => {
1189
1422
  extent: store.translateExtent
1190
1423
  }), ({ panZoom, extent }) => {
1191
1424
  panZoom?.setTranslateExtent(extent);
1192
- }), createEffect(() => new Set(store.nodes.map((n) => n.id)), (currentIds) => {
1193
- setMeasurementsStore((draft) => {
1194
- for (let id of Object.keys(draft)) currentIds.has(id) || delete draft[id];
1195
- });
1196
1425
  }), {
1197
1426
  store,
1198
1427
  flow,
@@ -1210,8 +1439,6 @@ const createLayoutedEdges = (source) => {
1210
1439
  applyNodeChanges,
1211
1440
  markInitialNodesMeasured,
1212
1441
  setMeasureRequester,
1213
- resetStoreValues,
1214
- setAriaLabelConfig,
1215
1442
  setAriaLiveMessage,
1216
1443
  setClickConnectStartHandle,
1217
1444
  setConfig,
@@ -1219,15 +1446,11 @@ const createLayoutedEdges = (source) => {
1219
1446
  setDeleteKeyPressed,
1220
1447
  setDomNode,
1221
1448
  setDragging,
1222
- get setEdges() {
1223
- return setEdgesStore;
1224
- },
1449
+ setEdges: setEdgesStore,
1225
1450
  setElementsSelectable,
1226
1451
  setHeight,
1227
1452
  setMultiselectionKeyPressed,
1228
- get setNodes() {
1229
- return setNodesStore;
1230
- },
1453
+ setNodes: setNodesStore,
1231
1454
  setNodesConnectable,
1232
1455
  setNodesDraggable,
1233
1456
  setPanActivationKeyPressed,
@@ -1235,9 +1458,7 @@ const createLayoutedEdges = (source) => {
1235
1458
  setSelectionKeyPressed,
1236
1459
  setSelectionRect,
1237
1460
  setSelectionRectMode,
1238
- get setViewport() {
1239
- return (viewport) => setViewportStore(() => viewport);
1240
- },
1461
+ setViewport: stableSetViewport,
1241
1462
  setWidth,
1242
1463
  setZoomActivationKeyPressed,
1243
1464
  addEdge,
@@ -1246,7 +1467,6 @@ const createLayoutedEdges = (source) => {
1246
1467
  zoomOut,
1247
1468
  fitView,
1248
1469
  setCenter,
1249
- setPaneClickDistance,
1250
1470
  unselectNodesAndEdges,
1251
1471
  addSelectedNodes,
1252
1472
  addSelectedEdges,
@@ -1258,25 +1478,28 @@ const createLayoutedEdges = (source) => {
1258
1478
  reset
1259
1479
  }
1260
1480
  };
1481
+ }, createEdgeStore = (edges) => {
1482
+ let [store, setStore] = typeof edges == "function" ? createStore(edges, []) : createStore(edges);
1483
+ return [store, setStore];
1261
1484
  }, createNodeStore = (nodes) => {
1262
- let [store, setStore] = createStore(nodes);
1485
+ let [store, setStore] = typeof nodes == "function" ? createStore(nodes, []) : createStore(nodes);
1263
1486
  return [store, setStore];
1264
1487
  }, EdgeWrapper = (props) => {
1265
- let edgeRef, { store, actions } = useInternalSolidFlow(), edgeId = () => props.edgeId, edge = () => actions.getEdge(edgeId()), edgeType = () => edge().type ?? "default", selectable = () => edge().selectable ?? store.elementsSelectable, focusable = () => edge().focusable ?? store.edgesFocusable, edgeComponent = () => store.edgeTypes[edgeType()], markerStartUrl = () => edge().markerStart ? `url('#${getMarkerId(edge().markerStart, store.id)}')` : void 0, markerEndUrl = () => edge().markerEnd ? `url('#${getMarkerId(edge().markerEnd, store.id)}')` : void 0, onClick = (event) => {
1488
+ let edgeRef, { store, actions } = useInternalSolidFlow(), edgeId = () => props.edgeId, edge = () => actions.getEdge(edgeId()), edgeType = () => edge().type ?? "default", selectable = () => isEdgeSelectable(edge(), store), focusable = () => edge().focusable ?? store.edgesFocusable, edgeComponent = () => store.edgeTypes[edgeType()], markerStartUrl = () => edge().markerStart ? `url('#${getMarkerId(edge().markerStart, store.id)}')` : void 0, markerEndUrl = () => edge().markerEnd ? `url('#${getMarkerId(edge().markerEnd, store.id)}')` : void 0, onClick = (event) => {
1266
1489
  selectable() && actions.handleEdgeSelection(edgeId()), props.onEdgeClick?.({
1267
1490
  edge: edge(),
1268
1491
  event
1269
1492
  });
1270
- }, onPointerEvent = (event, type) => {
1271
- ({
1272
- contextmenu: props.onEdgeContextMenu,
1273
- pointerenter: props.onEdgePointerEnter,
1274
- pointerleave: props.onEdgePointerLeave
1275
- })[type]?.({
1276
- edge: edge(),
1277
- event
1278
- });
1279
- }, onKeyDown = (event) => {
1493
+ }, onContextMenu = (event) => props.onEdgeContextMenu?.({
1494
+ edge: edge(),
1495
+ event
1496
+ }), onPointerEnter = (event) => props.onEdgePointerEnter?.({
1497
+ edge: edge(),
1498
+ event
1499
+ }), onPointerLeave = (event) => props.onEdgePointerLeave?.({
1500
+ edge: edge(),
1501
+ event
1502
+ }), onKeyDown = (event) => {
1280
1503
  store.disableKeyboardA11y || !elementSelectionKeys.includes(event.key) || !selectable() || (event.key === "Escape" ? actions.unselectNodesAndEdges({ edges: [edge()] }) : actions.addSelectedEdges([edge().id]));
1281
1504
  }, ariaLabel = () => edge().ariaLabel ?? `Edge from ${edge().source} to ${edge().target}`, culled = createMemo(() => isEdgeCulled(edge(), store.cullingViewport));
1282
1505
  return <EdgeIdContext value={edgeId}>
@@ -1295,7 +1518,7 @@ const createLayoutedEdges = (source) => {
1295
1518
  selectable: !!selectable()
1296
1519
  },
1297
1520
  edge().class
1298
- ]} onClick={onClick} onKeyDown={(e) => focusable() && onKeyDown(e)} onContextMenu={(e) => onPointerEvent(e, "contextmenu")} onPointerEnter={(e) => onPointerEvent(e, "pointerenter")} onPointerLeave={(e) => onPointerEvent(e, "pointerleave")} {...edge().domAttributes}>
1521
+ ]} onClick={onClick} onKeyDown={(e) => focusable() && onKeyDown(e)} onContextMenu={onContextMenu} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave} {...edge().domAttributes}>
1299
1522
  <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()} />
1300
1523
  </g>
1301
1524
  </svg>
@@ -1419,7 +1642,10 @@ const Handle = (props) => {
1419
1642
  position: "top",
1420
1643
  isConnectableStart: !0,
1421
1644
  isConnectableEnd: !0
1422
- }), { 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;
1645
+ }), { 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.connectionFromHandle, connectingFrom = () => {
1646
+ let fromHandle = store.connectionFromHandle;
1647
+ return fromHandle && fromHandle.nodeId === nodeId() && fromHandle.type === _props.type && fromHandle.id === handleId();
1648
+ }, targetState = () => store.connectionTargetByHandle[connectionKey(nodeId(), _props.type, handleId())], connectingTo = () => targetState() !== void 0, isPossibleTargetHandle = () => store.connectionMode === "strict" ? store.connectionFromHandle?.type !== _props.type : nodeId() !== store.connectionFromHandle?.nodeId || handleId() !== store.connectionFromHandle?.id, valid = () => targetState() === "valid", prevConnections = null;
1423
1649
  createEffect(() => {
1424
1650
  if (!_props.onConnect && !_props.onDisconnect) return null;
1425
1651
  let rec = connections[connectionKey(nodeId(), _props.type, _props.id)], map = /* @__PURE__ */ new Map();
@@ -1437,13 +1663,20 @@ const Handle = (props) => {
1437
1663
  }, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
1438
1664
  actions.addEdge(edge), store.onConnect?.(handleConnection);
1439
1665
  }, onPointerDown = (event) => {
1666
+ let gestureLookup = armConnectionGestureLookup({
1667
+ event,
1668
+ real: nodeLookup,
1669
+ domNode: store.domNode,
1670
+ getTransform: () => store.transform,
1671
+ connectionRadius: store.connectionRadius
1672
+ });
1440
1673
  XYHandle.onPointerDown(event, {
1441
1674
  handleId: handleId(),
1442
1675
  nodeId: nodeId(),
1443
1676
  isTarget: isTarget(),
1444
1677
  connectionRadius: store.connectionRadius,
1445
1678
  domNode: store.domNode,
1446
- nodeLookup,
1679
+ nodeLookup: gestureLookup,
1447
1680
  connectionMode: store.connectionMode,
1448
1681
  lib: store.lib,
1449
1682
  autoPanOnConnect: store.autoPanOnConnect,
@@ -1628,12 +1861,11 @@ const NodeWrapper = (props) => {
1628
1861
  ...h ? { height: toPxString(h) } : {}
1629
1862
  };
1630
1863
  }, culled = createMemo(() => isNodeCulled(node(), store.cullingViewport)), style = () => ({
1864
+ ...sizeStyle(),
1631
1865
  "z-index": node().internals.z,
1632
1866
  transform: transform(),
1633
1867
  visibility: culled() || !nodeHasDimensions(node()) ? "hidden" : "visible",
1634
- "pointer-events": culled() ? "none" : void 0,
1635
- ...sizeStyle(),
1636
- ...node().style ?? {}
1868
+ "pointer-events": culled() ? "none" : void 0
1637
1869
  });
1638
1870
  createEffect(() => ({
1639
1871
  valid: nodeTypeValid(),
@@ -1893,7 +2125,7 @@ const InitialNodeTypesMap = {
1893
2125
  pendingEntries = updateEntries, scheduleIdleCallback(() => {
1894
2126
  let updates = new Map(pendingEntries);
1895
2127
  pendingEntries = void 0;
1896
- let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode);
2128
+ let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode, store.nodeExtent);
1897
2129
  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());
1898
2130
  });
1899
2131
  };
@@ -2043,7 +2275,7 @@ const ConnectionLine = (props) => {
2043
2275
  for (let item of a) if (!b.has(item)) return !1;
2044
2276
  return !0;
2045
2277
  }, Pane = (props) => {
2046
- 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 = {
2278
+ let { store, nodeLookup, edgeLookup, connections, actions } = useInternalSolidFlow(), [containerRef, setContainerRef] = createSignal(), container, containerBounds = null, connectionEndedOnPane = !1, selectionInProgress = !1, selectionSpatialLookup = new GestureSpatialLookup(nodeLookup, 400), selectedNodeIds = /* @__PURE__ */ new Set(), selectedEdgeIds = /* @__PURE__ */ new Set(), autoPanId = 0, position = {
2047
2279
  x: 0,
2048
2280
  y: 0
2049
2281
  }, 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) => {
@@ -2058,7 +2290,7 @@ const ConnectionLine = (props) => {
2058
2290
  if (event.pointerType === "touch" && _panOnDrag() !== !1 && !store.selectionKeyPressed || (containerBounds = container?.getBoundingClientRect() ?? null, !containerBounds)) return;
2059
2291
  let eventTargetIsContainer = event.target === container, isNoKeyEvent = !eventTargetIsContainer && !!event.target.closest(".nokey"), isSelectionActive = props.selectionOnDrag && eventTargetIsContainer || store.selectionKeyPressed;
2060
2292
  if (isNoKeyEvent || !isSelecting() || !isSelectionActive || event.button !== 0 || !event.isPrimary) return;
2061
- event.target?.setPointerCapture?.(event.pointerId), selectionInProgress = !1, autoPanStarted = !1;
2293
+ event.target?.setPointerCapture?.(event.pointerId), selectionSpatialLookup.arm((node) => nodeToRect(node)), selectionInProgress = !1, autoPanStarted = !1;
2062
2294
  let { x, y } = getEventPosition(event, containerBounds), userSelectionFlowOrigin = pointToRendererPoint({
2063
2295
  x,
2064
2296
  y
@@ -2085,14 +2317,21 @@ const ConnectionLine = (props) => {
2085
2317
  width: Math.abs(mouseX - screenStart.x),
2086
2318
  height: Math.abs(mouseY - screenStart.y)
2087
2319
  }, prevSelectedNodeIds = selectedNodeIds, prevSelectedEdgeIds = selectedEdgeIds;
2088
- selectedNodeIds = new Set(getNodesInside(nodeLookup, nextUserSelectRect, store.transform, store.selectionMode === SelectionMode$1.Partial, !0).map((n) => n.id));
2089
- let edgesSelectable = store.defaultEdgeOptions.selectable ?? !0;
2090
- selectedEdgeIds = /* @__PURE__ */ new Set();
2320
+ {
2321
+ let [tx, ty, zoom] = store.transform;
2322
+ selectionSpatialLookup.setQueryRect({
2323
+ x: (nextUserSelectRect.x - tx) / zoom,
2324
+ y: (nextUserSelectRect.y - ty) / zoom,
2325
+ width: nextUserSelectRect.width / zoom,
2326
+ height: nextUserSelectRect.height / zoom
2327
+ });
2328
+ }
2329
+ selectedNodeIds = new Set(getNodesInside(selectionSpatialLookup, nextUserSelectRect, store.transform, store.selectionMode === SelectionMode$1.Partial, !0).map((n) => n.id)), selectedEdgeIds = /* @__PURE__ */ new Set();
2091
2330
  for (let nodeId of selectedNodeIds) {
2092
2331
  let nodeConnections = connections[nodeId];
2093
2332
  if (nodeConnections) for (let { edgeId } of Object.values(nodeConnections)) {
2094
2333
  let edge = edgeLookup[edgeId];
2095
- edge && (edge.selectable ?? edgesSelectable) && selectedEdgeIds.add(edgeId);
2334
+ edge && isEdgeSelectable(edge, store) && selectedEdgeIds.add(edgeId);
2096
2335
  }
2097
2336
  }
2098
2337
  isSetEqual(prevSelectedNodeIds, selectedNodeIds) || actions.setNodes((nodes) => {
@@ -2273,7 +2512,7 @@ const ConnectionLine = (props) => {
2273
2512
  <div class="solid-flow__selection" style={styles()} />
2274
2513
  </Show>;
2275
2514
  }, NodeSelection = (props) => {
2276
- let { store, nodeLookup, actions } = useInternalSolidFlow(), [ref, setRef] = createSignal(), bounds = () => store.selectionRectMode === "nodes" ? getInternalNodesBounds(nodeLookup, { filter: (node) => !!node.selected }) : null;
2515
+ let { store, nodeLookup, actions } = useInternalSolidFlow(), [ref, setRef] = createSignal(), bounds = createMemo(() => store.selectionRectMode === "nodes" ? getInternalNodesBounds(nodeLookup, { filter: (node) => !!node.selected }) : null);
2277
2516
  createEffect(() => ({
2278
2517
  el: ref(),
2279
2518
  focusable: !store.disableKeyboardA11y
@@ -2281,13 +2520,13 @@ const ConnectionLine = (props) => {
2281
2520
  focusable && el?.focus({ preventScroll: !0 });
2282
2521
  });
2283
2522
  let onContextMenu = (event) => {
2284
- let selectedNodes = store.nodes.filter((n) => n.selected);
2523
+ let selectedNodes = store.selectedNodes;
2285
2524
  props.onSelectionContextMenu?.({
2286
2525
  nodes: selectedNodes,
2287
2526
  event
2288
2527
  });
2289
2528
  }, onClick = (event) => {
2290
- let selectedNodes = store.nodes.filter((n) => n.selected);
2529
+ let selectedNodes = store.selectedNodes;
2291
2530
  props.onSelectionClick?.({
2292
2531
  nodes: selectedNodes,
2293
2532
  event
@@ -2452,14 +2691,11 @@ const KeyHandler = (props) => {
2452
2691
  }, handleWindowBlur = () => {
2453
2692
  resetKeysAndSelection(), cancelPointerGestures();
2454
2693
  }, handleDelete = async () => {
2455
- let selectedNodes = store.nodes.filter((node) => node.selected), selectedEdges = store.edges.filter((edge) => edge.selected), { deletedNodes, deletedEdges } = await deleteElements({
2694
+ let selectedNodes = store.selectedNodes, selectedEdges = store.selectedEdges;
2695
+ await deleteElements({
2456
2696
  nodes: selectedNodes,
2457
2697
  edges: selectedEdges
2458
2698
  });
2459
- (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
2460
- nodes: deletedNodes,
2461
- edges: deletedEdges
2462
- });
2463
2699
  };
2464
2700
  return isServer || (createEventListenerMap(window, {
2465
2701
  keydown: (event) => {
@@ -2477,34 +2713,23 @@ const KeyHandler = (props) => {
2477
2713
  capture: !0,
2478
2714
  passive: !0
2479
2715
  })), null;
2480
- }, SolidFlow = (props) => {
2716
+ }, FLOW_PROP_KEYS = /* @__PURE__ */ "ariaLabelConfig.ariaLiveMessage.attributionPosition.autoPanOnConnect.autoPanOnNodeDrag.autoPanOnNodeFocus.autoPanOnSelection.autoPanSpeed.class.clickConnect.colorMode.colorModeSSR.connectionDragThreshold.connectionLineComponent.connectionLineContainerStyle.connectionLineStyle.connectionLineType.connectionMode.connectionRadius.defaultEdgeOptions.defaultEdges.defaultMarkerColor.defaultNodes.deleteKey.disableKeyboardA11y.edgeTypes.edges.edgesFocusable.elementsSelectable.elevateEdgesOnSelect.elevateNodesOnSelect.fitView.fitViewOptions.height.id.initialViewport.isValidConnection.maxZoom.minZoom.multiSelectionKey.noDragClass.noPanClass.noWheelClass.nodeClickDistance.nodeDragThreshold.nodeExtent.nodeOrigin.nodeTypes.nodes.nodesConnectable.nodesDraggable.nodesFocusable.onBeforeConnect.onBeforeDelete.onBeforeReconnect.onClickConnectEnd.onClickConnectStart.onConnect.onConnectEnd.onConnectStart.onDelete.onEdgeClick.onEdgeContextMenu.onEdgePointerEnter.onEdgePointerLeave.onEdgesDelete.onFlowError.onInit.onMove.onMoveEnd.onMoveStart.onNodeClick.onNodeContextMenu.onNodeDrag.onNodeDragStart.onNodeDragStop.onNodePointerEnter.onNodePointerLeave.onNodePointerMove.onNodesDelete.onPaneClick.onPaneContextMenu.onReconnect.onReconnectEnd.onReconnectStart.onSelectionChange.onSelectionClick.onSelectionContextMenu.onSelectionDrag.onSelectionDragStart.onSelectionDragStop.onSelectionEnd.onSelectionStart.onlyRenderVisibleElements.panActivationKey.panOnDrag.panOnScroll.panOnScrollMode.panOnScrollSpeed.paneClickDistance.preventScrolling.proOptions.selectNodesOnDrag.selectionKey.selectionMode.selectionOnDrag.snapGrid.style.translateExtent.viewport.width.zIndexMode.zoomActivationKey.zoomOnDoubleClick.zoomOnPinch.zoomOnScroll".split("."), SolidFlow = (props) => {
2481
2717
  let [domNodeRef, setDomNodeRef] = createSignal(), domNode, _props = merge({
2482
2718
  ...getDefaultFlowStateProps(),
2483
2719
  colorMode: "light",
2484
- deleteKeyCode: "Backspace",
2485
- defaultViewport: {
2486
- x: 0,
2487
- y: 0,
2488
- zoom: 1
2489
- },
2490
- multiSelectionKeyCode: isMacOs() ? "Meta" : "Control",
2491
2720
  nodeClickDistance: 0,
2492
2721
  panOnScroll: !1,
2493
- panActivationKeyCode: "Space",
2494
2722
  preventScrolling: !0,
2495
2723
  panOnDrag: !0,
2496
2724
  panOnScrollSpeed: .5,
2497
2725
  panOnScrollMode: "free",
2498
2726
  paneClickDistance: 0,
2499
- reconnectRadius: 10,
2500
- selectionKeyCode: "Shift",
2501
2727
  selectionOnDrag: !1,
2502
2728
  translateExtent: infiniteExtent,
2503
- zoomActivationKeyCode: isMacOs() ? "Meta" : "Control",
2504
2729
  zoomOnPinch: !0,
2505
2730
  zoomOnDoubleClick: !0,
2506
2731
  zoomOnScroll: !0
2507
- }, props), htmlProps = omit(_props, "nodes", "edges", "defaultNodes", "defaultEdges", "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;
2732
+ }, props), htmlProps = omit(_props, ...FLOW_PROP_KEYS, "children"), TypedSolidFlowContext = SolidFlowContext, solidFlow = useContext(TypedSolidFlowContext) ?? createSolidFlow(_props), { store, actions } = solidFlow;
2508
2733
  onSettled(() => (actions.applyInitialFitView(_props.fitView), actions.setConfig(_props), actions.setDomNode(domNode), () => {
2509
2734
  actions.reset();
2510
2735
  })), createEffect(() => domNodeRef(), (el) => {
@@ -2513,11 +2738,6 @@ const KeyHandler = (props) => {
2513
2738
  actions.setWidth(el.clientWidth), actions.setHeight(el.clientHeight);
2514
2739
  });
2515
2740
  return observer.observe(el), () => observer.disconnect();
2516
- }), createEffect(() => ({
2517
- panZoom: store.panZoom,
2518
- distance: _props.paneClickDistance
2519
- }), ({ panZoom, distance }) => {
2520
- panZoom?.setClickDistance(distance);
2521
2741
  });
2522
2742
  let selectedElements = createMemo(() => ({
2523
2743
  nodes: store.selectedNodes,
@@ -2526,7 +2746,7 @@ const KeyHandler = (props) => {
2526
2746
  createEffect(() => selectedElements(), (params) => {
2527
2747
  untrack(() => _props.onSelectionChange)?.(params);
2528
2748
  });
2529
- let rootStyle = () => ({
2749
+ let asyncSeedGuard = () => (_props.nodes?.length, _props.edges?.length, null), rootStyle = () => ({
2530
2750
  width: toPxString(_props.width),
2531
2751
  height: toPxString(_props.height),
2532
2752
  ..._props.style
@@ -2546,12 +2766,13 @@ const KeyHandler = (props) => {
2546
2766
  });
2547
2767
  }} {...htmlProps}>
2548
2768
  <TypedSolidFlowContext value={solidFlow}>
2769
+ {asyncSeedGuard()}
2549
2770
  <KeyHandler selectionKey={_props.selectionKey} deleteKey={_props.deleteKey} panActivationKey={_props.panActivationKey} multiSelectionKey={_props.multiSelectionKey} zoomActivationKey={_props.zoomActivationKey} />
2550
2771
  <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}>
2551
2772
  <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}>
2552
2773
  <Viewport>
2553
2774
  <div class="solid-flow__container solid-flow__viewport-back" />
2554
- <EdgeRenderer reconnectRadius={_props.reconnectRadius} onEdgeClick={_props.onEdgeClick} onEdgeContextMenu={_props.onEdgeContextMenu} onEdgePointerEnter={_props.onEdgePointerEnter} onEdgePointerLeave={_props.onEdgePointerLeave} defaultEdgeOptions={_props.defaultEdgeOptions} />
2775
+ <EdgeRenderer onEdgeClick={_props.onEdgeClick} onEdgeContextMenu={_props.onEdgeContextMenu} onEdgePointerEnter={_props.onEdgePointerEnter} onEdgePointerLeave={_props.onEdgePointerLeave} />
2555
2776
  <div class="solid-flow__container solid-flow__edge-labels" />
2556
2777
  <ConnectionLine type={_props.connectionLineType} component={_props.connectionLineComponent} containerStyle={_props.connectionLineContainerStyle} style={_props.connectionLineStyle} />
2557
2778
  <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} />
@@ -2875,24 +3096,23 @@ const DotPattern = (props) => {
2875
3096
  nodeBorderRadius: 5,
2876
3097
  nodeStrokeWidth: 2,
2877
3098
  style: {}
2878
- }), paneProps = omit(_props, "class", "style", "position", "nodeClass", "nodeStrokeColor", "nodeColor", "pannable", "zoomable", "inversePan", "zoomStep", "bgColor", "width", "height", "maskColor", "maskStrokeColor", "maskStrokeWidth", "nodeBorderRadius", "nodeStrokeWidth", "nodeComponent", "onClick", "onNodeClick"), 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 = () => ({
3099
+ }), paneProps = omit(_props, "class", "style", "position", "nodeClass", "nodeStrokeColor", "nodeColor", "pannable", "zoomable", "inversePan", "zoomStep", "bgColor", "width", "height", "maskColor", "maskStrokeColor", "maskStrokeWidth", "nodeBorderRadius", "nodeStrokeWidth", "nodeComponent", "onClick", "onNodeClick"), 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}`), viewBB = createMemo(() => ({
2879
3100
  x: -store.viewport.x / store.viewport.zoom,
2880
3101
  y: -store.viewport.y / store.viewport.zoom,
2881
3102
  width: store.width / store.viewport.zoom,
2882
3103
  height: store.height / store.viewport.zoom
2883
- }), getBoundingRect = () => {
2884
- let viewBB = getViewBB();
2885
- return nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(nodeLookup), viewBB) : viewBB;
2886
- }, 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 = () => {
2887
- let boundingRect = getBoundingRect();
2888
- return boundingRect.x - (getViewWidth() - boundingRect.width) / 2 - getOffset();
3104
+ })), boundingRect = createMemo(() => {
3105
+ let view = viewBB();
3106
+ if (nodeLookup.size === 0) return view;
3107
+ let bounds = getInternalNodesBounds(nodeLookup);
3108
+ return !Number.isFinite(bounds.x) || !Number.isFinite(bounds.width) ? view : getBoundsOfRects(bounds, view);
3109
+ }), viewScale = createMemo(() => Math.max(boundingRect().width / _props.width, boundingRect().height / _props.height)), getViewWidth = () => viewScale() * _props.width, getViewHeight = () => viewScale() * _props.height, getOffset = () => 5 * viewScale(), getX = () => {
3110
+ let rect = boundingRect();
3111
+ return rect.x - (getViewWidth() - rect.width) / 2 - getOffset();
2889
3112
  }, getY = () => {
2890
- let boundingRect = getBoundingRect();
2891
- return boundingRect.y - (getViewHeight() - boundingRect.height) / 2 - getOffset();
2892
- }, getViewboxWidth = () => getViewWidth() + getOffset() * 2, getViewboxHeight = () => getViewHeight() + getOffset() * 2, strokeWidth = () => _props.maskStrokeWidth ? _props.maskStrokeWidth * getViewScale() : void 0, prevNodeIds = [], nodeIds = () => {
2893
- let currentNodeIds = store.nodes.map((node) => node.id), currentSet = new Set(currentNodeIds);
2894
- return (prevNodeIds.length !== currentNodeIds.length || !prevNodeIds.every((id) => currentSet.has(id))) && (prevNodeIds = currentNodeIds), prevNodeIds;
2895
- };
3113
+ let rect = boundingRect();
3114
+ return rect.y - (getViewHeight() - rect.height) / 2 - getOffset();
3115
+ }, getViewboxWidth = () => getViewWidth() + getOffset() * 2, getViewboxHeight = () => getViewHeight() + getOffset() * 2, strokeWidth = () => _props.maskStrokeWidth ? _props.maskStrokeWidth * viewScale() : void 0, nodeIds = createMemo(() => store.nodes.map((node) => node.id), { equals: (a, b) => a.length === b.length && a.every((id, i) => id === b[i]) });
2896
3116
  return <Panel position={_props.position} data-testid="solid-flow__minimap" class={["solid-flow__minimap", _props.class]} style={{
2897
3117
  "--xy-minimap-background-color-props": _props.bgColor,
2898
3118
  ..._props.style
@@ -2909,7 +3129,7 @@ const DotPattern = (props) => {
2909
3129
  domNode: el,
2910
3130
  panZoom,
2911
3131
  getTransform: () => store.transform,
2912
- getViewScale
3132
+ getViewScale: viewScale
2913
3133
  });
2914
3134
  return setMinimap(instance), () => {
2915
3135
  instance.destroy();
@@ -2949,12 +3169,12 @@ const DotPattern = (props) => {
2949
3169
  {(nodeId) => {
2950
3170
  let node = createMemo(() => nodeLookup.get(nodeId())), nodeVisible = () => !!(node() && nodeHasDimensions(node()) && !node().hidden);
2951
3171
  return <Show when={nodeVisible() && getNodeDimensions(node())}>
2952
- {(nodeDimensions) => <Dynamic component={_props.nodeComponent ?? MiniMapNode} id={nodeId()} 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())} style={node().style} onClick={_props.onNodeClick ? onSvgNodeClick : void 0} />}
3172
+ {(nodeDimensions) => <Dynamic component={_props.nodeComponent ?? MiniMapNode} id={nodeId()} 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())} style={node().style} onClick={_props.onNodeClick ? onSvgNodeClick : void 0} />}
2953
3173
  </Show>;
2954
3174
  }}
2955
3175
  </For>
2956
3176
  <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
2957
- M${getViewBB().x},${getViewBB().y}h${getViewBB().width}v${getViewBB().height}h${-getViewBB().width}z`} fill-rule="evenodd" pointer-events="none" />
3177
+ M${viewBB().x},${viewBB().y}h${viewBB().width}v${viewBB().height}h${-viewBB().width}z`} fill-rule="evenodd" pointer-events="none" />
2958
3178
  </svg>;
2959
3179
  }}
2960
3180
  </Show>