66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import React, { memo, useState, useCallback } from 'react';
|
|
import { type NodeProps } from '@xyflow/react';
|
|
import { ChevronDown, ChevronRight, FolderOpen } from 'lucide-react';
|
|
|
|
import type { ToolGroup } from '@mcpengine/ai-pipeline/types';
|
|
|
|
export interface GroupNodeData extends Record<string, unknown> {
|
|
group: ToolGroup;
|
|
childCount: number;
|
|
}
|
|
|
|
export const GroupNode = memo(function GroupNode({ data }: NodeProps) {
|
|
const { group, childCount } = data as GroupNodeData;
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
|
|
const toggleCollapse = useCallback((e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
setCollapsed((prev) => !prev);
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
className={`
|
|
bg-gray-800/30 border border-dashed border-gray-600 rounded-xl p-2
|
|
transition-all duration-200
|
|
${collapsed ? 'min-h-[56px]' : 'min-h-[120px]'}
|
|
`}
|
|
style={{ minWidth: 320 }}
|
|
>
|
|
{/* Header */}
|
|
<div
|
|
className="flex items-center gap-2 px-2 py-1.5 cursor-pointer select-none"
|
|
onClick={toggleCollapse}
|
|
>
|
|
<FolderOpen className="w-3.5 h-3.5 text-gray-500" />
|
|
<span className="text-xs uppercase text-gray-400 tracking-wider font-medium flex-1">
|
|
{group.name}
|
|
</span>
|
|
<span className="text-[10px] bg-gray-700/60 text-gray-400 px-1.5 py-0.5 rounded-full">
|
|
{childCount} tool{childCount !== 1 ? 's' : ''}
|
|
</span>
|
|
{collapsed ? (
|
|
<ChevronRight className="w-3.5 h-3.5 text-gray-500" />
|
|
) : (
|
|
<ChevronDown className="w-3.5 h-3.5 text-gray-500" />
|
|
)}
|
|
</div>
|
|
|
|
{/* Description (when expanded) */}
|
|
{!collapsed && group.description && (
|
|
<p className="px-2 pb-2 text-[11px] text-gray-500 leading-relaxed">
|
|
{group.description}
|
|
</p>
|
|
)}
|
|
|
|
{/* Child area — React Flow handles nested node rendering externally,
|
|
this is the visual container. Children are positioned by parentId in RF. */}
|
|
{!collapsed && (
|
|
<div className="min-h-[60px] rounded-lg" />
|
|
)}
|
|
</div>
|
|
);
|
|
});
|