/* ============================================================================= * M01_MasterData_UI_Tree.ts * 왼쪽 패널 트리 — 부모(접기/펴기) · 자식(들여쓰기) · 줄 수 표시 * ========================================================================== */ import { el } from "@ui/ui_template_elements"; export interface TreeNode { id: string; label: string; /** 줄 수 — null 이면 안 보임 */ count: number | null; /** 눌렀을 때 — 없으면 접기/펴기만 */ run?: () => void; children?: TreeNode[]; /** 처음부터 펴짐 */ open?: boolean; } /** 줄(버튼) 만들기 — 돌려받은 것 = 그 버튼 · click 은 부르는 쪽이 이음 */ export type MakeRow = ( node: TreeNode, caret: string, depth: number, onClick: () => void, ) => HTMLElement; export function renderTree(nodes: TreeNode[], make: MakeRow, depth = 0): HTMLElement[] { return nodes.map((node) => { if (!node.children?.length) return make(node, "", depth, () => node.run?.()); const kids = el("div", { className: "m01-tree__kids", children: renderTree(node.children, make, depth + 1), }); kids.hidden = !node.open; const row = make(node, kids.hidden ? "▸" : "▾", depth, () => { kids.hidden = !kids.hidden; const caret = row.querySelector(".m01-tree__caret"); if (caret) caret.textContent = kids.hidden ? "▸" : "▾"; node.run?.(); }); return el("div", { className: "m01-tree__branch", children: [row, kids] }); }); } /** 파일 목록 → 원문 › 부문 › 장 — 장 이름이 「공통 03장 …」 이면 부문 「공통」 */ export function bookTree( items: T[], leaf: (item: T, division: string) => TreeNode, id: (parts: string[]) => string, count: (list: T[]) => number, ): TreeNode[] { const books = new Map>(); for (const it of items) { const division = /^(\S+) \d+장/.exec(it.chapter)?.[1] ?? ""; const divs = books.get(it.book ?? "") ?? new Map(); divs.set(division, [...(divs.get(division) ?? []), it]); books.set(it.book ?? "", divs); } return [...books].map(([book, divs]) => ({ id: id([book]), label: book, count: count([...divs.values()].flat()), children: [...divs].flatMap(([division, list]) => division ? [ { id: id([book, division]), label: division, count: count(list), children: list.map((it) => leaf(it, division)), }, ] : list.map((it) => leaf(it, "")), ), })); }