104 lines
2.8 KiB
TypeScript
104 lines
2.8 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 콤팩트 노드 공통 쉘
|
||
|
|
* 다크 캔버스에서 사용하는 공통 노드 래퍼
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { memo, ReactNode } from "react";
|
||
|
|
import { Handle, Position } from "reactflow";
|
||
|
|
|
||
|
|
interface CompactNodeShellProps {
|
||
|
|
color: string;
|
||
|
|
label: string;
|
||
|
|
summary?: string;
|
||
|
|
icon: ReactNode;
|
||
|
|
selected?: boolean;
|
||
|
|
children?: ReactNode;
|
||
|
|
hasInput?: boolean;
|
||
|
|
hasOutput?: boolean;
|
||
|
|
inputHandleId?: string;
|
||
|
|
outputHandleId?: string;
|
||
|
|
/** 커스텀 출력 핸들(ConditionNode 등)을 사용할 경우 true */
|
||
|
|
customOutputHandles?: boolean;
|
||
|
|
/** 커스텀 입력 핸들을 사용할 경우 true */
|
||
|
|
customInputHandles?: boolean;
|
||
|
|
minWidth?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const CompactNodeShell = memo(
|
||
|
|
({
|
||
|
|
color,
|
||
|
|
label,
|
||
|
|
summary,
|
||
|
|
icon,
|
||
|
|
selected = false,
|
||
|
|
children,
|
||
|
|
hasInput = true,
|
||
|
|
hasOutput = true,
|
||
|
|
inputHandleId,
|
||
|
|
outputHandleId,
|
||
|
|
customOutputHandles = false,
|
||
|
|
customInputHandles = false,
|
||
|
|
minWidth = "260px",
|
||
|
|
}: CompactNodeShellProps) => {
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className={`rounded-lg border bg-zinc-900 shadow-lg transition-all ${
|
||
|
|
selected
|
||
|
|
? "border-violet-500 shadow-violet-500/20"
|
||
|
|
: "border-zinc-700 hover:border-zinc-600"
|
||
|
|
}`}
|
||
|
|
style={{ minWidth, maxWidth: "320px" }}
|
||
|
|
>
|
||
|
|
{/* 기본 입력 핸들 */}
|
||
|
|
{hasInput && !customInputHandles && (
|
||
|
|
<Handle
|
||
|
|
type="target"
|
||
|
|
position={Position.Left}
|
||
|
|
id={inputHandleId}
|
||
|
|
className="!h-2.5 !w-2.5 !border-2 !bg-zinc-900"
|
||
|
|
style={{ borderColor: color }}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* 컬러바 + 헤더 */}
|
||
|
|
<div className="flex items-center gap-2.5 px-3 py-2.5">
|
||
|
|
<div
|
||
|
|
className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md"
|
||
|
|
style={{ backgroundColor: `${color}20` }}
|
||
|
|
>
|
||
|
|
<div className="text-zinc-200" style={{ color }}>{icon}</div>
|
||
|
|
</div>
|
||
|
|
<div className="min-w-0 flex-1">
|
||
|
|
<div className="text-xs font-semibold text-zinc-200">{label}</div>
|
||
|
|
{summary && (
|
||
|
|
<div className="line-clamp-2 text-[10px] leading-relaxed text-zinc-500">{summary}</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 바디 (옵셔널) */}
|
||
|
|
{children && (
|
||
|
|
<div className="border-t border-zinc-800 px-3 py-2 text-[10px] text-zinc-400">
|
||
|
|
{children}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* 기본 출력 핸들 */}
|
||
|
|
{hasOutput && !customOutputHandles && (
|
||
|
|
<Handle
|
||
|
|
type="source"
|
||
|
|
position={Position.Right}
|
||
|
|
id={outputHandleId}
|
||
|
|
className="!h-2.5 !w-2.5 !border-2 !bg-zinc-900"
|
||
|
|
style={{ borderColor: color }}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
},
|
||
|
|
);
|
||
|
|
|
||
|
|
CompactNodeShell.displayName = "CompactNodeShell";
|