Add yaml compositino

This commit is contained in:
2026-03-05 23:57:46 +01:00
parent 8e5147cfd1
commit 48b9d29114
3 changed files with 194 additions and 3 deletions

View File

@@ -42,6 +42,10 @@ const initialEdges: Edge[] = [{ id: 'e-config-render', source: 'config-1', targe
export default function App() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
const [rfInstance, setRfInstance] = React.useState<any | null>(null)
const wrapperRef = React.useRef<HTMLDivElement | null>(null)
const [contextMenu, setContextMenu] = React.useState<null | { x: number; y: number; clientX: number; clientY: number }>(null)
const nodeTypes = React.useMemo(
() => ({ custom: CustomNode, config: ConfigNode, render: RenderingNode }),
@@ -53,8 +57,55 @@ export default function App() {
[setEdges]
)
const onInit = React.useCallback((instance: any) => {
setRfInstance(instance)
}, [])
const hideMenu = React.useCallback(() => setContextMenu(null), [])
const onCanvasContextMenu = React.useCallback((ev: React.MouseEvent) => {
ev.preventDefault()
const target = ev.target as HTMLElement
// don't show menu when right-clicking nodes or handles
if (target.closest('.react-flow__node') || target.closest('.react-flow__handle')) return
const rect = wrapperRef.current?.getBoundingClientRect()
const clientX = ev.clientX
const clientY = ev.clientY
const x = rect ? clientX - rect.left : ev.clientX
const y = rect ? clientY - rect.top : ev.clientY
setContextMenu({ x, y, clientX, clientY })
}, [])
const createNode = React.useCallback(
(type: string) => {
if (!rfInstance) return
if (!contextMenu) return
const { clientX, clientY } = contextMenu
const rect = wrapperRef.current?.getBoundingClientRect()
const point = rect ? { x: clientX - rect.left, y: clientY - rect.top } : { x: clientX, y: clientY }
let position = point
try {
// project to flow coords when possible
position = rfInstance.project ? rfInstance.project(point) : point
} catch (e) {
// ignore and use raw point
}
const id = genId()
const newNode: Node = {
id,
type: type === 'custom' ? 'custom' : type === 'config' ? 'config' : 'render',
position: { x: position.x, y: position.y },
data: type === 'config' ? { yaml: '# Enter YAML here\n', title: `config-${id}` } : {},
}
setNodes((nds) => nds.concat(newNode))
hideMenu()
},
[rfInstance, contextMenu, hideMenu, setNodes]
)
return (
<div className="reactflow-wrapper">
<div className="reactflow-wrapper" ref={wrapperRef} onContextMenu={onCanvasContextMenu} style={{ position: 'relative' }}>
<FlowContext.Provider value={{ nodes, setNodes, edges, setEdges }}>
<ReactFlow
nodes={nodes}
@@ -64,11 +115,35 @@ export default function App() {
onConnect={onConnect}
nodeTypes={nodeTypes}
fitView
onInit={onInit}
>
<Background />
<Controls />
<MiniMap />
</ReactFlow>
{contextMenu && (
<div
className="absolute bg-white border rounded shadow-md text-sm"
style={{ left: contextMenu.x, top: contextMenu.y, zIndex: 9999 }}
onMouseLeave={hideMenu}
>
<div className="p-2">
<div className="text-xs text-gray-700 mb-2">Create node</div>
<div className="space-y-1">
<button className="w-full text-left px-2 py-1 hover:bg-gray-100" onClick={() => createNode('config')}>
Configuration Node
</button>
<button className="w-full text-left px-2 py-1 hover:bg-gray-100" onClick={() => createNode('render')}>
Rendering Node
</button>
<button className="w-full text-left px-2 py-1 hover:bg-gray-100" onClick={() => createNode('custom')}>
Custom Node
</button>
</div>
</div>
</div>
)}
</FlowContext.Provider>
</div>
)