112 lines
2.6 KiB
TypeScript
112 lines
2.6 KiB
TypeScript
'use client';
|
|
|
|
import { ChevronRight, ChevronDown } from 'lucide-react';
|
|
import { DOMAIN_TEXT_COLORS } from '@/lib/taxonomy/types';
|
|
|
|
interface TreeNodeProps {
|
|
code: string;
|
|
name: string;
|
|
count?: number;
|
|
isExpanded?: boolean;
|
|
isSelected?: boolean;
|
|
isLeaf?: boolean;
|
|
level: 'domain' | 'category' | 'subcode';
|
|
domainKey: string;
|
|
onToggle?: () => void;
|
|
onClick?: () => void;
|
|
searchMatch?: boolean;
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
export default function TreeNode({
|
|
code,
|
|
name,
|
|
count,
|
|
isExpanded = false,
|
|
isSelected = false,
|
|
isLeaf = false,
|
|
level,
|
|
domainKey,
|
|
onToggle,
|
|
onClick,
|
|
searchMatch = false,
|
|
children,
|
|
}: TreeNodeProps) {
|
|
const textColor = DOMAIN_TEXT_COLORS[domainKey] || 'text-gray-400';
|
|
|
|
const handleClick = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
if (isLeaf) {
|
|
onClick?.();
|
|
} else {
|
|
onToggle?.();
|
|
}
|
|
};
|
|
|
|
const handleChevronClick = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
onToggle?.();
|
|
};
|
|
|
|
const getPadding = () => {
|
|
switch (level) {
|
|
case 'domain':
|
|
return 'pl-2';
|
|
case 'category':
|
|
return 'pl-6';
|
|
case 'subcode':
|
|
return 'pl-10';
|
|
default:
|
|
return 'pl-2';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<div
|
|
onClick={handleClick}
|
|
className={`
|
|
flex items-center gap-2 py-1.5 px-2 rounded cursor-pointer transition-colors
|
|
${getPadding()}
|
|
${isSelected ? 'bg-gray-700' : 'hover:bg-gray-800/50'}
|
|
${searchMatch ? 'ring-1 ring-yellow-500/50' : ''}
|
|
`}
|
|
>
|
|
{/* Expand/Collapse Icon */}
|
|
{!isLeaf ? (
|
|
<button
|
|
onClick={handleChevronClick}
|
|
className="w-4 h-4 flex items-center justify-center text-gray-500 hover:text-gray-300"
|
|
>
|
|
{isExpanded ? (
|
|
<ChevronDown className="w-4 h-4" />
|
|
) : (
|
|
<ChevronRight className="w-4 h-4" />
|
|
)}
|
|
</button>
|
|
) : (
|
|
<span className="w-4 h-4 flex items-center justify-center text-gray-600">
|
|
<span className="w-1.5 h-1.5 rounded-full bg-current" />
|
|
</span>
|
|
)}
|
|
|
|
{/* Code */}
|
|
<span className={`font-mono text-sm ${textColor}`}>{code}</span>
|
|
|
|
{/* Name */}
|
|
<span className="text-sm text-gray-300 truncate flex-1">{name}</span>
|
|
|
|
{/* Count Badge */}
|
|
{count !== undefined && (
|
|
<span className="text-xs text-gray-500 bg-gray-800 px-1.5 py-0.5 rounded">
|
|
{count}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* Children (expanded content) */}
|
|
{isExpanded && children && <div>{children}</div>}
|
|
</div>
|
|
);
|
|
}
|