@dschz/solid-flow 0.3.0-next.3 → 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.
@@ -5,9 +5,18 @@ 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
  },
@@ -1572,16 +1589,30 @@ const Handle = (props) => {
1572
1589
  };
1573
1590
  }), dragging;
1574
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
+ */
1575
1601
  function useNodeId() {
1576
1602
  let ctx = useContext(NodeIdContext);
1577
- 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)");
1578
1604
  return ctx;
1579
1605
  }
1580
1606
  //#endregion
1581
1607
  //#region src/components/node/NodeWrapper.tsx
1582
1608
  /** Internal per-node wrapper: dragging, selection, a11y, measurement, viewport culling, and the dynamic node component. */
1583
1609
  const NodeWrapper = (props) => {
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 = () => {
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 = () => {
1585
1616
  let { x, y } = node().internals.positionAbsolute;
1586
1617
  return `translate(${x}px, ${y}px)`;
1587
1618
  }, sizeStyle = () => {
@@ -1950,16 +1981,28 @@ const ConnectionLine = (props) => {
1950
1981
  </svg>
1951
1982
  </Show>;
1952
1983
  }, EdgeRenderer = (props) => {
1953
- let { store } = useInternalSolidFlow();
1954
- 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)}>
1955
1989
  <MarkerDefinition />
1956
1990
 
1957
1991
  <For each={store.visibleEdgeIds}>
1958
- {(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
+ }}
1959
2002
  </For>
1960
2003
  </div>;
1961
2004
  }, NodeRenderer = (props) => {
1962
- let { actions, store } = useInternalSolidFlow(), resizeObserver = isServer ? void 0 : new ResizeObserver((entries) => {
2005
+ let { actions, store, nodeLookup } = useInternalSolidFlow(), resizeObserver = isServer ? void 0 : new ResizeObserver((entries) => {
1963
2006
  actions.requestUpdateNodeInternals(entries.map((entry) => {
1964
2007
  let id = entry.target.getAttribute("data-id");
1965
2008
  return [id, {
@@ -1969,11 +2012,25 @@ const ConnectionLine = (props) => {
1969
2012
  }];
1970
2013
  }));
1971
2014
  });
1972
- return onCleanup(() => {
2015
+ onCleanup(() => {
1973
2016
  resizeObserver?.disconnect();
1974
- }), <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)}>
1975
2023
  <For each={store.visibleNodeIds}>
1976
- {(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
+ }}
1977
2034
  </For>
1978
2035
  </div>;
1979
2036
  }, isSetEqual = (a, b) => {
@@ -2285,25 +2342,14 @@ const ConnectionLine = (props) => {
2285
2342
  * `const { flow, commands } = useSolidFlow()`.
2286
2343
  *
2287
2344
  * @public
2288
- * @returns the flow's read struct, write surface, and deprecated aliases
2345
+ * @returns the flow's read struct and write surface
2289
2346
  */
2290
2347
  function useSolidFlow() {
2291
- let { flow, commands, store, edgeLookup } = useInternalSolidFlow(), getInternalNode = (id) => flow.internalNodes[id];
2348
+ let { flow, commands } = useInternalSolidFlow();
2292
2349
  return {
2293
2350
  ...commands,
2294
2351
  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)] ?? {})
2352
+ commands
2307
2353
  };
2308
2354
  }
2309
2355
  //#endregion
@@ -2534,19 +2580,6 @@ function useViewport() {
2534
2580
  return () => flow.viewport;
2535
2581
  }
2536
2582
  //#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
2583
  //#region src/hooks/useInitialized.ts
2551
2584
  /**
2552
2585
  * Hook for seeing if all nodes have been measured.
@@ -2576,11 +2609,16 @@ function useViewportInitialized() {
2576
2609
  //#endregion
2577
2610
  //#region src/hooks/useInternalNode.ts
2578
2611
  /**
2579
- * 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.
2580
2618
  *
2581
2619
  * @public
2582
- * @param id - the node id
2583
- * @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
2584
2622
  */
2585
2623
  function useInternalNode(id) {
2586
2624
  let { flow } = useInternalSolidFlow();
@@ -2667,7 +2705,6 @@ const DotPattern = (props) => {
2667
2705
  let _props = propDefaults(props, {
2668
2706
  variant: "dots",
2669
2707
  gap: 20,
2670
- size: 1,
2671
2708
  lineWidth: 1,
2672
2709
  style: {}
2673
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];
@@ -2763,8 +2800,8 @@ const DotPattern = (props) => {
2763
2800
  borderRadius: 5,
2764
2801
  width: 0,
2765
2802
  height: 0
2766
- }), style = () => Object.entries({
2767
- fill: _props.color,
2803
+ }), fill = () => _props.color ?? _props.style?.background ?? _props.style?.["background-color"], style = () => Object.entries({
2804
+ fill: fill(),
2768
2805
  stroke: _props.strokeColor,
2769
2806
  "stroke-width": _props.strokeWidth
2770
2807
  }).filter(([_, value]) => value !== void 0).reduce((acc, [key, value]) => (acc[key] = value, acc), {});
@@ -2772,7 +2809,7 @@ const DotPattern = (props) => {
2772
2809
  "solid-flow__minimap-node",
2773
2810
  { selected: !!_props.selected },
2774
2811
  _props.class
2775
- ]} 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} />;
2776
2813
  }, getAttrFunction = (func) => func instanceof Function ? func : () => func, MiniMap = (props) => {
2777
2814
  let { store, nodeLookup } = useInternalSolidFlow(), _props = propDefaults(props, {
2778
2815
  position: "bottom-right",
@@ -2785,7 +2822,7 @@ const DotPattern = (props) => {
2785
2822
  nodeBorderRadius: 5,
2786
2823
  nodeStrokeWidth: 2,
2787
2824
  style: {}
2788
- }), 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 = () => ({
2789
2826
  x: -store.viewport.x / store.viewport.zoom,
2790
2827
  y: -store.viewport.y / store.viewport.zoom,
2791
2828
  width: store.width / store.viewport.zoom,
@@ -2810,7 +2847,7 @@ const DotPattern = (props) => {
2810
2847
  <Show when={store.panZoom}>
2811
2848
  {(panZoom) => {
2812
2849
  let [ref, setRef] = createSignal(), [minimap, setMinimap] = createSignal();
2813
- return createEffect(() => ({
2850
+ createEffect(() => ({
2814
2851
  el: ref(),
2815
2852
  panZoom: panZoom()
2816
2853
  }), ({ el, panZoom }) => {
@@ -2837,7 +2874,19 @@ const DotPattern = (props) => {
2837
2874
  }
2838
2875
  }), ({ instance, options }) => {
2839
2876
  instance?.update(options);
2840
- }), <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={{
2841
2890
  "--xy-minimap-mask-background-color-props": _props.maskColor,
2842
2891
  "--xy-minimap-mask-stroke-color-props": _props.maskStrokeColor,
2843
2892
  "--xy-minimap-mask-stroke-width-props": strokeWidth()
@@ -2847,7 +2896,7 @@ const DotPattern = (props) => {
2847
2896
  {(nodeId) => {
2848
2897
  let node = createMemo(() => nodeLookup.get(nodeId())), nodeVisible = () => !!(node() && nodeHasDimensions(node()) && !node().hidden);
2849
2898
  return <Show when={nodeVisible() && getNodeDimensions(node())}>
2850
- {(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} />}
2851
2900
  </Show>;
2852
2901
  }}
2853
2902
  </For>
@@ -2964,7 +3013,7 @@ const DotPattern = (props) => {
2964
3013
  </EdgeLabel>
2965
3014
  </Show>;
2966
3015
  }, NodeToolbar = (props) => {
2967
- let { store } = useInternalSolidFlow(), { getNodes, getNodesBounds, getInternalNode } = useSolidFlow(), _props = propDefaults(props, {
3016
+ let { store } = useInternalSolidFlow(), { flow, getNodesBounds } = useSolidFlow(), _props = propDefaults(props, {
2968
3017
  offset: 10,
2969
3018
  position: "top",
2970
3019
  align: "center",
@@ -2973,7 +3022,7 @@ const DotPattern = (props) => {
2973
3022
  let id = useContext(NodeIdContext);
2974
3023
  return id ? id() : "";
2975
3024
  }, toolbarNodes = () => (Array.isArray(_props.nodeId) ? _props.nodeId : [_props.nodeId ?? ctxNodeId()]).reduce((res, nodeId) => {
2976
- let node = getInternalNode(nodeId);
3025
+ let node = flow.internalNodes[nodeId];
2977
3026
  return node && res.push(node), res;
2978
3027
  }, []), transform = () => {
2979
3028
  let nodeRect = getNodesBounds(toolbarNodes());
@@ -2981,7 +3030,7 @@ const DotPattern = (props) => {
2981
3030
  }, zIndex = () => {
2982
3031
  let nodes = toolbarNodes();
2983
3032
  return nodes.length === 0 ? 1 : Math.max(...nodes.map((node) => (node.internals.z || 5) + 1));
2984
- }, selectedNodesCount = () => getNodes().filter((node) => node.selected).length, isActive = () => {
3033
+ }, selectedNodesCount = () => flow.selection.nodes.length, isActive = () => {
2985
3034
  let nodes = toolbarNodes();
2986
3035
  return typeof _props.isVisible == "boolean" ? _props.isVisible : nodes.length === 1 && !!nodes[0].selected && selectedNodesCount() === 1;
2987
3036
  }, showPortal = () => !!(store.domNode && isActive() && toolbarNodes().length > 0);
@@ -2999,4 +3048,4 @@ const DotPattern = (props) => {
2999
3048
  </Show>;
3000
3049
  }, SelectionMode = SelectionMode$1, getEdgeCenter = getEdgeCenter$1, getBezierEdgeCenter = getBezierEdgeCenter$1;
3001
3050
  //#endregion
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 };
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.3",
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",