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