You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

74 lines
2.1 KiB
TypeScript

// frontend/components/tasks/TreeNode.tsx
"use client";
import { Icon } from "@/components/Icon";
import { cx } from "@/lib/cx";
import type { Project } from "@/lib/types";
interface Props {
node: Project;
depth: number;
sel: string;
onSelect: (id: string) => void;
expanded: Record<string, boolean>;
onToggle: (id: string) => void;
onPin: (id: string) => void;
counts: Record<string, number>;
}
export function TreeNode({ node, depth, sel, onSelect, expanded, onToggle, onPin, counts }: Props) {
const hasCh = node.children.length > 0;
const open = !!expanded[node.id];
return (
<>
<div
className={cx("tree-row", sel === node.id && "on")}
style={{ paddingLeft: 8 + depth * 15 }}
onClick={() => onSelect(node.id)}
>
{hasCh ? (
<button
className={cx("tree-chev", open && "open")}
onClick={(e) => {
e.stopPropagation();
onToggle(node.id);
}}
aria-label="펼치기"
>
<Icon name="chev" />
</button>
) : (
<span className="tree-chev-spacer" />
)}
<span className="tree-dot" style={{ background: `var(--${node.tone})` }} />
<span className="tree-name">{node.name}</span>
{counts[node.id] > 0 && <span className="tree-count">{counts[node.id]}</span>}
<button
className={cx("tree-pin", node.pinned && "on")}
onClick={(e) => {
e.stopPropagation();
onPin(node.id);
}}
aria-label="즐겨찾기"
>
<Icon name="star" w={node.pinned ? 0 : 1.9} fill={node.pinned ? "currentColor" : "none"} />
</button>
</div>
{hasCh &&
open &&
node.children.map((c) => (
<TreeNode
key={c.id}
node={c}
depth={depth + 1}
sel={sel}
onSelect={onSelect}
expanded={expanded}
onToggle={onToggle}
onPin={onPin}
counts={counts}
/>
))}
</>
);
}