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

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,13 +1,22 @@
1
- import { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, Position as Position$1, ResizeControlVariant, SelectionMode, SelectionMode as SelectionMode$1, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, addEdge as addEdge$1, areConnectionMapsEqual, calcAutoPan, calculateNodePosition, clampPosition, clampPositionToParent, createDevWarn, createMarkerIds, elementSelectionKeys, evaluateAbsolutePosition, fitViewport, getBezierEdgeCenter, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter, getEdgePosition, getEdgeToolbarTransform, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesBounds as getNodesBounds$1, getNodesInside, getOutgoers, getOverlappingArea, getSmoothStepPath, getSmoothStepPath as getSmoothStepPath$1, getStraightPath, getStraightPath as getStraightPath$1, getViewportForBounds, getViewportForBounds as getViewportForBounds$1, handleConnectionChange, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isInputDOMNode, isMacOs, isNodeBase, isNumeric, isRectObject, mergeAriaLabelConfig, nodeHasDimensions, nodeToRect, panBy, pointToRendererPoint, rendererPointToPoint, shallowNodeData, snapPosition } from "@xyflow/system";
1
+ 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";
2
2
  import { For, Show, createContext, createEffect, createMemo, createProjection, createSignal, createStore, flush, mapArray, merge, omit, onCleanup, onSettled, snapshot, untrack, useContext } from "solid-js";
3
3
  import { createMediaQuery } from "@solid-primitives/media";
4
4
  import { Dynamic, Portal, isServer } from "@solidjs/web";
5
5
  import { createEventListener, createEventListenerMap } from "@solid-primitives/event-listener";
6
6
  //#region src/contexts/edgeId.ts
7
7
  const EdgeIdContext = createContext(null);
8
+ /**
9
+ * Returns the id of the edge this component is rendered inside. Available
10
+ * anywhere in a custom edge's subtree (provided by the edge wrapper), so
11
+ * nested components — like a custom edge label — can learn their host edge
12
+ * without prop drilling.
13
+ *
14
+ * @public
15
+ * @returns a reactive accessor for the surrounding edge's id
16
+ */
8
17
  function useEdgeId() {
9
18
  let ctx = useContext(EdgeIdContext);
10
- if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useEdgeId");
19
+ if (!ctx) throw Error("solid-flow: useEdgeId must be called inside an edge component (anywhere under a custom edge's subtree)");
11
20
  return ctx;
12
21
  }
13
22
  //#endregion
@@ -141,11 +150,13 @@ const getEdgeId = (connection) => {
141
150
  size: 25,
142
151
  reconnecting: !1,
143
152
  style: {}
144
- }), rest = omit(_props, "type", "class", "style", "position", "size", "reconnecting", "children"), { store, nodeLookup, edgeLookup, actions } = useInternalSolidFlow(), edgeId = useEdgeId(), [reconnecting, setReconnecting] = createSignal(!1);
153
+ }), rest = omit(_props, "type", "class", "style", "position", "size", "reconnecting", "onReconnectingChange", "children"), { store, nodeLookup, edgeLookup, actions } = useInternalSolidFlow(), edgeId = useEdgeId(), [reconnecting, setReconnecting] = createSignal(!1);
145
154
  if (!edgeId()) throw Error("[solid-flow]: EdgeReconnectAnchor must be used within an Edge component");
