@dschz/solid-flow 0.3.0-next.5 → 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;
@@ -271,8 +516,6 @@ const getEdgeId = (connection) => {
271
516
  }, cullingViewport);
272
517
  }, getDefaultFlowStateProps = () => ({
273
518
  id: "1",
274
- nodes: [],
275
- edges: [],
276
519
  nodeOrigin: [0, 0],
277
520
  nodeExtent: infiniteExtent,
278
521
  defaultEdgeOptions: {},
@@ -371,14 +614,48 @@ var RecordMapFacade = class {
371
614
  }
372
615
  };
373
616
  //#endregion
374
- //#region src/core/projections/connections.ts
617
+ //#region src/core/measurementIngest.ts
375
618
  /**
376
- * Lookup keys for the connection index. Each edge is registered under six
377
- * keys for both of its endpoints: the node, the node+handle-type, and (when
378
- * a handle id is present) the node+type+handle:
379
- * `${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.
380
624
  */
381
- 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(() => {
382
659
  let out = {}, add = (key, entry, connection) => {
383
660
  (out[key] ??= {})[entry] = connection;
384
661
  };
@@ -443,10 +720,6 @@ const EMPTY_AUTO_INDEX = /* @__PURE__ */ new Map(), createInternalNodes = (sourc
443
720
  }, row.internals.z = z;
444
721
  } else source.nodes.length;
445
722
  }
446
- if (userNode.parentId) {
447
- let parent = entryById.get(userNode.parentId);
448
- parent?.store.row.internals.positionAbsolute.x, parent?.store.row.internals.z;
449
- }
450
723
  return { row };
451
724
  }, {}, { key: "id" }), entry = {
452
725
  store,
@@ -515,8 +788,8 @@ const createLayoutedEdges = (source) => {
515
788
  let rowStores = mapArray(() => source.edges, (edgeAccessor) => ({
516
789
  id: edgeAccessor().id,
517
790
  store: createProjection(() => {
518
- let edge = edgeAccessor(), row = buildRow(source, edge);
519
- 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) };
520
793
  }, { row: null }, { key: "id" })
521
794
  }), { keyed: (edge) => edge.id }), assigned = /* @__PURE__ */ new Map();
