@pauloricca/spagbol 0.1.0 → 0.1.3
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 +42 -4
- package/dist/core/controller.js +4 -4
- package/dist/core/controller.js.map +1 -1
- package/dist/core/model.d.ts +2 -1
- package/dist/core/model.d.ts.map +1 -1
- package/dist/core/model.js.map +1 -1
- package/dist/editors.css +157 -0
- package/dist/react/SpagbolCanvas.d.ts.map +1 -1
- package/dist/react/SpagbolCanvas.js +106 -29
- package/dist/react/SpagbolCanvas.js.map +1 -1
- package/dist/react/SpagbolCanvasControls.d.ts.map +1 -1
- package/dist/react/SpagbolCanvasControls.js +5 -5
- package/dist/react/SpagbolCanvasControls.js.map +1 -1
- package/dist/react/SpagbolControlJoystick.d.ts +9 -0
- package/dist/react/SpagbolControlJoystick.d.ts.map +1 -0
- package/dist/react/SpagbolControlJoystick.js +111 -0
- package/dist/react/SpagbolControlJoystick.js.map +1 -0
- package/dist/react/SpagbolControlZoom.d.ts.map +1 -1
- package/dist/react/SpagbolControlZoom.js +3 -2
- package/dist/react/SpagbolControlZoom.js.map +1 -1
- package/dist/react/SpagbolCurveEditor.d.ts +45 -0
- package/dist/react/SpagbolCurveEditor.d.ts.map +1 -0
- package/dist/react/SpagbolCurveEditor.js +142 -0
- package/dist/react/SpagbolCurveEditor.js.map +1 -0
- package/dist/react/SpagbolNodeFrame.d.ts.map +1 -1
- package/dist/react/SpagbolNodeFrame.js +18 -16
- package/dist/react/SpagbolNodeFrame.js.map +1 -1
- package/dist/react/SpagbolNodeFrameTypePicker.js +1 -1
- package/dist/react/SpagbolNodeFrameTypePicker.js.map +1 -1
- package/dist/react/SpagbolSequencer.d.ts +35 -0
- package/dist/react/SpagbolSequencer.d.ts.map +1 -0
- package/dist/react/SpagbolSequencer.js +166 -0
- package/dist/react/SpagbolSequencer.js.map +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -0
- package/dist/react/index.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/dist/react/zoom.d.ts +5 -0
- package/dist/react/zoom.d.ts.map +1 -0
- package/dist/react/zoom.js +19 -0
- package/dist/react/zoom.js.map +1 -0
- package/dist/styles.css +112 -41
- package/docs/language/standard-node-comparison.md +92 -0
- package/docs/language/standard-node-contracts.md +237 -0
- package/docs/language/standard-nodes.md +198 -0
- package/docs/language/ui-components.md +79 -0
- package/package.json +5 -3
- package/src/core/controller.ts +4 -4
- package/src/core/model.ts +2 -1
- package/src/react/SpagbolCanvas.tsx +121 -31
- package/src/react/SpagbolCanvasControls.tsx +2 -7
- package/src/react/SpagbolControlJoystick.tsx +147 -0
- package/src/react/SpagbolControlZoom.tsx +3 -2
- package/src/react/SpagbolCurveEditor.tsx +166 -0
- package/src/react/SpagbolNodeFrame.tsx +30 -24
- package/src/react/SpagbolNodeFrameTypePicker.tsx +1 -1
- package/src/react/SpagbolSequencer.tsx +265 -0
- package/src/react/editors.css +157 -0
- package/src/react/index.ts +4 -0
- package/src/react/nodeDrag.ts +33 -0
- package/src/react/styles.css +112 -41
- package/src/react/zoom.ts +18 -0
|
@@ -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 => {
|
|
@@ -107,13 +110,13 @@ export function SpagbolNodeFrame(props) {
|
|
|
107
110
|
event.preventDefault();
|
|
108
111
|
event.stopPropagation();
|
|
109
112
|
props.resetOverride();
|
|
110
|
-
} }, children: [!projected && !props.hidePorts && headerInput && _jsx(SpagbolNodePort, { port: headerInput, style: { top: "50%" } }), !projected && !props.hidePorts && headerOutput && _jsx(SpagbolNodePort, { port: headerOutput, style: { top: "50%" } }), _jsx("span", { className: "spagbol-node-frame-title", children: props.title ?? _jsx(SpagbolNodeFrameTypePicker, { node: node, compact: !!compact, selected: selected, disabled: projected || props.readOnly || !editor.editing }) }), !projected && _jsx("button", { className: "spagbol-node-frame-
|
|
111
|
-
event.stopPropagation();
|
|
112
|
-
editor.updateNode(node.id, { compact: !node.compact }, "layout");
|
|
113
|
-
}, children: node.compact ? "+" : "−" })), !projected && node.graph && features.nestedGraphs && (_jsx("button", { className: "spagbol-node-frame-header-button nodrag nopan", "aria-label": "Enter nested graph", title: "Enter nested graph", onClick: (event) => {
|
|
113
|
+
} }, children: [!projected && !props.hidePorts && headerInput && _jsx(SpagbolNodePort, { port: headerInput, style: { top: "50%" } }), !projected && !props.hidePorts && headerOutput && _jsx(SpagbolNodePort, { port: headerOutput, style: { top: "50%" } }), _jsx("span", { className: "spagbol-node-frame-title", children: props.title ?? _jsx(SpagbolNodeFrameTypePicker, { node: node, compact: !!compact, selected: selected, disabled: projected || props.readOnly || !editor.editing }) }), !projected && node.graph && features.nestedGraphs && (_jsx("button", { className: "spagbol-node-frame-header-button spagbol-node-frame-enter nodrag nopan", type: "button", onPointerDown: event => event.stopPropagation(), onDoubleClick: event => event.stopPropagation(), "aria-label": "Enter nested graph", title: "Enter nested graph", onClick: (event) => {
|
|
114
114
|
event.stopPropagation();
|
|
115
115
|
editor.enter(node.id);
|
|
116
|
-
}, children: "
|
|
116
|
+
}, children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "m9 6 6 6-6 6" }) }) })), !projected && _jsx("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'); }, children: _jsx("span", { className: "spagbol-node-frame-enabled-icon", "aria-hidden": "true" }) }), !projected && features.compact && !props.forceCompact && view?.compact !== false && (_jsx("button", { className: "spagbol-node-frame-header-button nodrag nopan", "aria-label": node.compact ? "Expand ports" : "Compact ports", title: node.compact ? "Expand ports" : "Compact ports", disabled: !editor.editing, onClick: (event) => {
|
|
117
|
+
event.stopPropagation();
|
|
118
|
+
editor.updateNode(node.id, { compact: !node.compact }, "layout");
|
|
119
|
+
}, children: node.compact ? "+" : "−" }))] }), _jsxs(_Fragment, { children: [children && (_jsx("div", { className: "spagbol-node-frame-body nodrag nopan", onPointerDown: (event) => event.stopPropagation(), children: children })), !props.hidePorts && (visibleInputs.length > 0 || visibleOutputs.length > 0 || newPortDirection) && (_jsxs("div", { style: inputPorts.length === 0 || outputPorts.length === 0 ? { gridTemplateColumns: "minmax(0, 1fr)" } : undefined, className: `spagbol-node-frame-ports${boundary ? " spagbol-node-frame-ports-boundary" : ""}`, children: [props.portsHeader, visibleInputs.map(row), visibleOutputs.map(row), newPortDirection && !projected && editor.editing && _jsxs("div", { style: { gridColumn: newPortDirection === "input" || inputPorts.length === 0 ? 1 : 2, gridRow: ports.length + 1 + (props.portsHeader ? 1 : 0), position: "relative" }, children: [_jsx(SpagbolNodePort, { port: { id: spagbolNewPortId, label: newPortLabel, direction: newPortDirection }, style: { top: "50%" } }), _jsx(SpagbolNodeGroupPortAdd, { label: newPortLabel, onAdd: label => { try {
|
|
117
120
|
editor.addBoundaryPort(node.id, label);
|
|
118
121
|
}
|
|
119
122
|
catch { /* Keep the valid boundary. */ } } })] })] })), !compact && props.portExtras, errors.length > 0 && (_jsx("div", { className: "spagbol-node-frame-diagnostics", role: "status", title: errors.map((error) => error.message).join("\n"), children: errors[0]?.message }))] }), props.overlay] }));
|
|
@@ -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 && node.graph && features.nestedGraphs && (\n <button\n className=\"spagbol-node-frame-header-button spagbol-node-frame-enter nodrag nopan\"\n type=\"button\"\n onPointerDown={event => event.stopPropagation()}\n onDoubleClick={event => event.stopPropagation()}\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 <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"m9 6 6 6-6 6\" />\n </svg>\n </button>\n )}\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 </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"]}
|
|
@@ -13,7 +13,7 @@ export function SpagbolNodeFrameTypePicker({ node, compact, selected, disabled }
|
|
|
13
13
|
const [highlight, setHighlight] = useState(0);
|
|
14
14
|
const input = useRef(null);
|
|
15
15
|
const menu = useRef(null);
|
|
16
|
-
const options = useMemo(() => [...editor.nodes.list().filter(item => !nodeKinds || nodeKinds.includes(item.kind)), ...(features.areas ? [{ kind: 'spagbol.area', label: '
|
|
16
|
+
const options = useMemo(() => [...editor.nodes.list().filter(item => !nodeKinds || nodeKinds.includes(item.kind)), ...(features.areas ? [{ kind: 'spagbol.area', label: 'Container' }] : [])].filter(item => `${item.kind} ${item.label}`.toLowerCase().includes(query.trim().toLowerCase())).sort((a, b) => Number(b.label.toLowerCase().startsWith(query.toLowerCase())) - Number(a.label.toLowerCase().startsWith(query.toLowerCase()))), [editor, query, nodeKinds, features.areas]);
|
|
17
17
|
useEffect(() => { if (!selected || disabled)
|
|
18
18
|
setOpen(false); }, [selected, disabled]);
|
|
19
19
|
useEffect(() => { if (open) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpagbolNodeFrameTypePicker.js","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrameTypePicker.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { GraphNode } from '../core/index.js';\nimport { useSpagbol } from './context.js';\n\n/** Teia's inline type-editing interaction, backed by the host's node catalogue. */\nexport function SpagbolNodeFrameTypePicker({ node, compact, selected, disabled }: { node: GraphNode; compact: boolean; selected: boolean; disabled: boolean }) {\n const { editor, nodeKinds, features, changeNodeKind } = useSpagbol();\n const typeLabel = node.label ?? editor.nodes.get(node.kind)?.label ?? node.kind;\n const label = compact && node.compactLabel?.trim() ? node.compactLabel : typeLabel;\n const press = useRef({ time: 0, compact: false });\n const [editingLabel, setEditingLabel] = useState(false);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(label);\n const [highlight, setHighlight] = useState(0);\n const input = useRef<HTMLInputElement>(null);\n const menu = useRef<HTMLDivElement>(null);\n const options = useMemo(() => [...editor.nodes.list().filter(item => !nodeKinds || nodeKinds.includes(item.kind)), ...(features.areas ? [{ kind: 'spagbol.area', label: 'Area' }] : [])].filter(item =>\n `${item.kind} ${item.label}`.toLowerCase().includes(query.trim().toLowerCase())\n ).sort((a, b) => Number(b.label.toLowerCase().startsWith(query.toLowerCase())) - Number(a.label.toLowerCase().startsWith(query.toLowerCase()))), [editor, query, nodeKinds, features.areas]);\n useEffect(() => { if (!selected || disabled) setOpen(false); }, [selected, disabled]);\n useEffect(() => { if (open) { input.current?.focus({ preventScroll: true }); input.current?.select(); } }, [open]);\n useEffect(() => { menu.current?.querySelector('[aria-selected=\"true\"]')?.scrollIntoView({ block: 'nearest' }); }, [highlight]);\n function commitLabel() { editor.updateNode(node.id, { compactLabel: query.trim() }, \"metadata\"); setOpen(false); }\n function choose(kind: string) { if (kind === 'spagbol.area') editor.convertNodeToArea(node.id); else if (changeNodeKind) void changeNodeKind(node.id, kind); else editor.changeNodeKind(node.id, kind); setOpen(false); }\n return <span className=\"spagbol-node-frame-type-picker\">\n {!open ? <button type=\"button\" className=\"spagbol-node-frame-type-picker-button\" onPointerDown={() => { const now = performance.now(); if (now - press.current.time > 500) press.current = { time: now, compact }; }} onDoubleClick={event => {\n event.preventDefault(); event.stopPropagation();\n if (disabled || !selected) return;\n const rename = press.current.compact; setEditingLabel(rename);\n setQuery(rename ? node.compactLabel ?? \"\" : typeLabel); setHighlight(0); setOpen(true);\n }}>{label}</button> : <>\n <span className=\"spagbol-node-frame-type-picker-placeholder\" aria-hidden=\"true\">{label}</span>\n <div className=\"spagbol-node-frame-type-picker-editor nodrag nopan nowheel\" onMouseDown={event => event.stopPropagation()}>\n <input ref={input} className=\"spagbol-node-frame-type-picker-input\" aria-label={editingLabel ? \"Node label\" : \"Node type\"} role=\"combobox\" aria-expanded=\"true\" aria-controls={`node-type-${node.id}`} aria-autocomplete=\"list\" value={query} spellCheck={false}\n onChange={event => { setQuery(event.target.value); setHighlight(0); }} onBlur={() => editingLabel ? commitLabel() : setOpen(false)}\n onPointerDown={event => event.stopPropagation()} onDoubleClick={event => event.stopPropagation()}\n onKeyDown={event => {\n event.stopPropagation();\n if (event.key === 'ArrowDown') { event.preventDefault(); setHighlight(i => Math.min(i + 1, Math.max(0, options.length - 1))); }\n if (event.key === 'ArrowUp') { event.preventDefault(); setHighlight(i => Math.max(0, i - 1)); }\n if (event.key === 'Enter') { event.preventDefault(); if (editingLabel) { commitLabel(); return; } const item = options[highlight] ?? options[0]; if (item) choose(item.kind); }\n if (event.key === 'Escape') { event.preventDefault(); setOpen(false); }\n }} />\n {!editingLabel && <div ref={menu} className=\"spagbol-node-frame-type-picker-menu nowheel\" id={`node-type-${node.id}`} role=\"listbox\" onMouseDown={event => { event.preventDefault(); event.stopPropagation(); }} onWheel={event => event.stopPropagation()}>\n {options.map((item, index) => <button key={item.kind} type=\"button\" role=\"option\" aria-selected={index === highlight} className={index === highlight ? 'active' : ''} onMouseDown={event => { event.preventDefault(); event.stopPropagation(); choose(item.kind); }}>{item.label}</button>)}\n {!options.length && <div className=\"spagbol-node-frame-type-picker-empty\">no match</div>}\n </div>}\n </div>\n </>}\n </span>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"SpagbolNodeFrameTypePicker.js","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrameTypePicker.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,mFAAmF;AACnF,MAAM,UAAU,0BAA0B,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAA+E;IAC3J,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,UAAU,EAAE,CAAC;IACrE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC;IAChF,MAAM,KAAK,GAAG,OAAO,IAAI,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IACnF,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAC1M,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAChF,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7L,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,IAAI,QAAQ;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtF,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC;QAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACnH,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,wBAAwB,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/H,SAAS,WAAW,KAAK,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAClH,SAAS,MAAM,CAAC,IAAY,IAAI,IAAI,IAAI,KAAK,cAAc;QAAE,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAAM,IAAI,cAAc;QAAE,KAAK,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;;QAAM,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzN,OAAO,eAAM,SAAS,EAAC,gCAAgC,YACpD,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAQ,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,uCAAuC,EAAC,aAAa,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG;gBAAE,KAAK,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE;gBAC3O,KAAK,CAAC,cAAc,EAAE,CAAC;gBAAC,KAAK,CAAC,eAAe,EAAE,CAAC;gBAChD,IAAI,QAAQ,IAAI,CAAC,QAAQ;oBAAE,OAAO;gBAClC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;gBAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBAC9D,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBAAC,YAAY,CAAC,CAAC,CAAC,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACzF,CAAC,YAAG,KAAK,GAAU,CAAC,CAAC,CAAC,8BACpB,eAAM,SAAS,EAAC,4CAA4C,iBAAa,MAAM,YAAE,KAAK,GAAQ,EAC9F,eAAK,SAAS,EAAC,4DAA4D,EAAC,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,aACvH,gBAAO,GAAG,EAAE,KAAK,EAAE,SAAS,EAAC,sCAAsC,gBAAa,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,EAAC,UAAU,mBAAe,MAAM,mBAAgB,aAAa,IAAI,CAAC,EAAE,EAAE,uBAAoB,MAAM,EAAC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAC7P,QAAQ,EAAE,KAAK,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAClI,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,EAChG,SAAS,EAAE,KAAK,CAAC,EAAE;gCACjB,KAAK,CAAC,eAAe,EAAE,CAAC;gCACxB,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;oCAAC,KAAK,CAAC,cAAc,EAAE,CAAC;oCAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gCAAC,CAAC;gCAC/H,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;oCAAC,KAAK,CAAC,cAAc,EAAE,CAAC;oCAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gCAAC,CAAC;gCAC/F,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;oCAAC,KAAK,CAAC,cAAc,EAAE,CAAC;oCAAC,IAAI,YAAY,EAAE,CAAC;wCAAC,WAAW,EAAE,CAAC;wCAAC,OAAO;oCAAC,CAAC;oCAAC,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;oCAAC,IAAI,IAAI;wCAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gCAAC,CAAC;gCAC/K,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;oCAAC,KAAK,CAAC,cAAc,EAAE,CAAC;oCAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gCAAC,CAAC;4BACzE,CAAC,GAAI,EACN,CAAC,YAAY,IAAI,eAAK,GAAG,EAAE,IAAI,EAAE,SAAS,EAAC,6CAA6C,EAAC,EAAE,EAAE,aAAa,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAC,SAAS,EAAC,WAAW,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,aACvP,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,iBAAwB,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAC,QAAQ,mBAAgB,KAAK,KAAK,SAAS,EAAE,SAAS,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAG,IAAI,CAAC,KAAK,IAArO,IAAI,CAAC,IAAI,CAAsO,CAAC,EAC1R,CAAC,OAAO,CAAC,MAAM,IAAI,cAAK,SAAS,EAAC,sCAAsC,yBAAe,IACpF,IACF,IACL,GACE,CAAC;AACV,CAAC","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { GraphNode } from '../core/index.js';\nimport { useSpagbol } from './context.js';\n\n/** Teia's inline type-editing interaction, backed by the host's node catalogue. */\nexport function SpagbolNodeFrameTypePicker({ node, compact, selected, disabled }: { node: GraphNode; compact: boolean; selected: boolean; disabled: boolean }) {\n const { editor, nodeKinds, features, changeNodeKind } = useSpagbol();\n const typeLabel = node.label ?? editor.nodes.get(node.kind)?.label ?? node.kind;\n const label = compact && node.compactLabel?.trim() ? node.compactLabel : typeLabel;\n const press = useRef({ time: 0, compact: false });\n const [editingLabel, setEditingLabel] = useState(false);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(label);\n const [highlight, setHighlight] = useState(0);\n const input = useRef<HTMLInputElement>(null);\n const menu = useRef<HTMLDivElement>(null);\n const options = useMemo(() => [...editor.nodes.list().filter(item => !nodeKinds || nodeKinds.includes(item.kind)), ...(features.areas ? [{ kind: 'spagbol.area', label: 'Container' }] : [])].filter(item =>\n `${item.kind} ${item.label}`.toLowerCase().includes(query.trim().toLowerCase())\n ).sort((a, b) => Number(b.label.toLowerCase().startsWith(query.toLowerCase())) - Number(a.label.toLowerCase().startsWith(query.toLowerCase()))), [editor, query, nodeKinds, features.areas]);\n useEffect(() => { if (!selected || disabled) setOpen(false); }, [selected, disabled]);\n useEffect(() => { if (open) { input.current?.focus({ preventScroll: true }); input.current?.select(); } }, [open]);\n useEffect(() => { menu.current?.querySelector('[aria-selected=\"true\"]')?.scrollIntoView({ block: 'nearest' }); }, [highlight]);\n function commitLabel() { editor.updateNode(node.id, { compactLabel: query.trim() }, \"metadata\"); setOpen(false); }\n function choose(kind: string) { if (kind === 'spagbol.area') editor.convertNodeToArea(node.id); else if (changeNodeKind) void changeNodeKind(node.id, kind); else editor.changeNodeKind(node.id, kind); setOpen(false); }\n return <span className=\"spagbol-node-frame-type-picker\">\n {!open ? <button type=\"button\" className=\"spagbol-node-frame-type-picker-button\" onPointerDown={() => { const now = performance.now(); if (now - press.current.time > 500) press.current = { time: now, compact }; }} onDoubleClick={event => {\n event.preventDefault(); event.stopPropagation();\n if (disabled || !selected) return;\n const rename = press.current.compact; setEditingLabel(rename);\n setQuery(rename ? node.compactLabel ?? \"\" : typeLabel); setHighlight(0); setOpen(true);\n }}>{label}</button> : <>\n <span className=\"spagbol-node-frame-type-picker-placeholder\" aria-hidden=\"true\">{label}</span>\n <div className=\"spagbol-node-frame-type-picker-editor nodrag nopan nowheel\" onMouseDown={event => event.stopPropagation()}>\n <input ref={input} className=\"spagbol-node-frame-type-picker-input\" aria-label={editingLabel ? \"Node label\" : \"Node type\"} role=\"combobox\" aria-expanded=\"true\" aria-controls={`node-type-${node.id}`} aria-autocomplete=\"list\" value={query} spellCheck={false}\n onChange={event => { setQuery(event.target.value); setHighlight(0); }} onBlur={() => editingLabel ? commitLabel() : setOpen(false)}\n onPointerDown={event => event.stopPropagation()} onDoubleClick={event => event.stopPropagation()}\n onKeyDown={event => {\n event.stopPropagation();\n if (event.key === 'ArrowDown') { event.preventDefault(); setHighlight(i => Math.min(i + 1, Math.max(0, options.length - 1))); }\n if (event.key === 'ArrowUp') { event.preventDefault(); setHighlight(i => Math.max(0, i - 1)); }\n if (event.key === 'Enter') { event.preventDefault(); if (editingLabel) { commitLabel(); return; } const item = options[highlight] ?? options[0]; if (item) choose(item.kind); }\n if (event.key === 'Escape') { event.preventDefault(); setOpen(false); }\n }} />\n {!editingLabel && <div ref={menu} className=\"spagbol-node-frame-type-picker-menu nowheel\" id={`node-type-${node.id}`} role=\"listbox\" onMouseDown={event => { event.preventDefault(); event.stopPropagation(); }} onWheel={event => event.stopPropagation()}>\n {options.map((item, index) => <button key={item.kind} type=\"button\" role=\"option\" aria-selected={index === highlight} className={index === highlight ? 'active' : ''} onMouseDown={event => { event.preventDefault(); event.stopPropagation(); choose(item.kind); }}>{item.label}</button>)}\n {!options.length && <div className=\"spagbol-node-frame-type-picker-empty\">no match</div>}\n </div>}\n </div>\n </>}\n </span>;\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface SpagbolSequencerItem {
|
|
3
|
+
id: number;
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
level: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SpagbolSequencerProps {
|
|
9
|
+
ariaLabel: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
minLevel?: number;
|
|
12
|
+
rows: number;
|
|
13
|
+
steps: number;
|
|
14
|
+
groupSize?: number;
|
|
15
|
+
currentStep?: number;
|
|
16
|
+
rowLabel: (row: number) => string;
|
|
17
|
+
renderRowLabel?: (row: number) => ReactNode;
|
|
18
|
+
labelPosition?: 'left' | 'right';
|
|
19
|
+
onRowRef?: (row: number, element: HTMLDivElement | null) => void;
|
|
20
|
+
itemsForRow: (row: number) => SpagbolSequencerItem[];
|
|
21
|
+
mode?: 'gate' | 'trigger';
|
|
22
|
+
defaultItemLength?: number;
|
|
23
|
+
allowMove?: boolean;
|
|
24
|
+
allowResize?: boolean;
|
|
25
|
+
allowLevel?: boolean;
|
|
26
|
+
onCreateItem: (row: number, start: number, end: number) => void;
|
|
27
|
+
onCreateItems?: (row: number, gates: Array<{
|
|
28
|
+
start: number;
|
|
29
|
+
end: number;
|
|
30
|
+
}>) => void;
|
|
31
|
+
onUpdateItem: (row: number, id: number, update: Partial<Pick<SpagbolSequencerItem, 'start' | 'end' | 'level'>>) => void;
|
|
32
|
+
onDeleteItem: (row: number, id: number) => void;
|
|
33
|
+
}
|
|
34
|
+
export declare function SpagbolSequencer({ ariaLabel, className, minLevel, rows, steps, groupSize, currentStep, rowLabel, renderRowLabel, labelPosition, onRowRef, itemsForRow, mode, defaultItemLength, allowMove, allowResize, allowLevel, onCreateItem, onCreateItems, onUpdateItem, onDeleteItem, }: SpagbolSequencerProps): import("react").JSX.Element;
|
|
35
|
+
//# sourceMappingURL=SpagbolSequencer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpagbolSequencer.d.ts","sourceRoot":"","sources":["../../src/react/SpagbolSequencer.tsx"],"names":[],"mappings":"AACA,OAAO,EAAsE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE3G,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACjE,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,oBAAoB,EAAE,CAAC;IACrD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IACpF,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;IACxH,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,SAAc,EACd,QAAc,EACd,IAAI,EACJ,KAAK,EACL,SAAa,EACb,WAAW,EACX,QAAQ,EACR,cAAc,EACd,aAAsB,EACtB,QAAQ,EACR,WAAW,EACX,IAAa,EACb,iBAAqB,EACrB,SAAgB,EAChB,WAAkB,EAClB,UAAiB,EACjB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,YAAY,GACb,EAAE,qBAAqB,+BA+MvB"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Controlled view projections and edit intents; no persisted sequence model or evaluator.
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
export function SpagbolSequencer({ ariaLabel, className = '', minLevel = 0.1, rows, steps, groupSize = 4, currentStep, rowLabel, renderRowLabel, labelPosition = 'left', onRowRef, itemsForRow, mode = 'gate', defaultItemLength = 1, allowMove = true, allowResize = true, allowLevel = true, onCreateItem, onCreateItems, onUpdateItem, onDeleteItem, }) {
|
|
5
|
+
const activeStep = Number.isFinite(currentStep) ? Math.floor(currentStep ?? -1) : -1;
|
|
6
|
+
const dragRef = useRef(null);
|
|
7
|
+
const createRef = useRef(null);
|
|
8
|
+
const [createPreview, setCreatePreview] = useState(null);
|
|
9
|
+
const positionInRow = (event) => {
|
|
10
|
+
const bounds = event.currentTarget.closest('.spagbol-sequencer-cells')?.getBoundingClientRect();
|
|
11
|
+
if (!bounds)
|
|
12
|
+
return 0;
|
|
13
|
+
return Math.max(0, Math.min(steps, ((event.clientX - bounds.left) / Math.max(1, bounds.width)) * steps));
|
|
14
|
+
};
|
|
15
|
+
const beginCreate = (event, row, step) => {
|
|
16
|
+
if (event.button !== 0)
|
|
17
|
+
return;
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
event.stopPropagation();
|
|
20
|
+
const existing = itemsForRow(row);
|
|
21
|
+
if (existing.some((gate) => step >= gate.start && step < gate.end))
|
|
22
|
+
return;
|
|
23
|
+
const nextStart = mode === 'gate'
|
|
24
|
+
? existing.filter((gate) => gate.start > step).reduce((boundary, gate) => Math.min(boundary, gate.start), steps)
|
|
25
|
+
: steps;
|
|
26
|
+
const end = mode === 'trigger' ? Math.min(steps, step + 1) : Math.min(nextStart, step + defaultItemLength);
|
|
27
|
+
if (end - step < 0.05)
|
|
28
|
+
return;
|
|
29
|
+
createRef.current = {
|
|
30
|
+
pointerId: event.pointerId,
|
|
31
|
+
row,
|
|
32
|
+
anchorX: event.clientX,
|
|
33
|
+
start: step,
|
|
34
|
+
end,
|
|
35
|
+
lastStep: step,
|
|
36
|
+
positions: new Set([step]),
|
|
37
|
+
moved: false,
|
|
38
|
+
};
|
|
39
|
+
setCreatePreview({ row, gates: [{ start: step, end }] });
|
|
40
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
41
|
+
};
|
|
42
|
+
const moveCreate = (event) => {
|
|
43
|
+
const create = createRef.current;
|
|
44
|
+
if (!create || create.pointerId !== event.pointerId)
|
|
45
|
+
return;
|
|
46
|
+
if (Math.abs(event.clientX - create.anchorX) >= 3)
|
|
47
|
+
create.moved = true;
|
|
48
|
+
if (mode === 'trigger') {
|
|
49
|
+
const nextStep = Math.max(0, Math.min(steps - 1, Math.floor(positionInRow(event))));
|
|
50
|
+
const direction = nextStep >= create.lastStep ? 1 : -1;
|
|
51
|
+
for (let step = create.lastStep; step !== nextStep + direction; step += direction)
|
|
52
|
+
create.positions.add(step);
|
|
53
|
+
create.lastStep = nextStep;
|
|
54
|
+
const occupied = itemsForRow(create.row);
|
|
55
|
+
const gates = [...create.positions]
|
|
56
|
+
.filter((step) => !occupied.some((gate) => Math.abs(gate.start - step) < 0.000001))
|
|
57
|
+
.sort((left, right) => left - right)
|
|
58
|
+
.map((step) => ({ start: step, end: Math.min(steps, step + 1) }));
|
|
59
|
+
setCreatePreview({ row: create.row, gates });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!create.moved)
|
|
63
|
+
return;
|
|
64
|
+
const nextStart = itemsForRow(create.row)
|
|
65
|
+
.filter((gate) => gate.start > create.start)
|
|
66
|
+
.reduce((boundary, gate) => Math.min(boundary, gate.start), steps);
|
|
67
|
+
create.end = Math.max(create.start + 0.05, Math.min(nextStart, positionInRow(event)));
|
|
68
|
+
setCreatePreview({ row: create.row, gates: [{ start: create.start, end: create.end }] });
|
|
69
|
+
};
|
|
70
|
+
const finishCreate = (pointerId) => {
|
|
71
|
+
const create = createRef.current;
|
|
72
|
+
if (!create || create.pointerId !== pointerId)
|
|
73
|
+
return;
|
|
74
|
+
const occupied = itemsForRow(create.row);
|
|
75
|
+
const gates = mode === 'trigger'
|
|
76
|
+
? [...create.positions]
|
|
77
|
+
.filter((step) => !occupied.some((gate) => Math.abs(gate.start - step) < 0.000001))
|
|
78
|
+
.sort((left, right) => left - right)
|
|
79
|
+
.map((step) => ({ start: step, end: Math.min(steps, step + 1) }))
|
|
80
|
+
: [{ start: create.start, end: create.end }];
|
|
81
|
+
if (onCreateItems)
|
|
82
|
+
onCreateItems(create.row, gates);
|
|
83
|
+
else
|
|
84
|
+
for (const gate of gates)
|
|
85
|
+
onCreateItem(create.row, gate.start, gate.end);
|
|
86
|
+
createRef.current = null;
|
|
87
|
+
setCreatePreview(null);
|
|
88
|
+
};
|
|
89
|
+
const beginDrag = (event, row, gate, action) => {
|
|
90
|
+
if (event.button !== 0)
|
|
91
|
+
return;
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
event.stopPropagation();
|
|
94
|
+
dragRef.current = { pointerId: event.pointerId, row, gate, x: event.clientX, y: event.clientY, action, moved: false };
|
|
95
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
96
|
+
};
|
|
97
|
+
const moveDrag = (event) => {
|
|
98
|
+
const drag = dragRef.current;
|
|
99
|
+
if (!drag || drag.pointerId !== event.pointerId)
|
|
100
|
+
return;
|
|
101
|
+
const bounds = event.currentTarget.closest('.spagbol-sequencer-cells')?.getBoundingClientRect();
|
|
102
|
+
const delta = ((event.clientX - drag.x) / Math.max(1, bounds?.width ?? steps * 26)) * steps;
|
|
103
|
+
const velocityDelta = (event.clientY - drag.y) / Math.max(1, bounds?.height ?? 26);
|
|
104
|
+
drag.moved ||= Math.hypot(event.clientX - drag.x, event.clientY - drag.y) >= 3;
|
|
105
|
+
if (!drag.moved)
|
|
106
|
+
return;
|
|
107
|
+
if (drag.action === 'level')
|
|
108
|
+
return onUpdateItem(drag.row, drag.gate.id, { level: Math.max(minLevel, Math.min(1, drag.gate.level - velocityDelta)) });
|
|
109
|
+
const length = drag.gate.end - drag.gate.start;
|
|
110
|
+
if (mode === 'trigger') {
|
|
111
|
+
const start = Math.max(0, Math.min(steps - 1, drag.gate.start + delta));
|
|
112
|
+
return onUpdateItem(drag.row, drag.gate.id, { start, end: Math.min(steps, start + 1) });
|
|
113
|
+
}
|
|
114
|
+
const otherGates = itemsForRow(drag.row).filter((gate) => gate.id !== drag.gate.id);
|
|
115
|
+
const previousEnd = otherGates
|
|
116
|
+
.filter((gate) => gate.start < drag.gate.start)
|
|
117
|
+
.reduce((boundary, gate) => Math.max(boundary, gate.end), 0);
|
|
118
|
+
const nextStart = otherGates
|
|
119
|
+
.filter((gate) => gate.start >= drag.gate.end)
|
|
120
|
+
.reduce((boundary, gate) => Math.min(boundary, gate.start), steps);
|
|
121
|
+
if (drag.action === 'move') {
|
|
122
|
+
const start = Math.max(previousEnd, Math.min(nextStart - length, drag.gate.start + delta));
|
|
123
|
+
return onUpdateItem(drag.row, drag.gate.id, { start, end: start + length });
|
|
124
|
+
}
|
|
125
|
+
if (drag.action === 'left')
|
|
126
|
+
return onUpdateItem(drag.row, drag.gate.id, { start: Math.max(previousEnd, Math.min(drag.gate.end - 0.05, drag.gate.start + delta)) });
|
|
127
|
+
onUpdateItem(drag.row, drag.gate.id, { end: Math.max(drag.gate.start + 0.05, Math.min(nextStart, drag.gate.end + delta)) });
|
|
128
|
+
};
|
|
129
|
+
const finishDrag = (pointerId) => {
|
|
130
|
+
const drag = dragRef.current;
|
|
131
|
+
if (!drag || drag.pointerId !== pointerId)
|
|
132
|
+
return;
|
|
133
|
+
if (!drag.moved && drag.action === 'move')
|
|
134
|
+
onDeleteItem(drag.row, drag.gate.id);
|
|
135
|
+
dragRef.current = null;
|
|
136
|
+
};
|
|
137
|
+
const endDrag = (event) => finishDrag(event.pointerId);
|
|
138
|
+
const cancelDrag = (pointerId) => {
|
|
139
|
+
if (dragRef.current?.pointerId === pointerId)
|
|
140
|
+
dragRef.current = null;
|
|
141
|
+
};
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
const finishPointer = (event) => {
|
|
144
|
+
finishCreate(event.pointerId);
|
|
145
|
+
finishDrag(event.pointerId);
|
|
146
|
+
};
|
|
147
|
+
const cancelPointer = (event) => {
|
|
148
|
+
if (createRef.current?.pointerId === event.pointerId) {
|
|
149
|
+
createRef.current = null;
|
|
150
|
+
setCreatePreview(null);
|
|
151
|
+
}
|
|
152
|
+
cancelDrag(event.pointerId);
|
|
153
|
+
};
|
|
154
|
+
window.addEventListener('pointerup', finishPointer);
|
|
155
|
+
window.addEventListener('pointercancel', cancelPointer);
|
|
156
|
+
return () => {
|
|
157
|
+
window.removeEventListener('pointerup', finishPointer);
|
|
158
|
+
window.removeEventListener('pointercancel', cancelPointer);
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
return (_jsx("div", { className: `spagbol-sequencer spagbol-sequencer-label-${labelPosition} ${className} nodrag nopan`, style: { '--spagbol-sequencer-steps': steps }, "aria-label": ariaLabel, onDoubleClick: (event) => event.stopPropagation(), children: Array.from({ length: rows }, (_, row) => (_jsxs("div", { className: "spagbol-sequencer-row", ref: (element) => onRowRef?.(row, element), children: [labelPosition === 'left' ? _jsx("div", { className: "spagbol-sequencer-label", children: renderRowLabel?.(row) ?? rowLabel(row) }) : null, _jsxs("div", { className: "spagbol-sequencer-cells", role: "row", children: [Array.from({ length: steps }, (_, step) => (_jsx("button", { className: ['spagbol-sequencer-cell', Math.floor(step / groupSize) % 2 === 1 ? 'spagbol-sequencer-cell-beat-highlight' : '', activeStep === step ? 'spagbol-sequencer-cell-current' : ''].filter(Boolean).join(' '), type: "button", "aria-label": `${rowLabel(row)}, step ${step + 1}`, "data-spagbol-sequencer-cell": "true", "data-row-index": row, "data-step-index": step, onPointerDown: (event) => beginCreate(event, row, step), onPointerMove: moveCreate, onPointerUp: (event) => { if (event.currentTarget.hasPointerCapture(event.pointerId))
|
|
162
|
+
event.currentTarget.releasePointerCapture(event.pointerId); finishCreate(event.pointerId); }, onPointerCancel: (event) => { createRef.current = null; setCreatePreview(null); if (event.currentTarget.hasPointerCapture(event.pointerId))
|
|
163
|
+
event.currentTarget.releasePointerCapture(event.pointerId); }, onLostPointerCapture: (event) => finishCreate(event.pointerId), onClick: (event) => { event.preventDefault(); event.stopPropagation(); } }, step))), createPreview?.row === row ? createPreview.gates.map((gate, index) => (_jsx("div", { className: "spagbol-sequencer-gate spagbol-sequencer-create-preview", style: { left: `${gate.start / steps * 100}%`, width: `${(gate.end - gate.start) / steps * 100}%` } }, `${gate.start}:${index}`))) : null, itemsForRow(row).map((gate) => (_jsxs("div", { className: "spagbol-sequencer-gate", style: { top: `${(1 - gate.level) * 100}%`, left: `${gate.start / steps * 100}%`, width: `${(gate.end - gate.start) / steps * 100}%` }, role: "button", tabIndex: 0, "aria-label": `${rowLabel(row)} ${mode} from ${gate.start.toFixed(2)} to ${gate.end.toFixed(2)}, level ${gate.level.toFixed(2)}`, onPointerDown: (event) => { if (allowMove && event.target === event.currentTarget)
|
|
164
|
+
beginDrag(event, row, gate, 'move'); }, onPointerMove: moveDrag, onPointerUp: endDrag, onPointerCancel: (event) => cancelDrag(event.pointerId), onLostPointerCapture: (event) => cancelDrag(event.pointerId), onClick: (event) => { event.preventDefault(); event.stopPropagation(); }, children: [allowLevel ? _jsx("span", { className: "spagbol-sequencer-level-handle", onPointerDown: (event) => beginDrag(event, row, gate, 'level'), onPointerMove: moveDrag, onPointerUp: endDrag, onPointerCancel: (event) => cancelDrag(event.pointerId) }) : null, allowResize ? _jsxs(_Fragment, { children: [_jsx("span", { className: "spagbol-sequencer-gate-handle spagbol-sequencer-gate-handle-left", onPointerDown: (event) => beginDrag(event, row, gate, 'left'), onPointerMove: moveDrag, onPointerUp: endDrag, onPointerCancel: (event) => cancelDrag(event.pointerId) }), _jsx("span", { className: "spagbol-sequencer-gate-handle spagbol-sequencer-gate-handle-right", onPointerDown: (event) => beginDrag(event, row, gate, 'right'), onPointerMove: moveDrag, onPointerUp: endDrag, onPointerCancel: (event) => cancelDrag(event.pointerId) })] }) : null] }, gate.id)))] }), labelPosition === 'right' ? _jsx("div", { className: "spagbol-sequencer-label", children: renderRowLabel?.(row) ?? rowLabel(row) }) : null] }, row))) }));
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=SpagbolSequencer.js.map
|