146
- let edge = () => edgeLookup[edgeId()], onPointerDown = (event) => {
155
+ let edge = () => edgeLookup[edgeId()], isReconnecting = () => _props.reconnecting || reconnecting(), setReconnectingState = (next) => {
156
+ setReconnecting(next), _props.onReconnectingChange?.(next);
157
+ }, onPointerDown = (event) => {
147
158
  if (event.button !== 0) return;
148
- setReconnecting(!0), store.onReconnectStart?.(event, edge(), _props.type);
159
+ setReconnectingState(!0), store.onReconnectStart?.(event, edge(), _props.type);
149
160
  let opposite = _props.type === "target" ? {
150
161
  nodeId: edge().source,
151
162
  handleId: edge().sourceHandle ?? null,
@@ -169,7 +180,9 @@ const getEdgeId = (connection) => {
169
180
  edgeUpdaterType: opposite.type,
170
181
  cancelConnection: actions.cancelConnection,
171
182
  panBy: actions.panBy,
172
- updateConnection: actions.setConnection,
183
+ updateConnection: (connection) => {
184
+ actions.setConnection(connection), flush();
185
+ },
173
186
  isValidConnection: store.isValidConnection,
174
187
  onConnectStart: store.onConnectStart,
175
188
  onConnectEnd: store.onConnectEnd,
@@ -181,7 +194,7 @@ const getEdgeId = (connection) => {
181
194
  newEdge = store.onBeforeReconnect?.(newEdge, edge()) ?? newEdge, newEdge && actions.setEdges((edges) => edges.map((e) => e.id === edge().id ? newEdge : e)), store.onReconnect?.(edge(), connection);
182
195
  },
183
196
  onReconnectEnd: (event, connectionState) => {
184
- setReconnecting(!1), store.onReconnectEnd?.(event, edge(), opposite.type, connectionState);
197
+ setReconnectingState(!1), store.onReconnectEnd?.(event, edge(), opposite.type, connectionState);
185
198
  },
186
199
  getTransform: () => store.transform,
187
200
  getFromHandle: () => store.connection.fromHandle,
@@ -204,7 +217,7 @@ const getEdgeId = (connection) => {
204
217
  cursor: "move",
205
218
  ..._props.style
206
219
  }}>
207
- <Show when={!reconnecting()}>{_props.children}</Show>
220
+ <Show when={!isReconnecting()}>{_props.children}</Show>
208
221
  </div>
209
222
  </EdgeLabel>;
210
223
  }, ARIA_NODE_DESC_KEY = "solid-flow__node-desc", ARIA_EDGE_DESC_KEY = "solid-flow__edge-desc", A11yDescriptions = () => {
@@ -227,7 +240,6 @@ const getEdgeId = (connection) => {
227
240
  let [store, setStore] = createStore(edges);
228
241
  return [store, setStore];
229
242
  }, 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
243
  let { width, height } = source;
232
244
  if (!width || !height) return null;
233
245
  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;
@@ -291,7 +303,7 @@ const getEdgeId = (connection) => {
291
303
  selectNodesOnDrag: !0,
292
304
  elevateNodesOnSelect: !0,
293
305
  zIndexMode: "basic",
294
- onlyRenderVisibleElements: !0,
306
+ onlyRenderVisibleElements: !1,
295
307
  disableKeyboardA11y: !1,
296
308
  defaultMarkerColor: "#b1b1b7",
297
309
  ariaLiveMessage: "",
@@ -581,9 +593,17 @@ const createLayoutedEdges = (source) => {
581
593
  return config().zIndexMode;
582
594
  }
583
595
  }), nodeLookup = new RecordMapFacade(internalNodes), initialViewport = getInitialViewport(_props.fitView, _props.initialViewport, _props.width ?? 0, _props.height ?? 0, nodeLookup), [viewportStore, setViewportStore] = createStore(_props.viewport ?? initialViewport);
584
- createEffect(() => config().nodes, (next) => {
596
+ createEffect(() => {
597
+ let next = config().nodes;
598
+ for (let node of next);
599
+ return { next };
600
+ }, ({ next }) => {
585
601
  setNodesStore(() => next);
586
- }, { defer: !0 }), createEffect(() => config().edges, (next) => {
602
+ }, { defer: !0 }), createEffect(() => {
603
+ let next = config().edges;
604
+ for (let edge of next);
605
+ return { next };
606
+ }, ({ next }) => {
587
607
  setEdgesStore(() => next);
588
608
  }, { defer: !0 }), createEffect(() => config().viewport, (next) => {
589
609
  next && setViewportStore(() => next);
@@ -744,9 +764,6 @@ const createLayoutedEdges = (source) => {
744
764
  return zoomActivationKeyPressed();
745
765
  }
746
766
  }), cullingViewport = createCullingViewport({
747
- get onlyRenderVisibleElements() {
748
- return store.onlyRenderVisibleElements;
749
- },
750
767
  get width() {
751
768
  return store.width;
752
769
  },
@@ -1390,6 +1407,7 @@ function useNodeConnectable() {
1390
1407
  }
1391
1408
  //#endregion
1392
1409
  //#region src/components/handle/Handle.tsx
1410
+ /** Connection point on a node; place inside custom nodes to make them connectable. */
1393
1411
  const Handle = (props) => {
1394
1412
  let _props = propDefaults(props, {
1395
1413
  type: "source",
@@ -1571,15 +1589,30 @@ const Handle = (props) => {
1571
1589
  };
1572
1590
  }), dragging;
1573
1591
  }, NodeIdContext = createContext(null);
1592
+ /**
1593
+ * Returns the id of the node this component is rendered inside. Available
1594
+ * anywhere in a custom node's subtree (provided by the node wrapper), so
1595
+ * nested components — like a custom `Handle` — can learn their host node
1596
+ * without prop drilling.
1597
+ *
1598
+ * @public
1599
+ * @returns a reactive accessor for the surrounding node's id
1600
+ */
1574
1601
  function useNodeId() {
1575
1602
  let ctx = useContext(NodeIdContext);
1576
- if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeId");
1603
+ if (!ctx) throw Error("solid-flow: useNodeId must be called inside a node component (anywhere under a custom node's subtree)");
1577
1604
  return ctx;
1578
1605
  }
1579
1606
  //#endregion
1580
1607
  //#region src/components/node/NodeWrapper.tsx
1608
+ /** Internal per-node wrapper: dragging, selection, a11y, measurement, viewport culling, and the dynamic node component. */
1581
1609
  const NodeWrapper = (props) => {
1582
- let { store, nodeLookup, parentIds, actions } = useInternalSolidFlow(), [nodeRef, setNodeRef] = createSignal(), node = () => nodeLookup.get(props.nodeId), nodeId = () => node().id, nodeType = () => node().type ?? "default", deletable = () => node().deletable ?? !0, selectable = () => node().selectable ?? store.elementsSelectable, focusable = () => node().focusable ?? store.nodesFocusable, draggable = () => node().draggable ?? store.nodesDraggable, connectable = () => node().connectable ?? store.nodesConnectable, userNode = () => node().internals.userNode, nodeTypeValid = () => nodeType() in store.nodeTypes, nodeComponent = () => store.nodeTypes[nodeType()], isParentNode = () => !!parentIds[node().id], transform = () => {
1610
+ let { store, nodeLookup, parentIds, actions } = useInternalSolidFlow(), [nodeRef, setNodeRef] = createSignal(), node = () => nodeLookup.get(props.nodeId);
1611
+ onCleanup(() => {
1612
+ let el = nodeRef();
1613
+ el && props.resizeObserver?.unobserve(el);
1614
+ });
1615
+ let nodeId = () => node().id, nodeType = () => node().type ?? "default", deletable = () => node().deletable ?? !0, selectable = () => node().selectable ?? store.elementsSelectable, focusable = () => node().focusable ?? store.nodesFocusable, draggable = () => node().draggable ?? store.nodesDraggable, connectable = () => node().connectable ?? store.nodesConnectable, userNode = () => node().internals.userNode, nodeTypeValid = () => nodeType() in store.nodeTypes, nodeComponent = () => store.nodeTypes[nodeType()], isParentNode = () => !!parentIds[node().id], transform = () => {
1583
1616
  let { x, y } = node().internals.positionAbsolute;
1584
1617
  return `translate(${x}px, ${y}px)`;
1585
1618
  }, sizeStyle = () => {
@@ -1874,6 +1907,7 @@ function useInternalSolidFlow() {
1874
1907
  }
1875
1908
  //#endregion
1876
1909
  //#region src/components/connection/ConnectionLine.tsx
1910
+ /** Internal component rendering the in-progress connection line. */
1877
1911
  const ConnectionLine = (props) => {
1878
1912
  let { store } = useInternalSolidFlow(), connectionStatus = () => getConnectionStatus(store.connection.isValid);
1879
1913
  return <Show when={store.connection.inProgress}>
@@ -1947,16 +1981,28 @@ const ConnectionLine = (props) => {
1947
1981
  </svg>
1948
1982
  </Show>;
1949
1983
  }, EdgeRenderer = (props) => {
1950
- let { store } = useInternalSolidFlow();
1951
- return <div class="solid-flow__edges">
1984
+ let { store, actions } = useInternalSolidFlow(), [focusedEdgeId, setFocusedEdgeId] = createSignal(null), onFocusIn = (event) => {
1985
+ let edgeElement = event.target.closest("[data-id]");
1986
+ setFocusedEdgeId(edgeElement?.getAttribute("data-id") ?? null);
1987
+ };
1988
+ return <div class="solid-flow__edges" onFocusIn={onFocusIn} onFocusOut={() => setFocusedEdgeId(null)}>
1952
1989
  <MarkerDefinition />
1953
1990
 
1954
1991
  <For each={store.visibleEdgeIds}>
1955
- {(edgeId) => <EdgeWrapper edgeId={edgeId} onEdgeClick={props.onEdgeClick} onEdgeContextMenu={props.onEdgeContextMenu} onEdgePointerEnter={props.onEdgePointerEnter} onEdgePointerLeave={props.onEdgePointerLeave} />}
1992
+ {(edgeId) => {
1993
+ let unmounted = createMemo(() => {
1994
+ if (!store.onlyRenderVisibleElements || focusedEdgeId() === edgeId) return !1;
1995
+ let edge = actions.getEdge(edgeId);
1996
+ return !!edge && isEdgeCulled(edge, store.cullingViewport);
1997
+ });
1998
+ return <Show when={!unmounted()}>
1999
+ <EdgeWrapper edgeId={edgeId} onEdgeClick={props.onEdgeClick} onEdgeContextMenu={props.onEdgeContextMenu} onEdgePointerEnter={props.onEdgePointerEnter} onEdgePointerLeave={props.onEdgePointerLeave} />
2000
+ </Show>;
2001
+ }}
1956
2002
  </For>
1957
2003
  </div>;
1958
2004
  }, NodeRenderer = (props) => {
1959
- let { actions, store } = useInternalSolidFlow(), resizeObserver = isServer ? void 0 : new ResizeObserver((entries) => {
2005
+ let { actions, store, nodeLookup } = useInternalSolidFlow(), resizeObserver = isServer ? void 0 : new ResizeObserver((entries) => {
1960
2006
  actions.requestUpdateNodeInternals(entries.map((entry) => {
1961
2007
  let id = entry.target.getAttribute("data-id");
1962
2008
  return [id, {
@@ -1966,11 +2012,25 @@ const ConnectionLine = (props) => {
1966
2012
  }];
1967
2013
  }));
1968
2014
  });
1969
- return onCleanup(() => {
2015
+ onCleanup(() => {
1970
2016
  resizeObserver?.disconnect();
1971
- }), <div class="solid-flow__container solid-flow__nodes">
2017
+ });
2018
+ let [focusedNodeId, setFocusedNodeId] = createSignal(null), onFocusIn = (event) => {
2019
+ let nodeElement = event.target.closest("[data-id]");
2020
+ setFocusedNodeId(nodeElement?.getAttribute("data-id") ?? null);
2021
+ };
2022
+ return <div class="solid-flow__container solid-flow__nodes" onFocusIn={onFocusIn} onFocusOut={() => setFocusedNodeId(null)}>
1972
2023
  <For each={store.visibleNodeIds}>
1973
- {(nodeId) => <NodeWrapper nodeId={nodeId} resizeObserver={resizeObserver} nodeClickDistance={props.nodeClickDistance} onNodeClick={props.onNodeClick} onNodePointerEnter={props.onNodePointerEnter} onNodePointerMove={props.onNodePointerMove} onNodePointerLeave={props.onNodePointerLeave} onNodeDrag={props.onNodeDrag} onNodeDragStart={props.onNodeDragStart} onNodeDragStop={props.onNodeDragStop} onNodeContextMenu={props.onNodeContextMenu} />}
2024
+ {(nodeId) => {
2025
+ let unmounted = createMemo(() => {
2026
+ if (!store.onlyRenderVisibleElements || focusedNodeId() === nodeId) return !1;
2027
+ let node = nodeLookup.get(nodeId);
2028
+ return !!node && isNodeCulled(node, store.cullingViewport);
2029
+ });
2030
+ return <Show when={!unmounted()}>
2031
+ <NodeWrapper nodeId={nodeId} resizeObserver={resizeObserver} nodeClickDistance={props.nodeClickDistance} onNodeClick={props.onNodeClick} onNodePointerEnter={props.onNodePointerEnter} onNodePointerMove={props.onNodePointerMove} onNodePointerLeave={props.onNodePointerLeave} onNodeDrag={props.onNodeDrag} onNodeDragStart={props.onNodeDragStart} onNodeDragStop={props.onNodeDragStop} onNodeContextMenu={props.onNodeContextMenu} />
2032
+ </Show>;
2033
+ }}
1974
2034
  </For>
1975
2035
  </div>;
1976
2036
  }, isSetEqual = (a, b) => {
@@ -2282,25 +2342,14 @@ const ConnectionLine = (props) => {
2282
2342
  * `const { flow, commands } = useSolidFlow()`.
2283
2343
  *
2284
2344
  * @public
2285
- * @returns the flow's read struct, write surface, and deprecated aliases
2345
+ * @returns the flow's read struct and write surface
2286
2346
  */
2287
2347
  function useSolidFlow() {
2288
- let { flow, commands, store, edgeLookup } = useInternalSolidFlow(), getInternalNode = (id) => flow.internalNodes[id];
2348
+ let { flow, commands } = useInternalSolidFlow();
2289
2349
  return {
2290
2350
  ...commands,
2291
2351
  flow,
2292
- commands,
2293
- getInternalNode,
2294
- getNode: (id) => getInternalNode(id)?.internals.userNode,
2295
- getNodes: (ids) => ids ? ids.flatMap((id) => {
2296
- let userNode = flow.internalNodes[id]?.internals.userNode;
2297
- return userNode ? [userNode] : [];
2298
- }) : [...flow.nodes],
2299
- getEdge: (id) => edgeLookup[id],
2300
- getEdges: (ids) => ids ? ids.flatMap((id) => edgeLookup[id] ? [edgeLookup[id]] : []) : [...flow.edges],
2301
- getViewport: () => snapshot(store.viewport),
2302
- getZoom: () => flow.viewport.zoom,
2303
- getHandleConnections: ({ type, id, nodeId }) => Object.values(flow.connections[connectionKey(nodeId, type, id ?? null)] ?? {})
2352
+ commands
2304
2353
  };
2305
2354
  }
2306
2355
  //#endregion
@@ -2531,18 +2580,6 @@ function useViewport() {
2531
2580
  return () => flow.viewport;
2532
2581
  }
2533
2582
  //#endregion
2534
- //#region src/hooks/useHandleEdgeSelect.ts
2535
- function useHandleEdgeSelect() {
2536
- let { store, edgeLookup, actions } = useInternalSolidFlow();
2537
- return (id) => {
2538
- let edge = edgeLookup[id];
2539
- edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (actions.setSelectionRect(void 0), actions.setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && actions.unselectNodesAndEdges({
2540
- nodes: [],
2541
- edges: [edge]
2542
- }) : actions.addSelectedEdges([id]));
2543
- };
2544
- }
2545
- //#endregion
2546
2583
  //#region src/hooks/useInitialized.ts
2547
2584
  /**
2548
2585
  * Hook for seeing if all nodes have been measured.
@@ -2572,11 +2609,16 @@ function useViewportInitialized() {
2572
2609
  //#endregion
2573
2610
  //#region src/hooks/useInternalNode.ts
2574
2611
  /**
2575
- * Hook to get an internal node by id.
2612
+ * Hook to get an internal node (the node enriched with measured dimensions,
2613
+ * absolute position, and z-order) by id.
2614
+ *
2615
+ * The id is an accessor (not a plain string) on purpose: passing a raw
2616
+ * reactive read like `props.nodeId` would capture the value once and silently
2617
+ * lose reactivity — `useInternalNode(() => props.nodeId)` keeps it live.
2576
2618
  *
2577
2619
  * @public
2578
- * @param id - the node id
2579
- * @returns an accessor with an internal node or undefined
2620
+ * @param id - a reactive accessor for the node id
2621
+ * @returns an accessor with the internal node, or undefined while absent
2580
2622
  */
2581
2623
  function useInternalNode(id) {
2582
2624
  let { flow } = useInternalSolidFlow();
@@ -2663,7 +2705,6 @@ const DotPattern = (props) => {
2663
2705
  let _props = propDefaults(props, {
2664
2706
  variant: "dots",
2665
2707
  gap: 20,
2666
- size: 1,
2667
2708
  lineWidth: 1,
2668
2709
  style: {}
2669
2710
  }), { store } = useInternalSolidFlow(), isDots = () => _props.variant === "dots", isCross = () => _props.variant === "cross", patternId = () => `background-pattern-${store.id}-${_props.id ?? ""}`, scaledSize = () => (_props.size ?? DEFAULT_SIZE[_props.variant]) * store.viewport.zoom, gapXY = () => Array.isArray(_props.gap) ? _props.gap : [_props.gap, _props.gap], scaledGap = () => [gapXY()[0] * store.viewport.zoom || 1, gapXY()[1] * store.viewport.zoom || 1], patternDimensions = () => isCross() ? [scaledSize(), scaledSize()] : scaledGap(), patternOffset = () => isDots() ? [scaledSize() / 2, scaledSize() / 2] : [patternDimensions()[0] / 2, patternDimensions()[1] / 2];
@@ -2759,8 +2800,8 @@ const DotPattern = (props) => {
2759
2800
  borderRadius: 5,
2760
2801
  width: 0,
2761
2802
  height: 0
2762
- }), style = () => Object.entries({
2763
- fill: _props.color,
2803
+ }), fill = () => _props.color ?? _props.style?.background ?? _props.style?.["background-color"], style = () => Object.entries({
2804
+ fill: fill(),
2764
2805
  stroke: _props.strokeColor,
2765
2806
  "stroke-width": _props.strokeWidth
2766
2807
  }).filter(([_, value]) => value !== void 0).reduce((acc, [key, value]) => (acc[key] = value, acc), {});
@@ -2768,7 +2809,7 @@ const DotPattern = (props) => {
2768
2809
  "solid-flow__minimap-node",
2769
2810
  { selected: !!_props.selected },
2770
2811
  _props.class
2771
- ]} x={_props.x} y={_props.y} rx={_props.borderRadius} ry={_props.borderRadius} width={_props.width} height={_props.height} shape-rendering={_props.shapeRendering} style={style()} />;
2812
+ ]} x={_props.x} y={_props.y} rx={_props.borderRadius} ry={_props.borderRadius} width={_props.width} height={_props.height} shape-rendering={_props.shapeRendering} style={style()} onClick={_props.onClick ? (event) => _props.onClick(event, _props.id) : void 0} />;
2772
2813
  }, getAttrFunction = (func) => func instanceof Function ? func : () => func, MiniMap = (props) => {
2773
2814
  let { store, nodeLookup } = useInternalSolidFlow(), _props = propDefaults(props, {
2774
2815
  position: "bottom-right",
@@ -2781,7 +2822,7 @@ const DotPattern = (props) => {
2781
2822
  nodeBorderRadius: 5,
2782
2823
  nodeStrokeWidth: 2,
2783
2824
  style: {}
2784
- }), paneProps = omit(_props, "class", "style", "position", "nodeClass", "nodeStrokeColor", "nodeColor", "pannable", "zoomable", "inversePan", "zoomStep", "bgColor", "width", "height", "maskColor", "maskStrokeColor", "maskStrokeWidth", "nodeBorderRadius", "nodeStrokeWidth"), nodeColorFunc = () => _props.nodeColor === void 0 ? void 0 : getAttrFunction(_props.nodeColor), nodeStrokeColorFunc = () => getAttrFunction(_props.nodeStrokeColor), nodeClassFunc = () => getAttrFunction(_props.nodeClass), shapeRendering = () => typeof window > "u" || window.chrome ? "crispEdges" : "geometricPrecision", labelledBy = createMemo(() => `solid-flow__minimap-desc-${store.id}`), getViewBB = () => ({
2825
+ }), paneProps = omit(_props, "class", "style", "position", "nodeClass", "nodeStrokeColor", "nodeColor", "pannable", "zoomable", "inversePan", "zoomStep", "bgColor", "width", "height", "maskColor", "maskStrokeColor", "maskStrokeWidth", "nodeBorderRadius", "nodeStrokeWidth", "nodeComponent", "onClick", "onNodeClick"), nodeColorFunc = () => _props.nodeColor === void 0 ? void 0 : getAttrFunction(_props.nodeColor), nodeStrokeColorFunc = () => getAttrFunction(_props.nodeStrokeColor), nodeClassFunc = () => getAttrFunction(_props.nodeClass), shapeRendering = () => typeof window > "u" || window.chrome ? "crispEdges" : "geometricPrecision", labelledBy = createMemo(() => `solid-flow__minimap-desc-${store.id}`), getViewBB = () => ({
2785
2826
  x: -store.viewport.x / store.viewport.zoom,
2786
2827
  y: -store.viewport.y / store.viewport.zoom,
2787
2828
  width: store.width / store.viewport.zoom,
@@ -2806,7 +2847,7 @@ const DotPattern = (props) => {
2806
2847
  <Show when={store.panZoom}>
2807
2848
  {(panZoom) => {
2808
2849
  let [ref, setRef] = createSignal(), [minimap, setMinimap] = createSignal();
2809
- return createEffect(() => ({
2850
+ createEffect(() => ({
2810
2851
  el: ref(),
2811
2852
  panZoom: panZoom()
2812
2853
  }), ({ el, panZoom }) => {
@@ -2833,7 +2874,19 @@ const DotPattern = (props) => {
2833
2874
  }
2834
2875
  }), ({ instance, options }) => {
2835
2876
  instance?.update(options);
2836
- }), <svg ref={setRef} width={_props.width} height={_props.height} viewBox={`${getX()} ${getY()} ${getViewboxWidth()} ${getViewboxHeight()}`} class="solid-flow__minimap-svg" role="img" aria-labelledby={labelledBy()} style={{
2877
+ });
2878
+ let onSvgClick = (event) => {
2879
+ if (!_props.onClick) return;
2880
+ let [x, y] = minimap()?.pointer(event) ?? [0, 0];
2881
+ _props.onClick(event, {
2882
+ x,
2883
+ y
2884
+ });
2885
+ }, onSvgNodeClick = (event, nodeId) => {
2886
+ let node = nodeLookup.get(nodeId)?.internals.userNode;
2887
+ node && _props.onNodeClick?.(event, node);
2888
+ };
2889
+ return <svg ref={setRef} width={_props.width} height={_props.height} viewBox={`${getX()} ${getY()} ${getViewboxWidth()} ${getViewboxHeight()}`} class="solid-flow__minimap-svg" role="img" aria-labelledby={labelledBy()} onClick={_props.onClick ? onSvgClick : void 0} style={{
2837
2890
  "--xy-minimap-mask-background-color-props": _props.maskColor,
2838
2891
  "--xy-minimap-mask-stroke-color-props": _props.maskStrokeColor,
2839
2892
  "--xy-minimap-mask-stroke-width-props": strokeWidth()
@@ -2843,7 +2896,7 @@ const DotPattern = (props) => {
2843
2896
  {(nodeId) => {
2844
2897
  let node = createMemo(() => nodeLookup.get(nodeId())), nodeVisible = () => !!(node() && nodeHasDimensions(node()) && !node().hidden);
2845
2898
  return <Show when={nodeVisible() && getNodeDimensions(node())}>
2846
- {(nodeDimensions) => <MiniMapNode x={node().internals.positionAbsolute.x} y={node().internals.positionAbsolute.y} borderRadius={_props.nodeBorderRadius} strokeWidth={_props.nodeStrokeWidth} shapeRendering={shapeRendering()} width={nodeDimensions().width} height={nodeDimensions().height} selected={node().selected} color={nodeColorFunc()?.call(null, node())} strokeColor={nodeStrokeColorFunc().call(null, node())} class={nodeClassFunc().call(null, node())} />}
2899
+ {(nodeDimensions) => <Dynamic component={_props.nodeComponent ?? MiniMapNode} id={nodeId()} x={node().internals.positionAbsolute.x} y={node().internals.positionAbsolute.y} borderRadius={_props.nodeBorderRadius} strokeWidth={_props.nodeStrokeWidth} shapeRendering={shapeRendering()} width={nodeDimensions().width} height={nodeDimensions().height} selected={node().selected} color={nodeColorFunc()?.call(null, node())} strokeColor={nodeStrokeColorFunc().call(null, node())} class={nodeClassFunc().call(null, node())} style={node().style} onClick={_props.onNodeClick ? onSvgNodeClick : void 0} />}
2847
2900
  </Show>;
2848
2901
  }}
2849
2902
  </For>
@@ -2960,7 +3013,7 @@ const DotPattern = (props) => {
2960
3013
  </EdgeLabel>
2961
3014
  </Show>;
2962
3015
  }, NodeToolbar = (props) => {
2963
- let { store } = useInternalSolidFlow(), { getNodes, getNodesBounds, getInternalNode } = useSolidFlow(), _props = propDefaults(props, {
3016
+ let { store } = useInternalSolidFlow(), { flow, getNodesBounds } = useSolidFlow(), _props = propDefaults(props, {
2964
3017
  offset: 10,
2965
3018
  position: "top",
2966
3019
  align: "center",
@@ -2969,7 +3022,7 @@ const DotPattern = (props) => {
2969
3022
  let id = useContext(NodeIdContext);
2970
3023
  return id ? id() : "";
2971
3024
  }, toolbarNodes = () => (Array.isArray(_props.nodeId) ? _props.nodeId : [_props.nodeId ?? ctxNodeId()]).reduce((res, nodeId) => {
2972
- let node = getInternalNode(nodeId);
3025
+ let node = flow.internalNodes[nodeId];
2973
3026
  return node && res.push(node), res;
2974
3027
  }, []), transform = () => {
2975
3028
  let nodeRect = getNodesBounds(toolbarNodes());
@@ -2977,7 +3030,7 @@ const DotPattern = (props) => {
2977
3030
  }, zIndex = () => {
2978
3031
  let nodes = toolbarNodes();
2979
3032
  return nodes.length === 0 ? 1 : Math.max(...nodes.map((node) => (node.internals.z || 5) + 1));
2980
- }, selectedNodesCount = () => getNodes().filter((node) => node.selected).length, isActive = () => {
3033
+ }, selectedNodesCount = () => flow.selection.nodes.length, isActive = () => {
2981
3034
  let nodes = toolbarNodes();
2982
3035
  return typeof _props.isVisible == "boolean" ? _props.isVisible : nodes.length === 1 && !!nodes[0].selected && selectedNodesCount() === 1;
2983
3036
  }, showPortal = () => !!(store.domNode && isActive() && toolbarNodes().length > 0);
@@ -2993,6 +3046,6 @@ const DotPattern = (props) => {
2993
3046
  </div>
2994
3047
  </Portal>
2995
3048
  </Show>;
2996
- };
3049
+ }, SelectionMode = SelectionMode$1, getEdgeCenter = getEdgeCenter$1, getBezierEdgeCenter = getBezierEdgeCenter$1;
2997
3050
  //#endregion
2998
- export { Background, BaseEdge, BezierEdge, BezierEdgeInternal, ConnectionLine, ConnectionLineType, ConnectionMode, ControlButton, Controls, DefaultNode, EdgeLabel, EdgeLabelRenderer, EdgeReconnectAnchor, EdgeRenderer, EdgeToolbar, EdgeWrapper, GroupNode, Handle, InputNode, Marker, MarkerDefinition, MarkerType, MiniMap, NodeRenderer, NodeResizer, NodeSelection, NodeToolbar, NodeWrapper, OutputNode, PanOnScrollMode, Pane, Panel, Position, ResizeControl, ResizeControlVariant, Selection, SelectionMode, SmoothStepEdge, SmoothStepEdgeInternal, SolidFlow, SolidFlowProvider, StepEdge, StepEdgeInternal, StraightEdge, StraightEdgeInternal, Viewport, ViewportPortal, Zoom, addEdge, connectionKey, createEdgeStore, createNodeStore, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, useColorMode, useConnection, useEdges, useHandleEdgeSelect, useInternalNode, useNodeConnections, useNodes, useNodesData, useNodesInitialized, useSolidFlow, useUpdateNodeInternals, useViewport, useViewportInitialized };
3051
+ export { Background, BaseEdge, BezierEdge, BezierEdgeInternal, ConnectionLine, ConnectionLineType, ConnectionMode, ControlButton, Controls, DefaultNode, EdgeLabel, EdgeLabelRenderer, EdgeReconnectAnchor, EdgeRenderer, EdgeToolbar, EdgeWrapper, GroupNode, Handle, InputNode, Marker, MarkerDefinition, MarkerType, MiniMap, MiniMapNode, 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, useEdgeId, useEdges, useInternalNode, useNodeConnections, useNodeId, useNodes, useNodesData, useNodesInitialized, useSolidFlow, useUpdateNodeInternals, useViewport, useViewportInitialized };
@@ -93,7 +93,7 @@
93
93
  --xy-connectionline-stroke-width-default: 1;
94
94
  }
95
95
 
96
- connectionline {
96
+ .solid-flow__connectionline {
97
97
  z-index: 1001;
98
98
  overflow: visible;
99
99
  position: absolute;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dschz/solid-flow",
3
- "version": "0.3.0-next.2",
3
+ "version": "0.3.0-next.4",
4
4
  "description": "Solid Flow - A highly customizable Solid library for building node-based editors, workflow systems, diagrams and more.",
5
5
  "keywords": [
6
6
  "ai workflows",