UNPKG

@circonomit/solid-flow

Version:

Solid Flow - A highly customizable Solid library for building node-based editors and interactive flow charts.

1,481 lines (1,468 loc) 213 kB
import { errorMessages, isEdgeBase, isNodeBase, withResolvers, getNodesBounds, isRectObject, getOverlappingArea, nodeToRect, getElementsToRemove, ResizeControlVariant, XYResizer, evaluateAbsolutePosition, handleExpandParent, XY_RESIZER_LINE_POSITIONS, XY_RESIZER_HANDLE_POSITIONS, Position, getNodeToolbarTransform, isNumeric, getBezierPath, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, PanOnScrollMode, infiniteExtent, SelectionMode, ConnectionLineType, isMacOs, handleConnectionChange, isMouseEvent, XYHandle, getHostForElement, XYMinimap, isInputDOMNode, rendererPointToPoint, pointToRendererPoint, getViewportForBounds, getBoundsOfRects, getInternalNodesBounds, ConnectionMode, nodeHasDimensions, addEdge, getNodeDimensions, updateConnectionLookup, adoptUserNodes, initialConnection, fitViewport, panBy, updateNodeInternals, updateAbsolutePositions, getConnectionStatus, isEdgeVisible, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, getEventPosition, getNodesInside, areSetsEqual, createMarkerIds, elementSelectionKeys, XYDrag, getDimensions, snapPosition, calculateNodePosition, MarkerType, devWarn, XYPanZoom } from '@xyflow/system'; export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, reconnectEdge } from '@xyflow/system'; import { delegateEvents, template, use, insert, createComponent, effect, setAttribute, style as style$1, spread, mergeProps as mergeProps$1, memo, addEventListener, className, Portal, Dynamic } from 'solid-js/web'; import cc from 'classcat'; import { createContext, useContext, batch, mergeProps, Show, splitProps, createMemo, createEffect, onCleanup, For, createSignal, onMount, untrack, Switch, Match } from 'solid-js'; import { ReactiveMap } from '@solid-primitives/map'; import { createStore } from 'solid-js/store'; // src/index.ts var StoreContext = createContext(null); var Provider = StoreContext.Provider; var StoreContext_default = StoreContext; // src/hooks/useStore.ts var zustandErrorMessage = errorMessages["error001"](); function useStore(selector26) { const store = useContext(StoreContext_default); if (store === null) { throw new Error(zustandErrorMessage); } return selector26(store); } function useStoreApi() { const store = useContext(StoreContext_default); if (store === null) { throw new Error(zustandErrorMessage); } return { ...store, batch: (fn) => { batch(() => { fn(store); }); } }; } // src/styles/utils.ts var containerStyle = { position: "absolute", width: "100%", height: "100%", top: 0, left: 0 }; // src/utils/hooks.ts var useRef = (initialValue) => { return new RefObject(initialValue); }; var RefObject = class { constructor(_current) { this._current = _current; } get current() { return this._current; } set current(value) { this._current = value; } }; var _tmpl$ = /* @__PURE__ */ template(`<svg><path></svg>`, false, true, false); var _tmpl$2 = /* @__PURE__ */ template(`<svg><circle></svg>`, false, true, false); function LinePattern(p) { return (() => { var _el$ = _tmpl$(); effect((_p$) => { var _v$ = p.lineWidth, _v$2 = `M${p.dimensions[0] / 2} 0 V${p.dimensions[1]} M0 ${p.dimensions[1] / 2} H${p.dimensions[0]}`, _v$3 = cc(["solid-flow__background-pattern", p.variant, p.className]); _v$ !== _p$.e && setAttribute(_el$, "stroke-width", _p$.e = _v$); _v$2 !== _p$.t && setAttribute(_el$, "d", _p$.t = _v$2); _v$3 !== _p$.a && setAttribute(_el$, "class", _p$.a = _v$3); return _p$; }, { e: void 0, t: void 0, a: void 0 }); return _el$; })(); } function DotPattern(p) { return (() => { var _el$2 = _tmpl$2(); effect((_p$) => { var _v$4 = p.radius, _v$5 = p.radius, _v$6 = p.radius, _v$7 = cc(["solid-flow__background-pattern", "dots", p.className]); _v$4 !== _p$.e && setAttribute(_el$2, "cx", _p$.e = _v$4); _v$5 !== _p$.t && setAttribute(_el$2, "cy", _p$.t = _v$5); _v$6 !== _p$.a && setAttribute(_el$2, "r", _p$.a = _v$6); _v$7 !== _p$.o && setAttribute(_el$2, "class", _p$.o = _v$7); return _p$; }, { e: void 0, t: void 0, a: void 0, o: void 0 }); return _el$2; })(); } // src/additional-components/Background/types.ts var BackgroundVariant = /* @__PURE__ */ ((BackgroundVariant2) => { BackgroundVariant2["Lines"] = "lines"; BackgroundVariant2["Dots"] = "dots"; BackgroundVariant2["Cross"] = "cross"; return BackgroundVariant2; })(BackgroundVariant || {}); // src/additional-components/Background/Background.tsx var _tmpl$3 = /* @__PURE__ */ template(`<svg data-testid=rf__background><pattern patternUnits=userSpaceOnUse></pattern><rect x=0 y=0 width=100% height=100%>`); var defaultSize = { ["dots" /* Dots */]: 1, ["lines" /* Lines */]: 1, ["cross" /* Cross */]: 6 }; var selector = (s) => ({ transform: s.transform, patternId: `pattern-${s.rfId.get()}` }); function BackgroundComponent(_p) { const p = mergeProps({ variant: "dots" /* Dots */, gap: 20, lineWidth: 1, offset: 0 }, _p); const ref = useRef(null); const storeData = useStore(selector); const patternSize = () => p.size || defaultSize[p.variant]; const isDots = () => p.variant === "dots" /* Dots */; const isCross = () => p.variant === "cross" /* Cross */; const gapXY = () => { if (Array.isArray(p.gap)) { return p.gap; } else { return [p.gap, p.gap]; } }; const scaledGap = () => [gapXY()[0] * storeData.transform.get()[2] || 1, gapXY()[1] * storeData.transform.get()[2] || 1]; const scaledSize = () => patternSize() * storeData.transform.get()[2]; const offsetXY = () => { if (Array.isArray(p.offset)) { return p.offset; } else { return [p.offset, p.offset]; } }; const patternDimensions = () => isCross() ? [scaledSize(), scaledSize()] : scaledGap(); const scaledOffset = () => [offsetXY()[0] * storeData.transform.get()[2] || 1 + patternDimensions()[0] / 2, offsetXY()[1] * storeData.transform.get()[2] || 1 + patternDimensions()[1] / 2]; const _patternId = () => `${storeData.patternId}${p.id ? p.id : ""}`; const style2 = () => { return { ...p.style, ...containerStyle, "--xy-background-color-props": p.bgColor, "--xy-background-pattern-color-props": p.color }; }; return (() => { var _el$ = _tmpl$3(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling; use((el) => ref.current = el, _el$); insert(_el$2, createComponent(Show, { get when() { return isDots(); }, get fallback() { return createComponent(LinePattern, { get dimensions() { return patternDimensions(); }, get lineWidth() { return p.lineWidth; }, get variant() { return p.variant; }, get className() { return p.patternClassName; } }); }, get children() { return createComponent(DotPattern, { get radius() { return scaledSize() / 2; }, get className() { return p.patternClassName; } }); } })); effect((_p$) => { var _v$ = cc(["solid-flow__background", p.className]), _v$2 = style2(), _v$3 = _patternId(), _v$4 = storeData.transform.get()[0] % scaledGap()[0], _v$5 = storeData.transform.get()[1] % scaledGap()[1], _v$6 = scaledGap()[0], _v$7 = scaledGap()[1], _v$8 = `translate(-${scaledOffset()[0]},-${scaledOffset()[1]})`, _v$9 = `url(#${_patternId()})`; _v$ !== _p$.e && setAttribute(_el$, "class", _p$.e = _v$); _p$.t = style$1(_el$, _v$2, _p$.t); _v$3 !== _p$.a && setAttribute(_el$2, "id", _p$.a = _v$3); _v$4 !== _p$.o && setAttribute(_el$2, "x", _p$.o = _v$4); _v$5 !== _p$.i && setAttribute(_el$2, "y", _p$.i = _v$5); _v$6 !== _p$.n && setAttribute(_el$2, "width", _p$.n = _v$6); _v$7 !== _p$.s && setAttribute(_el$2, "height", _p$.s = _v$7); _v$8 !== _p$.h && setAttribute(_el$2, "patternTransform", _p$.h = _v$8); _v$9 !== _p$.r && setAttribute(_el$3, "fill", _p$.r = _v$9); return _p$; }, { e: void 0, t: void 0, a: void 0, o: void 0, i: void 0, n: void 0, s: void 0, h: void 0, r: void 0 }); return _el$; })(); } BackgroundComponent.displayName = "Background"; var Background = BackgroundComponent; var _tmpl$4 = /* @__PURE__ */ template(`<button type=button>`); function ControlButton(p) { const [_, rest] = splitProps(p, ["children", "class"]); return (() => { var _el$ = _tmpl$4(); spread(_el$, mergeProps$1({ get ["class"]() { return cc(["solid-flow__controls-button", p.class]); } }, rest), false, true); insert(_el$, () => p.children); return _el$; })(); } var _tmpl$5 = /* @__PURE__ */ template(`<div>`); var selector2 = (s) => () => s.userSelectionActive.get() ? "none" : "all"; function Panel(_p) { const p = mergeProps({ position: "top-left" }, _p); const pointerEvents = useStore(selector2); const positionClasses = () => `${p.position}`.split("-"); const style2 = () => { if (typeof p.style === "object") { return { ...p.style, "pointer-events": pointerEvents() }; } else { return { "pointer-events": pointerEvents() }; } }; return (() => { var _el$ = _tmpl$5(); spread(_el$, mergeProps$1(p, { get ["class"]() { return cc(["solid-flow__panel", p.class, ...positionClasses()]); }, get style() { return style2(); } }), false, true); insert(_el$, () => p.children); return _el$; })(); } // src/utils/changes.ts function applyChanges(changes, elements) { const updatedElements = []; const changesMap = /* @__PURE__ */ new Map(); const addItemChanges = []; for (const change of changes) { if (change.type === "add") { addItemChanges.push(change); } else if (change.type === "remove" || change.type === "replace") { changesMap.set(change.id, [change]); } else { const elementChanges = changesMap.get(change.id); if (elementChanges) { elementChanges.push(change); } else { changesMap.set(change.id, [change]); } } } for (const element of elements) { const changes2 = changesMap.get(element.id); if (!changes2) { updatedElements.push(element); continue; } if (changes2[0].type === "remove") { continue; } if (changes2[0].type === "replace") { updatedElements.push({ ...changes2[0].item }); continue; } const updatedElement = { ...element }; for (const change of changes2) { applyChange(change, updatedElement); } updatedElements.push(updatedElement); } if (addItemChanges.length) { addItemChanges.forEach((change) => { if (change.index !== void 0) { updatedElements.splice(change.index, 0, { ...change.item }); } else { updatedElements.push({ ...change.item }); } }); } return updatedElements; } function applyChange(change, element) { switch (change.type) { case "select": { element.selected = change.selected; break; } case "position": { if (typeof change.position !== "undefined") { element.position = change.position; } if (typeof change.dragging !== "undefined") { element.dragging = change.dragging; } break; } case "dimensions": { if (typeof change.dimensions !== "undefined") { element.measured ??= {}; element.measured.width = change.dimensions.width; element.measured.height = change.dimensions.height; if (change.setAttributes) { if (change.setAttributes === true || change.setAttributes === "width") { element.width = change.dimensions.width; } if (change.setAttributes === true || change.setAttributes === "height") { element.height = change.dimensions.height; } } } if (typeof change.resizing === "boolean") { element.resizing = change.resizing; } break; } } } function applyNodeChanges(changes, nodes) { return applyChanges(changes, nodes); } function applyEdgeChanges(changes, edges) { return applyChanges(changes, edges); } function createSelectionChange(id, selected2) { return { id, type: "select", selected: selected2 }; } function getSelectionChanges(items, selectedIds = /* @__PURE__ */ new Set(), mutateItem = false) { const changes = []; for (const [id, item] of items) { const willBeSelected = selectedIds.has(id); if (!(item.selected === void 0 && !willBeSelected) && item.selected !== willBeSelected) { if (mutateItem) { item.selected = willBeSelected; } changes.push(createSelectionChange(item.id, willBeSelected)); } } return changes; } function getElementsDiffChanges({ items = [], lookup }) { const changes = []; const itemsLookup = new Map( items.map((item) => [item.id, item]) ); for (const [index, item] of items.entries()) { const lookupItem = lookup.get(item.id); const storeItem = lookupItem?.internals?.userNode ?? lookupItem; if (storeItem !== void 0 && storeItem !== item) { changes.push({ id: item.id, item, type: "replace" }); } if (storeItem === void 0) { changes.push({ item, type: "add", index }); } } for (const [id] of lookup) { const nextNode = itemsLookup.get(id); if (nextNode === void 0) { changes.push({ id, type: "remove" }); } } return changes; } function elementToRemoveChange(item) { return { id: item.id, type: "remove" }; } var isNode = (element) => isNodeBase(element); var isEdge = (element) => isEdgeBase(element); var useIsomorphicLayoutEffect = typeof window !== "undefined" ? createEffect : createEffect; var getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView, fitViewOptions, minZoom = 0.5, maxZoom = 2, nodeOrigin, nodeExtent } = {}) => { const nodeLookup = /* @__PURE__ */ new Map(); const parentLookup = /* @__PURE__ */ new Map(); const connectionLookup = /* @__PURE__ */ new Map(); const edgeLookup = /* @__PURE__ */ new Map(); const storeEdges = defaultEdges ?? edges ?? []; const storeNodes = defaultNodes ?? nodes ?? []; const storeNodeOrigin = nodeOrigin ?? [0, 0]; const storeNodeExtent = nodeExtent ?? infiniteExtent; updateConnectionLookup(connectionLookup, edgeLookup, storeEdges); const nodesInitialized = adoptUserNodes( storeNodes, nodeLookup, parentLookup, { nodeOrigin: storeNodeOrigin, nodeExtent: storeNodeExtent, elevateNodesOnSelect: false } ); let transform = [0, 0, 1]; if (fitView && width && height) { const bounds = getInternalNodesBounds(nodeLookup, { filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) }); const { x, y, zoom } = getViewportForBounds( bounds, width, height, minZoom, maxZoom, fitViewOptions?.padding ?? 0.1 ); transform = [x, y, zoom]; } return { rfId: new Writable("1"), width: new Writable(width ?? 0), height: new Writable(height ?? 0), transform: new Writable(transform), nodes: new Writable(storeNodes), nodesInitialized: new Writable(nodesInitialized), nodeLookup: new ReactiveMap(), parentLookup: new ReactiveMap(), edges: new Writable(storeEdges), edgeLookup: new ReactiveMap(), connectionLookup: new ReactiveMap(), onNodesChange: wNullable(), onEdgesChange: wNullable(), hasDefaultNodes: new Writable(defaultNodes !== void 0), hasDefaultEdges: new Writable(defaultEdges !== void 0), panZoom: new Writable(null), minZoom: new Writable(minZoom), maxZoom: new Writable(maxZoom), translateExtent: new Writable(infiniteExtent), nodeExtent: new Writable(storeNodeExtent), nodesSelectionActive: new Writable(false), userSelectionActive: new Writable(false), userSelectionRect: new Writable(null), connection: new ReactiveStore( { ...initialConnection }, "connection" ), connectionMode: new Writable(ConnectionMode.Strict), domNode: new Writable(null), paneDragging: new Writable(false, "paneDragging"), noPanClassName: new Writable("nopan"), nodeOrigin: new Writable(storeNodeOrigin), nodeDragThreshold: new Writable(1), snapGrid: new Writable([15, 15]), snapToGrid: new Writable(false), nodesDraggable: new Writable(true), nodesConnectable: new Writable(true), nodesFocusable: new Writable(true), edgesFocusable: new Writable(true), edgesReconnectable: new Writable(true), elementsSelectable: new Writable(true), elevateNodesOnSelect: new Writable(true), elevateEdgesOnSelect: new Writable(false), selectNodesOnDrag: new Writable(true), multiSelectionActive: new Writable(false), connectionClickStartHandle: wNullable(), connectOnClick: w(true), ariaLiveMessage: w(""), autoPanOnConnect: w(true), autoPanOnNodeDrag: w(true), autoPanSpeed: w(15), connectionRadius: w(20), onError: w(devWarn), isValidConnection: void 0, onSelectionChangeHandlers: w([]), onConnect: wEmpty(), onDelete: wEmpty(), onEdgesDelete: wEmpty(), onNodesDelete: wEmpty(), onBeforeDelete: wEmpty(), onViewportChange: wEmpty(), onViewportChangeEnd: wEmpty(), onViewportChangeStart: wEmpty(), onMoveStart: wEmpty(), onMove: wEmpty(), onMoveEnd: wEmpty(), lib: new Writable("solid"), debug: new Writable(false), // New properties for React compatibility fitViewQueued: new Writable(fitView ?? false), fitViewOptions: new Writable(fitViewOptions), fitViewResolver: wNullable() }; }; var initialState_default = getInitialState; var w = (x) => new Writable(x); var wEmpty = () => new Writable(void 0); var wNullable = () => new Writable(null); var Writable = class { constructor(initial, debugName) { this.debugName = debugName; this.s = createSignal(initial, { name: debugName }); } s; get = () => { const value = this.s[0](); return value; }; set = (v) => { this.s[1](() => { return v; }); }; setFromPrev = (fn) => { this.s[1]((prev) => { const newValue = fn(prev); return newValue; }); }; }; var ReactiveStore = class { constructor(initial, debugName) { this.debugName = debugName; this.store = createStore(initial); } store; get = () => { const value = this.store[0]; return value; }; setter = () => { return this.store[1]; }; }; // src/components/BatchProvider/useQueue.ts function useQueue(runQueue) { const [shouldFlush, setShouldFlush] = createSignal(false); const queue = createQueue(() => setShouldFlush(true)); useIsomorphicLayoutEffect(() => { if (!shouldFlush()) { queue.reset(); return; } const queueItems = queue.get(); if (queueItems.length) { runQueue(queueItems); queue.reset(); } setShouldFlush(false); }); return queue; } function createQueue(cb) { const queue = new Writable([]); return { get: () => queue.get(), reset: () => { queue.set([]); }, push: (item) => { queue.setFromPrev((prev) => [...prev, item]); cb(); } }; } // src/components/BatchProvider/index.tsx var BatchContext = createContext(null); function BatchProvider(p) { const store = useStoreApi(); const nodeQueueHandler = (queueItems) => { const { nodes, setNodes, hasDefaultNodes, onNodesChange: getOnNodesChange, nodeLookup // edgeLookup, } = store; let next = nodes.get(); for (const payload of queueItems) { next = typeof payload === "function" ? payload(next) : payload; } const onNodesChange = getOnNodesChange.get(); if (hasDefaultNodes.get()) { setNodes(next); } else if (onNodesChange) { onNodesChange(getElementsDiffChanges({ items: next, lookup: nodeLookup })); } }; const nodeQueue = useQueue(nodeQueueHandler); const edgeQueueHandler = (queueItems) => { const { edges, setEdges, hasDefaultEdges, onEdgesChange: getOnEdgesChange, edgeLookup } = store; let next = edges.get(); for (const payload of queueItems) { next = typeof payload === "function" ? payload(next) : payload; } const onEdgesChange = getOnEdgesChange.get(); if (hasDefaultEdges.get()) { setEdges(next); } else if (onEdgesChange) { onEdgesChange(getElementsDiffChanges({ items: next, lookup: edgeLookup })); } }; const edgeQueue = useQueue(edgeQueueHandler); const value = { nodeQueue, edgeQueue }; return createComponent(BatchContext.Provider, { value, get children() { return p.children; } }); } function useBatchContext() { const batchContext = useContext(BatchContext); if (!batchContext) { throw new Error("useBatchContext must be used within a BatchProvider"); } return batchContext; } var useViewportHelper = () => { const store = useStoreApi(); return createMemo(() => { const getState = () => ({ panZoom: store.panZoom.get(), transform: store.transform.get(), width: store.width.get(), height: store.height.get(), minZoom: store.minZoom.get(), maxZoom: store.maxZoom.get(), nodeOrigin: store.nodeOrigin.get(), nodeLookup: store.nodeLookup, snapGrid: store.snapGrid.get(), snapToGrid: store.snapToGrid.get(), domNode: store.domNode.get() }); return { zoomIn: (options) => { const { panZoom } = getState(); return panZoom ? panZoom.scaleBy(1.2, { duration: options?.duration }) : Promise.resolve(false); }, zoomOut: (options) => { const { panZoom } = getState(); return panZoom ? panZoom.scaleBy(1 / 1.2, { duration: options?.duration }) : Promise.resolve(false); }, zoomTo: (zoomLevel, options) => { const { panZoom } = getState(); return panZoom ? panZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(false); }, getZoom: () => getState().transform[2], setViewport: async (viewport, options) => { const { transform: [tX, tY, tZoom], panZoom } = getState(); if (!panZoom) { return Promise.resolve(false); } await panZoom.setViewport( { x: viewport.x ?? tX, y: viewport.y ?? tY, zoom: viewport.zoom ?? tZoom }, options ); return Promise.resolve(true); }, getViewport: () => { const [x, y, zoom] = getState().transform; return { x, y, zoom }; }, setCenter: async (x, y, options) => { const { width, height, maxZoom, panZoom } = getState(); const nextZoom = typeof options?.zoom !== "undefined" ? options.zoom : maxZoom; const centerX = width / 2 - x * nextZoom; const centerY = height / 2 - y * nextZoom; if (!panZoom) { return Promise.resolve(false); } await panZoom.setViewport( { x: centerX, y: centerY, zoom: nextZoom }, { duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate } ); return Promise.resolve(true); }, fitBounds: async (bounds, options) => { const { width, height, minZoom, maxZoom, panZoom } = getState(); const viewport = getViewportForBounds( bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1 ); if (!panZoom) { return Promise.resolve(false); } await panZoom.setViewport(viewport, { duration: options?.duration, ease: options?.ease, interpolate: options?.interpolate }); return Promise.resolve(true); }, screenToFlowPosition: (clientPosition, options = {}) => { const { transform, snapGrid, snapToGrid, domNode } = getState(); if (!domNode) { return clientPosition; } const { x: domX, y: domY } = domNode.getBoundingClientRect(); const correctedPosition = { x: clientPosition.x - domX, y: clientPosition.y - domY }; const _snapGrid = options.snapGrid ?? snapGrid; const _snapToGrid = options.snapToGrid ?? snapToGrid; return pointToRendererPoint( correctedPosition, transform, _snapToGrid, _snapGrid ); }, flowToScreenPosition: (flowPosition) => { const { transform, domNode } = getState(); if (!domNode) { return flowPosition; } const { x: domX, y: domY } = domNode.getBoundingClientRect(); const rendererPosition = rendererPointToPoint(flowPosition, transform); return { x: rendererPosition.x + domX, y: rendererPosition.y + domY }; } }; })(); }; var useViewportHelper_default = useViewportHelper; // src/hooks/useSolidFlow.ts var selector3 = (s) => () => !!s.panZoom.get(); function useSolidFlow() { const viewportHelper = useViewportHelper_default(); const store = useStoreApi(); const batchContext = useBatchContext(); const viewportInitialized = useStore(selector3); const generalHelper = createMemo(() => { const getInternalNode = (id) => store.nodeLookup.get(id); const setNodes = (payload) => { batchContext.nodeQueue.push(payload); }; const setEdges = (payload) => { batchContext.edgeQueue.push(payload); }; const getNodeRect = (node) => { const { nodeLookup, nodeOrigin } = store; const nodeToUse = isNode(node) ? node : nodeLookup.get(node.id); const position = nodeToUse.parentId ? evaluateAbsolutePosition( nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, nodeLookup, nodeOrigin.get() ) : nodeToUse.position; const nodeWithPosition = { ...nodeToUse, position, width: nodeToUse.measured?.width ?? nodeToUse.width, height: nodeToUse.measured?.height ?? nodeToUse.height }; return nodeToRect(nodeWithPosition); }; const updateNode = (id, nodeUpdate, options = { replace: false }) => { setNodes( (prevNodes) => prevNodes.map((node) => { if (node.id === id) { const nextNode = typeof nodeUpdate === "function" ? nodeUpdate(node) : nodeUpdate; return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode }; } return node; }) ); }; const updateEdge = (id, edgeUpdate, options = { replace: false }) => { setEdges( (prevEdges) => prevEdges.map((edge) => { if (edge.id === id) { const nextEdge = typeof edgeUpdate === "function" ? edgeUpdate(edge) : edgeUpdate; return options.replace && isEdge(nextEdge) ? nextEdge : { ...edge, ...nextEdge }; } return edge; }) ); }; return { getNodes: () => store.nodes.get().map((n) => ({ ...n })), getNode: (id) => getInternalNode(id)?.internals.userNode, getInternalNode, getEdges: () => { const { edges } = store; return edges.get().map((e) => ({ ...e })); }, getEdge: (id) => store.edgeLookup.get(id), setNodes, setEdges, addNodes: (payload) => { const newNodes = Array.isArray(payload) ? payload : [payload]; batchContext.nodeQueue.push((nodes) => [...nodes, ...newNodes]); }, addEdges: (payload) => { const newEdges = Array.isArray(payload) ? payload : [payload]; batchContext.edgeQueue.push((edges) => [...edges, ...newEdges]); }, toObject: () => { const { nodes, edges, transform } = store; const [x, y, zoom] = transform.get(); return { nodes: nodes.get().map((n) => ({ ...n })), edges: edges.get().map((e) => ({ ...e })), viewport: { x, y, zoom } }; }, deleteElements: async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => { const { nodes, edges, onNodesDelete, onEdgesDelete, triggerNodeChanges, triggerEdgeChanges, onDelete, onBeforeDelete } = store; const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({ nodesToRemove, edgesToRemove, nodes: nodes.get(), edges: edges.get(), onBeforeDelete: onBeforeDelete.get() }); const hasMatchingEdges = matchingEdges.length > 0; const hasMatchingNodes = matchingNodes.length > 0; if (hasMatchingEdges) { const edgeChanges = matchingEdges.map( elementToRemoveChange ); onEdgesDelete.get()?.(matchingEdges); triggerEdgeChanges(edgeChanges); } if (hasMatchingNodes) { const nodeChanges = matchingNodes.map( elementToRemoveChange ); onNodesDelete.get()?.(matchingNodes); triggerNodeChanges(nodeChanges); } if (hasMatchingNodes || hasMatchingEdges) { onDelete.get()?.({ nodes: matchingNodes, edges: matchingEdges }); } return { deletedNodes: matchingNodes, deletedEdges: matchingEdges }; }, getIntersectingNodes: (nodeOrRect, partially = true, nodes) => { const isRect = isRectObject(nodeOrRect); const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect); const hasNodesOption = nodes !== void 0; if (!nodeRect) { return []; } return (nodes || store.nodes.get()).filter((n) => { const internalNode = store.nodeLookup.get(n.id); if (internalNode && !isRect && (n.id === nodeOrRect.id || !internalNode.internals.positionAbsolute)) { return false; } const currNodeRect = nodeToRect(hasNodesOption ? n : internalNode); const overlappingArea = getOverlappingArea(currNodeRect, nodeRect); const partiallyVisible = partially && overlappingArea > 0; return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height; }); }, isNodeIntersecting: (nodeOrRect, area, partially = true) => { const isRect = isRectObject(nodeOrRect); const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect); if (!nodeRect) { return false; } const overlappingArea = getOverlappingArea(nodeRect, area); const partiallyVisible = partially && overlappingArea > 0; return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height; }, updateNode, updateNodeData: (id, dataUpdate, options = { replace: false }) => { updateNode( id, (node) => { const nextData = typeof dataUpdate === "function" ? dataUpdate(node) : dataUpdate; return options.replace ? { ...node, data: nextData } : { ...node, data: { ...node.data, ...nextData } }; }, options ); }, updateEdge, updateEdgeData: (id, dataUpdate, options = { replace: false }) => { updateEdge( id, (edge) => { const nextData = typeof dataUpdate === "function" ? dataUpdate(edge) : dataUpdate; return options.replace ? { ...edge, data: nextData } : { ...edge, data: { ...edge.data, ...nextData } }; }, options ); }, getNodesBounds: (nodes) => { const { nodeLookup, nodeOrigin } = store; return getNodesBounds(nodes, { nodeLookup, nodeOrigin: nodeOrigin.get() }); }, getHandleConnections: ({ type, id, nodeId }) => Array.from( store.connectionLookup.get(`${nodeId}-${type}${id ? `-${id}` : ""}`)?.values() ?? [] ), getNodeConnections: ({ type, handleId, nodeId }) => Array.from( store.connectionLookup.get( `${nodeId}${type ? handleId ? `-${type}-${handleId}` : `-${type}` : ""}` )?.values() ?? [] ), fitView: async (options) => { const fitViewResolver = store.fitViewResolver.get() ?? withResolvers(); store.fitViewQueued.set(true); store.fitViewOptions.set(options); store.fitViewResolver.set(fitViewResolver); batchContext.nodeQueue.push((nodes) => [...nodes]); return fitViewResolver.promise; } }; }); return createMemo(() => ({ ...generalHelper(), ...viewportHelper, viewportInitialized }))(); } var _tmpl$6 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 32 30"><path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z">`); function FitViewIcon() { return _tmpl$6(); } var _tmpl$7 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 25 32"><path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z">`); function LockIcon() { return _tmpl$7(); } var _tmpl$8 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 32 5"><path d="M0 0h32v4.2H0z">`); function MinusIcon() { return _tmpl$8(); } var _tmpl$9 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 32 32"><path d="M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z">`); function PlusIcon() { return _tmpl$9(); } var _tmpl$10 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 25 32"><path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z">`); function UnlockIcon() { return _tmpl$10(); } // src/additional-components/Controls/Controls.tsx var selector4 = (s) => ({ isInteractive: () => s.nodesDraggable.get() || s.nodesConnectable.get() || s.elementsSelectable.get(), minZoomReached: () => s.transform.get()[2] <= s.minZoom.get(), maxZoomReached: () => s.transform.get()[2] >= s.maxZoom.get() }); function ControlsComponent(_p) { const p = mergeProps({ showZoom: true, showFitView: true, showInteractive: true, position: "bottom-left", orientation: "vertical", "aria-label": "React Flow controls" }, _p); const store = useStoreApi(); const storeData = useStore(selector4); const { zoomIn, zoomOut, fitView } = useSolidFlow(); const onZoomInHandler = () => { zoomIn(); p.onZoomIn?.(); }; const onZoomOutHandler = () => { zoomOut(); p.onZoomOut?.(); }; const onFitViewHandler = () => { fitView(p.fitViewOptions); p.onFitView?.(); }; const onToggleInteractivity = () => { store.batch((store2) => { store2.nodesDraggable.set(!storeData.isInteractive()); store2.nodesConnectable.set(!storeData.isInteractive()); store2.elementsSelectable.set(!storeData.isInteractive()); }); p.onInteractiveChange?.(!storeData.isInteractive()); }; const orientationClass = () => p.orientation === "horizontal" ? "horizontal" : "vertical"; return createComponent(Panel, { get ["class"]() { return cc(["solid-flow__controls", orientationClass, p.className]); }, get position() { return p.position; }, get style() { return p.style; }, "data-testid": "rf__controls", get ["aria-label"]() { return p["aria-label"]; }, get children() { return [createComponent(Show, { get when() { return p.showZoom; }, get children() { return [createComponent(ControlButton, { onClick: onZoomInHandler, "class": "solid-flow__controls-zoomin", title: "zoom in", "aria-label": "zoom in", get disabled() { return storeData.maxZoomReached(); }, get children() { return createComponent(PlusIcon, {}); } }), createComponent(ControlButton, { onClick: onZoomOutHandler, "class": "solid-flow__controls-zoomout", title: "zoom out", "aria-label": "zoom out", get disabled() { return storeData.minZoomReached(); }, get children() { return createComponent(MinusIcon, {}); } })]; } }), createComponent(Show, { get when() { return p.showFitView; }, get children() { return createComponent(ControlButton, { "class": "solid-flow__controls-fitview", onClick: onFitViewHandler, title: "fit view", "aria-label": "fit view", get children() { return createComponent(FitViewIcon, {}); } }); } }), createComponent(Show, { get when() { return p.showInteractive; }, get children() { return createComponent(ControlButton, { "class": "solid-flow__controls-interactive", onClick: onToggleInteractivity, title: "toggle interactivity", "aria-label": "toggle interactivity", get children() { return memo(() => !!storeData.isInteractive())() ? createComponent(UnlockIcon, {}) : createComponent(LockIcon, {}); } }); } }), memo(() => p.children)]; } }); } ControlsComponent.displayName = "Controls"; var Controls = ControlsComponent; var _tmpl$11 = /* @__PURE__ */ template(`<svg><rect></svg>`, false, true, false); function MiniMapNodeComponent({ id, x, y, width, height, style: style2, color, strokeColor, strokeWidth, className, borderRadius, shapeRendering, selected: selected2, onClick }) { const { background, "background-color": backgroundColor } = style2 || {}; const fill = color || background || backgroundColor; return (() => { var _el$ = _tmpl$11(); addEventListener(_el$, "click", onClick ? (event) => onClick(event, id) : void 0, true); setAttribute(_el$, "x", x); setAttribute(_el$, "y", y); setAttribute(_el$, "rx", borderRadius); setAttribute(_el$, "ry", borderRadius); setAttribute(_el$, "width", width); setAttribute(_el$, "height", height); fill != null ? _el$.style.setProperty("fill", fill) : _el$.style.removeProperty("fill"); strokeColor != null ? _el$.style.setProperty("stroke", strokeColor) : _el$.style.removeProperty("stroke"); strokeWidth != null ? _el$.style.setProperty("stroke-width", strokeWidth) : _el$.style.removeProperty("stroke-width"); setAttribute(_el$, "shape-rendering", shapeRendering); effect(() => setAttribute(_el$, "class", cc(["solid-flow__minimap-node", { selected: selected2 }, className]))); return _el$; })(); } var MiniMapNode = MiniMapNodeComponent; delegateEvents(["click"]); // src/additional-components/MiniMap/MiniMapNodes.tsx var selectorNodeIds = (s) => () => s.nodes.get().map((node) => node.id); var getAttrFunction = (func) => func instanceof Function ? func : () => func; function MiniMapNodes(_p) { const p = mergeProps({ nodeClassName: "", nodeBorderRadius: 5, nodeComponent: MiniMapNode }, _p); const nodeIds = useStore(selectorNodeIds); const nodeColorFunc = getAttrFunction(p.nodeColor); const nodeStrokeColorFunc = getAttrFunction(p.nodeStrokeColor); const nodeClassNameFunc = getAttrFunction(p.nodeClassName); const shapeRendering = typeof window === "undefined" || !!window.chrome ? "crispEdges" : "geometricPrecision"; return createComponent(For, { get each() { return nodeIds(); }, children: (nodeId) => createComponent(NodeComponentWrapper, { id: nodeId, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, get nodeBorderRadius() { return p.nodeBorderRadius; }, get nodeStrokeWidth() { return p.nodeStrokeWidth; }, get NodeComponent() { return p.nodeComponent; }, get onClick() { return p.onClick; }, shapeRendering }) }); } function NodeComponentWrapperInner(p) { const storeData = useStore((s) => { const internalNode = s.nodeLookup.get(p.id); if (!internalNode) { return { node: () => null, x: () => 0, y: () => 0, width: () => 0, height: () => 0 }; } const userNode = internalNode.internals.userNode; const positionAbsolute = internalNode.internals.positionAbsolute; const dimensions = getNodeDimensions(userNode); return { node: () => userNode, x: () => positionAbsolute.x, y: () => positionAbsolute.y, width: () => dimensions.width, height: () => dimensions.height }; }); return createComponent(Show, { get when() { return memo(() => !!(storeData.node() && !storeData.node().hidden && nodeHasDimensions(storeData.node())))() ? storeData.node() : false; }, children: (validNode) => createComponent(Dynamic, { get component() { return p.NodeComponent; }, get x() { return storeData.x(); }, get y() { return storeData.y(); }, get width() { return storeData.width(); }, get height() { return storeData.height(); }, get style() { return validNode().style; }, get selected() { return !!validNode().selected; }, get className() { return p.nodeClassNameFunc(validNode()); }, get color() { return p.nodeColorFunc(validNode()); }, get borderRadius() { return p.nodeBorderRadius; }, get strokeColor() { return p.nodeStrokeColorFunc(validNode()); }, get strokeWidth() { return p.nodeStrokeWidth; }, get shapeRendering() { return p.shapeRendering; }, get onClick() { return p.onClick; }, get id() { return validNode().id; } }) }); } var NodeComponentWrapper = NodeComponentWrapperInner; var MiniMapNodes_default = MiniMapNodes; // src/additional-components/MiniMap/MiniMap.tsx var _tmpl$12 = /* @__PURE__ */ template(`<title>`); var _tmpl$22 = /* @__PURE__ */ template(`<svg class=solid-flow__minimap-svg role=img><path class=solid-flow__minimap-mask fill-rule=evenodd pointer-events=none>`); var defaultWidth = 200; var defaultHeight = 150; var filterHidden = (node) => !node.hidden; var selector5 = (s) => { const viewBB = () => ({ x: -s.transform.get()[0] / s.transform.get()[2], y: -s.transform.get()[1] / s.transform.get()[2], width: s.width.get() / s.transform.get()[2], height: s.height.get() / s.transform.get()[2] }); return { viewBB, boundingRect: () => s.nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup, { filter: filterHidden }), viewBB()) : viewBB(), rfId: () => s.rfId.get(), panZoom: () => s.panZoom.get(), translateExtent: () => s.translateExtent.get(), flowWidth: () => s.width.get(), flowHeight: () => s.height.get() }; }; var ARIA_LABEL_KEY = "solid-flow__minimap-desc"; function MiniMapComponent(_p) { const p = mergeProps({ nodeClassName: "", nodeBorderRadius: 5, position: "bottom-right", pannable: false, zoomable: false, ariaLabel: "React Flow mini map", zoomStep: 10, offsetScale: 5 }, _p); const store = useStoreApi(); let svg; const storeData = useStore(selector5); const elementWidth = () => p.style?.width ?? defaultWidth; const elementHeight = () => p.style?.height ?? defaultHeight; const scaledWidth = () => storeData.boundingRect().width / elementWidth(); const scaledHeight = () => storeData.boundingRect().height / elementHeight(); const viewScale = () => Math.max(scaledWidth(), scaledHeight()); const viewWidth = () => viewScale() * elementWidth(); const viewHeight = () => viewScale() * elementHeight(); const offset = () => p.offsetScale * viewScale(); const x = () => storeData.boundingRect().x - (viewWidth() - storeData.boundingRect().width) / 2 - offset(); const y = () => storeData.boundingRect().y - (viewHeight() - storeData.boundingRect().height) / 2 - offset(); const width = () => viewWidth() + offset() * 2; const height = () => viewHeight() + offset() * 2; const labelledBy = () => `${ARIA_LABEL_KEY}-${storeData.rfId()}`; let viewScaleRef = 0; let minimapInstance; createEffect(() => { viewScaleRef = viewScale(); }); createEffect(() => { const pZoom = storeData.panZoom(); if (svg && pZoom) { minimapInstance = XYMinimap({ domNode: svg, panZoom: pZoom, getTransform: () => store.transform.get(), getViewScale: () => viewScaleRef }); onCleanup(() => { minimapInstance?.destroy(); }); } }); createEffect(() => { minimapInstance?.update({ translateExtent: storeData.translateExtent(), width: storeData.flowWidth(), height: storeData.flowHeight(), inversePan: p.inversePan, pannable: p.pannable, zoomStep: p.zoomStep, zoomable: p.zoomable }); }); const onSvgClick = p.onClick ? (event) => { const [x2, y2] = minimapInstance?.pointer(event) || [0, 0]; p.onClick(event, { x: x2, y: y2 }); } : void 0; const onSvgNodeClick = p.onNodeClick ? (event, nodeId) => { const node = store.nodeLookup.get(nodeId).internals.userNode; p.onNodeClick(event, node); } : void 0; return createComponent(Panel, { get position() { return p.position; }, get style() { return { ...p.style, "--xy-minimap-background-color-props": typeof p.bgColor === "string" ? p.bgColor : void 0, "--xy-minimap-mask-background-color-props": typeof p.maskColor === "string" ? p.maskColor : void 0, "--xy-minimap-mask-stroke-color-props": typeof p.maskStrokeColor === "string" ? p.maskStrokeColor : void 0, "--xy-minimap-mask-stroke-width-props": typeof p.maskStrokeWidth === "number" ? p.maskStrokeWidth * viewScale() : void 0, "--xy-minimap-node-background-color-props": typeof p.nodeColor === "string" ? p.nodeColor : void 0, "--xy-minimap-node-stroke-color-props": typeof p.nodeStrokeColor === "string" ? p.nodeStrokeColor : void 0, "--xy-minimap-node-stroke-width-props": typeof p.nodeStrokeWidth === "number" ? p.nodeStrokeWidth : void 0 }; }, get ["class"]() { return cc(["solid-flow__minimap", p.class]); }, "data-testid": "rf__minimap", get children() { var _el$ = _tmpl$22(), _el$3 = _el$.firstChild; addEventListener(_el$, "click", onSvgClick, true); var _ref$ = svg; typeof _ref$ === "function" ? use(_ref$, _el$) : svg = _el$; insert(_el$, createComponent(Show, { get when() { return p.ariaLabel; }, get children() { var _el$2 = _tmpl$12(); insert(_el$2, () => p.ariaLabel); effect(() => setAttribute(_el$2, "id", labelledBy())); return _el$2; } }), _el$3); insert(_el$, createComponent(MiniMapNodes_default, { onClick: onSvgNodeClick, get nodeColor() { return p.nodeColor; }, get nodeStrokeColor() { return p.nodeStrokeColor; }, get nodeBorderRadius() { return p.nodeBorderRadius; }, get nodeClassName() { return p.nodeClassName; }, get nodeStrokeWidth() { return p.nodeStrokeWidth; }, get nodeComponent() { return p.nodeComponent; } }), _el$3); effect((_p$) => { var _v$ = elementWidth(), _v$2 = elementHeight(), _v$3 = `${x()} ${y()} ${width()} ${height()}`, _v$4 = labelled