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

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 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 as getBezierEdgeCenter$1, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter as getEdgeCenter$1, 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,233 @@ 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
+ /** Connection point on a node; place inside custom nodes to make them connectable. */
1394
+ const Handle = (props) => {
1395
+ let _props = propDefaults(props, {
1396
+ type: "source",
1397
+ position: "top",
1398
+ isConnectableStart: !0,
1399
+ isConnectableEnd: !0
1400
+ }), { 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;
1401
+ createEffect(() => {
1402
+ if (!_props.onConnect && !_props.onDisconnect) return null;
1403
+ let rec = connections[connectionKey(nodeId(), _props.type, _props.id)], map = /* @__PURE__ */ new Map();
1404
+ for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
1405
+ return { connections: map };
1406
+ }, (current) => {
1407
+ if (!current) return;
1408
+ let { connections: next } = current;
1409
+ prevConnections && !areConnectionMapsEqual(next, prevConnections) && (handleConnectionChange(prevConnections, next, props.onDisconnect), handleConnectionChange(next, prevConnections, props.onConnect)), prevConnections = next;
1410
+ });
1411
+ let onConnectExtended = (connection) => {
1412
+ let handleConnection = {
1413
+ ...connection,
1414
+ id: getEdgeId(connection)
1415
+ }, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
1416
+ actions.addEdge(edge), store.onConnect?.(handleConnection);
1417
+ }, onPointerDown = (event) => {
1418
+ XYHandle.onPointerDown(event, {
1419
+ handleId: handleId(),
1420
+ nodeId: nodeId(),
1421
+ isTarget: isTarget(),
1422
+ connectionRadius: store.connectionRadius,
1423
+ domNode: store.domNode,
1424
+ nodeLookup,
1425
+ connectionMode: store.connectionMode,
1426
+ lib: store.lib,
1427
+ autoPanOnConnect: store.autoPanOnConnect,
1428
+ flowId: store.id,
1429
+ isValidConnection: _props.isValidConnection ?? store.isValidConnection,
1430
+ updateConnection: ((connection) => {
1431
+ actions.setConnection(connection), flush();
1432
+ }),
1433
+ cancelConnection: actions.cancelConnection,
1434
+ panBy: actions.panBy,
1435
+ onConnect: onConnectExtended,
1436
+ onConnectStart: (event, startParams) => {
1437
+ store.onConnectStart?.(event, {
1438
+ nodeId: startParams.nodeId,
1439
+ handleId: startParams.handleId,
1440
+ handleType: startParams.handleType
1441
+ });
1442
+ },
1443
+ onConnectEnd: store.onConnectEnd,
1444
+ getTransform: () => store.transform,
1445
+ getFromHandle: () => store.connection.fromHandle,
1446
+ autoPanSpeed: store.autoPanSpeed,
1447
+ dragThreshold: store.connectionDragThreshold,
1448
+ handleDomNode: event.currentTarget
1449
+ });
1450
+ }, onClick = (event) => {
1451
+ if (!nodeId() || !store.clickConnectStartHandle && !_props.isConnectableStart) return;
1452
+ if (!store.clickConnectStartHandle) {
1453
+ store.onClickConnectStart?.(event, {
1454
+ nodeId: nodeId(),
1455
+ handleId: handleId(),
1456
+ handleType: _props.type
1457
+ }), actions.setClickConnectStartHandle({
1458
+ nodeId: nodeId(),
1459
+ type: _props.type,
1460
+ id: handleId()
1461
+ });
1462
+ return;
1463
+ }
1464
+ let doc = getHostForElement(event.target), isValidConnectionHandler = _props.isValidConnection ?? store.isValidConnection, { connection, isValid } = XYHandle.isValid(event, {
1465
+ handle: {
1430
1466
  nodeId: nodeId(),
1431
1467
  id: handleId(),
1432
1468
  type: _props.type
@@ -1542,7 +1578,8 @@ function useNodeId() {
1542
1578
  return ctx;
1543
1579
  }
1544
1580
  //#endregion
1545
- //#region src/components/graph/node/NodeWrapper.tsx
1581
+ //#region src/components/node/NodeWrapper.tsx
1582
+ /** Internal per-node wrapper: dragging, selection, a11y, measurement, viewport culling, and the dynamic node component. */
1546
1583
  const NodeWrapper = (props) => {
1547
1584
  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
1585
  let { x, y } = node().internals.positionAbsolute;
@@ -1554,10 +1591,11 @@ const NodeWrapper = (props) => {
1554
1591
  ...w ? { width: toPxString(w) } : {},
1555
1592
  ...h ? { height: toPxString(h) } : {}
1556
1593
  };
1557
- }, style = () => ({
1594
+ }, culled = createMemo(() => isNodeCulled(node(), store.cullingViewport)), style = () => ({
1558
1595
  "z-index": node().internals.z,
1559
1596
  transform: transform(),
1560
- visibility: nodeHasDimensions(node()) ? "visible" : "hidden",
1597
+ visibility: culled() || !nodeHasDimensions(node()) ? "hidden" : "visible",
1598
+ "pointer-events": culled() ? "none" : void 0,
1561
1599
  ...sizeStyle(),
1562
1600
  ...node().style ?? {}
1563
1601
  });
@@ -1685,7 +1723,7 @@ const NodeWrapper = (props) => {
1685
1723
  </>;
1686
1724
  };
1687
1725
  //#endregion
1688
- //#region src/data/xyflow.ts
1726
+ //#region src/browser/measure.ts
1689
1727
  /**
1690
1728
  * The DOM side of the measurement pipeline (fork of @xyflow/system's
1691
1729
  * updateNodeInternals): reads each updated node element's dimensions and
@@ -1795,7 +1833,7 @@ function handleExpandParent(children, nodeLookup, getChildNodes, nodeOrigin = [0
1795
1833
  }), changes;
1796
1834
  }
1797
1835
  //#endregion
1798
- //#region src/data/createSolidFlow.tsx
1836
+ //#region src/browser/createSolidFlow.ts
1799
1837
  const InitialNodeTypesMap = {
1800
1838
  input: InputNode,
1801
1839
  output: OutputNode,
@@ -1837,8 +1875,54 @@ function useInternalSolidFlow() {
1837
1875
  return ctx;
1838
1876
  }
1839
1877
  //#endregion
1840
- //#region src/components/graph/marker/Marker.tsx
1841
- const Marker = (props) => {
1878
+ //#region src/components/connection/ConnectionLine.tsx
1879
+ /** Internal component rendering the in-progress connection line. */
1880
+ const ConnectionLine = (props) => {
1881
+ let { store } = useInternalSolidFlow(), connectionStatus = () => getConnectionStatus(store.connection.isValid);
1882
+ return <Show when={store.connection.inProgress}>
1883
+ <svg class="solid-flow__container solid-flow__connectionline" width={store.width} height={store.height} style={props.containerStyle}>
1884
+ <g class={["solid-flow__connection", connectionStatus()]}>
1885
+ <Show when={props.component} fallback={<InternalConnectionLine style={props.style} />}>
1886
+ {(CustomComponent) => {
1887
+ let UserConnectionLine = CustomComponent();
1888
+ 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} />;
1889
+ }}
1890
+ </Show>
1891
+ </g>
1892
+ </svg>
1893
+ </Show>;
1894
+ }, InternalConnectionLine = (props) => {
1895
+ let { store } = useInternalSolidFlow(), path = () => {
1896
+ if (!store.connection.inProgress) return;
1897
+ let pathParams = {
1898
+ sourceX: store.connection.from.x,
1899
+ sourceY: store.connection.from.y,
1900
+ sourcePosition: store.connection.fromPosition,
1901
+ targetX: store.connection.to.x,
1902
+ targetY: store.connection.to.y,
1903
+ targetPosition: store.connection.toPosition
1904
+ };
1905
+ switch (store.connectionLineType) {
1906
+ case "default": {
1907
+ let [path] = getBezierPath$1(pathParams);
1908
+ return path;
1909
+ }
1910
+ case "straight": {
1911
+ let [path] = getStraightPath$1(pathParams);
1912
+ return path;
1913
+ }
1914
+ case "step":
1915
+ case "smoothstep": {
1916
+ let [path] = getSmoothStepPath$1({
1917
+ ...pathParams,
1918
+ borderRadius: store.connectionLineType === "step" ? 0 : void 0
1919
+ });
1920
+ return path;
1921
+ }
1922
+ }
1923
+ };
1924
+ return <path class="solid-flow__connection-path" fill="none" style={props.style} d={path()} />;
1925
+ }, Marker = (props) => {
1842
1926
  let _props = propDefaults(props, {
1843
1927
  markerUnits: "strokeWidth",
1844
1928
  orient: "auto-start-reverse",
@@ -2117,52 +2201,451 @@ const Marker = (props) => {
2117
2201
  }), <div ref={setRef} class="solid-flow__container solid-flow__zoom">
2118
2202
  {props.children}
2119
2203
  </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>
2204
+ }, Selection = (props) => {
2205
+ let _props = propDefaults(props, { isVisible: !0 }), styles = () => ({
2206
+ ..._props.width != null && { width: typeof _props.width == "string" ? _props.width : toPxString(_props.width) },
2207
+ ..._props.height != null && { height: typeof _props.height == "string" ? _props.height : toPxString(_props.height) },
2208
+ ..._props.x != null && _props.y != null && { transform: `translate(${props.x}px, ${props.y}px)` }
2209
+ });
2210
+ return <Show when={_props.isVisible}>
2211
+ <div class="solid-flow__selection" style={styles()} />
2133
2212
  </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;
2213
+ }, NodeSelection = (props) => {
2214
+ let { store, nodeLookup, actions } = useInternalSolidFlow(), [ref, setRef] = createSignal(), bounds = () => store.selectionRectMode === "nodes" ? getInternalNodesBounds(nodeLookup, { filter: (node) => !!node.selected }) : null;
2215
+ createEffect(() => ({
2216
+ el: ref(),
2217
+ focusable: !store.disableKeyboardA11y
2218
+ }), ({ el, focusable }) => {
2219
+ focusable && el?.focus({ preventScroll: !0 });
2220
+ });
2221
+ let onContextMenu = (event) => {
2222
+ let selectedNodes = store.nodes.filter((n) => n.selected);
2223
+ props.onSelectionContextMenu?.({
2224
+ nodes: selectedNodes,
2225
+ event
2226
+ });
2227
+ }, onClick = (event) => {
2228
+ let selectedNodes = store.nodes.filter((n) => n.selected);
2229
+ props.onSelectionClick?.({
2230
+ nodes: selectedNodes,
2231
+ event
2232
+ });
2233
+ };
2234
+ createDraggable(ref, () => ({
2235
+ disabled: !1,
2236
+ onDrag: (event, _, __, nodes) => {
2237
+ props.onNodeDrag?.({
2238
+ event,
2239
+ targetNode: null,
2240
+ nodes
2241
+ });
2242
+ },
2243
+ onDragStart: (event, _, __, nodes) => {
2244
+ props.onNodeDragStart?.({
2245
+ event,
2246
+ targetNode: null,
2247
+ nodes
2248
+ });
2249
+ },
2250
+ onDragStop: (event, _, __, nodes) => {
2251
+ props.onNodeDragStop?.({
2252
+ event,
2253
+ targetNode: null,
2254
+ nodes
2255
+ });
2256
+ }
2257
+ }));
2258
+ let onKeyDown = (event) => {
2259
+ let diff = ARROW_KEY_DIFFS[event.key];
2260
+ diff && (event.preventDefault(), actions.moveSelectedNodes(diff, event.shiftKey ? 4 : 1));
2261
+ };
2262
+ return <Show when={store.selectionRectMode === "nodes" && bounds() && isNumeric(bounds()?.x) && isNumeric(bounds()?.y)}>
2263
+ <div ref={setRef} class={["solid-flow__selection-wrapper", store.noPanClass]} style={{
2264
+ width: toPxString(bounds()?.width),
2265
+ height: toPxString(bounds()?.height),
2266
+ transform: `translate(${bounds()?.x}px, ${bounds()?.y}px)`
2267
+ }} onClick={onClick} onContextMenu={onContextMenu} role={store.disableKeyboardA11y ? void 0 : "button"} tabindex={store.disableKeyboardA11y ? void 0 : -1} onKeyDown={store.disableKeyboardA11y ? void 0 : onKeyDown}>
2268
+ <Selection width="100%" height="100%" x={0} y={0} />
2269
+ </div>
2270
+ </Show>;
2271
+ }, Attribution = (props) => <Show when={!props.proOptions?.hideAttribution}>
2272
+ <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">
2273
+ <a href="https://solidflow.dev" target="_blank" rel="noopener noreferrer" aria-label="Solid Flow attribution">
2274
+ Solid Flow
2275
+ </a>
2276
+ </Panel>
2277
+ </Show>;
2278
+ //#endregion
2279
+ //#region src/hooks/useSolidFlow.ts
2280
+ /**
2281
+ * Hook for accessing the flow instance: `{ flow, commands }` plus the
2282
+ * commands spread at the top level for upstream familiarity.
2283
+ *
2284
+ * `flow` and `commands` are stable identities, so destructuring them is safe:
2285
+ * `const { flow, commands } = useSolidFlow()`.
2286
+ *
2287
+ * @public
2288
+ * @returns the flow's read struct, write surface, and deprecated aliases
2289
+ */
2290
+ function useSolidFlow() {
2291
+ let { flow, commands, store, edgeLookup } = useInternalSolidFlow(), getInternalNode = (id) => flow.internalNodes[id];
2292
+ return {
2293
+ ...commands,
2294
+ flow,
2295
+ commands,
2296
+ getInternalNode,
2297
+ getNode: (id) => getInternalNode(id)?.internals.userNode,
2298
+ getNodes: (ids) => ids ? ids.flatMap((id) => {
2299
+ let userNode = flow.internalNodes[id]?.internals.userNode;
2300
+ return userNode ? [userNode] : [];
2301
+ }) : [...flow.nodes],
2302
+ getEdge: (id) => edgeLookup[id],
2303
+ getEdges: (ids) => ids ? ids.flatMap((id) => edgeLookup[id] ? [edgeLookup[id]] : []) : [...flow.edges],
2304
+ getViewport: () => snapshot(store.viewport),
2305
+ getZoom: () => flow.viewport.zoom,
2306
+ getHandleConnections: ({ type, id, nodeId }) => Object.values(flow.connections[connectionKey(nodeId, type, id ?? null)] ?? {})
2307
+ };
2308
+ }
2309
+ //#endregion
2310
+ //#region src/components/utility/KeyHandler.ts
2311
+ function isKeyObject(key) {
2312
+ return typeof key == "object" && !!key;
2313
+ }
2314
+ function getModifier(key) {
2315
+ return isKeyObject(key) && key.modifier || [];
2316
+ }
2317
+ function getKeyString(key) {
2318
+ return key == null ? "" : isKeyObject(key) ? key.key : key;
2319
+ }
2320
+ function matchesKey(event, keyDef) {
2321
+ if (!keyDef) return !1;
2322
+ let keyString = getKeyString(keyDef);
2323
+ if (!keyString) return !1;
2324
+ let modifiers = getModifier(keyDef);
2325
+ if (Array.isArray(modifiers)) {
2326
+ let modifierMatch = modifiers.flatMap((mod) => mod).every((mod) => {
2327
+ switch (mod.toLowerCase()) {
2328
+ case "meta": return event.metaKey;
2329
+ case "ctrl": return event.ctrlKey;
2330
+ case "alt": return event.altKey;
2331
+ case "shift": return event.shiftKey;
2332
+ default: return !1;
2161
2333
  }
2334
+ });
2335
+ return event.key === keyString && modifierMatch;
2336
+ }
2337
+ return event.key === keyString;
2338
+ }
2339
+ function matchesKeyArray(event, keyDefs) {
2340
+ return keyDefs ? (Array.isArray(keyDefs) ? keyDefs : [keyDefs]).some((keyDef) => matchesKey(event, keyDef)) : !1;
2341
+ }
2342
+ const KeyHandler = (props) => {
2343
+ let { store, actions } = useInternalSolidFlow(), { deleteElements } = useSolidFlow(), _props = {
2344
+ get selectionKey() {
2345
+ return props.selectionKey ?? "Shift";
2346
+ },
2347
+ get multiSelectionKey() {
2348
+ return props.multiSelectionKey ?? (isMacOs() ? "Meta" : "Control");
2349
+ },
2350
+ get deleteKey() {
2351
+ return props.deleteKey ?? "Backspace";
2352
+ },
2353
+ get panActivationKey() {
2354
+ return props.panActivationKey ?? " ";
2355
+ },
2356
+ get zoomActivationKey() {
2357
+ return props.zoomActivationKey ?? (isMacOs() ? "Meta" : "Control");
2358
+ }
2359
+ }, resetKeysAndSelection = () => {
2360
+ actions.setSelectionRect(void 0), actions.setSelectionKeyPressed(!1), actions.setMultiselectionKeyPressed(!1), actions.setDeleteKeyPressed(!1), actions.setPanActivationKeyPressed(!1), actions.setZoomActivationKeyPressed(!1);
2361
+ }, handleDelete = async () => {
2362
+ let selectedNodes = store.nodes.filter((node) => node.selected), selectedEdges = store.edges.filter((edge) => edge.selected), { deletedNodes, deletedEdges } = await deleteElements({
2363
+ nodes: selectedNodes,
2364
+ edges: selectedEdges
2365
+ });
2366
+ (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
2367
+ nodes: deletedNodes,
2368
+ edges: deletedEdges
2369
+ });
2370
+ };
2371
+ return isServer || createEventListenerMap(window, {
2372
+ keydown: (event) => {
2373
+ 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();
2374
+ },
2375
+ keyup: (event) => {
2376
+ 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();
2377
+ },
2378
+ blur: resetKeysAndSelection,
2379
+ contextmenu: resetKeysAndSelection
2380
+ }), null;
2381
+ }, SolidFlow = (props) => {
2382
+ let [domNodeRef, setDomNodeRef] = createSignal(), domNode, _props = merge({
2383
+ ...getDefaultFlowStateProps(),
2384
+ colorMode: "light",
2385
+ deleteKeyCode: "Backspace",
2386
+ defaultViewport: {
2387
+ x: 0,
2388
+ y: 0,
2389
+ zoom: 1
2390
+ },
2391
+ multiSelectionKeyCode: isMacOs() ? "Meta" : "Control",
2392
+ nodeClickDistance: 0,
2393
+ panOnScroll: !1,
2394
+ panActivationKeyCode: "Space",
2395
+ preventScrolling: !0,
2396
+ panOnDrag: !0,
2397
+ panOnScrollSpeed: .5,
2398
+ panOnScrollMode: "free",
2399
+ paneClickDistance: 0,
2400
+ reconnectRadius: 10,
2401
+ selectionKeyCode: "Shift",
2402
+ selectionOnDrag: !1,
2403
+ translateExtent: infiniteExtent,
2404
+ zoomActivationKeyCode: isMacOs() ? "Meta" : "Control",
2405
+ zoomOnPinch: !0,
2406
+ zoomOnDoubleClick: !0,
2407
+ zoomOnScroll: !0
2408
+ }, 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;
2409
+ onSettled(() => (actions.applyInitialFitView(_props.fitView), actions.setConfig(_props), actions.setDomNode(domNode), () => {
2410
+ actions.reset();
2411
+ })), createEffect(() => domNodeRef(), (el) => {
2412
+ if (!el) return;
2413
+ let observer = new ResizeObserver(() => {
2414
+ actions.setWidth(el.clientWidth), actions.setHeight(el.clientHeight);
2415
+ });
2416
+ return observer.observe(el), () => observer.disconnect();
2417
+ }), createEffect(() => ({
2418
+ panZoom: store.panZoom,
2419
+ distance: _props.paneClickDistance
2420
+ }), ({ panZoom, distance }) => {
2421
+ panZoom?.setClickDistance(distance);
2422
+ });
2423
+ let selectedElements = createMemo(() => ({
2424
+ nodes: store.selectedNodes,
2425
+ edges: store.selectedEdges
2426
+ }), { 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) });
2427
+ createEffect(() => selectedElements(), (params) => {
2428
+ untrack(() => _props.onSelectionChange)?.(params);
2429
+ });
2430
+ let rootStyle = () => ({
2431
+ width: toPxString(_props.width),
2432
+ height: toPxString(_props.height),
2433
+ ..._props.style
2434
+ });
2435
+ return <div role="application" data-testid="solid-flow__wrapper" ref={(el) => {
2436
+ domNode = el, setDomNodeRef(el);
2437
+ }} class={[
2438
+ "solid-flow",
2439
+ "solid-flow__container",
2440
+ _props.class,
2441
+ store.colorMode
2442
+ ]} style={rootStyle()} onScroll={(e) => {
2443
+ e.currentTarget.scrollTo({
2444
+ top: 0,
2445
+ left: 0,
2446
+ behavior: "auto"
2447
+ });
2448
+ }} {...htmlProps}>
2449
+ <TypedSolidFlowContext value={solidFlow}>
2450
+ <KeyHandler selectionKey={_props.selectionKey} deleteKey={_props.deleteKey} panActivationKey={_props.panActivationKey} multiSelectionKey={_props.multiSelectionKey} zoomActivationKey={_props.zoomActivationKey} />
2451
+ <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}>
2452
+ <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}>
2453
+ <Viewport>
2454
+ <div class="solid-flow__container solid-flow__viewport-back" />
2455
+ <EdgeRenderer reconnectRadius={_props.reconnectRadius} onEdgeClick={_props.onEdgeClick} onEdgeContextMenu={_props.onEdgeContextMenu} onEdgePointerEnter={_props.onEdgePointerEnter} onEdgePointerLeave={_props.onEdgePointerLeave} defaultEdgeOptions={_props.defaultEdgeOptions} />
2456
+ <div class="solid-flow__container solid-flow__edge-labels" />
2457
+ <ConnectionLine type={_props.connectionLineType} component={_props.connectionLineComponent} containerStyle={_props.connectionLineContainerStyle} style={_props.connectionLineStyle} />
2458
+ <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} />
2459
+ <NodeSelection onSelectionClick={_props.onSelectionClick} onSelectionContextMenu={_props.onSelectionContextMenu} onNodeDrag={_props.onNodeDrag} onNodeDragStart={_props.onNodeDragStart} onNodeDragStop={_props.onNodeDragStop} />
2460
+ </Viewport>
2461
+ <Selection isVisible={!!store.selectionRect && store.selectionRectMode === "user"} width={store.selectionRect?.width} height={store.selectionRect?.height} x={store.selectionRect?.x} y={store.selectionRect?.y} />
2462
+ </Pane>
2463
+ </Zoom>
2464
+ <Attribution proOptions={_props.proOptions} position={_props.attributionPosition} />
2465
+ <A11yDescriptions />
2466
+ {_props.children}
2467
+ </TypedSolidFlowContext>
2468
+ </div>;
2469
+ }, SolidFlowProvider = (props) => {
2470
+ let _props = merge(getDefaultFlowStateProps(), props), solidFlow = createSolidFlow(_props);
2471
+ onCleanup(() => {
2472
+ solidFlow.actions.reset();
2473
+ });
2474
+ let ContextProvider = SolidFlowContext;
2475
+ return <ContextProvider value={solidFlow}>{props.children}</ContextProvider>;
2476
+ };
2477
+ //#endregion
2478
+ //#region src/hooks/useColorMode.ts
2479
+ /**
2480
+ * Hook for receiving the current color mode class ('dark' or 'light').
2481
+ *
2482
+ * When the flow's `colorMode` prop is set to `"system"`, this resolves to the
2483
+ * user's current system preference.
2484
+ *
2485
+ * @public
2486
+ * @returns an accessor for the current color mode class
2487
+ */
2488
+ function useColorMode() {
2489
+ let { flow } = useInternalSolidFlow();
2490
+ return () => flow.colorMode;
2491
+ }
2492
+ //#endregion
2493
+ //#region src/hooks/useConnection.ts
2494
+ /**
2495
+ * Hook for receiving the current connection.
2496
+ *
2497
+ * @public
2498
+ * @returns current connection as a readable store
2499
+ */
2500
+ function useConnection() {
2501
+ let { flow } = useInternalSolidFlow();
2502
+ return () => flow.connection;
2503
+ }
2504
+ //#endregion
2505
+ //#region src/hooks/useGraph.ts
2506
+ /**
2507
+ * Hook for getting the current nodes from the store.
2508
+ *
2509
+ * @public
2510
+ * @returns store with an array of nodes
2511
+ */
2512
+ function useNodes() {
2513
+ let { store } = useInternalSolidFlow();
2514
+ return () => store.nodes;
2515
+ }
2516
+ /**
2517
+ * Hook for getting the current edges from the store.
2518
+ *
2519
+ * @public
2520
+ * @returns store with an array of edges
2521
+ */
2522
+ function useEdges() {
2523
+ let { store } = useInternalSolidFlow();
2524
+ return () => store.edges;
2525
+ }
2526
+ /**
2527
+ * Hook for getting the current viewport from the store.
2528
+ *
2529
+ * @public
2530
+ * @returns store with the viewport object
2531
+ */
2532
+ function useViewport() {
2533
+ let { flow } = useInternalSolidFlow();
2534
+ return () => flow.viewport;
2535
+ }
2536
+ //#endregion
2537
+ //#region src/hooks/useHandleEdgeSelect.ts
2538
+ /** Returns a callback applying edge-selection semantics for the given edge id. */
2539
+ function useHandleEdgeSelect() {
2540
+ let { store, edgeLookup, actions } = useInternalSolidFlow();
2541
+ return (id) => {
2542
+ let edge = edgeLookup[id];
2543
+ 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({
2544
+ nodes: [],
2545
+ edges: [edge]
2546
+ }) : actions.addSelectedEdges([id]));
2547
+ };
2548
+ }
2549
+ //#endregion
2550
+ //#region src/hooks/useInitialized.ts
2551
+ /**
2552
+ * Hook for seeing if all nodes have been measured.
2553
+ *
2554
+ * Returns `false` until every non-hidden node has been rendered and measured.
2555
+ * Useful for running layouting or fitView logic that depends on node dimensions.
2556
+ *
2557
+ * @public
2558
+ * @returns an accessor that indicates whether the nodes are initialized
2559
+ */
2560
+ function useNodesInitialized() {
2561
+ let { flow } = useInternalSolidFlow();
2562
+ return () => flow.nodesInitialized;
2563
+ }
2564
+ /**
2565
+ * Hook for seeing if the viewport is initialized.
2566
+ *
2567
+ * Returns `true` once the pan/zoom instance has been created for the flow.
2568
+ *
2569
+ * @public
2570
+ * @returns an accessor that indicates whether the viewport is initialized
2571
+ */
2572
+ function useViewportInitialized() {
2573
+ let { flow } = useInternalSolidFlow();
2574
+ return () => flow.viewportInitialized;
2575
+ }
2576
+ //#endregion
2577
+ //#region src/hooks/useInternalNode.ts
2578
+ /**
2579
+ * Hook to get an internal node by id.
2580
+ *
2581
+ * @public
2582
+ * @param id - the node id
2583
+ * @returns an accessor with an internal node or undefined
2584
+ */
2585
+ function useInternalNode(id) {
2586
+ let { flow } = useInternalSolidFlow();
2587
+ return () => flow.internalNodes[id()];
2588
+ }
2589
+ //#endregion
2590
+ //#region src/hooks/useNodeConnections.ts
2591
+ /**
2592
+ * Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
2593
+ *
2594
+ * @public
2595
+ * @param param.id - node id - optional if called inside a custom node
2596
+ * @param param.handleType - filter by handle type 'source' or 'target'
2597
+ * @param param.handleId - filter by handle id (this is only needed if the node has multiple handles of the same type)
2598
+ * @todo @param param.onConnect - gets called when a connection is established
2599
+ * @todo @param param.onDisconnect - gets called when a connection is removed
2600
+ * @returns an array with connections
2601
+ */
2602
+ const useNodeConnections = (params) => {
2603
+ let { flow } = useInternalSolidFlow(), ctxNodeId = () => {
2604
+ let id = useContext(NodeIdContext);
2605
+ return id ? id() : "";
2606
+ }, id = () => params().handleId, type = () => params().handleType, nodeId = () => params().id ?? ctxNodeId(), [connections, setConnections] = createSignal([]), prevConnections;
2607
+ return createEffect(() => {
2608
+ let rec = flow.connections[connectionKey(nodeId(), type(), id())], map = /* @__PURE__ */ new Map();
2609
+ for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
2610
+ return { connections: map };
2611
+ }, ({ connections: nextConnections }) => {
2612
+ areConnectionMapsEqual(nextConnections, prevConnections) || (prevConnections = nextConnections, setConnections(Array.from(nextConnections.values())));
2613
+ }), connections;
2614
+ };
2615
+ //#endregion
2616
+ //#region src/hooks/useNodesData.ts
2617
+ function useNodesData(nodeIds) {
2618
+ let { flow } = useInternalSolidFlow(), prevNodesData = [];
2619
+ return createMemo(() => {
2620
+ let nodesData = [], idValues = nodeIds();
2621
+ if (!idValues) return;
2622
+ let ids = Array.isArray(idValues) ? idValues : [idValues];
2623
+ for (let nodeId of ids) {
2624
+ let node = flow.internalNodes[nodeId]?.internals.userNode;
2625
+ node && nodesData.push({
2626
+ id: node.id,
2627
+ type: node.type,
2628
+ data: node.data
2629
+ });
2162
2630
  }
2163
- };
2164
- return <path class="solid-flow__connection-path" fill="none" style={props.style} d={path()} />;
2165
- }, DotPattern = (props) => {
2631
+ return shallowNodeData(nodesData, prevNodesData) || (prevNodesData = nodesData), Array.isArray(idValues) ? nodesData : nodesData[0];
2632
+ });
2633
+ }
2634
+ //#endregion
2635
+ //#region src/hooks/useUpdateNodeInternals.ts
2636
+ /**
2637
+ * Hook for updating node internals. Sugar for `commands.updateNodeInternals`.
2638
+ *
2639
+ * @public
2640
+ * @returns function for updating node internals
2641
+ */
2642
+ function useUpdateNodeInternals() {
2643
+ let { commands } = useInternalSolidFlow();
2644
+ return commands.updateNodeInternals;
2645
+ }
2646
+ //#endregion
2647
+ //#region src/plugins/background/DotPattern.tsx
2648
+ const DotPattern = (props) => {
2166
2649
  let _props = propDefaults(props, { radius: 5 });
2167
2650
  return <circle class={[
2168
2651
  "solid-flow__background-pattern",
@@ -2275,19 +2758,6 @@ const Marker = (props) => {
2275
2758
  {_props.children}
2276
2759
  {_props.afterControls}
2277
2760
  </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
2761
  }, MiniMapNode = (props) => {
2292
2762
  let _props = propDefaults(props, {
2293
2763
  borderRadius: 5,
@@ -2480,209 +2950,20 @@ const Marker = (props) => {
2480
2950
  {(position) => <ResizeControl position={position} class={props.handleClass} style={props.handleStyle} {...rest} />}
2481
2951
  </For>
2482
2952
  </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) => {
2953
+ }, EdgeToolbar = (props) => {
2954
+ 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");
2955
+ return <Show when={isActive()}>
2956
+ <EdgeLabel selectEdgeOnClick={props.selectEdgeOnClick} transparent>
2957
+ <div class={["solid-flow__edge-toolbar", props.class]} style={{
2958
+ position: "absolute",
2959
+ transform: transform(),
2960
+ "transform-origin": "0 0"
2961
+ }} data-id={edgeId()} {...rest}>
2962
+ {props.children}
2963
+ </div>
2964
+ </EdgeLabel>
2965
+ </Show>;
2966
+ }, NodeToolbar = (props) => {
2686
2967
  let { store } = useInternalSolidFlow(), { getNodes, getNodesBounds, getInternalNode } = useSolidFlow(), _props = propDefaults(props, {
2687
2968
  offset: 10,
2688
2969
  position: "top",
@@ -2716,253 +2997,6 @@ const NodeToolbar = (props) => {
2716
2997
  </div>
2717
2998
  </Portal>
2718
2999
  </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
- };
3000
+ }, SelectionMode = SelectionMode$1, getEdgeCenter = getEdgeCenter$1, getBezierEdgeCenter = getBezierEdgeCenter$1;
2967
3001
  //#endregion
2968
3002
  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 };