@dschz/solid-flow 0.3.0-next.1 → 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,9 +1,9 @@
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";
1
2
  import { For, Show, createContext, createEffect, createMemo, createProjection, createSignal, createStore, flush, mapArray, merge, omit, onCleanup, onSettled, snapshot, untrack, useContext } from "solid-js";
2
3
  import { createMediaQuery } from "@solid-primitives/media";
3
- import { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, Position as Position$1, ResizeControlVariant, SelectionMode, SelectionMode as SelectionMode$1, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, addEdge as addEdge$1, areConnectionMapsEqual, calcAutoPan, calculateNodePosition, clampPosition, clampPositionToParent, createDevWarn, createMarkerIds, elementSelectionKeys, evaluateAbsolutePosition, fitViewport, getBezierEdgeCenter, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter, getEdgePosition, getEdgeToolbarTransform, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesBounds as getNodesBounds$1, getNodesInside, getOutgoers, getOverlappingArea, getSmoothStepPath, getSmoothStepPath as getSmoothStepPath$1, getStraightPath, getStraightPath as getStraightPath$1, getViewportForBounds, getViewportForBounds as getViewportForBounds$1, handleConnectionChange, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isNodeBase, isNumeric, isRectObject, mergeAriaLabelConfig, nodeHasDimensions, nodeToRect, panBy, pointToRendererPoint, rendererPointToPoint, shallowNodeData, snapPosition } from "@xyflow/system";
4
4
  import { Dynamic, Portal, isServer } from "@solidjs/web";
5
5
  import { createEventListener, createEventListenerMap } from "@solid-primitives/event-listener";
6
- //#region src/components/contexts/edgeId.tsx
6
+ //#region src/contexts/edgeId.ts
7
7
  const EdgeIdContext = createContext(null);
8
8
  function useEdgeId() {
9
9
  let ctx = useContext(EdgeIdContext);
@@ -53,9 +53,10 @@ function propDefaults(props, defaults) {
53
53
  });
54
54
  return out;
55
55
  }