522
795
  return createProjection((draft) => {
@@ -532,7 +805,7 @@ const createLayoutedEdges = (source) => {
532
805
  });
533
806
  }, buildRow = (source, edge) => {
534
807
  let sourceNode = source.nodeLookup.get(edge.source), targetNode = source.nodeLookup.get(edge.target);
535
- if (!sourceNode || !targetNode) return source.nodeLookup.size, null;
808
+ if (!sourceNode || !targetNode) return null;
536
809
  let edgePosition = getEdgePosition({
537
810
  id: edge.id,
538
811
  sourceNode,
@@ -562,7 +835,33 @@ const createLayoutedEdges = (source) => {
562
835
  let out = {};
563
836
  for (let node of source.nodes) node.parentId && (out[node.parentId] = !0);
564
837
  return out;
565
- }, {}, { 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) => {
566
865
  if (fitView && !initialViewport && width && height) {
567
866
  let bounds = getInternalNodesBounds(nodeLookup, { filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) });
568
867
  return getViewportForBounds$1(bounds, width, height, .5, 2, .1);
@@ -573,7 +872,7 @@ const createLayoutedEdges = (source) => {
573
872
  zoom: 1
574
873
  };
575
874
  }, createFlowState = (props, injections = {}) => {
576
- 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({
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,19 +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
- for (let node of next);
599
- return { next };
600
- }, ({ next }) => {
601
- setNodesStore(() => next);
602
- }, { defer: !0 }), createEffect(() => {
603
- let next = config().edges;
604
- for (let edge of next);
605
- return { next };
606
- }, ({ next }) => {
607
- setEdgesStore(() => next);
608
- }, { defer: !0 }), createEffect(() => config().viewport, (next) => {
895
+ createEffect(() => config().viewport, (next) => {
609
896
  next && setViewportStore(() => next);
610
897
  }, { defer: !0 });
611
898
  let transform = createMemo(() => [
@@ -617,25 +904,29 @@ const createLayoutedEdges = (source) => {
617
904
  if (nodes.length === 0) return !1;
618
905
  for (let node of nodes) if (!node.hidden && (node.measured?.width === void 0 || node.measured?.height === void 0)) return !1;
619
906
  return !0;
620
- }), 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({
621
927
  width: 0,
622
928
  height: 0
623
929
  }, config, {
624
- get _colorMode() {
625
- return config().colorMode;
626
- },
627
- get _colorModeSSR() {
628
- return config().colorModeSSR;
629
- },
630
- get _connection() {
631
- return connection();
632
- },
633
- get _nodeTypes() {
634
- return config().nodeTypes;
635
- },
636
- get _edgeTypes() {
637
- return config().edgeTypes;
638
- },
639
930
  get ariaLabelConfig() {
640
931
  return ariaLabelConfig();
641
932
  },
@@ -646,14 +937,16 @@ const createLayoutedEdges = (source) => {
646
937
  return clickConnectStartHandle();
647
938
  },
648
939
  get colorMode() {
649
- return this._colorMode === "system" ? prefersDark() ? "dark" : "light" : this._colorMode;
940
+ return resolvedColorMode();
650
941
  },
651
942
  get connection() {
652
- let state = connection();
653
- return {
654
- ...state,
655
- to: state.inProgress ? pointToRendererPoint(state.to, this.transform) : state.to
656
- };
943
+ return projectedConnection();
944
+ },
945
+ get connectionFromHandle() {
946
+ return connectionFromHandle();
947
+ },
948
+ get connectionTargetByHandle() {
949
+ return connectionTargetByHandle;
657
950
  },
658
951
  get domNode() {
659
952
  return domNode();
@@ -662,10 +955,7 @@ const createLayoutedEdges = (source) => {
662
955
  return dragging();
663
956
  },
664
957
  get edgeTypes() {
665
- return {
666
- ...initialEdgeTypes,
667
- ...this._edgeTypes
668
- };
958
+ return mergedEdgeTypes();
669
959
  },
670
960
  get elementsSelectable() {
671
961
  return elementsSelectable();
@@ -698,19 +988,16 @@ const createLayoutedEdges = (source) => {
698
988
  return nodesDraggable();
699
989
  },
700
990
  get nodeTypes() {
701
- return {
702
- ...initialNodeTypes,
703
- ...this._nodeTypes
704
- };
991
+ return mergedNodeTypes();
705
992
  },
706
993
  get panZoom() {
707
994
  return panZoom();
708
995
  },
709
996
  get selectedNodes() {
710
- return nodesStore.filter((node) => node.selected);
997
+ return selectedNodesView();
711
998
  },
712
999
  get selectedEdges() {
713
- return edgesStore.filter((edge) => edge.selected);
1000
+ return selectedEdgesView();
714
1001
  },
715
1002
  get selectionRect() {
716
1003
  return selectionRect();
@@ -825,33 +1112,11 @@ const createLayoutedEdges = (source) => {
825
1112
  setNodesStore((nodes) => {
826
1113
  for (let node of nodes) nodeDragItems.has(node.id) && (node.dragging = dragging, node.position = nodeDragItems.get(node.id).position);
827
1114
  });
828
- }, applyMeasurementWrites = (writes) => {
829
- setMeasurementsStore((draft) => {
830
- for (let write of writes) if (write.hidden) {
831
- let entry = draft[write.id];
832
- entry && (entry.handleBounds = void 0);
833
- } else draft[write.id] = {
834
- measured: write.measured,
835
- handleBounds: write.handleBounds
836
- };
837
- });
838
- }, applyNodeChanges = (changes) => {
839
- changes.length !== 0 && setNodesStore((nodes) => {
840
- let nodeById = new Map(nodes.map((node) => [node.id, node]));
841
- for (let change of changes) {
842
- let node = nodeById.get(change.id);
843
- if (node) switch (change.type) {
844
- case "dimensions":
845
- change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
846
- ...node.measured,
847
- ...change.dimensions
848
- };
849
- break;
850
- case "position": node.position = change.position ?? node.position;
851
- }
852
- }
853
- });
854
- }, markInitialNodesMeasured = () => {
1115
+ }, { applyMeasurementWrites, applyNodeChanges } = createMeasurementIngest({
1116
+ setMeasurementsStore,
1117
+ setNodesStore,
1118
+ nodes: () => nodesStore
1119
+ }), markInitialNodesMeasured = () => {
855
1120
  initialNodesMeasured = !0, tryInitialFitView();
856
1121
  }, requestMeasure = () => {}, setMeasureRequester = (fn) => {
857
1122
  requestMeasure = fn;
@@ -866,66 +1131,16 @@ const createLayoutedEdges = (source) => {
866
1131
  ease: options?.ease,
867
1132
  interpolate: options?.interpolate
868
1133
  }), Promise.resolve(!0)) : Promise.resolve(!1);
869
- }, setPaneClickDistance = (distance) => {
870
- store.panZoom?.setClickDistance(distance);
871
- }, unselectNodesAndEdges = ({ nodes: _nodes, edges } = {}) => {
872
- let nodesToUnselect = new Set((_nodes || store.nodes).map(({ id }) => id));
873
- nodesToUnselect.size && setNodesStore((nodes) => {
874
- for (let node of nodes) nodesToUnselect.has(node.id) && (node.selected = !1);
875
- });
876
- let edgesToUnselect = new Set((edges ?? store.edges).map(({ id }) => id));
877
- edgesToUnselect.size && setEdgesStore((edges) => {
878
- for (let edge of edges) edgesToUnselect.has(edge.id) && (edge.selected = !1);
879
- }), flush();
880
- }, addSelectedNodes = (ids) => {
881
- let isMultiSelection = store.multiselectionKeyPressed;
882
- setNodesStore((nodes) => {
883
- for (let node of nodes) {
884
- let nodeWillBeSelected = ids.includes(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
885
- node.selected !== selected && (node.selected = selected);
886
- }
887
- }), isMultiSelection || unselectNodesAndEdges({ nodes: [] }), flush();
888
- }, addSelectedEdges = (ids) => {
889
- let isMultiSelection = store.multiselectionKeyPressed;
890
- setEdgesStore((edges) => {
891
- for (let edge of edges) {
892
- let edgeWillBeSelected = ids.includes(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
893
- edge.selected !== selected && (edge.selected = selected);
894
- }
895
- }), isMultiSelection || unselectNodesAndEdges({ edges: [] }), flush();
896
- }, handleNodeSelection = (id, unselect, nodeRef) => {
897
- let node = store.nodes.find((n) => n.id === id);
898
- node && (setSelectionRect(void 0), setSelectionRectMode(void 0), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
899
- nodes: [node],
900
- edges: []
901
- }), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]));
902
- }, handleEdgeSelection = (id) => {
903
- let edge = edgeLookup[id];
904
- edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (setSelectionRect(void 0), setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
905
- nodes: [],
906
- edges: [edge]
907
- }) : addSelectedEdges([id]));
908
- }, moveSelectedNodes = (direction, factor) => {
909
- 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;
910
- for (let node of nodeLookup.values()) {
911
- if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
912
- let nextPosition = {
913
- x: node.internals.positionAbsolute.x + xDiff,
914
- y: node.internals.positionAbsolute.y + yDiff
915
- };
916
- store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
917
- let { position } = calculateNodePosition({
918
- nodeId: node.id,
919
- nextPosition,
920
- nodeLookup,
921
- nodeExtent: store.nodeExtent,
922
- nodeOrigin: store.nodeOrigin,
923
- onError: store.onError
924
- });
925
- nodeUpdates.set(node.id, { position });
926
- }
927
- updateNodePositions(nodeUpdates);
928
- }, 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({
929
1144
  delta,
930
1145
  panZoom: store.panZoom,
931
1146
  transform: store.transform,
@@ -1002,8 +1217,27 @@ const createLayoutedEdges = (source) => {
1002
1217
  get snapGrid() {
1003
1218
  return store.snapGrid;
1004
1219
  }
1005
- }, getNodeRect = (node) => {
1006
- 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 = {
1007
1241
  ...nodeToUse,
1008
1242
  position,
1009
1243
  width: nodeToUse.measured?.width ?? nodeToUse.width,
@@ -1057,7 +1291,7 @@ const createLayoutedEdges = (source) => {
1057
1291
  x,
1058
1292
  y,
1059
1293
  zoom
1060
- ], _snapGrid !== null, _snapGrid || [1, 1]);
1294
+ ], !!_snapGrid, _snapGrid || [1, 1]);
1061
1295
  },
1062
1296
  flowToScreenPosition: (position) => {
1063
1297
  if (!store.domNode) return position;
@@ -1121,14 +1355,18 @@ const createLayoutedEdges = (source) => {
1121
1355
  let remainingNodes = store.nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id));
1122
1356
  store.onNodesDelete?.(matchingNodes), setNodesStore(() => remainingNodes);
1123
1357
  }
1124
- return {
1358
+ let deletedNodes = matchingNodes ?? [], deletedEdges = matchingEdges ?? [];
1359
+ return (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
1360
+ nodes: deletedNodes,
1361
+ edges: deletedEdges
1362
+ }), {
1125
1363
  deletedNodes: matchingNodes,
1126
1364
  deletedEdges: matchingEdges
1127
1365
  };
1128
1366
  },
1129
1367
  getIntersectingNodes: (nodeOrRect, partially = !0, nodesToIntersect) => {
1130
1368
  let isRect = isRectObject(nodeOrRect), nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
1131
- return nodeRect ? (nodesToIntersect || store.nodes).filter((n) => {
1369
+ return nodeRect ? (nodesToIntersect ?? queryIntersectionCandidates(nodeRect)).filter((n) => {
1132
1370
  let internalNode = nodeLookup.get(n.id);
1133
1371
  if (!internalNode || !isRect && n.id === nodeOrRect.id) return !1;
1134
1372
  let currNodeRect = nodeToRect(internalNode), overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
@@ -1184,10 +1422,6 @@ const createLayoutedEdges = (source) => {
1184
1422
  extent: store.translateExtent
1185
1423
  }), ({ panZoom, extent }) => {
1186
1424
  panZoom?.setTranslateExtent(extent);
1187
- }), createEffect(() => new Set(store.nodes.map((n) => n.id)), (currentIds) => {
1188
- setMeasurementsStore((draft) => {
1189
- for (let id of Object.keys(draft)) currentIds.has(id) || delete draft[id];
1190
- });
1191
1425
  }), {
1192
1426
  store,
1193
1427
  flow,
@@ -1205,8 +1439,6 @@ const createLayoutedEdges = (source) => {
1205
1439
  applyNodeChanges,
1206
1440
  markInitialNodesMeasured,
1207
1441
  setMeasureRequester,
1208
- resetStoreValues,
1209
- setAriaLabelConfig,
1210
1442
  setAriaLiveMessage,
1211
1443
  setClickConnectStartHandle,
1212
1444
  setConfig,
@@ -1214,15 +1446,11 @@ const createLayoutedEdges = (source) => {
1214
1446
  setDeleteKeyPressed,
1215
1447
  setDomNode,
1216
1448
  setDragging,
1217
- get setEdges() {
1218
- return setEdgesStore;
1219
- },
1449
+ setEdges: setEdgesStore,
1220
1450
  setElementsSelectable,
1221
1451
  setHeight,
1222
1452
  setMultiselectionKeyPressed,
1223
- get setNodes() {
1224
- return setNodesStore;
1225
- },
1453
+ setNodes: setNodesStore,
1226
1454
  setNodesConnectable,
1227
1455
  setNodesDraggable,
1228
1456
  setPanActivationKeyPressed,
@@ -1230,9 +1458,7 @@ const createLayoutedEdges = (source) => {
1230
1458
  setSelectionKeyPressed,
1231
1459
  setSelectionRect,
1232
1460
  setSelectionRectMode,
1233
- get setViewport() {
1234
- return (viewport) => setViewportStore(() => viewport);
1235
- },
1461
+ setViewport: stableSetViewport,
1236
1462
  setWidth,
1237
1463
  setZoomActivationKeyPressed,
1238
1464
  addEdge,
@@ -1241,7 +1467,6 @@ const createLayoutedEdges = (source) => {
1241
1467
  zoomOut,
1242
1468
  fitView,
1243
1469
  setCenter,
1244
- setPaneClickDistance,
1245
1470
  unselectNodesAndEdges,
1246
1471
  addSelectedNodes,
1247
1472
  addSelectedEdges,
@@ -1253,25 +1478,28 @@ const createLayoutedEdges = (source) => {
1253
1478
  reset
1254
1479
  }
1255
1480
  };
1481
+ }, createEdgeStore = (edges) => {
1482
+ let [store, setStore] = typeof edges == "function" ? createStore(edges, []) : createStore(edges);
1483
+ return [store, setStore];
1256
1484
  }, createNodeStore = (nodes) => {
1257
- let [store, setStore] = createStore(nodes);
1485
+ let [store, setStore] = typeof nodes == "function" ? createStore(nodes, []) : createStore(nodes);
1258
1486
  return [store, setStore];
1259
1487
  }, EdgeWrapper = (props) => {
1260
- 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) => {
1261
1489
  selectable() && actions.handleEdgeSelection(edgeId()), props.onEdgeClick?.({
1262
1490
  edge: edge(),
1263
1491
  event
1264
1492
  });
1265
- }, onPointerEvent = (event, type) => {
1266
- ({
1267
- contextmenu: props.onEdgeContextMenu,
1268
- pointerenter: props.onEdgePointerEnter,
1269
- pointerleave: props.onEdgePointerLeave
1270
- })[type]?.({
1271
- edge: edge(),
1272
- event
1273
- });
1274
- }, 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) => {
1275
1503
  store.disableKeyboardA11y || !elementSelectionKeys.includes(event.key) || !selectable() || (event.key === "Escape" ? actions.unselectNodesAndEdges({ edges: [edge()] }) : actions.addSelectedEdges([edge().id]));
1276
1504
  }, ariaLabel = () => edge().ariaLabel ?? `Edge from ${edge().source} to ${edge().target}`, culled = createMemo(() => isEdgeCulled(edge(), store.cullingViewport));
1277
1505
  return <EdgeIdContext value={edgeId}>
@@ -1290,7 +1518,7 @@ const createLayoutedEdges = (source) => {
1290
1518
  selectable: !!selectable()
1291
1519
  },
1292
1520
  edge().class
1293
- ]} 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}>
1294
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()} />
1295
1523
  </g>
1296
1524
  </svg>
@@ -1414,7 +1642,10 @@ const Handle = (props) => {
1414
1642
  position: "top",
1415
1643
  isConnectableStart: !0,
1416
1644
  isConnectableEnd: !0
1417
- }), { 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;
1418
1649
  createEffect(() => {
1419
1650
  if (!_props.onConnect && !_props.onDisconnect) return null;
1420
1651
  let rec = connections[connectionKey(nodeId(), _props.type, _props.id)], map = /* @__PURE__ */ new Map();
@@ -1432,13 +1663,20 @@ const Handle = (props) => {
1432
1663
  }, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
1433
1664
  actions.addEdge(edge), store.onConnect?.(handleConnection);
1434
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
+ });
1435
1673
  XYHandle.onPointerDown(event, {
1436
1674
  handleId: handleId(),
1437
1675
  nodeId: nodeId(),
1438
1676
  isTarget: isTarget(),
1439
1677
  connectionRadius: store.connectionRadius,
1440
1678
  domNode: store.domNode,
1441
- nodeLookup,
1679
+ nodeLookup: gestureLookup,
1442
1680
  connectionMode: store.connectionMode,
1443
1681
  lib: store.lib,
1444
1682
  autoPanOnConnect: store.autoPanOnConnect,
@@ -1623,12 +1861,11 @@ const NodeWrapper = (props) => {
1623
1861
  ...h ? { height: toPxString(h) } : {}
1624
1862
  };
1625
1863
  }, culled = createMemo(() => isNodeCulled(node(), store.cullingViewport)), style = () => ({
1864
+ ...sizeStyle(),
1626
1865
  "z-index": node().internals.z,
1627
1866
  transform: transform(),
1628
1867
  visibility: culled() || !nodeHasDimensions(node()) ? "hidden" : "visible",
1629
- "pointer-events": culled() ? "none" : void 0,
1630
- ...sizeStyle(),
1631
- ...node().style ?? {}
1868
+ "pointer-events": culled() ? "none" : void 0
1632
1869
  });
1633
1870
  createEffect(() => ({
1634
1871
  valid: nodeTypeValid(),
@@ -1888,7 +2125,7 @@ const InitialNodeTypesMap = {
1888
2125
  pendingEntries = updateEntries, scheduleIdleCallback(() => {
1889
2126
  let updates = new Map(pendingEntries);
1890
2127
  pendingEntries = void 0;
1891
- let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode);
2128
+ let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode, store.nodeExtent);
1892
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());
1893
2130
  });
1894
2131
  };
@@ -2038,7 +2275,7 @@ const ConnectionLine = (props) => {
2038
2275
  for (let item of a) if (!b.has(item)) return !1;
2039
2276
  return !0;
2040
2277
  }, Pane = (props) => {
2041
- 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 = {
2042
2279
  x: 0,
2043
2280
  y: 0
2044
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) => {
@@ -2053,7 +2290,7 @@ const ConnectionLine = (props) => {
2053
2290
  if (event.pointerType === "touch" && _panOnDrag() !== !1 && !store.selectionKeyPressed || (containerBounds = container?.getBoundingClientRect() ?? null, !containerBounds)) return;
2054
2291
  let eventTargetIsContainer = event.target === container, isNoKeyEvent = !eventTargetIsContainer && !!event.target.closest(".nokey"), isSelectionActive = props.selectionOnDrag && eventTargetIsContainer || store.selectionKeyPressed;
2055
2292
  if (isNoKeyEvent || !isSelecting() || !isSelectionActive || event.button !== 0 || !event.isPrimary) return;
2056
- event.target?.setPointerCapture?.(event.pointerId), selectionInProgress = !1, autoPanStarted = !1;
2293
+ event.target?.setPointerCapture?.(event.pointerId), selectionSpatialLookup.arm((node) => nodeToRect(node)), selectionInProgress = !1, autoPanStarted = !1;
2057
2294
  let { x, y } = getEventPosition(event, containerBounds), userSelectionFlowOrigin = pointToRendererPoint({
2058
2295
  x,
2059
2296
  y
@@ -2080,14 +2317,21 @@ const ConnectionLine = (props) => {
2080
2317
  width: Math.abs(mouseX - screenStart.x),
2081
2318
  height: Math.abs(mouseY - screenStart.y)
2082
2319
  }, prevSelectedNodeIds = selectedNodeIds, prevSelectedEdgeIds = selectedEdgeIds;
2083
- selectedNodeIds = new Set(getNodesInside(nodeLookup, nextUserSelectRect, store.transform, store.selectionMode === SelectionMode$1.Partial, !0).map((n) => n.id));
2084
- let edgesSelectable = store.defaultEdgeOptions.selectable ?? !0;
2085
- 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();
2086
2330
  for (let nodeId of selectedNodeIds) {
2087
2331
  let nodeConnections = connections[nodeId];
2088
2332
  if (nodeConnections) for (let { edgeId } of Object.values(nodeConnections)) {
2089
2333
  let edge = edgeLookup[edgeId];
2090
- edge && (edge.selectable ?? edgesSelectable) && selectedEdgeIds.add(edgeId);
2334
+ edge && isEdgeSelectable(edge, store) && selectedEdgeIds.add(edgeId);
2091
2335
  }
2092
2336
  }
2093
2337
  isSetEqual(prevSelectedNodeIds, selectedNodeIds) || actions.setNodes((nodes) => {
@@ -2268,7 +2512,7 @@ const ConnectionLine = (props) => {
2268
2512
  <div class="solid-flow__selection" style={styles()} />
2269
2513
  </Show>;
2270
2514
  }, NodeSelection = (props) => {
2271
- 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);
2272
2516
  createEffect(() => ({
2273
2517
  el: ref(),
2274
2518
  focusable: !store.disableKeyboardA11y
@@ -2276,13 +2520,13 @@ const ConnectionLine = (props) => {
2276
2520
  focusable && el?.focus({ preventScroll: !0 });
2277
2521
  });
2278
2522
  let onContextMenu = (event) => {
2279
- let selectedNodes = store.nodes.filter((n) => n.selected);
2523
+ let selectedNodes = store.selectedNodes;
2280
2524
  props.onSelectionContextMenu?.({
2281
2525
  nodes: selectedNodes,
2282
2526
  event
2283
2527
  });
2284
2528
  }, onClick = (event) => {
2285
- let selectedNodes = store.nodes.filter((n) => n.selected);
2529
+ let selectedNodes = store.selectedNodes;
2286
2530
  props.onSelectionClick?.({
2287
2531
  nodes: selectedNodes,
2288
2532
  event
@@ -2447,14 +2691,11 @@ const KeyHandler = (props) => {
2447
2691
  }, handleWindowBlur = () => {
2448
2692
  resetKeysAndSelection(), cancelPointerGestures();
2449
2693
  }, handleDelete = async () => {
2450
- 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({
2451
2696
  nodes: selectedNodes,
2452
2697
  edges: selectedEdges
2453
2698
  });
2454
- (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
2455
- nodes: deletedNodes,
2456
- edges: deletedEdges
2457
- });
2458
2699
  };
2459
2700
  return isServer || (createEventListenerMap(window, {
2460
2701
  keydown: (event) => {
@@ -2472,34 +2713,23 @@ const KeyHandler = (props) => {
2472
2713
  capture: !0,
2473
2714
  passive: !0
2474
2715
  })), null;
2475
- }, 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) => {
2476
2717
  let [domNodeRef, setDomNodeRef] = createSignal(), domNode, _props = merge({
2477
2718
  ...getDefaultFlowStateProps(),
2478
2719
  colorMode: "light",
2479
- deleteKeyCode: "Backspace",
2480
- defaultViewport: {
2481
- x: 0,
2482
- y: 0,
2483
- zoom: 1
2484
- },
2485
- multiSelectionKeyCode: isMacOs() ? "Meta" : "Control",
2486
2720
  nodeClickDistance: 0,
2487
2721
  panOnScroll: !1,
2488
- panActivationKeyCode: "Space",
2489
2722
  preventScrolling: !0,
2490
2723
  panOnDrag: !0,
2491
2724
  panOnScrollSpeed: .5,
2492
2725
  panOnScrollMode: "free",
2493
2726
  paneClickDistance: 0,
2494
- reconnectRadius: 10,
2495
- selectionKeyCode: "Shift",
2496
2727
  selectionOnDrag: !1,
2497
2728
  translateExtent: infiniteExtent,
2498
- zoomActivationKeyCode: isMacOs() ? "Meta" : "Control",
2499
2729
  zoomOnPinch: !0,
2500
2730
  zoomOnDoubleClick: !0,
2501
2731
  zoomOnScroll: !0
2502
- }, 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;
2732
+ }, props), htmlProps = omit(_props, ...FLOW_PROP_KEYS, "children"), TypedSolidFlowContext = SolidFlowContext, solidFlow = useContext(TypedSolidFlowContext) ?? createSolidFlow(_props), { store, actions } = solidFlow;
2503
2733
  onSettled(() => (actions.applyInitialFitView(_props.fitView), actions.setConfig(_props), actions.setDomNode(domNode), () => {
2504
2734
  actions.reset();
2505
2735
  })), createEffect(() => domNodeRef(), (el) => {
@@ -2508,11 +2738,6 @@ const KeyHandler = (props) => {
2508
2738
  actions.setWidth(el.clientWidth), actions.setHeight(el.clientHeight);
2509
2739
  });
2510
2740
  return observer.observe(el), () => observer.disconnect();
2511
- }), createEffect(() => ({
2512
- panZoom: store.panZoom,
2513
- distance: _props.paneClickDistance
2514
- }), ({ panZoom, distance }) => {
2515
- panZoom?.setClickDistance(distance);
2516
2741
  });
2517
2742
  let selectedElements = createMemo(() => ({
2518
2743
  nodes: store.selectedNodes,
@@ -2521,7 +2746,7 @@ const KeyHandler = (props) => {
2521
2746
  createEffect(() => selectedElements(), (params) => {
2522
2747
  untrack(() => _props.onSelectionChange)?.(params);
2523
2748
  });
2524
- let rootStyle = () => ({
2749
+ let asyncSeedGuard = () => (_props.nodes?.length, _props.edges?.length, null), rootStyle = () => ({
2525
2750
  width: toPxString(_props.width),
2526
2751
  height: toPxString(_props.height),
2527
2752
  ..._props.style
@@ -2541,12 +2766,13 @@ const KeyHandler = (props) => {
2541
2766
  });
2542
2767
  }} {...htmlProps}>
2543
2768
  <TypedSolidFlowContext value={solidFlow}>
2769
+ {asyncSeedGuard()}
2544
2770
  <KeyHandler selectionKey={_props.selectionKey} deleteKey={_props.deleteKey} panActivationKey={_props.panActivationKey} multiSelectionKey={_props.multiSelectionKey} zoomActivationKey={_props.zoomActivationKey} />
2545
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}>
2546
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}>
2547
2773
  <Viewport>
2548
2774
  <div class="solid-flow__container solid-flow__viewport-back" />
2549
- <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} />
2550
2776
  <div class="solid-flow__container solid-flow__edge-labels" />
2551
2777
  <ConnectionLine type={_props.connectionLineType} component={_props.connectionLineComponent} containerStyle={_props.connectionLineContainerStyle} style={_props.connectionLineStyle} />
2552
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} />
@@ -2870,24 +3096,23 @@ const DotPattern = (props) => {
2870
3096
  nodeBorderRadius: 5,
2871
3097
  nodeStrokeWidth: 2,
2872
3098
  style: {}
2873
- }), 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(() => ({
2874
3100
  x: -store.viewport.x / store.viewport.zoom,
2875
3101
  y: -store.viewport.y / store.viewport.zoom,
2876
3102
  width: store.width / store.viewport.zoom,
2877
3103
  height: store.height / store.viewport.zoom
2878
- }), getBoundingRect = () => {
2879
- let viewBB = getViewBB();
2880
- return nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(nodeLookup), viewBB) : viewBB;
2881
- }, 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 = () => {
2882
- let boundingRect = getBoundingRect();
2883
- 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();
2884
3112
  }, getY = () => {
2885
- let boundingRect = getBoundingRect();
2886
- return boundingRect.y - (getViewHeight() - boundingRect.height) / 2 - getOffset();
2887
- }, getViewboxWidth = () => getViewWidth() + getOffset() * 2, getViewboxHeight = () => getViewHeight() + getOffset() * 2, strokeWidth = () => _props.maskStrokeWidth ? _props.maskStrokeWidth * getViewScale() : void 0, prevNodeIds = [], nodeIds = () => {
2888
- let currentNodeIds = store.nodes.map((node) => node.id), currentSet = new Set(currentNodeIds);
2889
- return (prevNodeIds.length !== currentNodeIds.length || !prevNodeIds.every((id) => currentSet.has(id))) && (prevNodeIds = currentNodeIds), prevNodeIds;
2890
- };
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]) });
2891
3116
  return <Panel position={_props.position} data-testid="solid-flow__minimap" class={["solid-flow__minimap", _props.class]} style={{
2892
3117
  "--xy-minimap-background-color-props": _props.bgColor,
2893
3118
  ..._props.style
@@ -2904,7 +3129,7 @@ const DotPattern = (props) => {
2904
3129
  domNode: el,
2905
3130
  panZoom,
2906
3131
  getTransform: () => store.transform,
2907
- getViewScale
3132
+ getViewScale: viewScale
2908
3133
  });
2909
3134
  return setMinimap(instance), () => {
2910
3135
  instance.destroy();
@@ -2944,12 +3169,12 @@ const DotPattern = (props) => {
2944
3169
  {(nodeId) => {
2945
3170
  let node = createMemo(() => nodeLookup.get(nodeId())), nodeVisible = () => !!(node() && nodeHasDimensions(node()) && !node().hidden);
2946
3171
  return <Show when={nodeVisible() && getNodeDimensions(node())}>
2947
- {(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} />}
2948
3173
  </Show>;
2949
3174
  }}
2950
3175
  </For>
2951
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
2952
- 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" />
2953
3178
  </svg>;
2954
3179
  }}
2955
3180
  </Show>