@pauloricca/spagbol 0.1.0 → 0.1.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/README.md +10 -2
- package/dist/react/SpagbolCanvas.d.ts.map +1 -1
- package/dist/react/SpagbolCanvas.js +53 -15
- package/dist/react/SpagbolCanvas.js.map +1 -1
- package/dist/react/SpagbolNodeFrame.d.ts.map +1 -1
- package/dist/react/SpagbolNodeFrame.js +13 -11
- package/dist/react/SpagbolNodeFrame.js.map +1 -1
- package/dist/react/nodeDrag.d.ts +7 -0
- package/dist/react/nodeDrag.d.ts.map +1 -0
- package/dist/react/nodeDrag.js +27 -0
- package/dist/react/nodeDrag.js.map +1 -0
- package/package.json +1 -1
- package/src/react/SpagbolCanvas.tsx +47 -13
- package/src/react/SpagbolNodeFrame.tsx +12 -11
- package/src/react/nodeDrag.ts +33 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpagbolNodeFrame.d.ts","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,QAAQ,EAIT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgF,KAAK,aAAa,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACnJ,OAAO,EAOL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AACD,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,WAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,oBAAoB,+
|
|
1
|
+
{"version":3,"file":"SpagbolNodeFrame.d.ts","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,QAAQ,EAIT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgF,KAAK,aAAa,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACnJ,OAAO,EAOL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AACD,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,WAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,oBAAoB,+BA0B7G;AACD,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;IAC5C,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,qFAAqF;IACrF,iBAAiB,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,CAAC;IACpD,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AACD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,+BAyL5D;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,QAAQ,UAAQ,EAChB,SAAS,UAAQ,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,oBAAoB,CAkCtB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,sCAW/D"}
|
|
@@ -12,17 +12,19 @@ import { nodeFlowId, sourceHandle, targetHandle } from "./geometry.js";
|
|
|
12
12
|
/** Also usable in a completely custom node renderer. */
|
|
13
13
|
export function SpagbolNodePort({ port, connectable = true, style, position, className }) {
|
|
14
14
|
const { editor } = useSpagbol();
|
|
15
|
-
const selection = useEditorSelector(editor, snapshot => snapshot.selection);
|
|
16
15
|
const mode = useEditorSelector(editor, snapshot => snapshot.mode);
|
|
17
16
|
const flowId = useNodeId();
|
|
18
|
-
const selectedLink = mode === 'edit' && selection.linkIds.length === 1
|
|
19
|
-
? editor.graph.links.find(link => link.id === selection.linkIds[0]) : undefined;
|
|
20
17
|
const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;
|
|
21
|
-
const reconnecting =
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const reconnecting = useEditorSelector(editor, snapshot => {
|
|
19
|
+
if (snapshot.mode !== 'edit' || snapshot.selection.linkIds.length !== 1)
|
|
20
|
+
return false;
|
|
21
|
+
const link = graphAt(snapshot.document, snapshot.path).links.find(link => link.id === snapshot.selection.linkIds[0]);
|
|
22
|
+
return !!link && (port.direction === 'output'
|
|
23
|
+
? link.source.nodeId === nodeId && link.source.portId === port.id
|
|
24
|
+
: link.target.type === 'port' && link.target.nodeId === nodeId && link.target.portId === port.id);
|
|
25
|
+
});
|
|
24
26
|
const direction = port.direction;
|
|
25
|
-
return (_jsx(Handle, { id: direction === "input" ? targetHandle(port.id) : sourceHandle(port.id), type: direction === "input" ? "target" : "source", position: position ?? (direction === "input" ? Position.Left : Position.Right), className: `spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? ""}`, isConnectable:
|
|
27
|
+
return (_jsx(Handle, { id: direction === "input" ? targetHandle(port.id) : sourceHandle(port.id), type: direction === "input" ? "target" : "source", position: position ?? (direction === "input" ? Position.Left : Position.Right), className: `spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? ""}`, isConnectable: mode === "edit" && port.connectable !== false && connectable, style: style, "aria-label": `${port.label} ${direction}` }));
|
|
26
28
|
}
|
|
27
29
|
export function SpagbolNodeFrame(props) {
|
|
28
30
|
const { node, selected, projected, children } = props;
|
|
@@ -30,7 +32,8 @@ export function SpagbolNodeFrame(props) {
|
|
|
30
32
|
const [pointerOver, setPointerOver] = useState(false);
|
|
31
33
|
const updateInternals = useUpdateNodeInternals();
|
|
32
34
|
const flowId = useNodeId();
|
|
33
|
-
const
|
|
35
|
+
const singleSelected = useEditorSelector(editor, snapshot => snapshot.selection.nodeIds.length === 1 && snapshot.selection.nodeIds[0] === node.id);
|
|
36
|
+
const selectedPortId = useEditorSelector(editor, snapshot => snapshot.selection.port?.nodeId === node.id ? snapshot.selection.port.portId : undefined);
|
|
34
37
|
const mode = useEditorSelector(editor, snapshot => snapshot.mode);
|
|
35
38
|
const path = useEditorSelector(editor, snapshot => snapshot.path);
|
|
36
39
|
const boundary = isGroupBoundary(node);
|
|
@@ -38,7 +41,7 @@ export function SpagbolNodeFrame(props) {
|
|
|
38
41
|
const ports = portsFor(node, editor.nodes);
|
|
39
42
|
const inputPorts = ports.filter((port) => port.direction === "input" && props.portVisible?.(port) !== false);
|
|
40
43
|
const outputPorts = ports.filter((port) => port.direction === "output" && props.portVisible?.(port) !== false);
|
|
41
|
-
const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected &&
|
|
44
|
+
const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && singleSelected) && !(connecting && pointerOver);
|
|
42
45
|
const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;
|
|
43
46
|
const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;
|
|
44
47
|
const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);
|
|
@@ -86,7 +89,7 @@ export function SpagbolNodeFrame(props) {
|
|
|
86
89
|
}
|
|
87
90
|
function row(port, index) {
|
|
88
91
|
const input = port.direction === "input";
|
|
89
|
-
return (_jsxs("div", { className: `spagbol-node-port-row spagbol-node-port-row-${port.direction}`, "data-group-port": boundary ? port.id : undefined, style: { gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }, children: [!projected && !props.hidePorts && _jsx(SpagbolNodePort, { port: port }), props.renderPortContent ? props.renderPortContent(port) : _jsxs(_Fragment, { children: [boundary && !projected ? _jsx(SpagbolNodeGroupPort, { name: port.label, editable: editor.editing && !props.readOnly, draggable: editor.editing && !props.readOnly, selected:
|
|
92
|
+
return (_jsxs("div", { className: `spagbol-node-port-row spagbol-node-port-row-${port.direction}`, "data-group-port": boundary ? port.id : undefined, style: { gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }, children: [!projected && !props.hidePorts && _jsx(SpagbolNodePort, { port: port }), props.renderPortContent ? props.renderPortContent(port) : _jsxs(_Fragment, { children: [boundary && !projected ? _jsx(SpagbolNodeGroupPort, { name: port.label, editable: editor.editing && !props.readOnly, draggable: editor.editing && !props.readOnly, selected: selectedPortId === port.id, onChange: label => { try {
|
|
90
93
|
editor.renameBoundaryPort(node.id, port.id, label);
|
|
91
94
|
}
|
|
92
95
|
catch { /* Keep the valid label. */ } }, onPointerDown: event => {
|
|
@@ -122,7 +125,6 @@ export function useNodeViewProps(node, selected = false, projected = false, grou
|
|
|
122
125
|
const { editor, projection } = useSpagbol();
|
|
123
126
|
const owner = projection?.groupId ?? groupId;
|
|
124
127
|
const controlPath = projection?.path ?? [node.id];
|
|
125
|
-
const selection = useEditorSelector(editor, snapshot => snapshot.selection);
|
|
126
128
|
const mode = useEditorSelector(editor, snapshot => snapshot.mode);
|
|
127
129
|
const path = useEditorSelector(editor, snapshot => snapshot.path);
|
|
128
130
|
return useMemo(() => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpagbolNodeFrame.js","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"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","sourcesContent":["import { graphPortStates, emptyPortState } from \"./portState.js\";\nimport { SpagbolNodeGroupPortAdd } from \"./SpagbolNodeGroupPortAdd.js\";\nimport { SpagbolNodeGroupPort } from \"./SpagbolNodeGroupPort.js\";\nimport {\n Handle,\n Position,\n NodeResizer,\n useUpdateNodeInternals,\n useNodeId,\n} from \"@xyflow/react\";\nimport { useEffect, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent, type CSSProperties, type Ref, type ReactNode } from \"react\";\nimport {\n portsFor,\n graphAt,\n isGroupBoundary,\n spagbolGroupKinds,\n spagbolNewPortId,\n groupPanelLayout,\n type GraphNode,\n type PortDefinition,\n} from \"../core/index.js\";\nimport {\n GraphContext,\n useEditorSelector,\n useSpagbol,\n type SpagbolNodeViewProps,\n} from \"./context.js\";\nimport { SpagbolControlNumber } from \"./SpagbolControlNumber.js\";\nimport { SpagbolNodeFrameTypePicker } from './SpagbolNodeFrameTypePicker.js';\nimport { nodeFlowId, sourceHandle, targetHandle } from \"./geometry.js\";\n\nexport interface SpagbolNodePortProps {\n port: PortDefinition;\n connectable?: boolean;\n style?: CSSProperties;\n position?: Position;\n className?: string;\n}\n/** Also usable in a completely custom node renderer. */\nexport function SpagbolNodePort({ port, connectable = true, style, position, className }: SpagbolNodePortProps) {\n const { editor } = useSpagbol();\n const selection = useEditorSelector(editor, snapshot => snapshot.selection);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const flowId = useNodeId();\n const selectedLink = mode === 'edit' && selection.linkIds.length === 1\n ? editor.graph.links.find(link => link.id === selection.linkIds[0]) : undefined;\n const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;\n const reconnecting = selectedLink && (port.direction === 'output'\n ? selectedLink.source.nodeId === nodeId && selectedLink.source.portId === port.id\n : selectedLink.target.type === 'port' && selectedLink.target.nodeId === nodeId && selectedLink.target.portId === port.id);\n const direction = port.direction;\n return (\n <Handle\n id={direction === \"input\" ? targetHandle(port.id) : sourceHandle(port.id)}\n type={direction === \"input\" ? \"target\" : \"source\"}\n position={position ?? (direction === \"input\" ? Position.Left : Position.Right)}\n className={`spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? \"\"}`}\n isConnectable={\n editor.editing && port.connectable !== false && connectable\n }\n style={style}\n aria-label={`${port.label} ${direction}`}\n />\n );\n}\nexport interface SpagbolNodeFrameProps extends SpagbolNodeViewProps {\n children?: ReactNode;\n overlay?: ReactNode;\n rootRef?: Ref<HTMLDivElement>;\n title?: ReactNode;\n className?: string;\n hidePorts?: boolean;\n headerOutput?: boolean;\n portVisible?(port: PortDefinition): boolean;\n style?: CSSProperties;\n /** Domain widgets inside a shared port row. The canvas owns handles and gestures. */\n renderPortContent?(port: PortDefinition): ReactNode;\n portExtras?: ReactNode;\n portsHeader?: ReactNode;\n forceCompact?: boolean;\n}\nexport function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {\n const { node, selected, projected, children } = props;\n const { editor, views, features, connecting, diagnostics } = useSpagbol();\n const [pointerOver, setPointerOver] = useState(false);\n const updateInternals = useUpdateNodeInternals();\n const flowId = useNodeId();\n const selection = useEditorSelector(editor, snapshot => snapshot.selection);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n const boundary = isGroupBoundary(node);\n const reorder = useRef<{ pointer: number; portId: string; element: HTMLElement } | null>(null);\n const ports = portsFor(node, editor.nodes);\n const inputPorts = ports.filter((port) => port.direction === \"input\" && props.portVisible?.(port) !== false);\n const outputPorts = ports.filter((port) => port.direction === \"output\" && props.portVisible?.(port) !== false);\n const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && selection.nodeIds.length === 1) && !(connecting && pointerOver);\n const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;\n const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;\n const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);\n const connectedInputs = portState.inputs;\n const connectedOutputs = portState.outputs;\n const visibleInputs = inputPorts.filter(port => port !== headerInput && (!compact || connectedInputs.has(port.id)));\n const visibleOutputs = outputPorts.filter(port => port !== headerOutput && (!compact || connectedOutputs.has(port.id)));\n const definition = editor.nodes.get(node.kind);\n const view = views[node.kind];\n const newPortDirection = boundary ? (node.kind === spagbolGroupKinds.inputs ? \"output\" : \"input\") : definition?.editablePorts;\n const newPortLabel = definition?.newPortLabel ?? (node.kind === spagbolGroupKinds.inputs ? \"new input\" : \"new output\");\n const errors = diagnostics.filter((item) => item.nodeId === node.id);\n const portKey = ports\n .map((port) => `${port.direction}:${port.id}:${port.label}`)\n .join(\"|\");\n useEffect(() => {\n if (projected || !flowId) return;\n const frame = requestAnimationFrame(() => updateInternals(flowId));\n return () => cancelAnimationFrame(frame);\n }, [flowId, portKey, compact, projected, updateInternals]);\n const setInput = portState.replaced;\n function movePort(event: ReactPointerEvent<HTMLDivElement>) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.preventDefault(); event.stopPropagation();\n const rows = Array.from(event.currentTarget.querySelectorAll<HTMLElement>('[data-group-port]'));\n const target = rows.findIndex(row => event.clientY < row.getBoundingClientRect().bottom);\n editor.moveBoundaryPort(node.id, drag.portId, target < 0 ? rows.length - 1 : target);\n }\n function endPort(event: ReactPointerEvent<HTMLDivElement>, cancel = false) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.stopPropagation(); reorder.current = null;\n if (drag.element.hasPointerCapture(event.pointerId)) drag.element.releasePointerCapture(event.pointerId);\n if (cancel) editor.cancelGesture(); else editor.commitGesture();\n }\n function row(port: PortDefinition, index: number) {\n const input = port.direction === \"input\";\n return (\n <div\n className={`spagbol-node-port-row spagbol-node-port-row-${port.direction}`}\n key={`${port.direction}:${port.id}`}\n data-group-port={boundary ? port.id : undefined}\n style={{ gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }}\n >\n {!projected && !props.hidePorts && <SpagbolNodePort port={port} />}\n {props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={selection.port?.nodeId === node.id && selection.port.portId === port.id} onChange={label => { try { editor.renameBoundaryPort(node.id, port.id, label); } catch { /* Keep the valid label. */ } }} onPointerDown={event => {\n if (!editor.editing || props.readOnly || event.button !== 0) return;\n event.preventDefault(); event.stopPropagation();\n editor.select({ nodeIds: [node.id], port: { nodeId: node.id, portId: port.id, direction: port.direction } });\n editor.beginGesture('Reorder group ports');\n reorder.current = { pointer: event.pointerId, portId: port.id, element: event.currentTarget };\n event.currentTarget.setPointerCapture(event.pointerId);\n }} /> : <span className=\"spagbol-node-port-name\" title={port.label}>{port.label}</span>}\n {((input && node.kind !== spagbolGroupKinds.outputs) || node.kind === spagbolGroupKinds.inputs) && port.valueEditor !== false && (\n <SpagbolControlNumber\n value={node.values[port.id] ?? port.defaultValue ?? 0}\n onChange={(value) => props.setValue(port.id, value)}\n label={`${node.label ?? definition?.label ?? node.kind}: ${port.label}`}\n min={port.min}\n max={port.max}\n step={port.step}\n integer={port.integer}\n disabled={props.readOnly}\n replaced={setInput.has(port.id)}\n onGestureStart={() => props.beginGesture(`Change ${port.label}`)}\n onGestureEnd={(cancel) => props.endGesture(cancel)}\n />\n )}</>}\n </div>\n );\n }\n return (\n <div\n onPointerEnter={() => setPointerOver(true)} onPointerLeave={() => setPointerOver(false)}\n ref={props.rootRef}\n style={{ ...props.style, ...(node.scale ? { zoom: node.scale, width: view?.autoWidth ? props.style?.width ?? \"max-content\" : `${100 / node.scale}%`, height: `${100 / node.scale}%` } : view?.autoWidth ? { width: props.style?.width ?? \"max-content\" } : {}) }}\n onPointerMove={movePort} onPointerUp={event => endPort(event)} onPointerCancel={event => endPort(event, true)}\n className={`spagbol-node-frame${selected ? \" spagbol-node-frame-selected\" : \"\"}${node.enabled === false ? \" spagbol-node-frame-disabled\" : \"\"}${compact ? \" spagbol-node-frame-compact\" : \"\"}${props.forceCompact ? \" spagbol-node-frame-auto-compact\" : \"\"}${projected ? \" spagbol-node-frame-projected nodrag nopan\" : \"\"} ${props.className ?? \"\"}`}\n >\n {!projected && view?.resizable && editor.editing && (\n <NodeResizer\n isVisible={selected && !compact}\n minWidth={view.minWidth ?? 180}\n minHeight={view.minHeight ?? 80}\n onResizeStart={() => props.beginGesture(\"Resize node\")}\n onResize={(_, size) =>\n editor.updateNode(\n node.id,\n {\n size: { width: size.width / (node.scale ?? 1), height: size.height / (node.scale ?? 1) },\n position: { x: size.x, y: size.y },\n },\n \"layout\",\n )\n }\n onResizeEnd={() => props.endGesture()}\n />\n )}\n <div\n style={{ position: \"relative\" }}\n className=\"spagbol-node-frame-header spagbol-canvas-drag-handle\"\n onDoubleClickCapture={event => { if (projected && props.resetOverride) { event.preventDefault(); event.stopPropagation(); props.resetOverride(); } }}\n >\n {!projected && !props.hidePorts && headerInput && <SpagbolNodePort port={headerInput} style={{ top: \"50%\" }} />}\n {!projected && !props.hidePorts && headerOutput && <SpagbolNodePort port={headerOutput} style={{ top: \"50%\" }} />}\n <span className=\"spagbol-node-frame-title\">\n {props.title ?? <SpagbolNodeFrameTypePicker node={node} compact={!!compact} selected={selected} disabled={projected || props.readOnly || !editor.editing} />}\n </span>\n {!projected && <button className=\"spagbol-node-frame-enabled\" type=\"button\" aria-label={node.enabled === false ? 'Enable node' : 'Disable node'} title={node.enabled === false ? 'Enable node' : 'Disable node'} aria-pressed={node.enabled !== false} disabled={!editor.editing || props.readOnly} onPointerDown={event => event.stopPropagation()} onDoubleClick={event => event.stopPropagation()} onClick={event => { event.preventDefault(); event.stopPropagation(); editor.updateNode(node.id, { enabled: node.enabled === false }, 'structure'); }}><span className=\"spagbol-node-frame-enabled-icon\" aria-hidden=\"true\" /></button>}\n {!projected && features.compact && !props.forceCompact && view?.compact !== false && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label={node.compact ? \"Expand ports\" : \"Compact ports\"}\n title={node.compact ? \"Expand ports\" : \"Compact ports\"}\n disabled={!editor.editing}\n onClick={(event) => {\n event.stopPropagation();\n editor.updateNode(node.id, { compact: !node.compact }, \"layout\");\n }}\n >\n {node.compact ? \"+\" : \"−\"}\n </button>\n )}\n {!projected && node.graph && features.nestedGraphs && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label=\"Enter nested graph\"\n title=\"Enter nested graph\"\n onClick={(event) => {\n event.stopPropagation();\n editor.enter(node.id);\n }}\n >\n ↳\n </button>\n )}\n </div>\n <>\n {children && (\n <div\n className=\"spagbol-node-frame-body nodrag nopan\"\n onPointerDown={(event) => event.stopPropagation()}\n >\n {children}\n </div>\n )}\n {!props.hidePorts && (visibleInputs.length > 0 || visibleOutputs.length > 0 || newPortDirection) && (\n <div style={inputPorts.length === 0 || outputPorts.length === 0 ? { gridTemplateColumns: \"minmax(0, 1fr)\" } : undefined} className={`spagbol-node-frame-ports${boundary ? \" spagbol-node-frame-ports-boundary\" : \"\"}`}>\n {props.portsHeader}\n {visibleInputs.map(row)}\n {visibleOutputs.map(row)}\n {newPortDirection && !projected && editor.editing && <div style={{ gridColumn: newPortDirection === \"input\" || inputPorts.length === 0 ? 1 : 2, gridRow: ports.length + 1 + (props.portsHeader ? 1 : 0), position: \"relative\" }}><SpagbolNodePort port={{ id: spagbolNewPortId, label: newPortLabel, direction: newPortDirection }} style={{ top: \"50%\" }} /><SpagbolNodeGroupPortAdd label={newPortLabel} onAdd={label => { try { editor.addBoundaryPort(node.id, label); } catch { /* Keep the valid boundary. */ } }} /></div>}\n </div>\n )}\n {!compact && props.portExtras}\n {errors.length > 0 && (\n <div\n className=\"spagbol-node-frame-diagnostics\"\n role=\"status\"\n title={errors.map((error) => error.message).join(\"\\n\")}\n >\n {errors[0]?.message}\n </div>\n )}\n </>\n {props.overlay}\n </div>\n );\n}\n\nexport function useNodeViewProps(\n node: GraphNode,\n selected = false,\n projected = false,\n groupId?: string,\n): SpagbolNodeViewProps {\n const { editor, projection } = useSpagbol();\n const owner = projection?.groupId ?? groupId;\n const controlPath = projection?.path ?? [node.id];\n const selection = useEditorSelector(editor, snapshot => snapshot.selection);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n return useMemo(\n () => ({\n node,\n selected,\n projected,\n path: projection ? [...path, projection.groupId, ...projection.path.slice(0, -1)] : path,\n resetOverride: owner ? () => editor.resetOverride(owner, controlPath) : undefined,\n readOnly: mode === \"readonly\",\n setValue: (portId: string, value: number) =>\n owner\n ? editor.setOverride(owner, controlPath, portId, value)\n : editor.setValue(node.id, portId, value),\n patchData: (data: Partial<GraphNode[\"data\"]>, structural = false) => {\n if (projected) { if (!structural && owner) editor.setDataOverride(owner, controlPath, data); return; }\n editor.updateNode(\n node.id,\n { data: { ...node.data, ...data } as GraphNode[\"data\"] },\n structural ? \"structure\" : \"values\",\n );\n },\n beginGesture: (label = \"Edit control\") => {\n if (!editor.getSnapshot().gesture) editor.beginGesture(label);\n },\n endGesture: (cancel = false) =>\n cancel ? editor.cancelGesture() : editor.commitGesture(),\n }),\n [editor, owner, projection, node, projected, selected, mode, path],\n );\n}\n\nexport function SpagbolNodePanel({ group }: { group: GraphNode }) {\n const context = useSpagbol();\n const layout = groupPanelLayout(group);\n if (!layout) return null;\n return <div className=\"spagbol-node-panel nodrag nopan\" style={{ position: 'relative', width: layout.size.width, height: layout.size.height }} aria-label=\"Instance controls\" onPointerDown={event => event.stopPropagation()}>\n {layout.nodes.map(node => <GraphContext.Provider key={node.id} value={{ ...context, projection: { groupId: context.projection?.groupId ?? group.id, path: [...(context.projection?.path ?? []), node.id] } }}>\n <div className=\"spagbol-node-panel-item\" style={{ position: 'absolute', left: node.position.x - layout.position.x, top: node.position.y - layout.position.y, width: context.views[node.kind]?.autoWidth ? \"max-content\" : (node.size?.width ?? 220) * (node.scale ?? 1) }}>\n <SpagbolNodePanelItem node={node} />\n </div>\n </GraphContext.Provider>)}\n </div>;\n}\nfunction SpagbolNodePanelItem({ node }: { node: GraphNode }) {\n const { views } = useSpagbol();\n const props = useNodeViewProps(node, false, true);\n const Renderer = views[node.kind]?.projected ?? views[node.kind]?.render;\n if (Renderer) return <Renderer {...props} />;\n const Body = views[node.kind]?.body;\n return <SpagbolNodeFrame {...props} hidePorts={!!Body}>{Body ? <Body {...props} /> : null}</SpagbolNodeFrame>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"SpagbolNodeFrame.js","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"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","sourcesContent":["import { graphPortStates, emptyPortState } from \"./portState.js\";\nimport { SpagbolNodeGroupPortAdd } from \"./SpagbolNodeGroupPortAdd.js\";\nimport { SpagbolNodeGroupPort } from \"./SpagbolNodeGroupPort.js\";\nimport {\n Handle,\n Position,\n NodeResizer,\n useUpdateNodeInternals,\n useNodeId,\n} from \"@xyflow/react\";\nimport { useEffect, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent, type CSSProperties, type Ref, type ReactNode } from \"react\";\nimport {\n portsFor,\n graphAt,\n isGroupBoundary,\n spagbolGroupKinds,\n spagbolNewPortId,\n groupPanelLayout,\n type GraphNode,\n type PortDefinition,\n} from \"../core/index.js\";\nimport {\n GraphContext,\n useEditorSelector,\n useSpagbol,\n type SpagbolNodeViewProps,\n} from \"./context.js\";\nimport { SpagbolControlNumber } from \"./SpagbolControlNumber.js\";\nimport { SpagbolNodeFrameTypePicker } from './SpagbolNodeFrameTypePicker.js';\nimport { nodeFlowId, sourceHandle, targetHandle } from \"./geometry.js\";\n\nexport interface SpagbolNodePortProps {\n port: PortDefinition;\n connectable?: boolean;\n style?: CSSProperties;\n position?: Position;\n className?: string;\n}\n/** Also usable in a completely custom node renderer. */\nexport function SpagbolNodePort({ port, connectable = true, style, position, className }: SpagbolNodePortProps) {\n const { editor } = useSpagbol();\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const flowId = useNodeId();\n const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;\n const reconnecting = useEditorSelector(editor, snapshot => {\n if (snapshot.mode !== 'edit' || snapshot.selection.linkIds.length !== 1) return false;\n const link = graphAt(snapshot.document, snapshot.path).links.find(link => link.id === snapshot.selection.linkIds[0]);\n return !!link && (port.direction === 'output'\n ? link.source.nodeId === nodeId && link.source.portId === port.id\n : link.target.type === 'port' && link.target.nodeId === nodeId && link.target.portId === port.id);\n });\n const direction = port.direction;\n return (\n <Handle\n id={direction === \"input\" ? targetHandle(port.id) : sourceHandle(port.id)}\n type={direction === \"input\" ? \"target\" : \"source\"}\n position={position ?? (direction === \"input\" ? Position.Left : Position.Right)}\n className={`spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? \"\"}`}\n isConnectable={\n mode === \"edit\" && port.connectable !== false && connectable\n }\n style={style}\n aria-label={`${port.label} ${direction}`}\n />\n );\n}\nexport interface SpagbolNodeFrameProps extends SpagbolNodeViewProps {\n children?: ReactNode;\n overlay?: ReactNode;\n rootRef?: Ref<HTMLDivElement>;\n title?: ReactNode;\n className?: string;\n hidePorts?: boolean;\n headerOutput?: boolean;\n portVisible?(port: PortDefinition): boolean;\n style?: CSSProperties;\n /** Domain widgets inside a shared port row. The canvas owns handles and gestures. */\n renderPortContent?(port: PortDefinition): ReactNode;\n portExtras?: ReactNode;\n portsHeader?: ReactNode;\n forceCompact?: boolean;\n}\nexport function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {\n const { node, selected, projected, children } = props;\n const { editor, views, features, connecting, diagnostics } = useSpagbol();\n const [pointerOver, setPointerOver] = useState(false);\n const updateInternals = useUpdateNodeInternals();\n const flowId = useNodeId();\n const singleSelected = useEditorSelector(editor, snapshot => snapshot.selection.nodeIds.length === 1 && snapshot.selection.nodeIds[0] === node.id);\n const selectedPortId = useEditorSelector(editor, snapshot => snapshot.selection.port?.nodeId === node.id ? snapshot.selection.port.portId : undefined);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n const boundary = isGroupBoundary(node);\n const reorder = useRef<{ pointer: number; portId: string; element: HTMLElement } | null>(null);\n const ports = portsFor(node, editor.nodes);\n const inputPorts = ports.filter((port) => port.direction === \"input\" && props.portVisible?.(port) !== false);\n const outputPorts = ports.filter((port) => port.direction === \"output\" && props.portVisible?.(port) !== false);\n const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && singleSelected) && !(connecting && pointerOver);\n const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;\n const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;\n const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);\n const connectedInputs = portState.inputs;\n const connectedOutputs = portState.outputs;\n const visibleInputs = inputPorts.filter(port => port !== headerInput && (!compact || connectedInputs.has(port.id)));\n const visibleOutputs = outputPorts.filter(port => port !== headerOutput && (!compact || connectedOutputs.has(port.id)));\n const definition = editor.nodes.get(node.kind);\n const view = views[node.kind];\n const newPortDirection = boundary ? (node.kind === spagbolGroupKinds.inputs ? \"output\" : \"input\") : definition?.editablePorts;\n const newPortLabel = definition?.newPortLabel ?? (node.kind === spagbolGroupKinds.inputs ? \"new input\" : \"new output\");\n const errors = diagnostics.filter((item) => item.nodeId === node.id);\n const portKey = ports\n .map((port) => `${port.direction}:${port.id}:${port.label}`)\n .join(\"|\");\n useEffect(() => {\n if (projected || !flowId) return;\n const frame = requestAnimationFrame(() => updateInternals(flowId));\n return () => cancelAnimationFrame(frame);\n }, [flowId, portKey, compact, projected, updateInternals]);\n const setInput = portState.replaced;\n function movePort(event: ReactPointerEvent<HTMLDivElement>) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.preventDefault(); event.stopPropagation();\n const rows = Array.from(event.currentTarget.querySelectorAll<HTMLElement>('[data-group-port]'));\n const target = rows.findIndex(row => event.clientY < row.getBoundingClientRect().bottom);\n editor.moveBoundaryPort(node.id, drag.portId, target < 0 ? rows.length - 1 : target);\n }\n function endPort(event: ReactPointerEvent<HTMLDivElement>, cancel = false) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.stopPropagation(); reorder.current = null;\n if (drag.element.hasPointerCapture(event.pointerId)) drag.element.releasePointerCapture(event.pointerId);\n if (cancel) editor.cancelGesture(); else editor.commitGesture();\n }\n function row(port: PortDefinition, index: number) {\n const input = port.direction === \"input\";\n return (\n <div\n className={`spagbol-node-port-row spagbol-node-port-row-${port.direction}`}\n key={`${port.direction}:${port.id}`}\n data-group-port={boundary ? port.id : undefined}\n style={{ gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }}\n >\n {!projected && !props.hidePorts && <SpagbolNodePort port={port} />}\n {props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={selectedPortId === port.id} onChange={label => { try { editor.renameBoundaryPort(node.id, port.id, label); } catch { /* Keep the valid label. */ } }} onPointerDown={event => {\n if (!editor.editing || props.readOnly || event.button !== 0) return;\n event.preventDefault(); event.stopPropagation();\n editor.select({ nodeIds: [node.id], port: { nodeId: node.id, portId: port.id, direction: port.direction } });\n editor.beginGesture('Reorder group ports');\n reorder.current = { pointer: event.pointerId, portId: port.id, element: event.currentTarget };\n event.currentTarget.setPointerCapture(event.pointerId);\n }} /> : <span className=\"spagbol-node-port-name\" title={port.label}>{port.label}</span>}\n {((input && node.kind !== spagbolGroupKinds.outputs) || node.kind === spagbolGroupKinds.inputs) && port.valueEditor !== false && (\n <SpagbolControlNumber\n value={node.values[port.id] ?? port.defaultValue ?? 0}\n onChange={(value) => props.setValue(port.id, value)}\n label={`${node.label ?? definition?.label ?? node.kind}: ${port.label}`}\n min={port.min}\n max={port.max}\n step={port.step}\n integer={port.integer}\n disabled={props.readOnly}\n replaced={setInput.has(port.id)}\n onGestureStart={() => props.beginGesture(`Change ${port.label}`)}\n onGestureEnd={(cancel) => props.endGesture(cancel)}\n />\n )}</>}\n </div>\n );\n }\n return (\n <div\n onPointerEnter={() => setPointerOver(true)} onPointerLeave={() => setPointerOver(false)}\n ref={props.rootRef}\n style={{ ...props.style, ...(node.scale ? { zoom: node.scale, width: view?.autoWidth ? props.style?.width ?? \"max-content\" : `${100 / node.scale}%`, height: `${100 / node.scale}%` } : view?.autoWidth ? { width: props.style?.width ?? \"max-content\" } : {}) }}\n onPointerMove={movePort} onPointerUp={event => endPort(event)} onPointerCancel={event => endPort(event, true)}\n className={`spagbol-node-frame${selected ? \" spagbol-node-frame-selected\" : \"\"}${node.enabled === false ? \" spagbol-node-frame-disabled\" : \"\"}${compact ? \" spagbol-node-frame-compact\" : \"\"}${props.forceCompact ? \" spagbol-node-frame-auto-compact\" : \"\"}${projected ? \" spagbol-node-frame-projected nodrag nopan\" : \"\"} ${props.className ?? \"\"}`}\n >\n {!projected && view?.resizable && editor.editing && (\n <NodeResizer\n isVisible={selected && !compact}\n minWidth={view.minWidth ?? 180}\n minHeight={view.minHeight ?? 80}\n onResizeStart={() => props.beginGesture(\"Resize node\")}\n onResize={(_, size) =>\n editor.updateNode(\n node.id,\n {\n size: { width: size.width / (node.scale ?? 1), height: size.height / (node.scale ?? 1) },\n position: { x: size.x, y: size.y },\n },\n \"layout\",\n )\n }\n onResizeEnd={() => props.endGesture()}\n />\n )}\n <div\n style={{ position: \"relative\" }}\n className=\"spagbol-node-frame-header spagbol-canvas-drag-handle\"\n onDoubleClickCapture={event => { if (projected && props.resetOverride) { event.preventDefault(); event.stopPropagation(); props.resetOverride(); } }}\n >\n {!projected && !props.hidePorts && headerInput && <SpagbolNodePort port={headerInput} style={{ top: \"50%\" }} />}\n {!projected && !props.hidePorts && headerOutput && <SpagbolNodePort port={headerOutput} style={{ top: \"50%\" }} />}\n <span className=\"spagbol-node-frame-title\">\n {props.title ?? <SpagbolNodeFrameTypePicker node={node} compact={!!compact} selected={selected} disabled={projected || props.readOnly || !editor.editing} />}\n </span>\n {!projected && <button className=\"spagbol-node-frame-enabled\" type=\"button\" aria-label={node.enabled === false ? 'Enable node' : 'Disable node'} title={node.enabled === false ? 'Enable node' : 'Disable node'} aria-pressed={node.enabled !== false} disabled={!editor.editing || props.readOnly} onPointerDown={event => event.stopPropagation()} onDoubleClick={event => event.stopPropagation()} onClick={event => { event.preventDefault(); event.stopPropagation(); editor.updateNode(node.id, { enabled: node.enabled === false }, 'structure'); }}><span className=\"spagbol-node-frame-enabled-icon\" aria-hidden=\"true\" /></button>}\n {!projected && features.compact && !props.forceCompact && view?.compact !== false && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label={node.compact ? \"Expand ports\" : \"Compact ports\"}\n title={node.compact ? \"Expand ports\" : \"Compact ports\"}\n disabled={!editor.editing}\n onClick={(event) => {\n event.stopPropagation();\n editor.updateNode(node.id, { compact: !node.compact }, \"layout\");\n }}\n >\n {node.compact ? \"+\" : \"−\"}\n </button>\n )}\n {!projected && node.graph && features.nestedGraphs && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label=\"Enter nested graph\"\n title=\"Enter nested graph\"\n onClick={(event) => {\n event.stopPropagation();\n editor.enter(node.id);\n }}\n >\n ↳\n </button>\n )}\n </div>\n <>\n {children && (\n <div\n className=\"spagbol-node-frame-body nodrag nopan\"\n onPointerDown={(event) => event.stopPropagation()}\n >\n {children}\n </div>\n )}\n {!props.hidePorts && (visibleInputs.length > 0 || visibleOutputs.length > 0 || newPortDirection) && (\n <div style={inputPorts.length === 0 || outputPorts.length === 0 ? { gridTemplateColumns: \"minmax(0, 1fr)\" } : undefined} className={`spagbol-node-frame-ports${boundary ? \" spagbol-node-frame-ports-boundary\" : \"\"}`}>\n {props.portsHeader}\n {visibleInputs.map(row)}\n {visibleOutputs.map(row)}\n {newPortDirection && !projected && editor.editing && <div style={{ gridColumn: newPortDirection === \"input\" || inputPorts.length === 0 ? 1 : 2, gridRow: ports.length + 1 + (props.portsHeader ? 1 : 0), position: \"relative\" }}><SpagbolNodePort port={{ id: spagbolNewPortId, label: newPortLabel, direction: newPortDirection }} style={{ top: \"50%\" }} /><SpagbolNodeGroupPortAdd label={newPortLabel} onAdd={label => { try { editor.addBoundaryPort(node.id, label); } catch { /* Keep the valid boundary. */ } }} /></div>}\n </div>\n )}\n {!compact && props.portExtras}\n {errors.length > 0 && (\n <div\n className=\"spagbol-node-frame-diagnostics\"\n role=\"status\"\n title={errors.map((error) => error.message).join(\"\\n\")}\n >\n {errors[0]?.message}\n </div>\n )}\n </>\n {props.overlay}\n </div>\n );\n}\n\nexport function useNodeViewProps(\n node: GraphNode,\n selected = false,\n projected = false,\n groupId?: string,\n): SpagbolNodeViewProps {\n const { editor, projection } = useSpagbol();\n const owner = projection?.groupId ?? groupId;\n const controlPath = projection?.path ?? [node.id];\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n return useMemo(\n () => ({\n node,\n selected,\n projected,\n path: projection ? [...path, projection.groupId, ...projection.path.slice(0, -1)] : path,\n resetOverride: owner ? () => editor.resetOverride(owner, controlPath) : undefined,\n readOnly: mode === \"readonly\",\n setValue: (portId: string, value: number) =>\n owner\n ? editor.setOverride(owner, controlPath, portId, value)\n : editor.setValue(node.id, portId, value),\n patchData: (data: Partial<GraphNode[\"data\"]>, structural = false) => {\n if (projected) { if (!structural && owner) editor.setDataOverride(owner, controlPath, data); return; }\n editor.updateNode(\n node.id,\n { data: { ...node.data, ...data } as GraphNode[\"data\"] },\n structural ? \"structure\" : \"values\",\n );\n },\n beginGesture: (label = \"Edit control\") => {\n if (!editor.getSnapshot().gesture) editor.beginGesture(label);\n },\n endGesture: (cancel = false) =>\n cancel ? editor.cancelGesture() : editor.commitGesture(),\n }),\n [editor, owner, projection, node, projected, selected, mode, path],\n );\n}\n\nexport function SpagbolNodePanel({ group }: { group: GraphNode }) {\n const context = useSpagbol();\n const layout = groupPanelLayout(group);\n if (!layout) return null;\n return <div className=\"spagbol-node-panel nodrag nopan\" style={{ position: 'relative', width: layout.size.width, height: layout.size.height }} aria-label=\"Instance controls\" onPointerDown={event => event.stopPropagation()}>\n {layout.nodes.map(node => <GraphContext.Provider key={node.id} value={{ ...context, projection: { groupId: context.projection?.groupId ?? group.id, path: [...(context.projection?.path ?? []), node.id] } }}>\n <div className=\"spagbol-node-panel-item\" style={{ position: 'absolute', left: node.position.x - layout.position.x, top: node.position.y - layout.position.y, width: context.views[node.kind]?.autoWidth ? \"max-content\" : (node.size?.width ?? 220) * (node.scale ?? 1) }}>\n <SpagbolNodePanelItem node={node} />\n </div>\n </GraphContext.Provider>)}\n </div>;\n}\nfunction SpagbolNodePanelItem({ node }: { node: GraphNode }) {\n const { views } = useSpagbol();\n const props = useNodeViewProps(node, false, true);\n const Renderer = views[node.kind]?.projected ?? views[node.kind]?.render;\n if (Renderer) return <Renderer {...props} />;\n const Body = views[node.kind]?.body;\n return <SpagbolNodeFrame {...props} hidePorts={!!Body}>{Body ? <Body {...props} /> : null}</SpagbolNodeFrame>;\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Node, NodeChange } from "@xyflow/react";
|
|
2
|
+
import type { GraphDocument, Point } from "../core/index.js";
|
|
3
|
+
/** Accumulate only changed native node positions; keep model records immutable. */
|
|
4
|
+
export declare function previewNodePositions<N extends Node>(previous: ReadonlyMap<string, Point>, changes: readonly NodeChange<N>[]): ReadonlyMap<string, Point>;
|
|
5
|
+
/** Apply a completed multi-node drag as one atomic controller edit. */
|
|
6
|
+
export declare function applyNodePositions(graph: GraphDocument, positions: ReadonlyMap<string, Point>): void;
|
|
7
|
+
//# sourceMappingURL=nodeDrag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeDrag.d.ts","sourceRoot":"","sources":["../../src/react/nodeDrag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE7D,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,IAAI,EACjD,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,EACpC,OAAO,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,GAChC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAc5B;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,GACpC,IAAI,CAKN"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Accumulate only changed native node positions; keep model records immutable. */
|
|
2
|
+
export function previewNodePositions(previous, changes) {
|
|
3
|
+
let next;
|
|
4
|
+
for (const change of changes) {
|
|
5
|
+
if (change.type !== "position" || !change.position || !change.id.startsWith("node:"))
|
|
6
|
+
continue;
|
|
7
|
+
const { x, y } = change.position;
|
|
8
|
+
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
9
|
+
continue;
|
|
10
|
+
const id = change.id.slice(5);
|
|
11
|
+
const before = (next ?? previous).get(id);
|
|
12
|
+
if (before?.x === x && before.y === y)
|
|
13
|
+
continue;
|
|
14
|
+
next ??= new Map(previous);
|
|
15
|
+
next.set(id, { x, y });
|
|
16
|
+
}
|
|
17
|
+
return next ?? previous;
|
|
18
|
+
}
|
|
19
|
+
/** Apply a completed multi-node drag as one atomic controller edit. */
|
|
20
|
+
export function applyNodePositions(graph, positions) {
|
|
21
|
+
for (const node of graph.nodes) {
|
|
22
|
+
const position = positions.get(node.id);
|
|
23
|
+
if (position)
|
|
24
|
+
node.position = { ...position };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=nodeDrag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeDrag.js","sourceRoot":"","sources":["../../src/react/nodeDrag.ts"],"names":[],"mappings":"AAGA,mFAAmF;AACnF,MAAM,UAAU,oBAAoB,CAClC,QAAoC,EACpC,OAAiC;IAEjC,IAAI,IAAoC,CAAC;IACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;YAClF,SAAS;QACX,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAS;QACzD,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,IAAI,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC;YAAE,SAAS;QAChD,IAAI,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,IAAI,QAAQ,CAAC;AAC1B,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAChC,KAAoB,EACpB,SAAqC;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IAChD,CAAC;AACH,CAAC","sourcesContent":["import type { Node, NodeChange } from \"@xyflow/react\";\nimport type { GraphDocument, Point } from \"../core/index.js\";\n\n/** Accumulate only changed native node positions; keep model records immutable. */\nexport function previewNodePositions<N extends Node>(\n previous: ReadonlyMap<string, Point>,\n changes: readonly NodeChange<N>[],\n): ReadonlyMap<string, Point> {\n let next: Map<string, Point> | undefined;\n for (const change of changes) {\n if (change.type !== \"position\" || !change.position || !change.id.startsWith(\"node:\"))\n continue;\n const { x, y } = change.position;\n if (!Number.isFinite(x) || !Number.isFinite(y)) continue;\n const id = change.id.slice(5);\n const before = (next ?? previous).get(id);\n if (before?.x === x && before.y === y) continue;\n next ??= new Map(previous);\n next.set(id, { x, y });\n }\n return next ?? previous;\n}\n\n/** Apply a completed multi-node drag as one atomic controller edit. */\nexport function applyNodePositions(\n graph: GraphDocument,\n positions: ReadonlyMap<string, Point>,\n): void {\n for (const node of graph.nodes) {\n const position = positions.get(node.id);\n if (position) node.position = { ...position };\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { previewNodePositions, applyNodePositions } from "./nodeDrag.js";
|
|
1
2
|
import { createPortal } from "react-dom";
|
|
2
3
|
import { SpagbolCanvasLinkControls } from "./SpagbolCanvasLinkControls.js";
|
|
3
4
|
import { SpagbolCanvasFlow } from "./SpagbolCanvasFlow.js";
|
|
@@ -146,6 +147,7 @@ export interface SpagbolCanvasProps extends SpagbolCanvasSlots {
|
|
|
146
147
|
controls?: SpagbolCanvasControlsOptions | false;
|
|
147
148
|
ariaLabel?: string;
|
|
148
149
|
}
|
|
150
|
+
const emptyFlowNodes: FlowNode[] = [];
|
|
149
151
|
const emptyViews: Readonly<Record<string, SpagbolNodeView>> = Object.freeze({});
|
|
150
152
|
const emptyPresentations: Readonly<Record<string, SpagbolLinkPresentation>> =
|
|
151
153
|
Object.freeze({});
|
|
@@ -404,8 +406,8 @@ const SpagbolCanvasLink = memo(function SpagbolCanvasLink(props: EdgeProps<FlowL
|
|
|
404
406
|
const flow = useReactFlow();
|
|
405
407
|
const viewport = useViewport();
|
|
406
408
|
const [controlsVisible, setControlsVisible] = useState(false);
|
|
407
|
-
const
|
|
408
|
-
const showControls = props.selected &&
|
|
409
|
+
const selectedLinkId = useEditorSelector(editor, snapshot => snapshot.selection.linkIds.length === 1 ? snapshot.selection.linkIds[0] : undefined);
|
|
410
|
+
const showControls = props.selected && selectedLinkId === props.id && editor.editing && !connecting && !props.data?.virtual;
|
|
409
411
|
useEffect(() => {
|
|
410
412
|
if (!showControls) { setControlsVisible(false); return; }
|
|
411
413
|
const timer = window.setTimeout(() => setControlsVisible(true), 260);
|
|
@@ -507,7 +509,9 @@ function SpagbolCanvasInner({
|
|
|
507
509
|
const flow = useReactFlow<FlowNode, FlowLink>();
|
|
508
510
|
const flowStore = useStoreApi();
|
|
509
511
|
const nodesInitialized = useNodesInitialized();
|
|
510
|
-
|
|
512
|
+
// Only cable-property anchors need live native geometry. Subscribing to every
|
|
513
|
+
// position change otherwise causes redundant full-canvas renders during drags.
|
|
514
|
+
const flowNodes = useStore((state) => features.linkProperties ? state.nodes : emptyFlowNodes);
|
|
511
515
|
// Measurements are canvas state, not saved graph data. Retaining them keeps
|
|
512
516
|
// React Flow from hiding/reinitializing every node on each drag preview frame.
|
|
513
517
|
const measuredSizes = useRef(
|
|
@@ -559,10 +563,25 @@ function SpagbolCanvasInner({
|
|
|
559
563
|
const duplicate = useRef<DuplicateDrag | null>(null);
|
|
560
564
|
const [duplicatePreview, setDuplicatePreview] =
|
|
561
565
|
useState<DuplicateDrag | null>(null);
|
|
562
|
-
const
|
|
566
|
+
const cancelledNodeDrag = useRef(false);
|
|
567
|
+
// Position previews belong to the canvas, not the persistent document. A drag
|
|
568
|
+
// commits once, avoiding whole-document edits/validation on every pointer move.
|
|
569
|
+
const nodeDrag = useRef<ReadonlyMap<string, Point> | null>(null);
|
|
570
|
+
const [positionRevision, setPositionRevision] = useState(0);
|
|
571
|
+
const clearNodeDrag = () => {
|
|
572
|
+
nodeDrag.current = null;
|
|
573
|
+
// Force a controlled refresh on cancellation, even though the saved position
|
|
574
|
+
// never changed: React Flow currently holds the transient pointer position.
|
|
575
|
+
setPositionRevision(value => value + 1);
|
|
576
|
+
};
|
|
577
|
+
|
|
563
578
|
useEffect(() => {
|
|
579
|
+
if (!snapshot.gesture && nodeDrag.current) {
|
|
580
|
+
cancelledNodeDrag.current = true;
|
|
581
|
+
clearNodeDrag();
|
|
582
|
+
}
|
|
564
583
|
if (!snapshot.gesture && duplicate.current) {
|
|
565
|
-
|
|
584
|
+
cancelledNodeDrag.current = true;
|
|
566
585
|
duplicate.current = null;
|
|
567
586
|
setDuplicatePreview(null);
|
|
568
587
|
}
|
|
@@ -612,7 +631,7 @@ function SpagbolCanvasInner({
|
|
|
612
631
|
if (phase === 'start') {
|
|
613
632
|
const area = editor.graph.areas.find(area => area.id === id);
|
|
614
633
|
if (!area) return;
|
|
615
|
-
|
|
634
|
+
cancelledNodeDrag.current = false;
|
|
616
635
|
const fragment = copyFragment(editor.graph, [], [id]);
|
|
617
636
|
duplicate.current = { namespace: createId('preview'), fragment, nodeId: '', origin: area.position, positions: new Map(fragment.nodes.map(node => [node.id, node.position])), areaPositions: new Map(fragment.areas.map(area => [area.id, area.position])), external };
|
|
618
637
|
setDuplicatePreview(duplicate.current);
|
|
@@ -723,7 +742,7 @@ function SpagbolCanvasInner({
|
|
|
723
742
|
(node): FlowNode => ({
|
|
724
743
|
id: nodeFlowId(node.id),
|
|
725
744
|
type: "spagbolNode",
|
|
726
|
-
position: node.position,
|
|
745
|
+
position: nodeDrag.current?.get(node.id) ?? node.position,
|
|
727
746
|
selected: snapshot.selection.nodeIds.includes(node.id),
|
|
728
747
|
hidden: collapsed.nodes.has(node.id),
|
|
729
748
|
draggable:
|
|
@@ -742,6 +761,7 @@ function SpagbolCanvasInner({
|
|
|
742
761
|
deletable: false,
|
|
743
762
|
dragHandle: ".spagbol-canvas-drag-handle",
|
|
744
763
|
data: {
|
|
764
|
+
positionRevision,
|
|
745
765
|
node: duplicatePresentation?.originalNodes.get(node.id) ?? node,
|
|
746
766
|
projected: collapsed.projected.has(node.id),
|
|
747
767
|
stationaryOriginal: duplicatePresentation?.originalNodes.has(
|
|
@@ -770,6 +790,7 @@ function SpagbolCanvasInner({
|
|
|
770
790
|
],
|
|
771
791
|
[
|
|
772
792
|
renderGraph,
|
|
793
|
+
positionRevision,
|
|
773
794
|
duplicatePresentation,
|
|
774
795
|
snapshot.selection,
|
|
775
796
|
snapshot.mode,
|
|
@@ -836,6 +857,7 @@ function SpagbolCanvasInner({
|
|
|
836
857
|
collapsed,
|
|
837
858
|
flow,
|
|
838
859
|
measurementKey,
|
|
860
|
+
measurementRevision,
|
|
839
861
|
settledViewport.zoom,
|
|
840
862
|
editorSize,
|
|
841
863
|
props.panBounds,
|
|
@@ -1181,7 +1203,12 @@ function SpagbolCanvasInner({
|
|
|
1181
1203
|
setDuplicatePreview(next);
|
|
1182
1204
|
return;
|
|
1183
1205
|
}
|
|
1184
|
-
if (
|
|
1206
|
+
if (cancelledNodeDrag.current) return;
|
|
1207
|
+
if (positions.length && nodeDrag.current) {
|
|
1208
|
+
const next = previewNodePositions(nodeDrag.current, positions);
|
|
1209
|
+
if (next !== nodeDrag.current) nodeDrag.current = next;
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1185
1212
|
if (positions.length)
|
|
1186
1213
|
editor.edit("Move nodes", "layout", (graph) => {
|
|
1187
1214
|
for (const change of positions)
|
|
@@ -1362,7 +1389,8 @@ function SpagbolCanvasInner({
|
|
|
1362
1389
|
const command = event.metaKey || event.ctrlKey,
|
|
1363
1390
|
key = event.key.toLowerCase();
|
|
1364
1391
|
if (key === "escape") {
|
|
1365
|
-
if (duplicate.current)
|
|
1392
|
+
if (duplicate.current || nodeDrag.current) cancelledNodeDrag.current = true;
|
|
1393
|
+
if (nodeDrag.current) clearNodeDrag();
|
|
1366
1394
|
duplicate.current = null;
|
|
1367
1395
|
setDuplicatePreview(null);
|
|
1368
1396
|
editor.cancelGesture();
|
|
@@ -1539,7 +1567,8 @@ function SpagbolCanvasInner({
|
|
|
1539
1567
|
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
1540
1568
|
}}
|
|
1541
1569
|
onPointerCancel={() => {
|
|
1542
|
-
if (duplicate.current)
|
|
1570
|
+
if (duplicate.current || nodeDrag.current) cancelledNodeDrag.current = true;
|
|
1571
|
+
if (nodeDrag.current) clearNodeDrag();
|
|
1543
1572
|
duplicate.current = null;
|
|
1544
1573
|
setDuplicatePreview(null);
|
|
1545
1574
|
setDrawing(null);
|
|
@@ -1569,10 +1598,11 @@ function SpagbolCanvasInner({
|
|
|
1569
1598
|
}}
|
|
1570
1599
|
onNodeDragStart={(event, node) => {
|
|
1571
1600
|
if (!node.data.node) return;
|
|
1572
|
-
|
|
1601
|
+
cancelledNodeDrag.current = false;
|
|
1573
1602
|
editor.beginGesture(
|
|
1574
1603
|
event.altKey ? "Duplicate nodes" : "Move nodes",
|
|
1575
1604
|
);
|
|
1605
|
+
if (!event.altKey) nodeDrag.current = new Map();
|
|
1576
1606
|
if (event.altKey) {
|
|
1577
1607
|
const selection = editor.getSnapshot().selection;
|
|
1578
1608
|
const ids = selection.nodeIds.includes(node.data.node.id)
|
|
@@ -1594,11 +1624,13 @@ function SpagbolCanvasInner({
|
|
|
1594
1624
|
}
|
|
1595
1625
|
}}
|
|
1596
1626
|
onNodeDragStop={(_event, node) => {
|
|
1627
|
+
const positions = nodeDrag.current;
|
|
1628
|
+
if (positions) clearNodeDrag();
|
|
1597
1629
|
const current = duplicate.current;
|
|
1598
1630
|
duplicate.current = null;
|
|
1599
1631
|
setDuplicatePreview(null);
|
|
1600
|
-
if (
|
|
1601
|
-
|
|
1632
|
+
if (cancelledNodeDrag.current) {
|
|
1633
|
+
cancelledNodeDrag.current = false;
|
|
1602
1634
|
editor.cancelGesture();
|
|
1603
1635
|
editor.select(editor.getSnapshot().selection);
|
|
1604
1636
|
return;
|
|
@@ -1620,6 +1652,8 @@ function SpagbolCanvasInner({
|
|
|
1620
1652
|
return;
|
|
1621
1653
|
}
|
|
1622
1654
|
}
|
|
1655
|
+
if (positions?.size)
|
|
1656
|
+
editor.edit("Move nodes", "layout", graph => applyNodePositions(graph, positions));
|
|
1623
1657
|
editor.commitGesture();
|
|
1624
1658
|
}}
|
|
1625
1659
|
onConnectStart={(_event, params) => {
|
|
@@ -39,15 +39,16 @@ export interface SpagbolNodePortProps {
|
|
|
39
39
|
/** Also usable in a completely custom node renderer. */
|
|
40
40
|
export function SpagbolNodePort({ port, connectable = true, style, position, className }: SpagbolNodePortProps) {
|
|
41
41
|
const { editor } = useSpagbol();
|
|
42
|
-
const selection = useEditorSelector(editor, snapshot => snapshot.selection);
|
|
43
42
|
const mode = useEditorSelector(editor, snapshot => snapshot.mode);
|
|
44
43
|
const flowId = useNodeId();
|
|
45
|
-
const selectedLink = mode === 'edit' && selection.linkIds.length === 1
|
|
46
|
-
? editor.graph.links.find(link => link.id === selection.linkIds[0]) : undefined;
|
|
47
44
|
const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;
|
|
48
|
-
const reconnecting =
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
const reconnecting = useEditorSelector(editor, snapshot => {
|
|
46
|
+
if (snapshot.mode !== 'edit' || snapshot.selection.linkIds.length !== 1) return false;
|
|
47
|
+
const link = graphAt(snapshot.document, snapshot.path).links.find(link => link.id === snapshot.selection.linkIds[0]);
|
|
48
|
+
return !!link && (port.direction === 'output'
|
|
49
|
+
? link.source.nodeId === nodeId && link.source.portId === port.id
|
|
50
|
+
: link.target.type === 'port' && link.target.nodeId === nodeId && link.target.portId === port.id);
|
|
51
|
+
});
|
|
51
52
|
const direction = port.direction;
|
|
52
53
|
return (
|
|
53
54
|
<Handle
|
|
@@ -56,7 +57,7 @@ export function SpagbolNodePort({ port, connectable = true, style, position, cla
|
|
|
56
57
|
position={position ?? (direction === "input" ? Position.Left : Position.Right)}
|
|
57
58
|
className={`spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? ""}`}
|
|
58
59
|
isConnectable={
|
|
59
|
-
|
|
60
|
+
mode === "edit" && port.connectable !== false && connectable
|
|
60
61
|
}
|
|
61
62
|
style={style}
|
|
62
63
|
aria-label={`${port.label} ${direction}`}
|
|
@@ -85,7 +86,8 @@ export function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {
|
|
|
85
86
|
const [pointerOver, setPointerOver] = useState(false);
|
|
86
87
|
const updateInternals = useUpdateNodeInternals();
|
|
87
88
|
const flowId = useNodeId();
|
|
88
|
-
const
|
|
89
|
+
const singleSelected = useEditorSelector(editor, snapshot => snapshot.selection.nodeIds.length === 1 && snapshot.selection.nodeIds[0] === node.id);
|
|
90
|
+
const selectedPortId = useEditorSelector(editor, snapshot => snapshot.selection.port?.nodeId === node.id ? snapshot.selection.port.portId : undefined);
|
|
89
91
|
const mode = useEditorSelector(editor, snapshot => snapshot.mode);
|
|
90
92
|
const path = useEditorSelector(editor, snapshot => snapshot.path);
|
|
91
93
|
const boundary = isGroupBoundary(node);
|
|
@@ -93,7 +95,7 @@ export function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {
|
|
|
93
95
|
const ports = portsFor(node, editor.nodes);
|
|
94
96
|
const inputPorts = ports.filter((port) => port.direction === "input" && props.portVisible?.(port) !== false);
|
|
95
97
|
const outputPorts = ports.filter((port) => port.direction === "output" && props.portVisible?.(port) !== false);
|
|
96
|
-
const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected &&
|
|
98
|
+
const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && singleSelected) && !(connecting && pointerOver);
|
|
97
99
|
const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;
|
|
98
100
|
const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;
|
|
99
101
|
const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);
|
|
@@ -140,7 +142,7 @@ export function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {
|
|
|
140
142
|
style={{ gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }}
|
|
141
143
|
>
|
|
142
144
|
{!projected && !props.hidePorts && <SpagbolNodePort port={port} />}
|
|
143
|
-
{props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={
|
|
145
|
+
{props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={selectedPortId === port.id} onChange={label => { try { editor.renameBoundaryPort(node.id, port.id, label); } catch { /* Keep the valid label. */ } }} onPointerDown={event => {
|
|
144
146
|
if (!editor.editing || props.readOnly || event.button !== 0) return;
|
|
145
147
|
event.preventDefault(); event.stopPropagation();
|
|
146
148
|
editor.select({ nodeIds: [node.id], port: { nodeId: node.id, portId: port.id, direction: port.direction } });
|
|
@@ -274,7 +276,6 @@ export function useNodeViewProps(
|
|
|
274
276
|
const { editor, projection } = useSpagbol();
|
|
275
277
|
const owner = projection?.groupId ?? groupId;
|
|
276
278
|
const controlPath = projection?.path ?? [node.id];
|
|
277
|
-
const selection = useEditorSelector(editor, snapshot => snapshot.selection);
|
|
278
279
|
const mode = useEditorSelector(editor, snapshot => snapshot.mode);
|
|
279
280
|
const path = useEditorSelector(editor, snapshot => snapshot.path);
|
|
280
281
|
return useMemo(
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Node, NodeChange } from "@xyflow/react";
|
|
2
|
+
import type { GraphDocument, Point } from "../core/index.js";
|
|
3
|
+
|
|
4
|
+
/** Accumulate only changed native node positions; keep model records immutable. */
|
|
5
|
+
export function previewNodePositions<N extends Node>(
|
|
6
|
+
previous: ReadonlyMap<string, Point>,
|
|
7
|
+
changes: readonly NodeChange<N>[],
|
|
8
|
+
): ReadonlyMap<string, Point> {
|
|
9
|
+
let next: Map<string, Point> | undefined;
|
|
10
|
+
for (const change of changes) {
|
|
11
|
+
if (change.type !== "position" || !change.position || !change.id.startsWith("node:"))
|
|
12
|
+
continue;
|
|
13
|
+
const { x, y } = change.position;
|
|
14
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
|
|
15
|
+
const id = change.id.slice(5);
|
|
16
|
+
const before = (next ?? previous).get(id);
|
|
17
|
+
if (before?.x === x && before.y === y) continue;
|
|
18
|
+
next ??= new Map(previous);
|
|
19
|
+
next.set(id, { x, y });
|
|
20
|
+
}
|
|
21
|
+
return next ?? previous;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Apply a completed multi-node drag as one atomic controller edit. */
|
|
25
|
+
export function applyNodePositions(
|
|
26
|
+
graph: GraphDocument,
|
|
27
|
+
positions: ReadonlyMap<string, Point>,
|
|
28
|
+
): void {
|
|
29
|
+
for (const node of graph.nodes) {
|
|
30
|
+
const position = positions.get(node.id);
|
|
31
|
+
if (position) node.position = { ...position };
|
|
32
|
+
}
|
|
33
|
+
}
|