@dschz/solid-flow 0.3.0-next.0 → 0.3.0-next.1
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 +202 -246
- package/dist/index/index.js +1548 -1229
- package/dist/index/index.jsx +1278 -959
- package/dist/styles/index.css +747 -12
- package/package.json +1 -2
package/dist/index/index.jsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { For, Show, createContext, createEffect, createMemo,
|
|
2
|
-
import { ReactiveMap } from "@solid-primitives/map";
|
|
1
|
+
import { For, Show, createContext, createEffect, createMemo, createProjection, createSignal, createStore, flush, mapArray, merge, omit, onCleanup, onSettled, snapshot, untrack, useContext } from "solid-js";
|
|
3
2
|
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
|
|
3
|
+
import { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, Position as Position$1, ResizeControlVariant, SelectionMode, SelectionMode as SelectionMode$1, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, addEdge as addEdge$1, areConnectionMapsEqual, calcAutoPan, calculateNodePosition, clampPosition, clampPositionToParent, createDevWarn, createMarkerIds, elementSelectionKeys, evaluateAbsolutePosition, fitViewport, getBezierEdgeCenter, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter, getEdgePosition, getEdgeToolbarTransform, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesBounds as getNodesBounds$1, getNodesInside, getOutgoers, getOverlappingArea, getSmoothStepPath, getSmoothStepPath as getSmoothStepPath$1, getStraightPath, getStraightPath as getStraightPath$1, getViewportForBounds, getViewportForBounds as getViewportForBounds$1, handleConnectionChange, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isNodeBase, isNumeric, isRectObject, mergeAriaLabelConfig, nodeHasDimensions, nodeToRect, panBy, pointToRendererPoint, rendererPointToPoint, shallowNodeData, snapPosition } from "@xyflow/system";
|
|
5
4
|
import { Dynamic, Portal, isServer } from "@solidjs/web";
|
|
6
5
|
import { createEventListener, createEventListenerMap } from "@solid-primitives/event-listener";
|
|
7
6
|
//#region src/components/contexts/edgeId.tsx
|
|
@@ -143,7 +142,7 @@ const EdgeLabelRenderer = (props) => {
|
|
|
143
142
|
style: {}
|
|
144
143
|
}), rest = omit(_props, "type", "class", "style", "position", "size", "reconnecting", "children"), { store, nodeLookup, edgeLookup, actions } = useInternalSolidFlow(), edgeId = useEdgeId(), [reconnecting, setReconnecting] = createSignal(!1);
|
|
145
144
|
if (!edgeId()) throw Error("[solid-flow]: EdgeReconnectAnchor must be used within an Edge component");
|
|
146
|
-
let edge = () => edgeLookup
|
|
145
|
+
let edge = () => edgeLookup[edgeId()], onPointerDown = (event) => {
|
|
147
146
|
if (event.button !== 0) return;
|
|
148
147
|
setReconnecting(!0), store.onReconnectStart?.(event, edge(), _props.type);
|
|
149
148
|
let opposite = _props.type === "target" ? {
|
|
@@ -362,533 +361,335 @@ const EdgeLabelRenderer = (props) => {
|
|
|
362
361
|
};
|
|
363
362
|
};
|
|
364
363
|
return <BaseEdge path={pathData().path} labelX={pathData().labelX} labelY={pathData().labelY} label={props.label} labelStyle={props.labelStyle} markerStart={props.markerStart} markerEnd={props.markerEnd} interactionWidth={props.interactionWidth} style={props.style} />;
|
|
365
|
-
},
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
364
|
+
}, getDefaultFlowStateProps = () => ({
|
|
365
|
+
id: "1",
|
|
366
|
+
nodes: [],
|
|
367
|
+
edges: [],
|
|
368
|
+
nodeOrigin: [0, 0],
|
|
369
|
+
nodeExtent: infiniteExtent,
|
|
370
|
+
defaultEdgeOptions: {},
|
|
371
|
+
colorMode: "system",
|
|
372
|
+
colorModeSSR: "light",
|
|
373
|
+
connectionMode: "strict",
|
|
374
|
+
connectionLineType: "default",
|
|
375
|
+
connectionRadius: 20,
|
|
376
|
+
nodeDragThreshold: 1,
|
|
377
|
+
minZoom: .5,
|
|
378
|
+
maxZoom: 2,
|
|
379
|
+
selectionMode: "partial",
|
|
380
|
+
fitView: !1,
|
|
381
|
+
noPanClass: "nopan",
|
|
382
|
+
noDragClass: "nodrag",
|
|
383
|
+
noWheelClass: "nowheel",
|
|
384
|
+
autoPanOnNodeDrag: !0,
|
|
385
|
+
autoPanOnConnect: !0,
|
|
386
|
+
autoPanOnNodeFocus: !0,
|
|
387
|
+
autoPanOnSelection: !0,
|
|
388
|
+
autoPanSpeed: 15,
|
|
389
|
+
elevateEdgesOnSelect: !0,
|
|
390
|
+
nodesDraggable: !0,
|
|
391
|
+
nodesConnectable: !0,
|
|
392
|
+
nodesFocusable: !0,
|
|
393
|
+
edgesFocusable: !0,
|
|
394
|
+
elementsSelectable: !0,
|
|
395
|
+
selectNodesOnDrag: !0,
|
|
396
|
+
elevateNodesOnSelect: !0,
|
|
397
|
+
zIndexMode: "basic",
|
|
398
|
+
onlyRenderVisibleElements: !1,
|
|
399
|
+
disableKeyboardA11y: !1,
|
|
400
|
+
defaultMarkerColor: "#b1b1b7",
|
|
401
|
+
ariaLiveMessage: "",
|
|
402
|
+
style: {},
|
|
403
|
+
isValidConnection: (() => !0),
|
|
404
|
+
onFlowError: createDevWarn("Solid Flow", "https://solidflow.dev/")
|
|
405
|
+
});
|
|
374
406
|
//#endregion
|
|
375
|
-
//#region src/
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
407
|
+
//#region src/core/facades.ts
|
|
408
|
+
/**
|
|
409
|
+
* A read-only `Map` view over an id-keyed record projection, for
|
|
410
|
+
* @xyflow/system interop: system helpers take `Map`s, our reactive lookups
|
|
411
|
+
* are records. Every read passes through to the record, so reads inside
|
|
412
|
+
* tracked scopes subscribe normally — `get`/`has` check with `in` first,
|
|
413
|
+
* which also subscribes while the key is still absent (the projection
|
|
414
|
+
* absent-key footgun), and `size`/iteration read the key set structurally.
|
|
415
|
+
*
|
|
416
|
+
* The mutating `Map` methods throw: writes belong to the roots the
|
|
417
|
+
* projection derives from.
|
|
418
|
+
*/
|
|
419
|
+
var RecordMapFacade = class {
|
|
420
|
+
#record;
|
|
421
|
+
constructor(record) {
|
|
422
|
+
this.#record = record;
|
|
423
|
+
}
|
|
424
|
+
get(key) {
|
|
425
|
+
return key in this.#record ? this.#record[key] : void 0;
|
|
426
|
+
}
|
|
427
|
+
has(key) {
|
|
428
|
+
return key in this.#record;
|
|
429
|
+
}
|
|
430
|
+
get size() {
|
|
431
|
+
return Object.keys(this.#record).length;
|
|
432
|
+
}
|
|
433
|
+
*keys() {
|
|
434
|
+
yield* Object.keys(this.#record);
|
|
435
|
+
}
|
|
436
|
+
*values() {
|
|
437
|
+
for (let key of Object.keys(this.#record)) yield this.#record[key];
|
|
438
|
+
}
|
|
439
|
+
*entries() {
|
|
440
|
+
for (let key of Object.keys(this.#record)) yield [key, this.#record[key]];
|
|
441
|
+
}
|
|
442
|
+
[Symbol.iterator]() {
|
|
443
|
+
return this.entries();
|
|
444
|
+
}
|
|
445
|
+
forEach(callback, thisArg) {
|
|
446
|
+
for (let [key, value] of this.entries()) callback.call(thisArg, value, key, this);
|
|
447
|
+
}
|
|
448
|
+
[Symbol.toStringTag] = "RecordMapFacade";
|
|
449
|
+
set() {
|
|
450
|
+
throw Error("RecordMapFacade is read-only; write to the projection's source roots");
|
|
451
|
+
}
|
|
452
|
+
getOrInsert() {
|
|
453
|
+
throw Error("RecordMapFacade is read-only; write to the projection's source roots");
|
|
454
|
+
}
|
|
455
|
+
getOrInsertComputed() {
|
|
456
|
+
throw Error("RecordMapFacade is read-only; write to the projection's source roots");
|
|
457
|
+
}
|
|
458
|
+
delete() {
|
|
459
|
+
throw Error("RecordMapFacade is read-only; write to the projection's source roots");
|
|
460
|
+
}
|
|
461
|
+
clear() {
|
|
462
|
+
throw Error("RecordMapFacade is read-only; write to the projection's source roots");
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/core/projections/connections.ts
|
|
467
|
+
/**
|
|
468
|
+
* Lookup keys for the connection index. Each edge is registered under six
|
|
469
|
+
* keys — for both of its endpoints: the node, the node+handle-type, and (when
|
|
470
|
+
* a handle id is present) the node+type+handle:
|
|
471
|
+
* `${nodeId}` · `${nodeId}-${type}` · `${nodeId}-${type}-${handleId}`
|
|
472
|
+
*/
|
|
473
|
+
const connectionKey = (nodeId, type, handleId) => `${nodeId}${type ? handleId ? `-${type}-${handleId}` : `-${type}` : ""}`, pairKey = (aNode, aHandle, bNode, bHandle) => `${aNode}-${aHandle}--${bNode}-${bHandle}`, createConnections = (source) => createProjection(() => {
|
|
474
|
+
let out = {}, add = (key, entry, connection) => {
|
|
475
|
+
(out[key] ??= {})[entry] = connection;
|
|
476
|
+
};
|
|
477
|
+
for (let edge of source.edges) {
|
|
478
|
+
let sourceHandle = edge.sourceHandle ?? null, targetHandle = edge.targetHandle ?? null, connection = {
|
|
479
|
+
edgeId: edge.id,
|
|
480
|
+
source: edge.source,
|
|
481
|
+
target: edge.target,
|
|
482
|
+
sourceHandle,
|
|
483
|
+
targetHandle
|
|
484
|
+
}, sourceKey = pairKey(edge.source, sourceHandle, edge.target, targetHandle), targetKey = pairKey(edge.target, targetHandle, edge.source, sourceHandle);
|
|
485
|
+
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);
|
|
486
|
+
}
|
|
487
|
+
return out;
|
|
488
|
+
}, {}, { key: "id" }), createEdgeLookup = (source) => createProjection(() => {
|
|
489
|
+
let out = {};
|
|
490
|
+
for (let edge of source.edges) out[edge.id] = edge;
|
|
491
|
+
return out;
|
|
492
|
+
}, {}, { key: "id" });
|
|
493
|
+
function isManualZIndexMode(zIndexMode) {
|
|
494
|
+
return zIndexMode === "manual";
|
|
495
|
+
}
|
|
496
|
+
function calculateZ(node, selectedNodeZ, zIndexMode) {
|
|
497
|
+
let zIndex = isNumeric(node.zIndex) ? node.zIndex : 0;
|
|
498
|
+
return isManualZIndexMode(zIndexMode) ? zIndex : zIndex + (node.selected ? selectedNodeZ : 0);
|
|
499
|
+
}
|
|
500
|
+
const EMPTY_AUTO_INDEX = /* @__PURE__ */ new Map(), createInternalNodes = (source) => {
|
|
501
|
+
let autoIndex = createMemo(() => {
|
|
502
|
+
if (source.zIndexMode !== "auto") return EMPTY_AUTO_INDEX;
|
|
503
|
+
let index = /* @__PURE__ */ new Map(), rootIds = /* @__PURE__ */ new Set();
|
|
504
|
+
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);
|
|
505
|
+
return index;
|
|
506
|
+
}), entryById = /* @__PURE__ */ new Map(), rowStores = mapArray(() => source.nodes, (userNodeAccessor, index) => {
|
|
507
|
+
let id = userNodeAccessor().id, store = createProjection(() => {
|
|
508
|
+
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 = {
|
|
509
|
+
width: userNode.measured?.width ?? measurement?.measured.width,
|
|
510
|
+
height: userNode.measured?.height ?? measurement?.measured.height
|
|
511
|
+
}, dimensions = getNodeDimensions({
|
|
512
|
+
measured,
|
|
513
|
+
width: userNode.width,
|
|
514
|
+
height: userNode.height,
|
|
515
|
+
initialWidth: userNode.initialWidth,
|
|
516
|
+
initialHeight: userNode.initialHeight
|
|
517
|
+
}), rootParentIndex = autoIndex().get(userNode.id), row = {
|
|
518
|
+
...userNode,
|
|
519
|
+
measured,
|
|
520
|
+
internals: {
|
|
521
|
+
positionAbsolute: clampPosition(getNodePositionWithOrigin(userNode, nodeOrigin), isCoordinateExtent(userNode.extent) ? userNode.extent : nodeExtent, dimensions),
|
|
522
|
+
handleBounds: measurement?.handleBounds,
|
|
523
|
+
z: calculateZ(userNode, selectedNodeZ, zIndexMode) + (rootParentIndex === void 0 ? 0 : rootParentIndex * 10),
|
|
524
|
+
...rootParentIndex === void 0 ? {} : { rootParentIndex },
|
|
525
|
+
userNode
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
if (userNode.parentId) {
|
|
529
|
+
let parentEntry = entryById.get(userNode.parentId);
|
|
530
|
+
if (parentEntry && parentEntry.index() < index()) {
|
|
531
|
+
let { x, y, z } = calculateChildXYZ(row, parentEntry.store.row, nodeOrigin, nodeExtent, selectedNodeZ, zIndexMode);
|
|
532
|
+
row.internals.positionAbsolute = {
|
|
533
|
+
x,
|
|
534
|
+
y
|
|
535
|
+
}, row.internals.z = z;
|
|
536
|
+
} else source.nodes.length;
|
|
537
|
+
}
|
|
538
|
+
if (userNode.parentId) {
|
|
539
|
+
let parent = entryById.get(userNode.parentId);
|
|
540
|
+
parent?.store.row.internals.positionAbsolute.x, parent?.store.row.internals.z;
|
|
541
|
+
}
|
|
542
|
+
return { row };
|
|
543
|
+
}, {}, { key: "id" }), entry = {
|
|
544
|
+
store,
|
|
545
|
+
index
|
|
546
|
+
};
|
|
547
|
+
return entryById.set(id, entry), onCleanup(() => {
|
|
548
|
+
entryById.get(id) === entry && entryById.delete(id);
|
|
549
|
+
}), {
|
|
550
|
+
id,
|
|
551
|
+
store
|
|
552
|
+
};
|
|
553
|
+
}, { keyed: (userNode) => userNode.id }), assigned = /* @__PURE__ */ new Map();
|
|
554
|
+
return createProjection((draft) => {
|
|
555
|
+
let seen = /* @__PURE__ */ new Set();
|
|
556
|
+
for (let { id, store } of rowStores()) {
|
|
557
|
+
seen.add(id);
|
|
558
|
+
let row = store.row;
|
|
559
|
+
assigned.get(id) !== row && (assigned.set(id, row), draft[id] = row);
|
|
393
560
|
}
|
|
394
|
-
|
|
561
|
+
for (let id of assigned.keys()) seen.has(id) || (assigned.delete(id), delete draft[id]);
|
|
562
|
+
}, {}, {
|
|
563
|
+
key: null,
|
|
564
|
+
shallow: !0
|
|
395
565
|
});
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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()
|
|
566
|
+
};
|
|
567
|
+
function calculateChildXYZ(childNode, parentNode, nodeOrigin, nodeExtent, selectedNodeZ, zIndexMode) {
|
|
568
|
+
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({
|
|
569
|
+
x: parentX + clampedPosition.x,
|
|
570
|
+
y: parentY + clampedPosition.y
|
|
571
|
+
}, nodeExtent, childDimensions);
|
|
572
|
+
childNode.extent === "parent" && (absolutePosition = clampPositionToParent(absolutePosition, childDimensions, parentNode));
|
|
573
|
+
let childZ = calculateZ(childNode, selectedNodeZ, zIndexMode), parentZ = parentNode.internals.z ?? 0;
|
|
574
|
+
return {
|
|
575
|
+
x: absolutePosition.x,
|
|
576
|
+
y: absolutePosition.y,
|
|
577
|
+
z: parentZ >= childZ ? parentZ + 1 : childZ
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/core/projections/layoutedEdges.ts
|
|
582
|
+
/**
|
|
583
|
+
* Edge layout join: user edges × internal nodes → screen-space edge geometry,
|
|
584
|
+
* decomposed into SUB-STORES (spike 13): each edge is its own keyed
|
|
585
|
+
* projection holding `{ row }` — the layouted row, or null while the edge
|
|
586
|
+
* produces none (missing/unready endpoints, culled) — and the public record
|
|
587
|
+
* is a SHALLOW projection holding the PRESENT rows' proxies by reference.
|
|
588
|
+
*
|
|
589
|
+
* Reads chain: `record[id].sourceX` goes through the shallow slot into the
|
|
590
|
+
* edge's own store, so every materialized leaf signal hangs off its EDGE's
|
|
591
|
+
* computed (defeating rc.1's per-update companion walk — see
|
|
592
|
+
* internalNodes.ts). The edge projection tracks exactly what the join reads —
|
|
593
|
+
* the edge's props and its endpoints' geometry leaves through nodeLookup
|
|
594
|
+
* (which chains into the node row stores) — so one node move re-runs only
|
|
595
|
+
* the adjacent edges' projections; the record computed re-runs only when
|
|
596
|
+
* membership or presence changes.
|
|
597
|
+
*
|
|
598
|
+
* The viewport participates only while onlyRenderVisibleElements is active —
|
|
599
|
+
* panning must not touch edge rows otherwise.
|
|
600
|
+
*
|
|
601
|
+
* Rows whose endpoints are missing or unmeasured simply drop out of the
|
|
602
|
+
* record — the same "no entry" contract the ReactiveMap pipeline had.
|
|
603
|
+
*/
|
|
604
|
+
const createLayoutedEdges = (source) => {
|
|
605
|
+
let rowStores = mapArray(() => source.edges, (edgeAccessor) => ({
|
|
606
|
+
id: edgeAccessor().id,
|
|
607
|
+
store: createProjection(() => {
|
|
608
|
+
let edge = edgeAccessor(), row = buildRow(source, edge);
|
|
609
|
+
return source.nodeLookup.get(edge.source)?.internals.handleBounds, source.nodeLookup.get(edge.target)?.internals.handleBounds, { row };
|
|
610
|
+
}, { row: null }, { key: "id" })
|
|
611
|
+
}), { keyed: (edge) => edge.id }), assigned = /* @__PURE__ */ new Map();
|
|
612
|
+
return createProjection((draft) => {
|
|
613
|
+
let seen = /* @__PURE__ */ new Set();
|
|
614
|
+
for (let { id, store } of rowStores()) {
|
|
615
|
+
let row = store.row;
|
|
616
|
+
row && (seen.add(id), assigned.get(id) !== row && (assigned.set(id, row), draft[id] = row));
|
|
485
617
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
let _props = propDefaults(props, {
|
|
491
|
-
targetPosition: Position$1.Top,
|
|
492
|
-
sourcePosition: Position$1.Bottom
|
|
618
|
+
for (let id of assigned.keys()) seen.has(id) || (assigned.delete(id), delete draft[id]);
|
|
619
|
+
}, {}, {
|
|
620
|
+
key: null,
|
|
621
|
+
shallow: !0
|
|
493
622
|
});
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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 ?? {}
|
|
584
|
-
});
|
|
585
|
-
createEffect(() => ({
|
|
586
|
-
valid: nodeTypeValid(),
|
|
587
|
-
nodeType: nodeType()
|
|
588
|
-
}), ({ valid, nodeType }) => {}), createEffect(() => ({
|
|
589
|
-
id: node().id,
|
|
590
|
-
nodeElement: nodeRef(),
|
|
591
|
-
nodeType: nodeType(),
|
|
592
|
-
sourcePosition: node().sourcePosition,
|
|
593
|
-
targetPosition: node().targetPosition
|
|
594
|
-
}), (current, prev) => {
|
|
595
|
-
current.nodeElement && (prev && prev.nodeElement === current.nodeElement && prev.sourcePosition === current.sourcePosition && prev.targetPosition === current.targetPosition && prev.nodeType === current.nodeType || actions.requestUpdateNodeInternals([[current.id, {
|
|
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));
|
|
623
|
+
}, buildRow = (source, edge) => {
|
|
624
|
+
let sourceNode = source.nodeLookup.get(edge.source), targetNode = source.nodeLookup.get(edge.target);
|
|
625
|
+
if (!sourceNode || !targetNode) return source.nodeLookup.size, null;
|
|
626
|
+
if (source.onlyRenderVisibleElements && !isEdgeVisible({
|
|
627
|
+
sourceNode,
|
|
628
|
+
targetNode,
|
|
629
|
+
width: source.width ?? 0,
|
|
630
|
+
height: source.height ?? 0,
|
|
631
|
+
transform: source.transform
|
|
632
|
+
})) return null;
|
|
633
|
+
let edgePosition = getEdgePosition({
|
|
634
|
+
id: edge.id,
|
|
635
|
+
sourceNode,
|
|
636
|
+
targetNode,
|
|
637
|
+
sourceHandle: edge.sourceHandle || null,
|
|
638
|
+
targetHandle: edge.targetHandle || null,
|
|
639
|
+
connectionMode: source.connectionMode,
|
|
640
|
+
onError: source.onError
|
|
606
641
|
});
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
});
|
|
642
|
+
return edgePosition ? {
|
|
643
|
+
...source.defaultEdgeOptions,
|
|
644
|
+
...edge,
|
|
645
|
+
...edgePosition,
|
|
646
|
+
zIndex: getElevatedEdgeZIndex({
|
|
647
|
+
selected: edge.selected,
|
|
648
|
+
zIndex: edge.zIndex ?? source.defaultEdgeOptions.zIndex,
|
|
649
|
+
sourceNode,
|
|
650
|
+
targetNode,
|
|
651
|
+
elevateOnSelect: source.elevateEdgesOnSelect,
|
|
652
|
+
zIndexMode: source.zIndexMode
|
|
653
|
+
}),
|
|
654
|
+
sourceNode,
|
|
655
|
+
targetNode,
|
|
656
|
+
edge
|
|
657
|
+
} : null;
|
|
658
|
+
}, createParentIds = (source) => createProjection(() => {
|
|
659
|
+
let out = {};
|
|
660
|
+
for (let node of source.nodes) node.parentId && (out[node.parentId] = !0);
|
|
661
|
+
return out;
|
|
662
|
+
}, {}, { key: "id" }), getInitialViewport = (fitView, initialViewport, width, height, nodeLookup) => {
|
|
663
|
+
if (fitView && !initialViewport && width && height) {
|
|
664
|
+
let bounds = getInternalNodesBounds(nodeLookup, { filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) });
|
|
665
|
+
return getViewportForBounds$1(bounds, width, height, .5, 2, .1);
|
|
666
|
+
}
|
|
667
|
+
return initialViewport ?? {
|
|
668
|
+
x: 0,
|
|
669
|
+
y: 0,
|
|
670
|
+
zoom: 1
|
|
671
|
+
};
|
|
672
|
+
}, createFlowState = (props, injections = {}) => {
|
|
673
|
+
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({
|
|
674
|
+
get nodes() {
|
|
675
|
+
return nodesStore;
|
|
651
676
|
},
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
event,
|
|
655
|
-
targetNode,
|
|
656
|
-
nodes
|
|
657
|
-
});
|
|
677
|
+
get measurements() {
|
|
678
|
+
return measurementsStore;
|
|
658
679
|
},
|
|
659
|
-
|
|
660
|
-
|
|
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
|
|
680
|
+
get nodeOrigin() {
|
|
681
|
+
return config().nodeOrigin;
|
|
679
682
|
},
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
|
683
|
+
get nodeExtent() {
|
|
684
|
+
return config().nodeExtent;
|
|
685
|
+
},
|
|
686
|
+
get elevateNodesOnSelect() {
|
|
687
|
+
return config().elevateNodesOnSelect;
|
|
688
|
+
},
|
|
689
|
+
get zIndexMode() {
|
|
690
|
+
return config().zIndexMode;
|
|
816
691
|
}
|
|
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);
|
|
692
|
+
}), nodeLookup = new RecordMapFacade(internalNodes), initialViewport = getInitialViewport(_props.fitView, _props.initialViewport, _props.width ?? 0, _props.height ?? 0, nodeLookup), [viewportStore, setViewportStore] = createStore(_props.viewport ?? initialViewport);
|
|
892
693
|
createEffect(() => config().nodes, (next) => {
|
|
893
694
|
setNodesStore(() => next);
|
|
894
695
|
}, { defer: !0 }), createEffect(() => config().edges, (next) => {
|
|
@@ -934,7 +735,7 @@ const InitialNodeTypesMap = {
|
|
|
934
735
|
return clickConnectStartHandle();
|
|
935
736
|
},
|
|
936
737
|
get colorMode() {
|
|
937
|
-
return this._colorMode === "system" ?
|
|
738
|
+
return this._colorMode === "system" ? prefersDark() ? "dark" : "light" : this._colorMode;
|
|
938
739
|
},
|
|
939
740
|
get connection() {
|
|
940
741
|
let state = connection();
|
|
@@ -951,7 +752,7 @@ const InitialNodeTypesMap = {
|
|
|
951
752
|
},
|
|
952
753
|
get edgeTypes() {
|
|
953
754
|
return {
|
|
954
|
-
...
|
|
755
|
+
...initialEdgeTypes,
|
|
955
756
|
...this._edgeTypes
|
|
956
757
|
};
|
|
957
758
|
},
|
|
@@ -987,7 +788,7 @@ const InitialNodeTypesMap = {
|
|
|
987
788
|
},
|
|
988
789
|
get nodeTypes() {
|
|
989
790
|
return {
|
|
990
|
-
...
|
|
791
|
+
...initialNodeTypes,
|
|
991
792
|
...this._nodeTypes
|
|
992
793
|
};
|
|
993
794
|
},
|
|
@@ -1051,7 +852,45 @@ const InitialNodeTypesMap = {
|
|
|
1051
852
|
get zoomActivationKeyPressed() {
|
|
1052
853
|
return zoomActivationKeyPressed();
|
|
1053
854
|
}
|
|
1054
|
-
}), visibleNodesMap = createMemo(() => nodeLookup), visibleNodeIds = createMemo(() =>
|
|
855
|
+
}), visibleNodesMap = createMemo(() => nodeLookup), visibleNodeIds = createMemo(() => Object.keys(internalNodes)), parentIds = createParentIds({ get nodes() {
|
|
856
|
+
return store.nodes;
|
|
857
|
+
} }), edgeLookup = createEdgeLookup({ get edges() {
|
|
858
|
+
return store.edges;
|
|
859
|
+
} }), connections = createConnections({ get edges() {
|
|
860
|
+
return store.edges;
|
|
861
|
+
} }), layoutedEdges = createLayoutedEdges({
|
|
862
|
+
get edges() {
|
|
863
|
+
return store.edges;
|
|
864
|
+
},
|
|
865
|
+
get connectionMode() {
|
|
866
|
+
return store.connectionMode;
|
|
867
|
+
},
|
|
868
|
+
get defaultEdgeOptions() {
|
|
869
|
+
return store.defaultEdgeOptions;
|
|
870
|
+
},
|
|
871
|
+
get elevateEdgesOnSelect() {
|
|
872
|
+
return store.elevateEdgesOnSelect;
|
|
873
|
+
},
|
|
874
|
+
get zIndexMode() {
|
|
875
|
+
return store.zIndexMode;
|
|
876
|
+
},
|
|
877
|
+
get onlyRenderVisibleElements() {
|
|
878
|
+
return store.onlyRenderVisibleElements;
|
|
879
|
+
},
|
|
880
|
+
get width() {
|
|
881
|
+
return store.width;
|
|
882
|
+
},
|
|
883
|
+
get height() {
|
|
884
|
+
return store.height;
|
|
885
|
+
},
|
|
886
|
+
get transform() {
|
|
887
|
+
return store.transform;
|
|
888
|
+
},
|
|
889
|
+
get onError() {
|
|
890
|
+
return store.onError;
|
|
891
|
+
},
|
|
892
|
+
nodeLookup
|
|
893
|
+
}), visibleEdgeIds = createMemo(() => Object.keys(layoutedEdges)), getEdge = (id) => layoutedEdges[id], fitView = async (options) => store.panZoom ? await fitViewport({
|
|
1055
894
|
nodes: nodeLookup,
|
|
1056
895
|
width: store.width,
|
|
1057
896
|
height: store.height,
|
|
@@ -1077,42 +916,36 @@ const InitialNodeTypesMap = {
|
|
|
1077
916
|
setNodesStore((nodes) => {
|
|
1078
917
|
for (let node of nodes) nodeDragItems.has(node.id) && (node.dragging = dragging, node.position = nodeDragItems.get(node.id).position);
|
|
1079
918
|
});
|
|
1080
|
-
},
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
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();
|
|
919
|
+
}, applyMeasurementWrites = (writes) => {
|
|
920
|
+
setMeasurementsStore((draft) => {
|
|
921
|
+
for (let write of writes) if (write.hidden) {
|
|
922
|
+
let entry = draft[write.id];
|
|
923
|
+
entry && (entry.handleBounds = void 0);
|
|
924
|
+
} else draft[write.id] = {
|
|
925
|
+
measured: write.measured,
|
|
926
|
+
handleBounds: write.handleBounds
|
|
927
|
+
};
|
|
928
|
+
});
|
|
929
|
+
}, applyNodeChanges = (changes) => {
|
|
930
|
+
changes.length !== 0 && setNodesStore((nodes) => {
|
|
931
|
+
let nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
932
|
+
for (let change of changes) {
|
|
933
|
+
let node = nodeById.get(change.id);
|
|
934
|
+
if (node) switch (change.type) {
|
|
935
|
+
case "dimensions":
|
|
936
|
+
change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
|
|
937
|
+
...node.measured,
|
|
938
|
+
...change.dimensions
|
|
939
|
+
};
|
|
940
|
+
break;
|
|
941
|
+
case "position": node.position = change.position ?? node.position;
|
|
942
|
+
}
|
|
1114
943
|
}
|
|
1115
944
|
});
|
|
945
|
+
}, markInitialNodesMeasured = () => {
|
|
946
|
+
initialNodesMeasured = !0, tryInitialFitView();
|
|
947
|
+
}, requestMeasure = () => {}, setMeasureRequester = (fn) => {
|
|
948
|
+
requestMeasure = fn;
|
|
1116
949
|
}, 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
950
|
let nextZoom = options?.zoom === void 0 ? store.maxZoom : options.zoom, currentPanZoom = store.panZoom;
|
|
1118
951
|
return currentPanZoom ? (await currentPanZoom.setViewport({
|
|
@@ -1134,25 +967,292 @@ const InitialNodeTypesMap = {
|
|
|
1134
967
|
let edgesToUnselect = new Set((edges ?? store.edges).map(({ id }) => id));
|
|
1135
968
|
edgesToUnselect.size && setEdgesStore((edges) => {
|
|
1136
969
|
for (let edge of edges) edgesToUnselect.has(edge.id) && (edge.selected = !1);
|
|
1137
|
-
});
|
|
970
|
+
}), flush();
|
|
1138
971
|
}, addSelectedNodes = (ids) => {
|
|
1139
|
-
let isMultiSelection = store.multiselectionKeyPressed
|
|
972
|
+
let isMultiSelection = store.multiselectionKeyPressed;
|
|
1140
973
|
setNodesStore((nodes) => {
|
|
1141
974
|
for (let node of nodes) {
|
|
1142
975
|
let nodeWillBeSelected = ids.includes(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
|
|
1143
|
-
|
|
1144
|
-
let internalNode = nodeLookup.get(node.id);
|
|
1145
|
-
internalNode && (internalNode.selected = selected), node.selected = selected;
|
|
976
|
+
node.selected !== selected && (node.selected = selected);
|
|
1146
977
|
}
|
|
1147
|
-
}), isMultiSelection || unselectNodesAndEdges({ nodes: [] });
|
|
978
|
+
}), isMultiSelection || unselectNodesAndEdges({ nodes: [] }), flush();
|
|
1148
979
|
}, addSelectedEdges = (ids) => {
|
|
1149
|
-
let isMultiSelection = store.multiselectionKeyPressed
|
|
980
|
+
let isMultiSelection = store.multiselectionKeyPressed;
|
|
1150
981
|
setEdgesStore((edges) => {
|
|
1151
982
|
for (let edge of edges) {
|
|
1152
983
|
let edgeWillBeSelected = ids.includes(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
|
|
1153
|
-
|
|
984
|
+
edge.selected !== selected && (edge.selected = selected);
|
|
985
|
+
}
|
|
986
|
+
}), isMultiSelection || unselectNodesAndEdges({ edges: [] }), flush();
|
|
987
|
+
}, handleNodeSelection = (id, unselect, nodeRef) => {
|
|
988
|
+
let node = store.nodes.find((n) => n.id === id);
|
|
989
|
+
node && (setSelectionRect(void 0), setSelectionRectMode(void 0), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
|
|
990
|
+
nodes: [node],
|
|
991
|
+
edges: []
|
|
992
|
+
}), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]));
|
|
993
|
+
}, handleEdgeSelection = (id) => {
|
|
994
|
+
let edge = edgeLookup[id];
|
|
995
|
+
edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (setSelectionRect(void 0), setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
|
|
996
|
+
nodes: [],
|
|
997
|
+
edges: [edge]
|
|
998
|
+
}) : addSelectedEdges([id]));
|
|
999
|
+
}, moveSelectedNodes = (direction, factor) => {
|
|
1000
|
+
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;
|
|
1001
|
+
for (let node of nodeLookup.values()) {
|
|
1002
|
+
if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
|
|
1003
|
+
let nextPosition = {
|
|
1004
|
+
x: node.internals.positionAbsolute.x + xDiff,
|
|
1005
|
+
y: node.internals.positionAbsolute.y + yDiff
|
|
1006
|
+
};
|
|
1007
|
+
store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
|
|
1008
|
+
let { position } = calculateNodePosition({
|
|
1009
|
+
nodeId: node.id,
|
|
1010
|
+
nextPosition,
|
|
1011
|
+
nodeLookup,
|
|
1012
|
+
nodeExtent: store.nodeExtent,
|
|
1013
|
+
nodeOrigin: store.nodeOrigin,
|
|
1014
|
+
onError: store.onError
|
|
1015
|
+
});
|
|
1016
|
+
nodeUpdates.set(node.id, { position });
|
|
1017
|
+
}
|
|
1018
|
+
updateNodePositions(nodeUpdates);
|
|
1019
|
+
}, panBy$1 = (delta) => panBy({
|
|
1020
|
+
delta,
|
|
1021
|
+
panZoom: store.panZoom,
|
|
1022
|
+
transform: store.transform,
|
|
1023
|
+
translateExtent: store.translateExtent,
|
|
1024
|
+
width: store.width,
|
|
1025
|
+
height: store.height
|
|
1026
|
+
}), cancelConnection = () => {
|
|
1027
|
+
setConnection({ ...initialConnection });
|
|
1028
|
+
}, reset = () => {
|
|
1029
|
+
resetStoreValues(), unselectNodesAndEdges();
|
|
1030
|
+
}, flow = {
|
|
1031
|
+
get nodes() {
|
|
1032
|
+
return store.nodes;
|
|
1033
|
+
},
|
|
1034
|
+
get edges() {
|
|
1035
|
+
return store.edges;
|
|
1036
|
+
},
|
|
1037
|
+
get internalNodes() {
|
|
1038
|
+
return internalNodes;
|
|
1039
|
+
},
|
|
1040
|
+
get layoutedEdges() {
|
|
1041
|
+
return layoutedEdges;
|
|
1042
|
+
},
|
|
1043
|
+
get connections() {
|
|
1044
|
+
return connections;
|
|
1045
|
+
},
|
|
1046
|
+
selection: {
|
|
1047
|
+
get nodes() {
|
|
1048
|
+
return store.selectedNodes;
|
|
1049
|
+
},
|
|
1050
|
+
get edges() {
|
|
1051
|
+
return store.selectedEdges;
|
|
1052
|
+
}
|
|
1053
|
+
},
|
|
1054
|
+
get nodesInitialized() {
|
|
1055
|
+
return store.nodesInitialized;
|
|
1056
|
+
},
|
|
1057
|
+
get viewportInitialized() {
|
|
1058
|
+
return store.viewportInitialized;
|
|
1059
|
+
},
|
|
1060
|
+
get viewport() {
|
|
1061
|
+
return store.viewport;
|
|
1062
|
+
},
|
|
1063
|
+
get width() {
|
|
1064
|
+
return store.width;
|
|
1065
|
+
},
|
|
1066
|
+
get height() {
|
|
1067
|
+
return store.height;
|
|
1068
|
+
},
|
|
1069
|
+
get colorMode() {
|
|
1070
|
+
return store.colorMode;
|
|
1071
|
+
},
|
|
1072
|
+
get connection() {
|
|
1073
|
+
return store.connection;
|
|
1074
|
+
},
|
|
1075
|
+
get dragging() {
|
|
1076
|
+
return store.dragging;
|
|
1077
|
+
},
|
|
1078
|
+
get minZoom() {
|
|
1079
|
+
return store.minZoom;
|
|
1080
|
+
},
|
|
1081
|
+
get maxZoom() {
|
|
1082
|
+
return store.maxZoom;
|
|
1083
|
+
},
|
|
1084
|
+
get nodesDraggable() {
|
|
1085
|
+
return store.nodesDraggable;
|
|
1086
|
+
},
|
|
1087
|
+
get nodesConnectable() {
|
|
1088
|
+
return store.nodesConnectable;
|
|
1089
|
+
},
|
|
1090
|
+
get elementsSelectable() {
|
|
1091
|
+
return store.elementsSelectable;
|
|
1092
|
+
},
|
|
1093
|
+
get snapGrid() {
|
|
1094
|
+
return store.snapGrid;
|
|
1095
|
+
}
|
|
1096
|
+
}, getNodeRect = (node) => {
|
|
1097
|
+
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 = {
|
|
1098
|
+
...nodeToUse,
|
|
1099
|
+
position,
|
|
1100
|
+
width: nodeToUse.measured?.width ?? nodeToUse.width,
|
|
1101
|
+
height: nodeToUse.measured?.height ?? nodeToUse.height
|
|
1102
|
+
};
|
|
1103
|
+
return nodeToRect(nodeWithPosition);
|
|
1104
|
+
}, updateNode = (id, nodeUpdate, options = { replace: !1 }) => {
|
|
1105
|
+
setNodesStore((nodes) => {
|
|
1106
|
+
let index = nodes.findIndex((node) => node.id === id);
|
|
1107
|
+
if (index === -1) return;
|
|
1108
|
+
let node = nodes[index], nextNode = typeof nodeUpdate == "function" ? nodeUpdate(node) : nodeUpdate;
|
|
1109
|
+
nodes[index] = options?.replace && isNode(nextNode) ? nextNode : {
|
|
1110
|
+
...node,
|
|
1111
|
+
...nextNode
|
|
1112
|
+
};
|
|
1113
|
+
});
|
|
1114
|
+
}, commands = {
|
|
1115
|
+
fitView,
|
|
1116
|
+
fitBounds: async (bounds, options) => {
|
|
1117
|
+
if (!store.panZoom) return !1;
|
|
1118
|
+
let viewport = getViewportForBounds$1(bounds, store.width, store.height, store.minZoom, store.maxZoom, options?.padding ?? .1);
|
|
1119
|
+
return await store.panZoom.setViewport(viewport, {
|
|
1120
|
+
duration: options?.duration,
|
|
1121
|
+
ease: options?.ease,
|
|
1122
|
+
interpolate: options?.interpolate
|
|
1123
|
+
}), !0;
|
|
1124
|
+
},
|
|
1125
|
+
zoomIn,
|
|
1126
|
+
zoomOut,
|
|
1127
|
+
setZoom: (zoomLevel, options) => {
|
|
1128
|
+
let currentPanZoom = store.panZoom;
|
|
1129
|
+
return currentPanZoom ? currentPanZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(!1);
|
|
1130
|
+
},
|
|
1131
|
+
setCenter,
|
|
1132
|
+
setViewport: async (nextViewport, options) => {
|
|
1133
|
+
let currentViewport = store.viewport;
|
|
1134
|
+
return store.panZoom ? (await store.panZoom.setViewport({
|
|
1135
|
+
x: nextViewport.x ?? currentViewport.x,
|
|
1136
|
+
y: nextViewport.y ?? currentViewport.y,
|
|
1137
|
+
zoom: nextViewport.zoom ?? currentViewport.zoom
|
|
1138
|
+
}, options), !0) : !1;
|
|
1139
|
+
},
|
|
1140
|
+
panBy: panBy$1,
|
|
1141
|
+
screenToFlowPosition: (position, options = { snapToGrid: !0 }) => {
|
|
1142
|
+
if (!store.domNode) return position;
|
|
1143
|
+
let _snapGrid = options.snapToGrid ? store.snapGrid : !1, { x, y, zoom } = store.viewport, { x: domX, y: domY } = store.domNode.getBoundingClientRect(), correctedPosition = {
|
|
1144
|
+
x: position.x - domX,
|
|
1145
|
+
y: position.y - domY
|
|
1146
|
+
};
|
|
1147
|
+
return pointToRendererPoint(correctedPosition, [
|
|
1148
|
+
x,
|
|
1149
|
+
y,
|
|
1150
|
+
zoom
|
|
1151
|
+
], _snapGrid !== null, _snapGrid || [1, 1]);
|
|
1152
|
+
},
|
|
1153
|
+
flowToScreenPosition: (position) => {
|
|
1154
|
+
if (!store.domNode) return position;
|
|
1155
|
+
let { x, y, zoom } = store.viewport, { x: domX, y: domY } = store.domNode.getBoundingClientRect(), rendererPosition = rendererPointToPoint(position, [
|
|
1156
|
+
x,
|
|
1157
|
+
y,
|
|
1158
|
+
zoom
|
|
1159
|
+
]);
|
|
1160
|
+
return {
|
|
1161
|
+
x: rendererPosition.x + domX,
|
|
1162
|
+
y: rendererPosition.y + domY
|
|
1163
|
+
};
|
|
1164
|
+
},
|
|
1165
|
+
addNodes: (payload) => {
|
|
1166
|
+
let newNodes = Array.isArray(payload) ? payload : [payload];
|
|
1167
|
+
setNodesStore((nodes) => [...nodes, ...newNodes]);
|
|
1168
|
+
},
|
|
1169
|
+
addEdges: (payload) => {
|
|
1170
|
+
let newEdges = Array.isArray(payload) ? payload : [payload];
|
|
1171
|
+
setEdgesStore((edges) => [...edges, ...newEdges]);
|
|
1172
|
+
},
|
|
1173
|
+
setNodes: setNodesStore,
|
|
1174
|
+
setEdges: setEdgesStore,
|
|
1175
|
+
updateNode,
|
|
1176
|
+
updateNodeData: (id, dataUpdate, options) => {
|
|
1177
|
+
let node = nodeLookup.get(id)?.internals.userNode;
|
|
1178
|
+
if (!node) return;
|
|
1179
|
+
let nextData = typeof dataUpdate == "function" ? dataUpdate(node) : dataUpdate;
|
|
1180
|
+
updateNode(id, (current) => ({
|
|
1181
|
+
...current,
|
|
1182
|
+
data: options?.replace ? nextData : {
|
|
1183
|
+
...current.data,
|
|
1184
|
+
...nextData
|
|
1185
|
+
}
|
|
1186
|
+
}));
|
|
1187
|
+
},
|
|
1188
|
+
updateEdge: (id, edgeUpdate, options = { replace: !1 }) => {
|
|
1189
|
+
setEdgesStore((edges) => {
|
|
1190
|
+
let index = edges.findIndex((edge) => edge.id === id);
|
|
1191
|
+
if (index === -1) return;
|
|
1192
|
+
let edge = edges[index], nextEdge = typeof edgeUpdate == "function" ? edgeUpdate(edge) : edgeUpdate;
|
|
1193
|
+
edges[index] = options.replace && isEdge(nextEdge) ? nextEdge : {
|
|
1194
|
+
...edge,
|
|
1195
|
+
...nextEdge
|
|
1196
|
+
};
|
|
1197
|
+
});
|
|
1198
|
+
},
|
|
1199
|
+
deleteElements: async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => {
|
|
1200
|
+
let { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
|
|
1201
|
+
nodesToRemove,
|
|
1202
|
+
edgesToRemove,
|
|
1203
|
+
nodes: store.nodes,
|
|
1204
|
+
edges: store.edges,
|
|
1205
|
+
onBeforeDelete: store.onBeforeDelete
|
|
1206
|
+
});
|
|
1207
|
+
if (matchingEdges) {
|
|
1208
|
+
let remainingEdges = store.edges.filter((edge) => !matchingEdges.some(({ id }) => id === edge.id));
|
|
1209
|
+
store.onEdgesDelete?.(matchingEdges), setEdgesStore(() => remainingEdges);
|
|
1210
|
+
}
|
|
1211
|
+
if (matchingNodes) {
|
|
1212
|
+
let remainingNodes = store.nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id));
|
|
1213
|
+
store.onNodesDelete?.(matchingNodes), setNodesStore(() => remainingNodes);
|
|
1214
|
+
}
|
|
1215
|
+
return {
|
|
1216
|
+
deletedNodes: matchingNodes,
|
|
1217
|
+
deletedEdges: matchingEdges
|
|
1218
|
+
};
|
|
1219
|
+
},
|
|
1220
|
+
getIntersectingNodes: (nodeOrRect, partially = !0, nodesToIntersect) => {
|
|
1221
|
+
let isRect = isRectObject(nodeOrRect), nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
|
|
1222
|
+
return nodeRect ? (nodesToIntersect || store.nodes).filter((n) => {
|
|
1223
|
+
let internalNode = nodeLookup.get(n.id);
|
|
1224
|
+
if (!internalNode || !isRect && n.id === nodeOrRect.id) return !1;
|
|
1225
|
+
let currNodeRect = nodeToRect(internalNode), overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
|
|
1226
|
+
return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
1227
|
+
}) : [];
|
|
1228
|
+
},
|
|
1229
|
+
isNodeIntersecting: (nodeOrRect, area, partially = !0) => {
|
|
1230
|
+
let nodeRect = isRectObject(nodeOrRect) ? nodeOrRect : getNodeRect(nodeOrRect);
|
|
1231
|
+
if (!nodeRect) return !1;
|
|
1232
|
+
let overlappingArea = getOverlappingArea(nodeRect, area);
|
|
1233
|
+
return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
1234
|
+
},
|
|
1235
|
+
getNodesBounds: (nodesToMeasure) => getNodesBounds$1(nodesToMeasure, {
|
|
1236
|
+
nodeLookup,
|
|
1237
|
+
nodeOrigin: store.nodeOrigin
|
|
1238
|
+
}),
|
|
1239
|
+
updateNodeInternals: (id) => {
|
|
1240
|
+
let updateIds = Array.isArray(id) ? id : [id], updates = [];
|
|
1241
|
+
for (let updateId of updateIds) {
|
|
1242
|
+
let nodeElement = store.domNode?.querySelector(`.solid-flow__node[data-id="${updateId}"]`);
|
|
1243
|
+
nodeElement && updates.push([updateId, {
|
|
1244
|
+
id: updateId,
|
|
1245
|
+
nodeElement,
|
|
1246
|
+
force: !0
|
|
1247
|
+
}]);
|
|
1154
1248
|
}
|
|
1155
|
-
|
|
1249
|
+
requestMeasure(updates);
|
|
1250
|
+
},
|
|
1251
|
+
toObject: () => structuredClone({
|
|
1252
|
+
nodes: [...snapshot(store.nodes)],
|
|
1253
|
+
edges: [...snapshot(store.edges)],
|
|
1254
|
+
viewport: { ...snapshot(store.viewport) }
|
|
1255
|
+
})
|
|
1156
1256
|
};
|
|
1157
1257
|
return createEffect(() => !!(width() && height() && panZoom()), (ready) => {
|
|
1158
1258
|
ready && tryInitialFitView();
|
|
@@ -1163,195 +1263,571 @@ const InitialNodeTypesMap = {
|
|
|
1163
1263
|
y: store.viewport.y,
|
|
1164
1264
|
zoom: store.viewport.zoom
|
|
1165
1265
|
}
|
|
1166
|
-
}), ({ panZoom, viewport }) => {
|
|
1167
|
-
panZoom?.syncViewport(viewport);
|
|
1168
|
-
}), createEffect(() => ({
|
|
1169
|
-
panZoom: panZoom(),
|
|
1170
|
-
extent: [store.minZoom, store.maxZoom]
|
|
1171
|
-
}), ({ panZoom, extent }) => {
|
|
1172
|
-
panZoom?.setScaleExtent(extent);
|
|
1173
|
-
}), createEffect(() => ({
|
|
1174
|
-
panZoom: panZoom(),
|
|
1175
|
-
extent: store.translateExtent
|
|
1176
|
-
}), ({ panZoom, extent }) => {
|
|
1177
|
-
panZoom?.setTranslateExtent(extent);
|
|
1178
|
-
}),
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1266
|
+
}), ({ panZoom, viewport }) => {
|
|
1267
|
+
panZoom?.syncViewport(viewport);
|
|
1268
|
+
}), createEffect(() => ({
|
|
1269
|
+
panZoom: panZoom(),
|
|
1270
|
+
extent: [store.minZoom, store.maxZoom]
|
|
1271
|
+
}), ({ panZoom, extent }) => {
|
|
1272
|
+
panZoom?.setScaleExtent(extent);
|
|
1273
|
+
}), createEffect(() => ({
|
|
1274
|
+
panZoom: panZoom(),
|
|
1275
|
+
extent: store.translateExtent
|
|
1276
|
+
}), ({ panZoom, extent }) => {
|
|
1277
|
+
panZoom?.setTranslateExtent(extent);
|
|
1278
|
+
}), createEffect(() => new Set(store.nodes.map((n) => n.id)), (currentIds) => {
|
|
1279
|
+
setMeasurementsStore((draft) => {
|
|
1280
|
+
for (let id of Object.keys(draft)) currentIds.has(id) || delete draft[id];
|
|
1281
|
+
});
|
|
1282
|
+
}), {
|
|
1283
|
+
store,
|
|
1284
|
+
flow,
|
|
1285
|
+
commands,
|
|
1286
|
+
internalNodes,
|
|
1287
|
+
layoutedEdges,
|
|
1288
|
+
nodeLookup,
|
|
1289
|
+
edgeLookup,
|
|
1290
|
+
parentIds,
|
|
1291
|
+
connections,
|
|
1292
|
+
actions: {
|
|
1293
|
+
getEdge,
|
|
1294
|
+
applyInitialFitView,
|
|
1295
|
+
applyMeasurementWrites,
|
|
1296
|
+
applyNodeChanges,
|
|
1297
|
+
markInitialNodesMeasured,
|
|
1298
|
+
setMeasureRequester,
|
|
1299
|
+
resetStoreValues,
|
|
1300
|
+
setAriaLabelConfig,
|
|
1301
|
+
setAriaLiveMessage,
|
|
1302
|
+
setClickConnectStartHandle,
|
|
1303
|
+
setConfig,
|
|
1304
|
+
setConnection,
|
|
1305
|
+
setDeleteKeyPressed,
|
|
1306
|
+
setDomNode,
|
|
1307
|
+
setDragging,
|
|
1308
|
+
get setEdges() {
|
|
1309
|
+
return setEdgesStore;
|
|
1310
|
+
},
|
|
1311
|
+
setElementsSelectable,
|
|
1312
|
+
setHeight,
|
|
1313
|
+
setMultiselectionKeyPressed,
|
|
1314
|
+
get setNodes() {
|
|
1315
|
+
return setNodesStore;
|
|
1316
|
+
},
|
|
1317
|
+
setNodesConnectable,
|
|
1318
|
+
setNodesDraggable,
|
|
1319
|
+
setPanActivationKeyPressed,
|
|
1320
|
+
setPanZoom,
|
|
1321
|
+
setSelectionKeyPressed,
|
|
1322
|
+
setSelectionRect,
|
|
1323
|
+
setSelectionRectMode,
|
|
1324
|
+
get setViewport() {
|
|
1325
|
+
return (viewport) => setViewportStore(() => viewport);
|
|
1326
|
+
},
|
|
1327
|
+
setWidth,
|
|
1328
|
+
setZoomActivationKeyPressed,
|
|
1329
|
+
addEdge,
|
|
1330
|
+
updateNodePositions,
|
|
1331
|
+
zoomIn,
|
|
1332
|
+
zoomOut,
|
|
1333
|
+
fitView,
|
|
1334
|
+
setCenter,
|
|
1335
|
+
setPaneClickDistance,
|
|
1336
|
+
unselectNodesAndEdges,
|
|
1337
|
+
addSelectedNodes,
|
|
1338
|
+
addSelectedEdges,
|
|
1339
|
+
handleNodeSelection,
|
|
1340
|
+
handleEdgeSelection,
|
|
1341
|
+
moveSelectedNodes,
|
|
1342
|
+
panBy: panBy$1,
|
|
1343
|
+
cancelConnection,
|
|
1344
|
+
reset
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
}, getEdgeId = (connection) => {
|
|
1348
|
+
let { source, sourceHandle, target, targetHandle } = connection;
|
|
1349
|
+
return `xy-edge__${source}${sourceHandle || ""}-${target}${targetHandle || ""}`;
|
|
1350
|
+
}, NodeConnectableContext = createContext(null);
|
|
1351
|
+
function useNodeConnectable() {
|
|
1352
|
+
let ctx = useContext(NodeConnectableContext);
|
|
1353
|
+
if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeConnectable");
|
|
1354
|
+
return ctx;
|
|
1355
|
+
}
|
|
1356
|
+
//#endregion
|
|
1357
|
+
//#region src/components/graph/handle/Handle.tsx
|
|
1358
|
+
const Handle = (props) => {
|
|
1359
|
+
let _props = propDefaults(props, {
|
|
1360
|
+
type: "source",
|
|
1361
|
+
position: "top",
|
|
1362
|
+
isConnectableStart: !0,
|
|
1363
|
+
isConnectableEnd: !0
|
|
1364
|
+
}), { store, nodeLookup, connections, actions } = useInternalSolidFlow(), rest = omit(_props, "id", "type", "position", "isConnectable", "isConnectableStart", "isConnectableEnd", "isValidConnection", "onConnect", "onDisconnect", "children", "class", "style"), nodeId = useNodeId(), nodeConnectable = useNodeConnectable(), connectable = () => _props.isConnectable ?? nodeConnectable(), isTarget = () => _props.type === "target", handleId = () => _props.id ?? null, connectionInProcess = () => !!store.connection.fromHandle, connectingFrom = () => store.connection.fromHandle && store.connection.fromHandle.nodeId === nodeId() && store.connection.fromHandle.type === _props.type && store.connection.fromHandle.id === handleId(), connectingTo = () => store.connection.toHandle && store.connection.toHandle.nodeId === nodeId() && store.connection.toHandle.type === _props.type && store.connection.toHandle.id === handleId(), isPossibleTargetHandle = () => store.connectionMode === "strict" ? store.connection.fromHandle?.type !== _props.type : nodeId() !== store.connection.fromHandle?.nodeId || handleId() !== store.connection.fromHandle?.id, valid = () => !!(connectingTo() && store.connection.isValid), prevConnections = null;
|
|
1365
|
+
createEffect(() => {
|
|
1366
|
+
if (!_props.onConnect && !_props.onDisconnect) return null;
|
|
1367
|
+
let rec = connections[connectionKey(nodeId(), _props.type, _props.id)], map = /* @__PURE__ */ new Map();
|
|
1368
|
+
for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
|
|
1369
|
+
return { connections: map };
|
|
1370
|
+
}, (current) => {
|
|
1371
|
+
if (!current) return;
|
|
1372
|
+
let { connections: next } = current;
|
|
1373
|
+
prevConnections && !areConnectionMapsEqual(next, prevConnections) && (handleConnectionChange(prevConnections, next, props.onDisconnect), handleConnectionChange(next, prevConnections, props.onConnect)), prevConnections = next;
|
|
1374
|
+
});
|
|
1375
|
+
let onConnectExtended = (connection) => {
|
|
1376
|
+
let handleConnection = {
|
|
1377
|
+
...connection,
|
|
1378
|
+
id: getEdgeId(connection)
|
|
1379
|
+
}, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
|
|
1380
|
+
actions.addEdge(edge), store.onConnect?.(handleConnection);
|
|
1381
|
+
}, onPointerDown = (event) => {
|
|
1382
|
+
XYHandle.onPointerDown(event, {
|
|
1383
|
+
handleId: handleId(),
|
|
1384
|
+
nodeId: nodeId(),
|
|
1385
|
+
isTarget: isTarget(),
|
|
1386
|
+
connectionRadius: store.connectionRadius,
|
|
1387
|
+
domNode: store.domNode,
|
|
1388
|
+
nodeLookup,
|
|
1389
|
+
connectionMode: store.connectionMode,
|
|
1390
|
+
lib: store.lib,
|
|
1391
|
+
autoPanOnConnect: store.autoPanOnConnect,
|
|
1392
|
+
flowId: store.id,
|
|
1393
|
+
isValidConnection: _props.isValidConnection ?? store.isValidConnection,
|
|
1394
|
+
updateConnection: ((connection) => {
|
|
1395
|
+
actions.setConnection(connection), flush();
|
|
1396
|
+
}),
|
|
1397
|
+
cancelConnection: actions.cancelConnection,
|
|
1398
|
+
panBy: actions.panBy,
|
|
1399
|
+
onConnect: onConnectExtended,
|
|
1400
|
+
onConnectStart: (event, startParams) => {
|
|
1401
|
+
store.onConnectStart?.(event, {
|
|
1402
|
+
nodeId: startParams.nodeId,
|
|
1403
|
+
handleId: startParams.handleId,
|
|
1404
|
+
handleType: startParams.handleType
|
|
1405
|
+
});
|
|
1406
|
+
},
|
|
1407
|
+
onConnectEnd: store.onConnectEnd,
|
|
1408
|
+
getTransform: () => store.transform,
|
|
1409
|
+
getFromHandle: () => store.connection.fromHandle,
|
|
1410
|
+
autoPanSpeed: store.autoPanSpeed,
|
|
1411
|
+
dragThreshold: store.connectionDragThreshold,
|
|
1412
|
+
handleDomNode: event.currentTarget
|
|
1413
|
+
});
|
|
1414
|
+
}, onClick = (event) => {
|
|
1415
|
+
if (!nodeId() || !store.clickConnectStartHandle && !_props.isConnectableStart) return;
|
|
1416
|
+
if (!store.clickConnectStartHandle) {
|
|
1417
|
+
store.onClickConnectStart?.(event, {
|
|
1418
|
+
nodeId: nodeId(),
|
|
1419
|
+
handleId: handleId(),
|
|
1420
|
+
handleType: _props.type
|
|
1421
|
+
}), actions.setClickConnectStartHandle({
|
|
1422
|
+
nodeId: nodeId(),
|
|
1423
|
+
type: _props.type,
|
|
1424
|
+
id: handleId()
|
|
1425
|
+
});
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
let doc = getHostForElement(event.target), isValidConnectionHandler = _props.isValidConnection ?? store.isValidConnection, { connection, isValid } = XYHandle.isValid(event, {
|
|
1429
|
+
handle: {
|
|
1430
|
+
nodeId: nodeId(),
|
|
1431
|
+
id: handleId(),
|
|
1432
|
+
type: _props.type
|
|
1433
|
+
},
|
|
1434
|
+
connectionMode: store.connectionMode,
|
|
1435
|
+
fromNodeId: store.clickConnectStartHandle.nodeId,
|
|
1436
|
+
fromHandleId: store.clickConnectStartHandle.id ?? null,
|
|
1437
|
+
fromType: store.clickConnectStartHandle.type,
|
|
1438
|
+
isValidConnection: isValidConnectionHandler,
|
|
1439
|
+
flowId: store.id,
|
|
1440
|
+
doc,
|
|
1441
|
+
lib: store.lib,
|
|
1442
|
+
nodeLookup
|
|
1443
|
+
});
|
|
1444
|
+
isValid && connection && onConnectExtended(connection);
|
|
1445
|
+
let connectionClone = structuredClone(snapshot(store.connection));
|
|
1446
|
+
delete connectionClone.inProgress, connectionClone.toPosition = connectionClone.toHandle ? connectionClone.toHandle.position : null, store.onClickConnectEnd?.(event, connectionClone), actions.setClickConnectStartHandle(void 0);
|
|
1447
|
+
}, connectionIndicator = () => connectable() && (!connectionInProcess() || isPossibleTargetHandle()) && (connectionInProcess() || store.clickConnectStartHandle ? _props.isConnectableEnd : _props.isConnectableStart);
|
|
1448
|
+
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={[
|
|
1449
|
+
"solid-flow__handle",
|
|
1450
|
+
`solid-flow__handle-${_props.position}`,
|
|
1451
|
+
store.noDragClass,
|
|
1452
|
+
store.noPanClass,
|
|
1453
|
+
_props.class,
|
|
1454
|
+
{
|
|
1455
|
+
valid: valid(),
|
|
1456
|
+
connectingto: !!connectingTo(),
|
|
1457
|
+
connectingfrom: !!connectingFrom(),
|
|
1458
|
+
source: !isTarget(),
|
|
1459
|
+
target: isTarget(),
|
|
1460
|
+
connectablestart: _props.isConnectableStart,
|
|
1461
|
+
connectableend: _props.isConnectableEnd,
|
|
1462
|
+
connectable: !!connectable(),
|
|
1463
|
+
connectionindicator: connectionIndicator()
|
|
1464
|
+
}
|
|
1465
|
+
]}>
|
|
1466
|
+
{_props.children}
|
|
1467
|
+
</div>;
|
|
1468
|
+
}, DefaultNode = (props) => {
|
|
1469
|
+
let _props = propDefaults(props, {
|
|
1470
|
+
targetPosition: Position$1.Top,
|
|
1471
|
+
sourcePosition: Position$1.Bottom
|
|
1472
|
+
});
|
|
1473
|
+
return <>
|
|
1474
|
+
<Handle type="target" position={_props.targetPosition} isConnectable={_props.isConnectable} />
|
|
1475
|
+
{props.data.label}
|
|
1476
|
+
<Handle type="source" position={_props.sourcePosition} isConnectable={_props.isConnectable} />
|
|
1477
|
+
</>;
|
|
1478
|
+
}, GroupNode = (props) => <div style={{
|
|
1479
|
+
position: "absolute",
|
|
1480
|
+
left: 0,
|
|
1481
|
+
top: 0,
|
|
1482
|
+
width: toPxString(props.width),
|
|
1483
|
+
height: toPxString(props.height)
|
|
1484
|
+
}} />, InputNode = (props) => {
|
|
1485
|
+
let _props = propDefaults(props, { sourcePosition: Position$1.Bottom });
|
|
1486
|
+
return <>
|
|
1487
|
+
{props.data?.label}
|
|
1488
|
+
<Handle type="source" position={_props.sourcePosition} isConnectable={_props.isConnectable} />
|
|
1489
|
+
</>;
|
|
1490
|
+
}, createDraggable = (elem, params) => {
|
|
1491
|
+
let { store, nodeLookup, actions } = useInternalSolidFlow(), [dragging, setDragging] = createSignal(!1);
|
|
1492
|
+
return createEffect(() => ({
|
|
1493
|
+
el: elem(),
|
|
1494
|
+
current: params()
|
|
1495
|
+
}), ({ el, current }) => {
|
|
1496
|
+
if (!el || current.disabled) return;
|
|
1497
|
+
let { onDrag, onDragStart, onDragStop, onNodeMouseDown } = current, dragInstance = XYDrag({
|
|
1498
|
+
onDrag,
|
|
1499
|
+
onDragStart: (event, dragItems, node, nodes) => {
|
|
1500
|
+
setDragging(!0), onDragStart?.(event, dragItems, node, nodes);
|
|
1501
|
+
},
|
|
1502
|
+
onDragStop: (event, dragItems, node, nodes) => {
|
|
1503
|
+
setDragging(!1), onDragStop?.(event, dragItems, node, nodes);
|
|
1504
|
+
},
|
|
1505
|
+
onNodeMouseDown,
|
|
1506
|
+
getStoreItems: () => ({
|
|
1507
|
+
nodes: store.nodes,
|
|
1508
|
+
nodeLookup,
|
|
1509
|
+
edges: store.edges,
|
|
1510
|
+
nodeExtent: store.nodeExtent,
|
|
1511
|
+
snapGrid: store.snapGrid ?? [0, 0],
|
|
1512
|
+
snapToGrid: !!store.snapGrid,
|
|
1513
|
+
autoPanSpeed: store.autoPanSpeed,
|
|
1514
|
+
nodeOrigin: store.nodeOrigin,
|
|
1515
|
+
multiSelectionActive: store.multiselectionKeyPressed,
|
|
1516
|
+
domNode: store.domNode,
|
|
1517
|
+
transform: store.transform,
|
|
1518
|
+
autoPanOnNodeDrag: store.autoPanOnNodeDrag,
|
|
1519
|
+
nodesDraggable: store.nodesDraggable,
|
|
1520
|
+
selectNodesOnDrag: store.selectNodesOnDrag,
|
|
1521
|
+
nodeDragThreshold: store.nodeDragThreshold,
|
|
1522
|
+
unselectNodesAndEdges: actions.unselectNodesAndEdges,
|
|
1523
|
+
updateNodePositions: actions.updateNodePositions,
|
|
1524
|
+
panBy: actions.panBy
|
|
1525
|
+
})
|
|
1526
|
+
});
|
|
1527
|
+
return dragInstance.update({
|
|
1528
|
+
domNode: el,
|
|
1529
|
+
nodeId: current.nodeId,
|
|
1530
|
+
noDragClassName: current.noDragClass,
|
|
1531
|
+
handleSelector: current.handleSelector,
|
|
1532
|
+
isSelectable: current.isSelectable,
|
|
1533
|
+
nodeClickDistance: current.nodeClickDistance
|
|
1534
|
+
}), () => {
|
|
1535
|
+
dragInstance.destroy();
|
|
1536
|
+
};
|
|
1537
|
+
}), dragging;
|
|
1538
|
+
}, NodeIdContext = createContext(null);
|
|
1539
|
+
function useNodeId() {
|
|
1540
|
+
let ctx = useContext(NodeIdContext);
|
|
1541
|
+
if (!ctx) throw Error("solid-flow: Your application must be wrapped with <SolidFlow> in order to invoke useNodeId");
|
|
1542
|
+
return ctx;
|
|
1543
|
+
}
|
|
1544
|
+
//#endregion
|
|
1545
|
+
//#region src/components/graph/node/NodeWrapper.tsx
|
|
1546
|
+
const NodeWrapper = (props) => {
|
|
1547
|
+
let { store, nodeLookup, parentIds, actions } = useInternalSolidFlow(), [nodeRef, setNodeRef] = createSignal(), node = () => nodeLookup.get(props.nodeId), nodeId = () => node().id, nodeType = () => node().type ?? "default", deletable = () => node().deletable ?? !0, selectable = () => node().selectable ?? store.elementsSelectable, focusable = () => node().focusable ?? store.nodesFocusable, draggable = () => node().draggable ?? store.nodesDraggable, connectable = () => node().connectable ?? store.nodesConnectable, userNode = () => node().internals.userNode, nodeTypeValid = () => nodeType() in store.nodeTypes, nodeComponent = () => store.nodeTypes[nodeType()], isParentNode = () => !!parentIds[node().id], transform = () => {
|
|
1548
|
+
let { x, y } = node().internals.positionAbsolute;
|
|
1549
|
+
return `translate(${x}px, ${y}px)`;
|
|
1550
|
+
}, sizeStyle = () => {
|
|
1551
|
+
let w = node().width ?? node().initialWidth, h = node().height ?? node().initialHeight;
|
|
1552
|
+
return {
|
|
1553
|
+
...node().style,
|
|
1554
|
+
...w ? { width: toPxString(w) } : {},
|
|
1555
|
+
...h ? { height: toPxString(h) } : {}
|
|
1556
|
+
};
|
|
1557
|
+
}, style = () => ({
|
|
1558
|
+
"z-index": node().internals.z,
|
|
1559
|
+
transform: transform(),
|
|
1560
|
+
visibility: nodeHasDimensions(node()) ? "visible" : "hidden",
|
|
1561
|
+
...sizeStyle(),
|
|
1562
|
+
...node().style ?? {}
|
|
1563
|
+
});
|
|
1564
|
+
createEffect(() => ({
|
|
1565
|
+
valid: nodeTypeValid(),
|
|
1566
|
+
nodeType: nodeType()
|
|
1567
|
+
}), ({ valid, nodeType }) => {}), createEffect(() => ({
|
|
1568
|
+
id: node().id,
|
|
1569
|
+
nodeElement: nodeRef(),
|
|
1570
|
+
nodeType: nodeType(),
|
|
1571
|
+
sourcePosition: node().sourcePosition,
|
|
1572
|
+
targetPosition: node().targetPosition
|
|
1573
|
+
}), (current, prev) => {
|
|
1574
|
+
current.nodeElement && (prev && prev.nodeElement === current.nodeElement && prev.sourcePosition === current.sourcePosition && prev.targetPosition === current.targetPosition && prev.nodeType === current.nodeType || actions.requestUpdateNodeInternals([[current.id, {
|
|
1575
|
+
id: current.id,
|
|
1576
|
+
nodeElement: current.nodeElement,
|
|
1577
|
+
force: !0
|
|
1578
|
+
}]]));
|
|
1579
|
+
}), createEffect(() => ({
|
|
1580
|
+
nodeElement: nodeRef(),
|
|
1581
|
+
hasDimensions: nodeHasDimensions(node()),
|
|
1582
|
+
resizeObserver: props.resizeObserver
|
|
1583
|
+
}), (current, prev) => {
|
|
1584
|
+
current.nodeElement === prev?.nodeElement && current.resizeObserver === prev?.resizeObserver && current.hasDimensions || (prev?.nodeElement && prev.resizeObserver?.unobserve(prev.nodeElement), current.nodeElement && current.resizeObserver?.observe(current.nodeElement));
|
|
1585
|
+
});
|
|
1586
|
+
let onSelectNodeHandler = (event) => {
|
|
1587
|
+
selectable() && (!store.selectNodesOnDrag || !draggable() || store.nodeDragThreshold > 0) && actions.handleNodeSelection(node().id), props.onNodeClick?.({
|
|
1588
|
+
node: userNode(),
|
|
1589
|
+
event
|
|
1590
|
+
});
|
|
1591
|
+
}, onKeyDown = (event) => {
|
|
1592
|
+
if (isInputDOMNode(event) || store.disableKeyboardA11y) return;
|
|
1593
|
+
if (elementSelectionKeys.includes(event.key) && selectable()) {
|
|
1594
|
+
actions.handleNodeSelection(node().id, event.key === "Escape", nodeRef());
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
let arrowKeyDiff = ARROW_KEY_DIFFS[event.key];
|
|
1598
|
+
draggable() && node().selected && arrowKeyDiff && (event.preventDefault(), actions.setAriaLiveMessage(store.ariaLabelConfig["node.a11yDescription.ariaLiveMessage"]({
|
|
1599
|
+
direction: event.key.replace("Arrow", "").toLowerCase(),
|
|
1600
|
+
x: ~~node().internals.positionAbsolute.x,
|
|
1601
|
+
y: ~~node().internals.positionAbsolute.y
|
|
1602
|
+
})), actions.moveSelectedNodes(arrowKeyDiff, event.shiftKey ? 4 : 1));
|
|
1603
|
+
}, onFocus = () => {
|
|
1604
|
+
if (store.disableKeyboardA11y || !store.autoPanOnNodeFocus || !nodeRef()?.matches(":focus-visible")) return;
|
|
1605
|
+
let { width, height, viewport } = store;
|
|
1606
|
+
getNodesInside(/* @__PURE__ */ new Map([[node().id, node()]]), {
|
|
1607
|
+
x: 0,
|
|
1608
|
+
y: 0,
|
|
1609
|
+
width,
|
|
1610
|
+
height
|
|
1611
|
+
}, [
|
|
1612
|
+
viewport.x,
|
|
1613
|
+
viewport.y,
|
|
1614
|
+
viewport.zoom
|
|
1615
|
+
], !0).length > 0 || actions.setCenter(node().position.x + (node().measured.width ?? 0) / 2, node().position.y + (node().measured.height ?? 0) / 2, { zoom: viewport.zoom });
|
|
1616
|
+
}, dragging = createDraggable(nodeRef, () => ({
|
|
1617
|
+
nodeId: node().id,
|
|
1618
|
+
isSelectable: selectable(),
|
|
1619
|
+
disabled: !draggable(),
|
|
1620
|
+
handleSelector: node().dragHandle,
|
|
1621
|
+
noDragClass: store.noDragClass,
|
|
1622
|
+
nodeClickDistance: props.nodeClickDistance,
|
|
1623
|
+
onNodeMouseDown: actions.handleNodeSelection,
|
|
1624
|
+
onDrag: (event, _, targetNode, nodes) => {
|
|
1625
|
+
props.onNodeDrag?.({
|
|
1626
|
+
event,
|
|
1627
|
+
targetNode,
|
|
1628
|
+
nodes
|
|
1629
|
+
});
|
|
1630
|
+
},
|
|
1631
|
+
onDragStart: (event, _, targetNode, nodes) => {
|
|
1632
|
+
props.onNodeDragStart?.({
|
|
1633
|
+
event,
|
|
1634
|
+
targetNode,
|
|
1635
|
+
nodes
|
|
1636
|
+
});
|
|
1637
|
+
},
|
|
1638
|
+
onDragStop: (event, _, targetNode, nodes) => {
|
|
1639
|
+
props.onNodeDragStop?.({
|
|
1640
|
+
event,
|
|
1641
|
+
targetNode,
|
|
1642
|
+
nodes
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
}));
|
|
1646
|
+
return <Show when={!node().hidden}>
|
|
1647
|
+
<div ref={setNodeRef} data-id={node().id} class={[
|
|
1648
|
+
"solid-flow__node",
|
|
1649
|
+
`solid-flow__node-${nodeType()}`,
|
|
1650
|
+
{
|
|
1651
|
+
connectable: !!connectable(),
|
|
1652
|
+
draggable: !!draggable(),
|
|
1653
|
+
dragging: dragging(),
|
|
1654
|
+
nopan: !!draggable(),
|
|
1655
|
+
parent: isParentNode(),
|
|
1656
|
+
selectable: !!selectable(),
|
|
1657
|
+
selected: !!node().selected
|
|
1658
|
+
},
|
|
1659
|
+
node().class
|
|
1660
|
+
]} style={style()} onClick={onSelectNodeHandler} onPointerEnter={(event) => props.onNodePointerEnter?.({
|
|
1661
|
+
node: userNode(),
|
|
1662
|
+
event
|
|
1663
|
+
})} onPointerLeave={(event) => props.onNodePointerLeave?.({
|
|
1664
|
+
node: userNode(),
|
|
1665
|
+
event
|
|
1666
|
+
})} onPointerMove={(event) => props.onNodePointerMove?.({
|
|
1667
|
+
node: userNode(),
|
|
1668
|
+
event
|
|
1669
|
+
})} onContextMenu={(event) => props.onNodeContextMenu?.({
|
|
1670
|
+
node: userNode(),
|
|
1671
|
+
event
|
|
1672
|
+
})} 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}>
|
|
1673
|
+
<NodeIdContext value={nodeId}>
|
|
1674
|
+
<NodeConnectableContext value={connectable}>
|
|
1675
|
+
<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} />
|
|
1676
|
+
</NodeConnectableContext>
|
|
1677
|
+
</NodeIdContext>
|
|
1678
|
+
</div>
|
|
1679
|
+
</Show>;
|
|
1680
|
+
}, OutputNode = (props) => {
|
|
1681
|
+
let _props = propDefaults(props, { targetPosition: Position$1.Top });
|
|
1682
|
+
return <>
|
|
1683
|
+
<Handle type="target" position={_props.targetPosition} isConnectable={_props.isConnectable} />
|
|
1684
|
+
{props.data?.label}
|
|
1685
|
+
</>;
|
|
1686
|
+
};
|
|
1687
|
+
//#endregion
|
|
1688
|
+
//#region src/data/xyflow.ts
|
|
1689
|
+
/**
|
|
1690
|
+
* The DOM side of the measurement pipeline (fork of @xyflow/system's
|
|
1691
|
+
* updateNodeInternals): reads each updated node element's dimensions and
|
|
1692
|
+
* handle bounds from the DOM and reports them as data — measurement writes
|
|
1693
|
+
* for the measurements root plus user-facing dimension/position changes —
|
|
1694
|
+
* instead of writing into a node lookup. The internalNodes projection turns
|
|
1695
|
+
* the measurement writes back into internal-node state.
|
|
1696
|
+
*
|
|
1697
|
+
* `parentExpandChildren` must be turned into changes via
|
|
1698
|
+
* {@link handleExpandParent} only AFTER the measurement writes have been
|
|
1699
|
+
* flushed, so parent geometry reflects this measuring pass.
|
|
1700
|
+
*/
|
|
1701
|
+
function measureNodeInternals(updates, nodeLookup, domNode, nodeExtent) {
|
|
1702
|
+
let measurementWrites = [], changes = [], parentExpandChildren = [], updatedInternals = !1, viewportNode = domNode?.querySelector(".xyflow__viewport");
|
|
1703
|
+
if (!viewportNode) return {
|
|
1704
|
+
updatedInternals,
|
|
1705
|
+
measurementWrites,
|
|
1706
|
+
changes,
|
|
1707
|
+
parentExpandChildren
|
|
1708
|
+
};
|
|
1709
|
+
let style = window.getComputedStyle(viewportNode), { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
|
|
1710
|
+
for (let update of updates.values()) {
|
|
1711
|
+
let node = nodeLookup.get(update.id);
|
|
1712
|
+
if (!node) continue;
|
|
1713
|
+
if (node.hidden) {
|
|
1714
|
+
measurementWrites.push({
|
|
1715
|
+
id: node.id,
|
|
1716
|
+
hidden: !0
|
|
1717
|
+
}), updatedInternals = !0;
|
|
1718
|
+
continue;
|
|
1719
|
+
}
|
|
1720
|
+
let dimensions = getDimensions(update.nodeElement), dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height;
|
|
1721
|
+
if (dimensions.width && dimensions.height && (dimensionChanged || !node.internals.handleBounds || update.force)) {
|
|
1722
|
+
let nodeBounds = update.nodeElement.getBoundingClientRect(), extent = isCoordinateExtent(node.extent) ? node.extent : nodeExtent, { positionAbsolute } = node.internals;
|
|
1723
|
+
node.parentId && node.extent === "parent" ? positionAbsolute = clampPositionToParent(positionAbsolute, dimensions, nodeLookup.get(node.parentId)) : extent && (positionAbsolute = clampPosition(positionAbsolute, extent, dimensions)), measurementWrites.push({
|
|
1724
|
+
id: node.id,
|
|
1725
|
+
measured: dimensions,
|
|
1726
|
+
handleBounds: {
|
|
1727
|
+
source: getHandleBounds("source", update.nodeElement, nodeBounds, zoom, node.id),
|
|
1728
|
+
target: getHandleBounds("target", update.nodeElement, nodeBounds, zoom, node.id)
|
|
1729
|
+
}
|
|
1730
|
+
}), updatedInternals = !0, dimensionChanged && (changes.push({
|
|
1731
|
+
id: node.id,
|
|
1732
|
+
type: "dimensions",
|
|
1733
|
+
dimensions
|
|
1734
|
+
}), node.expandParent && node.parentId && parentExpandChildren.push({
|
|
1735
|
+
id: node.id,
|
|
1736
|
+
parentId: node.parentId,
|
|
1737
|
+
rect: nodeToRect({
|
|
1738
|
+
...node,
|
|
1739
|
+
measured: dimensions,
|
|
1740
|
+
internals: {
|
|
1741
|
+
...node.internals,
|
|
1742
|
+
positionAbsolute
|
|
1743
|
+
}
|
|
1744
|
+
})
|
|
1745
|
+
}));
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return {
|
|
1749
|
+
updatedInternals,
|
|
1750
|
+
measurementWrites,
|
|
1751
|
+
changes,
|
|
1752
|
+
parentExpandChildren
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
function handleExpandParent(children, nodeLookup, getChildNodes, nodeOrigin = [0, 0]) {
|
|
1756
|
+
let changes = [], parentExpansions = /* @__PURE__ */ new Map();
|
|
1757
|
+
for (let child of children) {
|
|
1758
|
+
let parent = nodeLookup.get(child.parentId);
|
|
1759
|
+
if (!parent) continue;
|
|
1760
|
+
let parentRect = parentExpansions.get(child.parentId)?.expandedRect ?? nodeToRect(parent), expandedRect = getBoundsOfRects(parentRect, child.rect);
|
|
1761
|
+
parentExpansions.set(child.parentId, {
|
|
1762
|
+
expandedRect,
|
|
1763
|
+
parent
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
return parentExpansions.size > 0 && parentExpansions.forEach(({ expandedRect, parent }, parentId) => {
|
|
1767
|
+
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];
|
|
1768
|
+
if (xChange > 0 || yChange > 0 || widthChange || heightChange) {
|
|
1769
|
+
changes.push({
|
|
1770
|
+
id: parentId,
|
|
1771
|
+
type: "position",
|
|
1772
|
+
position: {
|
|
1773
|
+
x: parent.position.x - xChange + widthChange,
|
|
1774
|
+
y: parent.position.y - yChange + heightChange
|
|
1191
1775
|
}
|
|
1192
|
-
};
|
|
1193
|
-
nodeLookup.set(userNode.id, updatedNodeInternals), userNode.parentId && updateChildNode(updatedNodeInternals, nodeLookup, parentLookup, {
|
|
1194
|
-
nodeOrigin: store.nodeOrigin,
|
|
1195
|
-
nodeExtent: store.nodeExtent,
|
|
1196
|
-
elevateNodesOnSelect: store.elevateNodesOnSelect,
|
|
1197
|
-
zIndexMode: store.zIndexMode,
|
|
1198
|
-
checkEquality: !0
|
|
1199
|
-
});
|
|
1200
|
-
}, () => {}), onCleanup(() => {});
|
|
1201
|
-
}), () => {}), createEffect(() => new Set(store.nodes.map((n) => n.id)), (currentIds) => {
|
|
1202
|
-
for (let id of untrack(() => Array.from(nodeLookup.keys()))) currentIds.has(id) || nodeLookup.delete(id);
|
|
1203
|
-
}), createRenderEffect(mapArray(() => store.edges, (edge) => {
|
|
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);
|
|
1225
|
-
}
|
|
1226
|
-
let edgePosition = getEdgePosition({
|
|
1227
|
-
id: edge.id,
|
|
1228
|
-
sourceNode,
|
|
1229
|
-
targetNode,
|
|
1230
|
-
sourceHandle: edge.sourceHandle || null,
|
|
1231
|
-
targetHandle: edge.targetHandle || null,
|
|
1232
|
-
connectionMode: store.connectionMode,
|
|
1233
|
-
onError: store.onError
|
|
1234
1776
|
});
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
sourceNode,
|
|
1243
|
-
targetNode,
|
|
1244
|
-
elevateOnSelect: store.elevateEdgesOnSelect,
|
|
1245
|
-
zIndexMode: store.zIndexMode
|
|
1246
|
-
}),
|
|
1247
|
-
sourceNode,
|
|
1248
|
-
targetNode,
|
|
1249
|
-
edge
|
|
1777
|
+
for (let childNode of getChildNodes(parentId)) children.some((child) => child.id === childNode.id) || changes.push({
|
|
1778
|
+
id: childNode.id,
|
|
1779
|
+
type: "position",
|
|
1780
|
+
position: {
|
|
1781
|
+
x: childNode.position.x + xChange,
|
|
1782
|
+
y: childNode.position.y + yChange
|
|
1783
|
+
}
|
|
1250
1784
|
});
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1785
|
+
}
|
|
1786
|
+
(dimensions.width < expandedRect.width || dimensions.height < expandedRect.height || xChange || yChange) && changes.push({
|
|
1787
|
+
id: parentId,
|
|
1788
|
+
type: "dimensions",
|
|
1789
|
+
setAttributes: !0,
|
|
1790
|
+
dimensions: {
|
|
1791
|
+
width: newWidth + (xChange ? origin[0] * xChange - widthChange : 0),
|
|
1792
|
+
height: newHeight + (yChange ? origin[1] * yChange - heightChange : 0)
|
|
1793
|
+
}
|
|
1253
1794
|
});
|
|
1254
|
-
}),
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1795
|
+
}), changes;
|
|
1796
|
+
}
|
|
1797
|
+
//#endregion
|
|
1798
|
+
//#region src/data/createSolidFlow.tsx
|
|
1799
|
+
const InitialNodeTypesMap = {
|
|
1800
|
+
input: InputNode,
|
|
1801
|
+
output: OutputNode,
|
|
1802
|
+
default: DefaultNode,
|
|
1803
|
+
group: GroupNode
|
|
1804
|
+
}, InitialEdgeTypesMap = {
|
|
1805
|
+
straight: StraightEdgeInternal,
|
|
1806
|
+
smoothstep: SmoothStepEdgeInternal,
|
|
1807
|
+
default: BezierEdgeInternal,
|
|
1808
|
+
step: StepEdgeInternal
|
|
1809
|
+
}, createSolidFlow = (props) => {
|
|
1810
|
+
let state = createFlowState(props, {
|
|
1811
|
+
prefersDark: createMediaQuery("(prefers-color-scheme: dark)", props.colorModeSSR === "dark"),
|
|
1812
|
+
initialNodeTypes: InitialNodeTypesMap,
|
|
1813
|
+
initialEdgeTypes: InitialEdgeTypesMap
|
|
1814
|
+
}), { store, nodeLookup, actions } = state, pendingEntries, requestUpdateNodeInternals = (updateEntries) => {
|
|
1815
|
+
if (pendingEntries) {
|
|
1816
|
+
pendingEntries.push(...updateEntries);
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
pendingEntries = updateEntries, scheduleIdleCallback(() => {
|
|
1820
|
+
let updates = new Map(pendingEntries);
|
|
1821
|
+
pendingEntries = void 0;
|
|
1822
|
+
let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode);
|
|
1823
|
+
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());
|
|
1824
|
+
});
|
|
1825
|
+
};
|
|
1826
|
+
return actions.setMeasureRequester(requestUpdateNodeInternals), {
|
|
1827
|
+
...state,
|
|
1262
1828
|
actions: {
|
|
1263
|
-
|
|
1264
|
-
|
|
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();
|
|
1354
|
-
}
|
|
1829
|
+
...actions,
|
|
1830
|
+
requestUpdateNodeInternals
|
|
1355
1831
|
}
|
|
1356
1832
|
};
|
|
1357
1833
|
}, SolidFlowContext = createContext(null);
|
|
@@ -1421,7 +1897,7 @@ const Marker = (props) => {
|
|
|
1421
1897
|
for (let item of a) if (!b.has(item)) return !1;
|
|
1422
1898
|
return !0;
|
|
1423
1899
|
}, Pane = (props) => {
|
|
1424
|
-
let { store, nodeLookup, edgeLookup,
|
|
1900
|
+
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
1901
|
x: 0,
|
|
1426
1902
|
y: 0
|
|
1427
1903
|
}, 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 +1943,9 @@ const Marker = (props) => {
|
|
|
1467
1943
|
let edgesSelectable = store.defaultEdgeOptions.selectable ?? !0;
|
|
1468
1944
|
selectedEdgeIds = /* @__PURE__ */ new Set();
|
|
1469
1945
|
for (let nodeId of selectedNodeIds) {
|
|
1470
|
-
let
|
|
1471
|
-
if (
|
|
1472
|
-
let edge = edgeLookup
|
|
1946
|
+
let nodeConnections = connections[nodeId];
|
|
1947
|
+
if (nodeConnections) for (let { edgeId } of Object.values(nodeConnections)) {
|
|
1948
|
+
let edge = edgeLookup[edgeId];
|
|
1473
1949
|
edge && (edge.selectable ?? edgesSelectable) && selectedEdgeIds.add(edgeId);
|
|
1474
1950
|
}
|
|
1475
1951
|
}
|
|
@@ -1800,7 +2276,7 @@ const Marker = (props) => {
|
|
|
1800
2276
|
{_props.afterControls}
|
|
1801
2277
|
</Panel>;
|
|
1802
2278
|
}, EdgeToolbar = (props) => {
|
|
1803
|
-
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
|
|
2279
|
+
let rest = omit(props, "x", "y", "alignX", "alignY", "isVisible", "selectEdgeOnClick", "class", "children"), { store, edgeLookup } = useInternalSolidFlow(), edgeId = useEdgeId(), isActive = () => typeof props.isVisible == "boolean" ? props.isVisible : !!edgeLookup[edgeId()]?.selected, transform = () => getEdgeToolbarTransform(props.x, props.y, store.viewport.zoom, props.alignX ?? "center", props.alignY ?? "center");
|
|
1804
2280
|
return <Show when={isActive()}>
|
|
1805
2281
|
<EdgeLabel selectEdgeOnClick={props.selectEdgeOnClick} transparent>
|
|
1806
2282
|
<div class={["solid-flow__edge-toolbar", props.class]} style={{
|
|
@@ -2017,8 +2493,8 @@ const Marker = (props) => {
|
|
|
2017
2493
|
* @returns an accessor for the current color mode class
|
|
2018
2494
|
*/
|
|
2019
2495
|
function useColorMode() {
|
|
2020
|
-
let {
|
|
2021
|
-
return () =>
|
|
2496
|
+
let { flow } = useInternalSolidFlow();
|
|
2497
|
+
return () => flow.colorMode;
|
|
2022
2498
|
}
|
|
2023
2499
|
//#endregion
|
|
2024
2500
|
//#region src/hooks/useConnection.tsx
|
|
@@ -2029,8 +2505,8 @@ function useColorMode() {
|
|
|
2029
2505
|
* @returns current connection as a readable store
|
|
2030
2506
|
*/
|
|
2031
2507
|
function useConnection() {
|
|
2032
|
-
let {
|
|
2033
|
-
return () =>
|
|
2508
|
+
let { flow } = useInternalSolidFlow();
|
|
2509
|
+
return () => flow.connection;
|
|
2034
2510
|
}
|
|
2035
2511
|
//#endregion
|
|
2036
2512
|
//#region src/hooks/useGraph.ts
|
|
@@ -2061,15 +2537,15 @@ function useEdges() {
|
|
|
2061
2537
|
* @returns store with the viewport object
|
|
2062
2538
|
*/
|
|
2063
2539
|
function useViewport() {
|
|
2064
|
-
let {
|
|
2065
|
-
return () =>
|
|
2540
|
+
let { flow } = useInternalSolidFlow();
|
|
2541
|
+
return () => flow.viewport;
|
|
2066
2542
|
}
|
|
2067
2543
|
//#endregion
|
|
2068
2544
|
//#region src/hooks/useHandleEdgeSelect.tsx
|
|
2069
2545
|
function useHandleEdgeSelect() {
|
|
2070
2546
|
let { store, edgeLookup, actions } = useInternalSolidFlow();
|
|
2071
2547
|
return (id) => {
|
|
2072
|
-
let edge = edgeLookup
|
|
2548
|
+
let edge = edgeLookup[id];
|
|
2073
2549
|
edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (actions.setSelectionRect(void 0), actions.setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && actions.unselectNodesAndEdges({
|
|
2074
2550
|
nodes: [],
|
|
2075
2551
|
edges: [edge]
|
|
@@ -2088,8 +2564,8 @@ function useHandleEdgeSelect() {
|
|
|
2088
2564
|
* @returns an accessor that indicates whether the nodes are initialized
|
|
2089
2565
|
*/
|
|
2090
2566
|
function useNodesInitialized() {
|
|
2091
|
-
let {
|
|
2092
|
-
return () =>
|
|
2567
|
+
let { flow } = useInternalSolidFlow();
|
|
2568
|
+
return () => flow.nodesInitialized;
|
|
2093
2569
|
}
|
|
2094
2570
|
/**
|
|
2095
2571
|
* Hook for seeing if the viewport is initialized.
|
|
@@ -2100,8 +2576,8 @@ function useNodesInitialized() {
|
|
|
2100
2576
|
* @returns an accessor that indicates whether the viewport is initialized
|
|
2101
2577
|
*/
|
|
2102
2578
|
function useViewportInitialized() {
|
|
2103
|
-
let {
|
|
2104
|
-
return () =>
|
|
2579
|
+
let { flow } = useInternalSolidFlow();
|
|
2580
|
+
return () => flow.viewportInitialized;
|
|
2105
2581
|
}
|
|
2106
2582
|
//#endregion
|
|
2107
2583
|
//#region src/hooks/useInternalNode.tsx
|
|
@@ -2113,8 +2589,8 @@ function useViewportInitialized() {
|
|
|
2113
2589
|
* @returns an accessor with an internal node or undefined
|
|
2114
2590
|
*/
|
|
2115
2591
|
function useInternalNode(id) {
|
|
2116
|
-
let {
|
|
2117
|
-
return () =>
|
|
2592
|
+
let { flow } = useInternalSolidFlow();
|
|
2593
|
+
return () => flow.internalNodes[id()];
|
|
2118
2594
|
}
|
|
2119
2595
|
//#endregion
|
|
2120
2596
|
//#region src/hooks/useNodeConnections.ts
|
|
@@ -2130,24 +2606,28 @@ function useInternalNode(id) {
|
|
|
2130
2606
|
* @returns an array with connections
|
|
2131
2607
|
*/
|
|
2132
2608
|
const useNodeConnections = (params) => {
|
|
2133
|
-
let {
|
|
2609
|
+
let { flow } = useInternalSolidFlow(), ctxNodeId = () => {
|
|
2134
2610
|
let id = useContext(NodeIdContext);
|
|
2135
2611
|
return id ? id() : "";
|
|
2136
|
-
}, id = () => params().handleId, type = () => params().handleType, nodeId = () => params().id ?? ctxNodeId(), [connections, setConnections] = createSignal([]),
|
|
2137
|
-
return createEffect(() =>
|
|
2138
|
-
|
|
2612
|
+
}, id = () => params().handleId, type = () => params().handleType, nodeId = () => params().id ?? ctxNodeId(), [connections, setConnections] = createSignal([]), prevConnections;
|
|
2613
|
+
return createEffect(() => {
|
|
2614
|
+
let rec = flow.connections[connectionKey(nodeId(), type(), id())], map = /* @__PURE__ */ new Map();
|
|
2615
|
+
for (let key of Object.keys(rec ?? {})) map.set(key, { ...rec[key] });
|
|
2616
|
+
return { connections: map };
|
|
2617
|
+
}, ({ connections: nextConnections }) => {
|
|
2618
|
+
areConnectionMapsEqual(nextConnections, prevConnections) || (prevConnections = nextConnections, setConnections(Array.from(nextConnections.values())));
|
|
2139
2619
|
}), connections;
|
|
2140
2620
|
};
|
|
2141
2621
|
//#endregion
|
|
2142
2622
|
//#region src/hooks/useNodesData.ts
|
|
2143
2623
|
function useNodesData(nodeIds) {
|
|
2144
|
-
let {
|
|
2624
|
+
let { flow } = useInternalSolidFlow(), prevNodesData = [];
|
|
2145
2625
|
return createMemo(() => {
|
|
2146
2626
|
let nodesData = [], idValues = nodeIds();
|
|
2147
2627
|
if (!idValues) return;
|
|
2148
2628
|
let ids = Array.isArray(idValues) ? idValues : [idValues];
|
|
2149
2629
|
for (let nodeId of ids) {
|
|
2150
|
-
let node =
|
|
2630
|
+
let node = flow.internalNodes[nodeId]?.internals.userNode;
|
|
2151
2631
|
node && nodesData.push({
|
|
2152
2632
|
id: node.id,
|
|
2153
2633
|
type: node.type,
|
|
@@ -2160,206 +2640,45 @@ function useNodesData(nodeIds) {
|
|
|
2160
2640
|
//#endregion
|
|
2161
2641
|
//#region src/hooks/useSolidFlow.tsx
|
|
2162
2642
|
/**
|
|
2163
|
-
* Hook for accessing the
|
|
2643
|
+
* Hook for accessing the flow instance: `{ flow, commands }` plus the
|
|
2644
|
+
* commands spread at the top level for upstream familiarity.
|
|
2645
|
+
*
|
|
2646
|
+
* `flow` and `commands` are stable identities, so destructuring them is safe:
|
|
2647
|
+
* `const { flow, commands } = useSolidFlow()`.
|
|
2164
2648
|
*
|
|
2165
2649
|
* @public
|
|
2166
|
-
* @returns
|
|
2650
|
+
* @returns the flow's read struct, write surface, and deprecated aliases
|
|
2167
2651
|
*/
|
|
2168
2652
|
function useSolidFlow() {
|
|
2169
|
-
let {
|
|
2170
|
-
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 = {
|
|
2171
|
-
...nodeToUse,
|
|
2172
|
-
position,
|
|
2173
|
-
width: nodeToUse.measured?.width ?? nodeToUse.width,
|
|
2174
|
-
height: nodeToUse.measured?.height ?? nodeToUse.height
|
|
2175
|
-
};
|
|
2176
|
-
return nodeToRect(nodeWithPosition);
|
|
2177
|
-
}, updateNode = (id, nodeUpdate, options = { replace: !1 }) => {
|
|
2178
|
-
actions.setNodes((nodes) => {
|
|
2179
|
-
let index = nodes.findIndex((node) => node.id === id);
|
|
2180
|
-
if (index === -1) return;
|
|
2181
|
-
let node = nodes[index], nextNode = typeof nodeUpdate == "function" ? nodeUpdate(node) : nodeUpdate;
|
|
2182
|
-
nodes[index] = options?.replace && isNode(nextNode) ? nextNode : {
|
|
2183
|
-
...node,
|
|
2184
|
-
...nextNode
|
|
2185
|
-
};
|
|
2186
|
-
});
|
|
2187
|
-
}, updateEdge = (id, edgeUpdate, options = { replace: !1 }) => {
|
|
2188
|
-
actions.setEdges((edges) => {
|
|
2189
|
-
let index = edges.findIndex((edge) => edge.id === id);
|
|
2190
|
-
if (index === -1) return;
|
|
2191
|
-
let edge = edges[index], nextEdge = typeof edgeUpdate == "function" ? edgeUpdate(edge) : edgeUpdate;
|
|
2192
|
-
edges[index] = options.replace && isEdge(nextEdge) ? nextEdge : {
|
|
2193
|
-
...edge,
|
|
2194
|
-
...nextEdge
|
|
2195
|
-
};
|
|
2196
|
-
});
|
|
2197
|
-
}, getInternalNode = (id) => nodeLookup.get(id);
|
|
2653
|
+
let { flow, commands, store, edgeLookup } = useInternalSolidFlow(), getInternalNode = (id) => flow.internalNodes[id];
|
|
2198
2654
|
return {
|
|
2199
|
-
|
|
2200
|
-
|
|
2655
|
+
...commands,
|
|
2656
|
+
flow,
|
|
2657
|
+
commands,
|
|
2201
2658
|
getInternalNode,
|
|
2202
|
-
setCenter: actions.setCenter,
|
|
2203
|
-
fitView: actions.fitView,
|
|
2204
2659
|
getNode: (id) => getInternalNode(id)?.internals.userNode,
|
|
2205
|
-
getNodes: (ids) => ids ?
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
},
|
|
2212
|
-
addEdges: (payload) => {
|
|
2213
|
-
let newEdges = Array.isArray(payload) ? payload : [payload];
|
|
2214
|
-
actions.setEdges((edges) => [...edges, ...newEdges]);
|
|
2215
|
-
},
|
|
2660
|
+
getNodes: (ids) => ids ? ids.flatMap((id) => {
|
|
2661
|
+
let userNode = flow.internalNodes[id]?.internals.userNode;
|
|
2662
|
+
return userNode ? [userNode] : [];
|
|
2663
|
+
}) : [...flow.nodes],
|
|
2664
|
+
getEdge: (id) => edgeLookup[id],
|
|
2665
|
+
getEdges: (ids) => ids ? ids.flatMap((id) => edgeLookup[id] ? [edgeLookup[id]] : []) : [...flow.edges],
|
|
2216
2666
|
getViewport: () => snapshot(store.viewport),
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
return store.panZoom ? (await store.panZoom.setViewport({
|
|
2220
|
-
x: nextViewport.x ?? currentViewport.x,
|
|
2221
|
-
y: nextViewport.y ?? currentViewport.y,
|
|
2222
|
-
zoom: nextViewport.zoom ?? currentViewport.zoom
|
|
2223
|
-
}, options), Promise.resolve(!0)) : Promise.resolve(!1);
|
|
2224
|
-
},
|
|
2225
|
-
getZoom: () => store.viewport.zoom,
|
|
2226
|
-
setZoom: (zoomLevel, options) => {
|
|
2227
|
-
let panZoom = store.panZoom;
|
|
2228
|
-
return panZoom ? panZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(!1);
|
|
2229
|
-
},
|
|
2230
|
-
fitBounds: async (bounds, options) => {
|
|
2231
|
-
if (!store.panZoom) return Promise.resolve(!1);
|
|
2232
|
-
let viewport = getViewportForBounds$1(bounds, store.width, store.height, store.minZoom, store.maxZoom, options?.padding ?? .1);
|
|
2233
|
-
return await store.panZoom.setViewport(viewport, {
|
|
2234
|
-
duration: options?.duration,
|
|
2235
|
-
ease: options?.ease,
|
|
2236
|
-
interpolate: options?.interpolate
|
|
2237
|
-
}), Promise.resolve(!0);
|
|
2238
|
-
},
|
|
2239
|
-
getIntersectingNodes: (nodeOrRect, partially = !0, nodesToIntersect) => {
|
|
2240
|
-
let isRect = isRectObject(nodeOrRect), nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
|
|
2241
|
-
return nodeRect ? (nodesToIntersect || store.nodes).filter((n) => {
|
|
2242
|
-
let internalNode = nodeLookup.get(n.id);
|
|
2243
|
-
if (!internalNode || !isRect && n.id === nodeOrRect.id) return !1;
|
|
2244
|
-
let currNodeRect = nodeToRect(internalNode), overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
|
|
2245
|
-
return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
2246
|
-
}) : [];
|
|
2247
|
-
},
|
|
2248
|
-
isNodeIntersecting: (nodeOrRect, area, partially = !0) => {
|
|
2249
|
-
let nodeRect = isRectObject(nodeOrRect) ? nodeOrRect : getNodeRect(nodeOrRect);
|
|
2250
|
-
if (!nodeRect) return !1;
|
|
2251
|
-
let overlappingArea = getOverlappingArea(nodeRect, area);
|
|
2252
|
-
return partially && overlappingArea > 0 || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
2253
|
-
},
|
|
2254
|
-
deleteElements: async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => {
|
|
2255
|
-
let { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
|
|
2256
|
-
nodesToRemove,
|
|
2257
|
-
edgesToRemove,
|
|
2258
|
-
nodes: store.nodes,
|
|
2259
|
-
edges: store.edges,
|
|
2260
|
-
onBeforeDelete: store.onBeforeDelete
|
|
2261
|
-
});
|
|
2262
|
-
if (matchingEdges) {
|
|
2263
|
-
let remmainingEdges = store.edges.filter((edge) => !matchingEdges.some(({ id }) => id === edge.id));
|
|
2264
|
-
store.onEdgesDelete?.(matchingEdges), actions.setEdges(() => remmainingEdges);
|
|
2265
|
-
}
|
|
2266
|
-
if (matchingNodes) {
|
|
2267
|
-
let remmainingNodes = store.nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id));
|
|
2268
|
-
store.onNodesDelete?.(matchingNodes), actions.setNodes(() => remmainingNodes);
|
|
2269
|
-
}
|
|
2270
|
-
return {
|
|
2271
|
-
deletedNodes: matchingNodes,
|
|
2272
|
-
deletedEdges: matchingEdges
|
|
2273
|
-
};
|
|
2274
|
-
},
|
|
2275
|
-
screenToFlowPosition: (position, options = { snapToGrid: !0 }) => {
|
|
2276
|
-
if (!store.domNode) return position;
|
|
2277
|
-
let _snapGrid = options.snapToGrid ? store.snapGrid : !1, { x, y, zoom } = store.viewport, { x: domX, y: domY } = store.domNode.getBoundingClientRect(), correctedPosition = {
|
|
2278
|
-
x: position.x - domX,
|
|
2279
|
-
y: position.y - domY
|
|
2280
|
-
};
|
|
2281
|
-
return pointToRendererPoint(correctedPosition, [
|
|
2282
|
-
x,
|
|
2283
|
-
y,
|
|
2284
|
-
zoom
|
|
2285
|
-
], _snapGrid !== null, _snapGrid || [1, 1]);
|
|
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() ?? [])
|
|
2667
|
+
getZoom: () => flow.viewport.zoom,
|
|
2668
|
+
getHandleConnections: ({ type, id, nodeId }) => Object.values(flow.connections[connectionKey(nodeId, type, id ?? null)] ?? {})
|
|
2328
2669
|
};
|
|
2329
2670
|
}
|
|
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
2671
|
//#endregion
|
|
2342
2672
|
//#region src/hooks/useUpdateNodeInternals.ts
|
|
2343
2673
|
/**
|
|
2344
|
-
* Hook for updating node internals.
|
|
2674
|
+
* Hook for updating node internals. Sugar for `commands.updateNodeInternals`.
|
|
2345
2675
|
*
|
|
2346
2676
|
* @public
|
|
2347
2677
|
* @returns function for updating node internals
|
|
2348
2678
|
*/
|
|
2349
2679
|
function useUpdateNodeInternals() {
|
|
2350
|
-
let {
|
|
2351
|
-
return
|
|
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
|
-
}]);
|
|
2360
|
-
}
|
|
2361
|
-
actions.requestUpdateNodeInternals(updates);
|
|
2362
|
-
};
|
|
2680
|
+
let { commands } = useInternalSolidFlow();
|
|
2681
|
+
return commands.updateNodeInternals;
|
|
2363
2682
|
}
|
|
2364
2683
|
//#endregion
|
|
2365
2684
|
//#region src/components/graph/plugins/NodeToolbar.tsx
|
|
@@ -2646,4 +2965,4 @@ const KeyHandler = (props) => {
|
|
|
2646
2965
|
return [store, setStore];
|
|
2647
2966
|
};
|
|
2648
2967
|
//#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 };
|
|
2968
|
+
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 };
|