Files
invyone/frontend/components/control/ControlNode.tsx
T
2026-04-10 13:33:37 +09:00

104 lines
2.8 KiB
TypeScript

'use client';
import { useRef, useCallback } from 'react';
import { CTRL_NODE_TYPES, useControlMode } from './hooks/useControlMode';
import { PortHandle } from './PortHandle';
interface ControlNodeProps {
node: Record<string, any>;
onDragStart?: (nodeId: string, port: string, e: React.MouseEvent) => void;
onDragEnd?: (nodeId: string, port: string) => void;
}
/**
* 제어 노드 (16종) — mockup buildCtrlNode 포팅
*/
export function ControlNode({ node, onDragStart, onDragEnd }: ControlNodeProps) {
const { removeRuleNode, moveRuleNode, setConfigNodeId } = useControlMode();
const nodeRef = useRef<HTMLDivElement>(null);
const def = CTRL_NODE_TYPES[node.type];
if (!def) return null;
const outPorts = def.out || [{ port: 'out', label: '→', cls: '' }];
const handleHeadMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
const sx = e.clientX, sy = e.clientY;
const sl = node.x, st = node.y;
const el = nodeRef.current;
if (el) el.style.zIndex = '30';
const mv = (ev: MouseEvent) => {
moveRuleNode(node.id, sl + ev.clientX - sx, st + ev.clientY - sy);
};
const up = () => {
if (el) el.style.zIndex = '20';
document.removeEventListener('mousemove', mv);
document.removeEventListener('mouseup', up);
};
document.addEventListener('mousemove', mv);
document.addEventListener('mouseup', up);
}, [node.id, node.x, node.y, moveRuleNode]);
return (
<div
ref={nodeRef}
className="ctrl-action-node"
data-node-id={node.id}
data-node-type={node.type}
style={{
left: node.x,
top: node.y,
['--na-rgb' as string]: def.rgb,
}}
>
{/* Input 포트 */}
<PortHandle
nodeId={node.id}
port="in"
type="in"
onDragEnd={onDragEnd}
/>
{/* 헤더 */}
<div className="ctrl-an-head" onMouseDown={handleHeadMouseDown}>
<div className="ctrl-an-icon">{def.icon}</div>
<span className="ctrl-an-name">{def.label}</span>
<button
className="ctrl-an-del"
onClick={(e) => { e.stopPropagation(); removeRuleNode(node.id); }}
>
</button>
</div>
{/* 본문 */}
<div
className="ctrl-an-body"
onClick={() => setConfigNodeId(node.id)}
>
<div className="ctrl-an-summary">
{node.config?.summary || '클릭하여 설정'}
</div>
</div>
{/* Output 포트 */}
<div className="ctrl-an-ports-out">
{outPorts.map((p) => (
<PortHandle
key={p.port}
nodeId={node.id}
port={p.port}
type="out"
cls={p.cls}
label={p.label}
onDragStart={onDragStart}
/>
))}
</div>
</div>
);
}