56
- //#endregion
57
- //#region src/components/graph/edge/EdgeLabelRenderer.tsx
58
- 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) => {
59
60
  let { store } = useInternalSolidFlow(), labelNode = () => store.domNode?.querySelector(".solid-flow__edge-labels");
60
61
  return <Show when={labelNode()}>{(root) => <Portal mount={root()}>{props.children}</Portal>}</Show>;
61
62
  }, EdgeLabel = (props) => {
@@ -222,145 +223,40 @@ const EdgeLabelRenderer = (props) => {
222
223
  </div>
223
224
  </Show>
224
225
  </>;
225
- }, EdgeWrapper = (props) => {
226
- 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) => {
227
- selectable() && actions.handleEdgeSelection(edgeId()), props.onEdgeClick?.({
228
- edge: edge(),
229
- event
230
- });
231
- }, onPointerEvent = (event, type) => {
232
- ({
233
- contextmenu: props.onEdgeContextMenu,
234
- pointerenter: props.onEdgePointerEnter,
235
- pointerleave: props.onEdgePointerLeave
236
- })[type]?.({
237
- edge: edge(),
238
- event
239
- });
240
- }, onKeyDown = (event) => {
241
- store.disableKeyboardA11y || !elementSelectionKeys.includes(event.key) || !selectable() || (event.key === "Escape" ? actions.unselectNodesAndEdges({ edges: [edge()] }) : actions.addSelectedEdges([edge().id]));
242
- }, ariaLabel = () => edge().ariaLabel ?? `Edge from ${edge().source} to ${edge().target}`;
243
- return <EdgeIdContext value={edgeId}>
244
- <Show when={!edge().hidden}>
245
- <svg class="solid-flow__edge-wrapper" style={{ "z-index": edge().zIndex }}>
246
- <g ref={edgeRef} data-id={edge().id} tabindex={focusable() ? 0 : void 0} role={edge().ariaRole ?? (focusable() ? "group" : "img")} aria-label={ariaLabel()} aria-roledescription="edge" aria-describedby={focusable() ? `${ARIA_EDGE_DESC_KEY}-${store.id}` : void 0} class={[
247
- "solid-flow__edge",
248
- `solid-flow__edge-${edgeType()}`,
249
- {
250
- animated: !!edge().animated,
251
- selected: !!edge().selected,
252
- selectable: !!selectable()
253
- },
254
- edge().class
255
- ]} onClick={onClick} onKeyDown={(e) => focusable() && onKeyDown(e)} onContextMenu={(e) => onPointerEvent(e, "contextmenu")} onPointerEnter={(e) => onPointerEvent(e, "pointerenter")} onPointerLeave={(e) => onPointerEvent(e, "pointerleave")} {...edge().domAttributes}>
256
- <Dynamic component={edgeComponent()} id={edge().id} source={edge().source} target={edge().target} sourceX={edge().sourceX} sourceY={edge().sourceY} targetX={edge().targetX} targetY={edge().targetY} sourcePosition={edge().sourcePosition} targetPosition={edge().targetPosition} animated={edge().animated} selected={edge().selected} label={edge().label} labelStyle={edge().labelStyle} data={edge().data} style={edge().style} interactionWidth={edge().interactionWidth} selectable={selectable()} deletable={edge().deletable ?? !0} type={edgeType()} sourceHandleId={edge().sourceHandle} targetHandleId={edge().targetHandle} markerStart={markerStartUrl()} markerEnd={markerEndUrl()} />
257
- </g>
258
- </svg>
259
- </Show>
260
- </EdgeIdContext>;
261
- }, SmoothStepEdge = (props) => {
262
- let pathData = createMemo(() => {
263
- let [path, labelX, labelY] = getSmoothStepPath$1({
264
- sourceX: props.sourceX,
265
- sourceY: props.sourceY,
266
- targetX: props.targetX,
267
- targetY: props.targetY,
268
- sourcePosition: props.sourcePosition,
269
- targetPosition: props.targetPosition,
270
- borderRadius: props.pathOptions?.borderRadius,
271
- offset: props.pathOptions?.offset
272
- });
273
- return {
274
- path,
275
- labelX,
276
- labelY
277
- };
278
- });
279
- 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} />;
280
- }, SmoothStepEdgeInternal = (props) => {
281
- let pathData = () => {
282
- let [path, labelX, labelY] = getSmoothStepPath$1({
283
- sourceX: props.sourceX,
284
- sourceY: props.sourceY,
285
- targetX: props.targetX,
286
- targetY: props.targetY,
287
- sourcePosition: props.sourcePosition,
288
- targetPosition: props.targetPosition
289
- });
290
- return {
291
- path,
292
- labelX,
293
- labelY
294
- };
295
- };
296
- 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} />;
297
- }, StepEdge = (props) => {
298
- let pathData = createMemo(() => {
299
- let [path, labelX, labelY] = getSmoothStepPath$1({
300
- sourceX: props.sourceX,
301
- sourceY: props.sourceY,
302
- targetX: props.targetX,
303
- targetY: props.targetY,
304
- sourcePosition: props.sourcePosition,
305
- targetPosition: props.targetPosition,
306
- borderRadius: 0,
307
- offset: props.pathOptions?.offset
308
- });
309
- return {
310
- path,
311
- labelX,
312
- labelY
313
- };
314
- });
315
- 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} />;
316
- }, StepEdgeInternal = (props) => {
317
- let pathData = () => {
318
- let [path, labelX, labelY] = getSmoothStepPath$1({
319
- sourceX: props.sourceX,
320
- sourceY: props.sourceY,
321
- targetX: props.targetX,
322
- targetY: props.targetY,
323
- sourcePosition: props.sourcePosition,
324
- targetPosition: props.targetPosition,
325
- borderRadius: 0
326
- });
327
- return {
328
- path,
329
- labelX,
330
- labelY
331
- };
332
- };
333
- 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} />;
334
- }, StraightEdge = (props) => {
335
- let pathData = createMemo(() => {
336
- let [path, labelX, labelY] = getStraightPath$1({
337
- sourceX: props.sourceX,
338
- sourceY: props.sourceY,
339
- targetX: props.targetX,
340
- targetY: props.targetY
341
- });
342
- return {
343
- path,
344
- labelX,
345
- labelY
346
- };
347
- });
348
- 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} />;
349
- }, StraightEdgeInternal = (props) => {
350
- let pathData = () => {
351
- let [path, labelX, labelY] = getStraightPath$1({
352
- sourceX: props.sourceX,
353
- sourceY: props.sourceY,
354
- targetX: props.targetX,
355
- targetY: props.targetY
356
- });
357
- return {
358
- path,
359
- labelX,
360
- labelY
361
- };
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
362
239
  };
363
- return <BaseEdge path={pathData().path} labelX={pathData().labelX} labelY={pathData().labelY} label={props.label} labelStyle={props.labelStyle} markerStart={props.markerStart} markerEnd={props.markerEnd} interactionWidth={props.interactionWidth} style={props.style} />;
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);
364
260
  }, getDefaultFlowStateProps = () => ({
365
261
  id: "1",
366
262
  nodes: [],
@@ -395,7 +291,7 @@ const EdgeLabelRenderer = (props) => {
395
291
  selectNodesOnDrag: !0,
396
292
  elevateNodesOnSelect: !0,
397
293
  zIndexMode: "basic",
398
- onlyRenderVisibleElements: !1,
294
+ onlyRenderVisibleElements: !0,
399
295
  disableKeyboardA11y: !1,
400
296
  defaultMarkerColor: "#b1b1b7",
401
297
  ariaLiveMessage: "",
@@ -595,8 +491,10 @@ function calculateChildXYZ(childNode, parentNode, nodeOrigin, nodeExtent, select
595
491
  * the adjacent edges' projections; the record computed re-runs only when
596
492
  * membership or presence changes.
597
493
  *
598
- * The viewport participates only while onlyRenderVisibleElements is active
599
- * panning must not touch edge rows otherwise.
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).
600
498
  *
601
499
  * Rows whose endpoints are missing or unmeasured simply drop out of the
602
500
  * record — the same "no entry" contract the ReactiveMap pipeline had.
@@ -623,13 +521,6 @@ const createLayoutedEdges = (source) => {
623
521
  }, buildRow = (source, edge) => {
624
522
  let sourceNode = source.nodeLookup.get(edge.source), targetNode = source.nodeLookup.get(edge.target);
625
523
  if (!sourceNode || !targetNode) return source.nodeLookup.size, null;
626
- if (source.onlyRenderVisibleElements && !isEdgeVisible({
627
- sourceNode,
628
- targetNode,
629
- width: source.width ?? 0,
630
- height: source.height ?? 0,
631
- transform: source.transform
632
- })) return null;
633
524
  let edgePosition = getEdgePosition({
634
525
  id: edge.id,
635
526
  sourceNode,
@@ -825,8 +716,8 @@ const createLayoutedEdges = (source) => {
825
716
  get visibleNodeIds() {
826
717
  return visibleNodeIds();
827
718
  },
828
- get visibleNodesMap() {
829
- return visibleNodesMap();
719
+ get cullingViewport() {
720
+ return cullingViewport();
830
721
  },
831
722
  get transform() {
832
723
  return transform();
@@ -852,7 +743,20 @@ const createLayoutedEdges = (source) => {
852
743
  get zoomActivationKeyPressed() {
853
744
  return zoomActivationKeyPressed();
854
745
  }
855
- }), visibleNodesMap = createMemo(() => nodeLookup), visibleNodeIds = createMemo(() => Object.keys(internalNodes)), parentIds = createParentIds({ get nodes() {
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() {
856
760
  return store.nodes;
857
761
  } }), edgeLookup = createEdgeLookup({ get edges() {
858
762
  return store.edges;
@@ -874,18 +778,6 @@ const createLayoutedEdges = (source) => {
874
778
  get zIndexMode() {
875
779
  return store.zIndexMode;
876
780
  },
877
- get onlyRenderVisibleElements() {
878
- return store.onlyRenderVisibleElements;
879
- },
880
- get width() {
881
- return store.width;
882
- },
883
- get height() {
884
- return store.height;
885
- },
886
- get transform() {
887
- return store.transform;
888
- },
889
781
  get onError() {
890
782
  return store.onError;
891
783
  },
@@ -1344,89 +1236,232 @@ const createLayoutedEdges = (source) => {
1344
1236
  reset
1345
1237
  }
1346
1238
  };
1347
- }, getEdgeId = (connection) => {
1348
- let { source, sourceHandle, target, targetHandle } = connection;
1349
- return `xy-edge__${source}${sourceHandle || ""}-${target}${targetHandle || ""}`;
1350
- }, NodeConnectableContext = createContext(null);
1351
- function useNodeConnectable() {
1352
- let ctx = useContext(NodeConnectableContext);
1353
- if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeConnectable");
1354
- return ctx;
1355
- }
1356
- //#endregion
1357
- //#region src/components/graph/handle/Handle.tsx
1358
- const Handle = (props) => {
1359
- let _props = propDefaults(props, {
1360
- type: "source",
1361
- position: "top",
1362
- isConnectableStart: !0,
1363
- isConnectableEnd: !0
1364
- }), { store, nodeLookup, connections, actions } = useInternalSolidFlow(), rest = omit(_props, "id", "type", "position", "isConnectable", "isConnectableStart", "isConnectableEnd", "isValidConnection", "onConnect", "onDisconnect", "children", "class", "style"), nodeId = useNodeId(), nodeConnectable = useNodeConnectable(), connectable = () => _props.isConnectable ?? nodeConnectable(), isTarget = () => _props.type === "target", handleId = () => _props.id ?? null, connectionInProcess = () => !!store.connection.fromHandle, connectingFrom = () => store.connection.fromHandle && store.connection.fromHandle.nodeId === nodeId() && store.connection.fromHandle.type === _props.type && store.connection.fromHandle.id === handleId(), connectingTo = () => store.connection.toHandle && store.connection.toHandle.nodeId === nodeId() && store.connection.toHandle.type === _props.type && store.connection.toHandle.id === handleId(), isPossibleTargetHandle = () => store.connectionMode === "strict" ? store.connection.fromHandle?.type !== _props.type : nodeId() !== store.connection.fromHandle?.nodeId || handleId() !== store.connection.fromHandle?.id, valid = () => !!(connectingTo() && store.connection.isValid), prevConnections = null;
1365
- createEffect(() => {
1366
- if (!_props.onConnect && !_props.onDisconnect) return null;
1367
- let rec = connections[connectionKey(nodeId(), _props.type, _props.id)], map = /* @__PURE__ */ new Map();
1368
- for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
1369
- return { connections: map };
1370
- }, (current) => {
1371
- if (!current) return;
1372
- let { connections: next } = current;
1373
- prevConnections && !areConnectionMapsEqual(next, prevConnections) && (handleConnectionChange(prevConnections, next, props.onDisconnect), handleConnectionChange(next, prevConnections, props.onConnect)), prevConnections = next;
1374
- });
1375
- let onConnectExtended = (connection) => {
1376
- let handleConnection = {
1377
- ...connection,
1378
- id: getEdgeId(connection)
1379
- }, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
1380
- actions.addEdge(edge), store.onConnect?.(handleConnection);
1381
- }, onPointerDown = (event) => {
1382
- XYHandle.onPointerDown(event, {
1383
- handleId: handleId(),
1384
- nodeId: nodeId(),
1385
- isTarget: isTarget(),
1386
- connectionRadius: store.connectionRadius,
1387
- domNode: store.domNode,
1388
- nodeLookup,
1389
- connectionMode: store.connectionMode,
1390
- lib: store.lib,
1391
- autoPanOnConnect: store.autoPanOnConnect,
1392
- flowId: store.id,
1393
- isValidConnection: _props.isValidConnection ?? store.isValidConnection,
1394
- updateConnection: ((connection) => {
1395
- actions.setConnection(connection), flush();
1396
- }),
1397
- cancelConnection: actions.cancelConnection,
1398
- panBy: actions.panBy,
1399
- onConnect: onConnectExtended,
1400
- onConnectStart: (event, startParams) => {
1401
- store.onConnectStart?.(event, {
1402
- nodeId: startParams.nodeId,
1403
- handleId: startParams.handleId,
1404
- handleType: startParams.handleType
1405
- });
1406
- },
1407
- onConnectEnd: store.onConnectEnd,
1408
- getTransform: () => store.transform,
1409
- getFromHandle: () => store.connection.fromHandle,
1410
- autoPanSpeed: store.autoPanSpeed,
1411
- dragThreshold: store.connectionDragThreshold,
1412
- handleDomNode: event.currentTarget
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
1413
1247
  });
1414
- }, onClick = (event) => {
1415
- if (!nodeId() || !store.clickConnectStartHandle && !_props.isConnectableStart) return;
1416
- if (!store.clickConnectStartHandle) {
1417
- store.onClickConnectStart?.(event, {
1418
- nodeId: nodeId(),
1419
- handleId: handleId(),
1420
- handleType: _props.type
1421
- }), actions.setClickConnectStartHandle({
1422
- nodeId: nodeId(),
1423
- type: _props.type,
1424
- id: handleId()
1425
- });
1426
- return;
1427
- }
1428
- let doc = getHostForElement(event.target), isValidConnectionHandler = _props.isValidConnection ?? store.isValidConnection, { connection, isValid } = XYHandle.isValid(event, {
1429
- handle: {
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: {
1430
1465
  nodeId: nodeId(),
1431
1466
  id: handleId(),
1432
1467
  type: _props.type
@@ -1542,7 +1577,7 @@ function useNodeId() {
1542
1577
  return ctx;
1543
1578
  }
1544
1579
  //#endregion
1545
- //#region src/components/graph/node/NodeWrapper.tsx
1580
+ //#region src/components/node/NodeWrapper.tsx
1546
1581
  const NodeWrapper = (props) => {
1547
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 = () => {
1548
1583
  let { x, y } = node().internals.positionAbsolute;
@@ -1554,10 +1589,11 @@ const NodeWrapper = (props) => {
1554
1589
  ...w ? { width: toPxString(w) } : {},
1555
1590
  ...h ? { height: toPxString(h) } : {}
1556
1591
  };
1557
- }, style = () => ({
1592
+ }, culled = createMemo(() => isNodeCulled(node(), store.cullingViewport)), style = () => ({
1558
1593
  "z-index": node().internals.z,
1559
1594
  transform: transform(),
1560
- visibility: nodeHasDimensions(node()) ? "visible" : "hidden",
1595
+ visibility: culled() || !nodeHasDimensions(node()) ? "hidden" : "visible",
1596
+ "pointer-events": culled() ? "none" : void 0,
1561
1597
  ...sizeStyle(),
1562
1598
  ...node().style ?? {}
1563
1599
  });
@@ -1685,7 +1721,7 @@ const NodeWrapper = (props) => {
1685
1721
  </>;
1686
1722
  };
1687
1723
  //#endregion
1688
- //#region src/data/xyflow.ts
1724
+ //#region src/browser/measure.ts
1689
1725
  /**
1690
1726
  * The DOM side of the measurement pipeline (fork of @xyflow/system's
1691
1727
  * updateNodeInternals): reads each updated node element's dimensions and
@@ -1795,7 +1831,7 @@ function handleExpandParent(children, nodeLookup, getChildNodes, nodeOrigin = [0
1795
1831
  }), changes;
1796
1832
  }
1797
1833
  //#endregion
1798
- //#region src/data/createSolidFlow.tsx
1834
+ //#region src/browser/createSolidFlow.ts
1799
1835
  const InitialNodeTypesMap = {
1800
1836
  input: InputNode,
1801
1837
  output: OutputNode,
@@ -1837,8 +1873,53 @@ function useInternalSolidFlow() {
1837
1873
  return ctx;
1838
1874
  }
1839
1875
  //#endregion
1840
- //#region src/components/graph/marker/Marker.tsx
1841
- const Marker = (props) => {
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;
1906
+ }
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;
1918
+ }
1919
+ }
1920
+ };
1921
+ return <path class="solid-flow__connection-path" fill="none" style={props.style} d={path()} />;
1922
+ }, Marker = (props) => {
1842
1923
  let _props = propDefaults(props, {
1843
1924
  markerUnits: "strokeWidth",
1844
1925
  orient: "auto-start-reverse",
@@ -2117,52 +2198,450 @@ const Marker = (props) => {
2117
2198
  }), <div ref={setRef} class="solid-flow__container solid-flow__zoom">
2118
2199
  {props.children}
2119
2200
  </div>;
2120
- }, ConnectionLine = (props) => {
2121
- let { store } = useInternalSolidFlow(), connectionStatus = () => getConnectionStatus(store.connection.isValid);
2122
- return <Show when={store.connection.inProgress}>
2123
- <svg class="solid-flow__container solid-flow__connectionline" width={store.width} height={store.height} style={props.containerStyle}>
2124
- <g class={["solid-flow__connection", connectionStatus()]}>
2125
- <Show when={props.component} fallback={<InternalConnectionLine style={props.style} />}>
2126
- {(CustomComponent) => {
2127
- let UserConnectionLine = CustomComponent();
2128
- 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} />;
2129
- }}
2130
- </Show>
2131
- </g>
2132
- </svg>
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()} />
2133
2209
  </Show>;
2134
- }, InternalConnectionLine = (props) => {
2135
- let { store } = useInternalSolidFlow(), path = () => {
2136
- if (!store.connection.inProgress) return;
2137
- let pathParams = {
2138
- sourceX: store.connection.from.x,
2139
- sourceY: store.connection.from.y,
2140
- sourcePosition: store.connection.fromPosition,
2141
- targetX: store.connection.to.x,
2142
- targetY: store.connection.to.y,
2143
- targetPosition: store.connection.toPosition
2144
- };
2145
- switch (store.connectionLineType) {
2146
- case "default": {
2147
- let [path] = getBezierPath$1(pathParams);
2148
- return path;
2149
- }
2150
- case "straight": {
2151
- let [path] = getStraightPath$1(pathParams);
2152
- return path;
2153
- }
2154
- case "step":
2155
- case "smoothstep": {
2156
- let [path] = getSmoothStepPath$1({
2157
- ...pathParams,
2158
- borderRadius: store.connectionLineType === "step" ? 0 : void 0
2159
- });
2160
- return path;
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
2252
+ });
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;
2161
2330
  }
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) => {
2409
+ if (!el) return;
2410
+ let observer = new ResizeObserver(() => {
2411
+ actions.setWidth(el.clientWidth), actions.setHeight(el.clientHeight);
2412
+ });
2413
+ return observer.observe(el), () => observer.disconnect();
2414
+ }), createEffect(() => ({
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),
2430
+ ..._props.style
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>
2465
+ </div>;
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>;
2473
+ };
2474
+ //#endregion
2475
+ //#region src/hooks/useColorMode.ts
2476
+ /**
2477
+ * Hook for receiving the current color mode class ('dark' or 'light').
2478
+ *
2479
+ * When the flow's `colorMode` prop is set to `"system"`, this resolves to the
2480
+ * user's current system preference.
2481
+ *
2482
+ * @public
2483
+ * @returns an accessor for the current color mode class
2484
+ */
2485
+ function useColorMode() {
2486
+ let { flow } = useInternalSolidFlow();
2487
+ return () => flow.colorMode;
2488
+ }
2489
+ //#endregion
2490
+ //#region src/hooks/useConnection.ts
2491
+ /**
2492
+ * Hook for receiving the current connection.
2493
+ *
2494
+ * @public
2495
+ * @returns current connection as a readable store
2496
+ */
2497
+ function useConnection() {
2498
+ let { flow } = useInternalSolidFlow();
2499
+ return () => flow.connection;
2500
+ }
2501
+ //#endregion
2502
+ //#region src/hooks/useGraph.ts
2503
+ /**
2504
+ * Hook for getting the current nodes from the store.
2505
+ *
2506
+ * @public
2507
+ * @returns store with an array of nodes
2508
+ */
2509
+ function useNodes() {
2510
+ let { store } = useInternalSolidFlow();
2511
+ return () => store.nodes;
2512
+ }
2513
+ /**
2514
+ * Hook for getting the current edges from the store.
2515
+ *
2516
+ * @public
2517
+ * @returns store with an array of edges
2518
+ */
2519
+ function useEdges() {
2520
+ let { store } = useInternalSolidFlow();
2521
+ return () => store.edges;
2522
+ }
2523
+ /**
2524
+ * Hook for getting the current viewport from the store.
2525
+ *
2526
+ * @public
2527
+ * @returns store with the viewport object
2528
+ */
2529
+ function useViewport() {
2530
+ let { flow } = useInternalSolidFlow();
2531
+ return () => flow.viewport;
2532
+ }
2533
+ //#endregion
2534
+ //#region src/hooks/useHandleEdgeSelect.ts
2535
+ function useHandleEdgeSelect() {
2536
+ let { store, edgeLookup, actions } = useInternalSolidFlow();
2537
+ return (id) => {
2538
+ let edge = edgeLookup[id];
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({
2540
+ nodes: [],
2541
+ edges: [edge]
2542
+ }) : actions.addSelectedEdges([id]));
2543
+ };
2544
+ }
2545
+ //#endregion
2546
+ //#region src/hooks/useInitialized.ts
2547
+ /**
2548
+ * Hook for seeing if all nodes have been measured.
2549
+ *
2550
+ * Returns `false` until every non-hidden node has been rendered and measured.
2551
+ * Useful for running layouting or fitView logic that depends on node dimensions.
2552
+ *
2553
+ * @public
2554
+ * @returns an accessor that indicates whether the nodes are initialized
2555
+ */
2556
+ function useNodesInitialized() {
2557
+ let { flow } = useInternalSolidFlow();
2558
+ return () => flow.nodesInitialized;
2559
+ }
2560
+ /**
2561
+ * Hook for seeing if the viewport is initialized.
2562
+ *
2563
+ * Returns `true` once the pan/zoom instance has been created for the flow.
2564
+ *
2565
+ * @public
2566
+ * @returns an accessor that indicates whether the viewport is initialized
2567
+ */
2568
+ function useViewportInitialized() {
2569
+ let { flow } = useInternalSolidFlow();
2570
+ return () => flow.viewportInitialized;
2571
+ }
2572
+ //#endregion
2573
+ //#region src/hooks/useInternalNode.ts
2574
+ /**
2575
+ * Hook to get an internal node by id.
2576
+ *
2577
+ * @public
2578
+ * @param id - the node id
2579
+ * @returns an accessor with an internal node or undefined
2580
+ */
2581
+ function useInternalNode(id) {
2582
+ let { flow } = useInternalSolidFlow();
2583
+ return () => flow.internalNodes[id()];
2584
+ }
2585
+ //#endregion
2586
+ //#region src/hooks/useNodeConnections.ts
2587
+ /**
2588
+ * Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
2589
+ *
2590
+ * @public
2591
+ * @param param.id - node id - optional if called inside a custom node
2592
+ * @param param.handleType - filter by handle type 'source' or 'target'
2593
+ * @param param.handleId - filter by handle id (this is only needed if the node has multiple handles of the same type)
2594
+ * @todo @param param.onConnect - gets called when a connection is established
2595
+ * @todo @param param.onDisconnect - gets called when a connection is removed
2596
+ * @returns an array with connections
2597
+ */
2598
+ const useNodeConnections = (params) => {
2599
+ let { flow } = useInternalSolidFlow(), ctxNodeId = () => {
2600
+ let id = useContext(NodeIdContext);
2601
+ return id ? id() : "";
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())));
2609
+ }), connections;
2610
+ };
2611
+ //#endregion
2612
+ //#region src/hooks/useNodesData.ts
2613
+ function useNodesData(nodeIds) {
2614
+ let { flow } = useInternalSolidFlow(), prevNodesData = [];
2615
+ return createMemo(() => {
2616
+ let nodesData = [], idValues = nodeIds();
2617
+ if (!idValues) return;
2618
+ let ids = Array.isArray(idValues) ? idValues : [idValues];
2619
+ for (let nodeId of ids) {
2620
+ let node = flow.internalNodes[nodeId]?.internals.userNode;
2621
+ node && nodesData.push({
2622
+ id: node.id,
2623
+ type: node.type,
2624
+ data: node.data
2625
+ });
2162
2626
  }
2163
- };
2164
- return <path class="solid-flow__connection-path" fill="none" style={props.style} d={path()} />;
2165
- }, DotPattern = (props) => {
2627
+ return shallowNodeData(nodesData, prevNodesData) || (prevNodesData = nodesData), Array.isArray(idValues) ? nodesData : nodesData[0];
2628
+ });
2629
+ }
2630
+ //#endregion
2631
+ //#region src/hooks/useUpdateNodeInternals.ts
2632
+ /**
2633
+ * Hook for updating node internals. Sugar for `commands.updateNodeInternals`.
2634
+ *
2635
+ * @public
2636
+ * @returns function for updating node internals
2637
+ */
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) => {
2166
2645
  let _props = propDefaults(props, { radius: 5 });
2167
2646
  return <circle class={[
2168
2647
  "solid-flow__background-pattern",
@@ -2275,19 +2754,6 @@ const Marker = (props) => {
2275
2754
  {_props.children}
2276
2755
  {_props.afterControls}
2277
2756
  </Panel>;
2278
- }, EdgeToolbar = (props) => {
2279
- let rest = omit(props, "x", "y", "alignX", "alignY", "isVisible", "selectEdgeOnClick", "class", "children"), { store, edgeLookup } = useInternalSolidFlow(), edgeId = useEdgeId(), isActive = () => typeof props.isVisible == "boolean" ? props.isVisible : !!edgeLookup[edgeId()]?.selected, transform = () => getEdgeToolbarTransform(props.x, props.y, store.viewport.zoom, props.alignX ?? "center", props.alignY ?? "center");
2280
- return <Show when={isActive()}>
2281
- <EdgeLabel selectEdgeOnClick={props.selectEdgeOnClick} transparent>
2282
- <div class={["solid-flow__edge-toolbar", props.class]} style={{
2283
- position: "absolute",
2284
- transform: transform(),
2285
- "transform-origin": "0 0"
2286
- }} data-id={edgeId()} {...rest}>
2287
- {props.children}
2288
- </div>
2289
- </EdgeLabel>
2290
- </Show>;
2291
2757
  }, MiniMapNode = (props) => {
2292
2758
  let _props = propDefaults(props, {
2293
2759
  borderRadius: 5,
@@ -2480,209 +2946,20 @@ const Marker = (props) => {
2480
2946
  {(position) => <ResizeControl position={position} class={props.handleClass} style={props.handleStyle} {...rest} />}
2481
2947
  </For>
2482
2948
  </Show>;
2483
- };
2484
- //#endregion
2485
- //#region src/hooks/useColorMode.ts
2486
- /**
2487
- * Hook for receiving the current color mode class ('dark' or 'light').
2488
- *
2489
- * When the flow's `colorMode` prop is set to `"system"`, this resolves to the
2490
- * user's current system preference.
2491
- *
2492
- * @public
2493
- * @returns an accessor for the current color mode class
2494
- */
2495
- function useColorMode() {
2496
- let { flow } = useInternalSolidFlow();
2497
- return () => flow.colorMode;
2498
- }
2499
- //#endregion
2500
- //#region src/hooks/useConnection.tsx
2501
- /**
2502
- * Hook for receiving the current connection.
2503
- *
2504
- * @public
2505
- * @returns current connection as a readable store
2506
- */
2507
- function useConnection() {
2508
- let { flow } = useInternalSolidFlow();
2509
- return () => flow.connection;
2510
- }
2511
- //#endregion
2512
- //#region src/hooks/useGraph.ts
2513
- /**
2514
- * Hook for getting the current nodes from the store.
2515
- *
2516
- * @public
2517
- * @returns store with an array of nodes
2518
- */
2519
- function useNodes() {
2520
- let { store } = useInternalSolidFlow();
2521
- return () => store.nodes;
2522
- }
2523
- /**
2524
- * Hook for getting the current edges from the store.
2525
- *
2526
- * @public
2527
- * @returns store with an array of edges
2528
- */
2529
- function useEdges() {
2530
- let { store } = useInternalSolidFlow();
2531
- return () => store.edges;
2532
- }
2533
- /**
2534
- * Hook for getting the current viewport from the store.
2535
- *
2536
- * @public
2537
- * @returns store with the viewport object
2538
- */
2539
- function useViewport() {
2540
- let { flow } = useInternalSolidFlow();
2541
- return () => flow.viewport;
2542
- }
2543
- //#endregion
2544
- //#region src/hooks/useHandleEdgeSelect.tsx
2545
- function useHandleEdgeSelect() {
2546
- let { store, edgeLookup, actions } = useInternalSolidFlow();
2547
- return (id) => {
2548
- let edge = edgeLookup[id];
2549
- edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (actions.setSelectionRect(void 0), actions.setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && actions.unselectNodesAndEdges({
2550
- nodes: [],
2551
- edges: [edge]
2552
- }) : actions.addSelectedEdges([id]));
2553
- };
2554
- }
2555
- //#endregion
2556
- //#region src/hooks/useInitialized.ts
2557
- /**
2558
- * Hook for seeing if all nodes have been measured.
2559
- *
2560
- * Returns `false` until every non-hidden node has been rendered and measured.
2561
- * Useful for running layouting or fitView logic that depends on node dimensions.
2562
- *
2563
- * @public
2564
- * @returns an accessor that indicates whether the nodes are initialized
2565
- */
2566
- function useNodesInitialized() {
2567
- let { flow } = useInternalSolidFlow();
2568
- return () => flow.nodesInitialized;
2569
- }
2570
- /**
2571
- * Hook for seeing if the viewport is initialized.
2572
- *
2573
- * Returns `true` once the pan/zoom instance has been created for the flow.
2574
- *
2575
- * @public
2576
- * @returns an accessor that indicates whether the viewport is initialized
2577
- */
2578
- function useViewportInitialized() {
2579
- let { flow } = useInternalSolidFlow();
2580
- return () => flow.viewportInitialized;
2581
- }
2582
- //#endregion
2583
- //#region src/hooks/useInternalNode.tsx
2584
- /**
2585
- * Hook to get an internal node by id.
2586
- *
2587
- * @public
2588
- * @param id - the node id
2589
- * @returns an accessor with an internal node or undefined
2590
- */
2591
- function useInternalNode(id) {
2592
- let { flow } = useInternalSolidFlow();
2593
- return () => flow.internalNodes[id()];
2594
- }
2595
- //#endregion
2596
- //#region src/hooks/useNodeConnections.ts
2597
- /**
2598
- * Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
2599
- *
2600
- * @public
2601
- * @param param.id - node id - optional if called inside a custom node
2602
- * @param param.handleType - filter by handle type 'source' or 'target'
2603
- * @param param.handleId - filter by handle id (this is only needed if the node has multiple handles of the same type)
2604
- * @todo @param param.onConnect - gets called when a connection is established
2605
- * @todo @param param.onDisconnect - gets called when a connection is removed
2606
- * @returns an array with connections
2607
- */
2608
- const useNodeConnections = (params) => {
2609
- let { flow } = useInternalSolidFlow(), ctxNodeId = () => {
2610
- let id = useContext(NodeIdContext);
2611
- return id ? id() : "";
2612
- }, id = () => params().handleId, type = () => params().handleType, nodeId = () => params().id ?? ctxNodeId(), [connections, setConnections] = createSignal([]), prevConnections;
2613
- return createEffect(() => {
2614
- let rec = flow.connections[connectionKey(nodeId(), type(), id())], map = /* @__PURE__ */ new Map();
2615
- for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
2616
- return { connections: map };
2617
- }, ({ connections: nextConnections }) => {
2618
- areConnectionMapsEqual(nextConnections, prevConnections) || (prevConnections = nextConnections, setConnections(Array.from(nextConnections.values())));
2619
- }), connections;
2620
- };
2621
- //#endregion
2622
- //#region src/hooks/useNodesData.ts
2623
- function useNodesData(nodeIds) {
2624
- let { flow } = useInternalSolidFlow(), prevNodesData = [];
2625
- return createMemo(() => {
2626
- let nodesData = [], idValues = nodeIds();
2627
- if (!idValues) return;
2628
- let ids = Array.isArray(idValues) ? idValues : [idValues];
2629
- for (let nodeId of ids) {
2630
- let node = flow.internalNodes[nodeId]?.internals.userNode;
2631
- node && nodesData.push({
2632
- id: node.id,
2633
- type: node.type,
2634
- data: node.data
2635
- });
2636
- }
2637
- return shallowNodeData(nodesData, prevNodesData) || (prevNodesData = nodesData), Array.isArray(idValues) ? nodesData : nodesData[0];
2638
- });
2639
- }
2640
- //#endregion
2641
- //#region src/hooks/useSolidFlow.tsx
2642
- /**
2643
- * Hook for accessing the flow instance: `{ flow, commands }` plus the
2644
- * commands spread at the top level for upstream familiarity.
2645
- *
2646
- * `flow` and `commands` are stable identities, so destructuring them is safe:
2647
- * `const { flow, commands } = useSolidFlow()`.
2648
- *
2649
- * @public
2650
- * @returns the flow's read struct, write surface, and deprecated aliases
2651
- */
2652
- function useSolidFlow() {
2653
- let { flow, commands, store, edgeLookup } = useInternalSolidFlow(), getInternalNode = (id) => flow.internalNodes[id];
2654
- return {
2655
- ...commands,
2656
- flow,
2657
- commands,
2658
- getInternalNode,
2659
- getNode: (id) => getInternalNode(id)?.internals.userNode,
2660
- getNodes: (ids) => ids ? ids.flatMap((id) => {
2661
- let userNode = flow.internalNodes[id]?.internals.userNode;
2662
- return userNode ? [userNode] : [];
2663
- }) : [...flow.nodes],
2664
- getEdge: (id) => edgeLookup[id],
2665
- getEdges: (ids) => ids ? ids.flatMap((id) => edgeLookup[id] ? [edgeLookup[id]] : []) : [...flow.edges],
2666
- getViewport: () => snapshot(store.viewport),
2667
- getZoom: () => flow.viewport.zoom,
2668
- getHandleConnections: ({ type, id, nodeId }) => Object.values(flow.connections[connectionKey(nodeId, type, id ?? null)] ?? {})
2669
- };
2670
- }
2671
- //#endregion
2672
- //#region src/hooks/useUpdateNodeInternals.ts
2673
- /**
2674
- * Hook for updating node internals. Sugar for `commands.updateNodeInternals`.
2675
- *
2676
- * @public
2677
- * @returns function for updating node internals
2678
- */
2679
- function useUpdateNodeInternals() {
2680
- let { commands } = useInternalSolidFlow();
2681
- return commands.updateNodeInternals;
2682
- }
2683
- //#endregion
2684
- //#region src/components/graph/plugins/NodeToolbar.tsx
2685
- const NodeToolbar = (props) => {
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) => {
2686
2963
  let { store } = useInternalSolidFlow(), { getNodes, getNodesBounds, getInternalNode } = useSolidFlow(), _props = propDefaults(props, {
2687
2964
  offset: 10,
2688
2965
  position: "top",
@@ -2716,253 +2993,6 @@ const NodeToolbar = (props) => {
2716
2993
  </div>
2717
2994
  </Portal>
2718
2995
  </Show>;
2719
- }, Selection = (props) => {
2720
- let _props = propDefaults(props, { isVisible: !0 }), styles = () => ({
2721
- ..._props.width != null && { width: typeof _props.width == "string" ? _props.width : toPxString(_props.width) },
2722
- ..._props.height != null && { height: typeof _props.height == "string" ? _props.height : toPxString(_props.height) },
2723
- ..._props.x != null && _props.y != null && { transform: `translate(${props.x}px, ${props.y}px)` }
2724
- });
2725
- return <Show when={_props.isVisible}>
2726
- <div class="solid-flow__selection" style={styles()} />
2727
- </Show>;
2728
- }, NodeSelection = (props) => {
2729
- let { store, nodeLookup, actions } = useInternalSolidFlow(), [ref, setRef] = createSignal(), bounds = () => store.selectionRectMode === "nodes" ? getInternalNodesBounds(nodeLookup, { filter: (node) => !!node.selected }) : null;
2730
- createEffect(() => ({
2731
- el: ref(),
2732
- focusable: !store.disableKeyboardA11y
2733
- }), ({ el, focusable }) => {
2734
- focusable && el?.focus({ preventScroll: !0 });
2735
- });
2736
- let onContextMenu = (event) => {
2737
- let selectedNodes = store.nodes.filter((n) => n.selected);
2738
- props.onSelectionContextMenu?.({
2739
- nodes: selectedNodes,
2740
- event
2741
- });
2742
- }, onClick = (event) => {
2743
- let selectedNodes = store.nodes.filter((n) => n.selected);
2744
- props.onSelectionClick?.({
2745
- nodes: selectedNodes,
2746
- event
2747
- });
2748
- };
2749
- createDraggable(ref, () => ({
2750
- disabled: !1,
2751
- onDrag: (event, _, __, nodes) => {
2752
- props.onNodeDrag?.({
2753
- event,
2754
- targetNode: null,
2755
- nodes
2756
- });
2757
- },
2758
- onDragStart: (event, _, __, nodes) => {
2759
- props.onNodeDragStart?.({
2760
- event,
2761
- targetNode: null,
2762
- nodes
2763
- });
2764
- },
2765
- onDragStop: (event, _, __, nodes) => {
2766
- props.onNodeDragStop?.({
2767
- event,
2768
- targetNode: null,
2769
- nodes
2770
- });
2771
- }
2772
- }));
2773
- let onKeyDown = (event) => {
2774
- let diff = ARROW_KEY_DIFFS[event.key];
2775
- diff && (event.preventDefault(), actions.moveSelectedNodes(diff, event.shiftKey ? 4 : 1));
2776
- };
2777
- return <Show when={store.selectionRectMode === "nodes" && bounds() && isNumeric(bounds()?.x) && isNumeric(bounds()?.y)}>
2778
- <div ref={setRef} class={["solid-flow__selection-wrapper", store.noPanClass]} style={{
2779
- width: toPxString(bounds()?.width),
2780
- height: toPxString(bounds()?.height),
2781
- transform: `translate(${bounds()?.x}px, ${bounds()?.y}px)`
2782
- }} onClick={onClick} onContextMenu={onContextMenu} role={store.disableKeyboardA11y ? void 0 : "button"} tabindex={store.disableKeyboardA11y ? void 0 : -1} onKeyDown={store.disableKeyboardA11y ? void 0 : onKeyDown}>
2783
- <Selection width="100%" height="100%" x={0} y={0} />
2784
- </div>
2785
- </Show>;
2786
- }, Attribution = (props) => <Show when={!props.proOptions?.hideAttribution}>
2787
- <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">
2788
- <a href="https://solidflow.dev" target="_blank" rel="noopener noreferrer" aria-label="Solid Flow attribution">
2789
- Solid Flow
2790
- </a>
2791
- </Panel>
2792
- </Show>;
2793
- //#endregion
2794
- //#region src/components/utility/KeyHandler.tsx
2795
- function isKeyObject(key) {
2796
- return typeof key == "object" && !!key;
2797
- }
2798
- function getModifier(key) {
2799
- return isKeyObject(key) && key.modifier || [];
2800
- }
2801
- function getKeyString(key) {
2802
- return key == null ? "" : isKeyObject(key) ? key.key : key;
2803
- }
2804
- function matchesKey(event, keyDef) {
2805
- if (!keyDef) return !1;
2806
- let keyString = getKeyString(keyDef);
2807
- if (!keyString) return !1;
2808
- let modifiers = getModifier(keyDef);
2809
- if (Array.isArray(modifiers)) {
2810
- let modifierMatch = modifiers.flatMap((mod) => mod).every((mod) => {
2811
- switch (mod.toLowerCase()) {
2812
- case "meta": return event.metaKey;
2813
- case "ctrl": return event.ctrlKey;
2814
- case "alt": return event.altKey;
2815
- case "shift": return event.shiftKey;
2816
- default: return !1;
2817
- }
2818
- });
2819
- return event.key === keyString && modifierMatch;
2820
- }
2821
- return event.key === keyString;
2822
- }
2823
- function matchesKeyArray(event, keyDefs) {
2824
- return keyDefs ? (Array.isArray(keyDefs) ? keyDefs : [keyDefs]).some((keyDef) => matchesKey(event, keyDef)) : !1;
2825
- }
2826
- const KeyHandler = (props) => {
2827
- let { store, actions } = useInternalSolidFlow(), { deleteElements } = useSolidFlow(), _props = {
2828
- get selectionKey() {
2829
- return props.selectionKey ?? "Shift";
2830
- },
2831
- get multiSelectionKey() {
2832
- return props.multiSelectionKey ?? (isMacOs() ? "Meta" : "Control");
2833
- },
2834
- get deleteKey() {
2835
- return props.deleteKey ?? "Backspace";
2836
- },
2837
- get panActivationKey() {
2838
- return props.panActivationKey ?? " ";
2839
- },
2840
- get zoomActivationKey() {
2841
- return props.zoomActivationKey ?? (isMacOs() ? "Meta" : "Control");
2842
- }
2843
- }, resetKeysAndSelection = () => {
2844
- actions.setSelectionRect(void 0), actions.setSelectionKeyPressed(!1), actions.setMultiselectionKeyPressed(!1), actions.setDeleteKeyPressed(!1), actions.setPanActivationKeyPressed(!1), actions.setZoomActivationKeyPressed(!1);
2845
- }, handleDelete = async () => {
2846
- let selectedNodes = store.nodes.filter((node) => node.selected), selectedEdges = store.edges.filter((edge) => edge.selected), { deletedNodes, deletedEdges } = await deleteElements({
2847
- nodes: selectedNodes,
2848
- edges: selectedEdges
2849
- });
2850
- (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
2851
- nodes: deletedNodes,
2852
- edges: deletedEdges
2853
- });
2854
- };
2855
- return isServer || createEventListenerMap(window, {
2856
- keydown: (event) => {
2857
- matchesKeyArray(event, _props.selectionKey) && actions.setSelectionKeyPressed(!0), matchesKeyArray(event, _props.multiSelectionKey) && actions.setMultiselectionKeyPressed(!0), matchesKeyArray(event, _props.deleteKey) && !isInputDOMNode(event) && (event.ctrlKey || event.metaKey || event.shiftKey || (actions.setDeleteKeyPressed(!0), handleDelete())), matchesKeyArray(event, _props.panActivationKey) && actions.setPanActivationKeyPressed(!0), matchesKeyArray(event, _props.zoomActivationKey) && actions.setZoomActivationKeyPressed(!0), flush();
2858
- },
2859
- keyup: (event) => {
2860
- matchesKeyArray(event, _props.selectionKey) && actions.setSelectionKeyPressed(!1), matchesKeyArray(event, _props.multiSelectionKey) && actions.setMultiselectionKeyPressed(!1), matchesKeyArray(event, _props.deleteKey) && actions.setDeleteKeyPressed(!1), matchesKeyArray(event, _props.panActivationKey) && actions.setPanActivationKeyPressed(!1), matchesKeyArray(event, _props.zoomActivationKey) && actions.setZoomActivationKeyPressed(!1), flush();
2861
- },
2862
- blur: resetKeysAndSelection,
2863
- contextmenu: resetKeysAndSelection
2864
- }), null;
2865
- }, SolidFlow = (props) => {
2866
- let [domNodeRef, setDomNodeRef] = createSignal(), domNode, _props = merge({
2867
- ...getDefaultFlowStateProps(),
2868
- colorMode: "light",
2869
- deleteKeyCode: "Backspace",
2870
- defaultViewport: {
2871
- x: 0,
2872
- y: 0,
2873
- zoom: 1
2874
- },
2875
- multiSelectionKeyCode: isMacOs() ? "Meta" : "Control",
2876
- nodeClickDistance: 0,
2877
- panOnScroll: !1,
2878
- panActivationKeyCode: "Space",
2879
- preventScrolling: !0,
2880
- panOnDrag: !0,
2881
- panOnScrollSpeed: .5,
2882
- panOnScrollMode: "free",
2883
- paneClickDistance: 0,
2884
- reconnectRadius: 10,
2885
- selectionKeyCode: "Shift",
2886
- selectionOnDrag: !1,
2887
- translateExtent: infiniteExtent,
2888
- zoomActivationKeyCode: isMacOs() ? "Meta" : "Control",
2889
- zoomOnPinch: !0,
2890
- zoomOnDoubleClick: !0,
2891
- zoomOnScroll: !0
2892
- }, props), htmlProps = omit(_props, "nodes", "edges", "nodeTypes", "edgeTypes", "width", "height", "fitView", "fitViewOptions", "nodeOrigin", "nodeDragThreshold", "paneClickDistance", "nodeClickDistance", "minZoom", "maxZoom", "zIndexMode", "initialViewport", "viewport", "translateExtent", "nodeExtent", "selectionKey", "panActivationKey", "deleteKey", "multiSelectionKey", "zoomActivationKey", "panOnDrag", "panOnScroll", "panOnScrollMode", "panOnScrollSpeed", "selectionOnDrag", "selectNodesOnDrag", "preventScrolling", "zoomOnScroll", "zoomOnDoubleClick", "zoomOnPinch", "onlyRenderVisibleElements", "autoPanOnConnect", "autoPanOnNodeDrag", "autoPanOnNodeFocus", "autoPanOnSelection", "autoPanSpeed", "connectionRadius", "connectionMode", "connectionLineType", "connectionLineComponent", "connectionLineStyle", "connectionLineContainerStyle", "connectionDragThreshold", "isValidConnection", "clickConnect", "reconnectRadius", "selectionMode", "elementsSelectable", "nodesDraggable", "nodesConnectable", "nodesFocusable", "edgesFocusable", "disableKeyboardA11y", "ariaLabelConfig", "ariaLiveMessage", "colorMode", "colorModeSSR", "class", "style", "snapGrid", "defaultMarkerColor", "defaultEdgeOptions", "elevateNodesOnSelect", "elevateEdgesOnSelect", "noDragClass", "noPanClass", "noWheelClass", "attributionPosition", "proOptions", "onInit", "onMoveStart", "onMove", "onMoveEnd", "onFlowError", "onNodeClick", "onNodeContextMenu", "onNodeDrag", "onNodeDragStart", "onNodeDragStop", "onNodePointerEnter", "onNodePointerMove", "onNodePointerLeave", "onEdgeClick", "onEdgeContextMenu", "onEdgePointerEnter", "onEdgePointerLeave", "onPaneClick", "onPaneContextMenu", "onSelectionChange", "onSelectionClick", "onSelectionContextMenu", "onSelectionDrag", "onSelectionDragStart", "onSelectionDragStop", "onSelectionStart", "onSelectionEnd", "onConnect", "onConnectStart", "onConnectEnd", "onReconnect", "onReconnectStart", "onReconnectEnd", "onClickConnectStart", "onClickConnectEnd", "onBeforeConnect", "onBeforeReconnect", "onDelete", "onBeforeDelete", "deleteKeyCode", "selectionKeyCode", "panActivationKeyCode", "multiSelectionKeyCode", "zoomActivationKeyCode", "children"), TypedSolidFlowContext = SolidFlowContext, solidFlow = useContext(TypedSolidFlowContext) ?? createSolidFlow(_props), { store, actions } = solidFlow;
2893
- onSettled(() => (actions.applyInitialFitView(_props.fitView), actions.setConfig(_props), actions.setDomNode(domNode), () => {
2894
- actions.reset();
2895
- })), createEffect(() => domNodeRef(), (el) => {
2896
- if (!el) return;
2897
- let observer = new ResizeObserver(() => {
2898
- actions.setWidth(el.clientWidth), actions.setHeight(el.clientHeight);
2899
- });
2900
- return observer.observe(el), () => observer.disconnect();
2901
- }), createEffect(() => ({
2902
- panZoom: store.panZoom,
2903
- distance: _props.paneClickDistance
2904
- }), ({ panZoom, distance }) => {
2905
- panZoom?.setClickDistance(distance);
2906
- });
2907
- let selectedElements = createMemo(() => ({
2908
- nodes: store.selectedNodes,
2909
- edges: store.selectedEdges
2910
- }), { equals: (a, b) => a.nodes.length === b.nodes.length && a.edges.length === b.edges.length && a.nodes.every((node, i) => node.id === b.nodes[i].id) && a.edges.every((edge, i) => edge.id === b.edges[i].id) });
2911
- createEffect(() => selectedElements(), (params) => {
2912
- untrack(() => _props.onSelectionChange)?.(params);
2913
- });
2914
- let rootStyle = () => ({
2915
- width: toPxString(_props.width),
2916
- height: toPxString(_props.height),
2917
- ..._props.style
2918
- });
2919
- return <div role="application" data-testid="solid-flow__wrapper" ref={(el) => {
2920
- domNode = el, setDomNodeRef(el);
2921
- }} class={[
2922
- "solid-flow",
2923
- "solid-flow__container",
2924
- _props.class,
2925
- store.colorMode
2926
- ]} style={rootStyle()} onScroll={(e) => {
2927
- e.currentTarget.scrollTo({
2928
- top: 0,
2929
- left: 0,
2930
- behavior: "auto"
2931
- });
2932
- }} {...htmlProps}>
2933
- <TypedSolidFlowContext value={solidFlow}>
2934
- <KeyHandler selectionKey={_props.selectionKey} deleteKey={_props.deleteKey} panActivationKey={_props.panActivationKey} multiSelectionKey={_props.multiSelectionKey} zoomActivationKey={_props.zoomActivationKey} />
2935
- <Zoom panOnScrollMode={_props.panOnScrollMode} preventScrolling={_props.preventScrolling} zoomOnScroll={_props.zoomOnScroll} zoomOnDoubleClick={_props.zoomOnDoubleClick} zoomOnPinch={_props.zoomOnPinch} panOnScroll={_props.panOnScroll} panOnScrollSpeed={_props.panOnScrollSpeed} panOnDrag={_props.panOnDrag} paneClickDistance={_props.paneClickDistance} selectionOnDrag={_props.selectionOnDrag} onMoveStart={_props.onMoveStart} onMove={_props.onMove} onMoveEnd={_props.onMoveEnd} onViewportInitialized={_props.onInit} initialViewport={_props.viewport || _props.initialViewport}>
2936
- <Pane onPaneClick={_props.onPaneClick} onPaneContextMenu={_props.onPaneContextMenu} onSelectionStart={_props.onSelectionStart} onSelectionEnd={_props.onSelectionEnd} panOnDrag={_props.panOnDrag} selectionOnDrag={_props.selectionOnDrag} paneClickDistance={_props.paneClickDistance} autoPanOnSelection={_props.autoPanOnSelection}>
2937
- <Viewport>
2938
- <div class="solid-flow__container solid-flow__viewport-back" />
2939
- <EdgeRenderer reconnectRadius={_props.reconnectRadius} onEdgeClick={_props.onEdgeClick} onEdgeContextMenu={_props.onEdgeContextMenu} onEdgePointerEnter={_props.onEdgePointerEnter} onEdgePointerLeave={_props.onEdgePointerLeave} defaultEdgeOptions={_props.defaultEdgeOptions} />
2940
- <div class="solid-flow__container solid-flow__edge-labels" />
2941
- <ConnectionLine type={_props.connectionLineType} component={_props.connectionLineComponent} containerStyle={_props.connectionLineContainerStyle} style={_props.connectionLineStyle} />
2942
- <NodeRenderer nodeClickDistance={_props.nodeClickDistance} onNodeClick={_props.onNodeClick} onNodeContextMenu={_props.onNodeContextMenu} onNodePointerEnter={_props.onNodePointerEnter} onNodePointerMove={_props.onNodePointerMove} onNodePointerLeave={_props.onNodePointerLeave} onNodeDrag={_props.onNodeDrag} onNodeDragStart={_props.onNodeDragStart} onNodeDragStop={_props.onNodeDragStop} />
2943
- <NodeSelection onSelectionClick={_props.onSelectionClick} onSelectionContextMenu={_props.onSelectionContextMenu} onNodeDrag={_props.onNodeDrag} onNodeDragStart={_props.onNodeDragStart} onNodeDragStop={_props.onNodeDragStop} />
2944
- </Viewport>
2945
- <Selection isVisible={!!store.selectionRect && store.selectionRectMode === "user"} width={store.selectionRect?.width} height={store.selectionRect?.height} x={store.selectionRect?.x} y={store.selectionRect?.y} />
2946
- </Pane>
2947
- </Zoom>
2948
- <Attribution proOptions={_props.proOptions} position={_props.attributionPosition} />
2949
- <A11yDescriptions />
2950
- {_props.children}
2951
- </TypedSolidFlowContext>
2952
- </div>;
2953
- }, SolidFlowProvider = (props) => {
2954
- let _props = merge(getDefaultFlowStateProps(), props), solidFlow = createSolidFlow(_props);
2955
- onCleanup(() => {
2956
- solidFlow.actions.reset();
2957
- });
2958
- let ContextProvider = SolidFlowContext;
2959
- return <ContextProvider value={solidFlow}>{props.children}</ContextProvider>;
2960
- }, createEdgeStore = (edges) => {
2961
- let [store, setStore] = createStore(edges);
2962
- return [store, setStore];
2963
- }, createNodeStore = (nodes) => {
2964
- let [store, setStore] = createStore(nodes);
2965
- return [store, setStore];
2966
2996
  };
2967
2997
  //#endregion
2968
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 };