@dschz/solid-flow 0.3.0-next.0 → 0.3.0-next.2

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