/* ============================================================================= * M02_SO_Def_Spec_Model.ts * 구조물 명세(PLAN 52 · `design_structure_spec`) 이름 고치기(52-11)의 자료 손질 — 화면 그리기 없음. * * · 명세 = 구조 정본(키 · 컨테이너 · 필수/선택 · 꼴) — 여기서는 **이름만** 고침 * · 이름 자리 = 정의 `종류.{종류}.이름`(키 `{컨테이너}` · `{컨테이너}.{항목}` · `{컨테이너}.{항목}={값}`) * · 필수 컨테이너 제목 = 이름 표에 없으면 정의 그룹(`그룹.{종류}` 의 `id = link`) 이름 — B05 그리기 * (`containerTitle`)와 같은 차례로 읽고 그 자리에 씀 * · 명세 없는 종류(정의 그룹 꼴 · 52-12) = 그룹 제목(`그룹.이름`) › 칸 이름(옵션 `label`) › * 선택지 이름(이름 표 키 `{옵션}={값}` · 값은 그대로) — 그룹 밖 칸은 맨 앞 머리 줄 아래 * import 는 꼴만 — 시험이 이 파일만 옮겨 돌림. * ========================================================================== */ import type { StructureSpec } from "../../A00_Common/design/design_structure_spec"; import type { StructDef, TypeDef } from "./M02_SO_Def_Model"; export interface SpecNameRow { /** 0 = 컨테이너 · 1 = 항목 · 2 = 값 */ level: 0 | 1 | 2; /** 이름 표 키 — 빈 글 = 고칠 이름 없는 머리 줄(그룹 밖 칸) */ nameKey: string; /** 읽기만 표시 — 컨테이너 id · 항목 id · 값 */ key: string; /** 지금 이름 */ label: string; /** 읽기만 표시 — 필수/선택 · 꼴 */ tag: "required" | "optional" | "select" | "number" | "auto" | "text" | "bool" | null; /** 필수 컨테이너 제목이 정의 그룹 이름에서 옴 — 고치면 그 그룹 이름에 씀 */ group: string | null; /** 정의 옵션 이름(`label`)에서 옴 — 고치면 그 옵션 이름에 씀 */ option: string | null; } /** 명세 차례대로 이름 줄 — 같은 이름 키(갈래마다 겹친 값)는 한 줄. */ export function specNameRows(def: StructDef, spec: StructureSpec): SpecNameRow[] { const names = def.종류[spec.typeId]?.이름 ?? {}; const groups = def.그룹[spec.typeId] ?? []; const rows: SpecNameRow[] = []; const seen = new Set(); const push = (row: SpecNameRow): void => { if (seen.has(row.nameKey)) return; seen.add(row.nameKey); rows.push(row); }; for (const c of spec.containers) { const group = c.required && names[c.id] === undefined ? groups.find((g) => g.id === c.link) : undefined; push({ level: 0, nameKey: c.id, key: c.id, label: names[c.id] ?? group?.이름 ?? c.title, tag: c.required ? "required" : "optional", group: group ? group.id : null, option: null, }); for (const item of c.items) { const itemKey = `${c.id}.${item.id}`; push({ level: 1, nameKey: itemKey, key: item.id, label: names[itemKey] ?? item.label, tag: item.kind, group: null, option: null, }); const choices = [ ...(item.choices ?? []), ...Object.values(item.by?.map ?? {}).flatMap((b) => b.choices ?? []), ]; for (const ch of choices) { const choiceKey = `${itemKey}=${ch.value}`; push({ level: 2, nameKey: choiceKey, key: ch.value, label: names[choiceKey] ?? ch.label, tag: null, group: null, option: null, }); } } } return rows; } /** 정의 그룹 꼴 종류(명세 없음) 이름 줄 — 그룹 밖 칸 · 그룹마다 제목 › 칸 › 선택지(B05 그리기 차례). */ export function groupNameRows(def: StructDef, type: TypeDef): SpecNameRow[] { const names = def.종류[type.type_id]?.이름 ?? {}; const options = type.options.filter((o) => o.enabled !== false); const groups = def.그룹[type.type_id] ?? []; const inGroup = new Set(groups.flatMap((g) => g.옵션)); const rows: SpecNameRow[] = []; const optionRows = (keys: readonly string[]): void => { for (const o of options.filter((x) => keys.includes(x.key))) { rows.push({ level: 1, nameKey: o.key, key: o.key, label: o.label, tag: o.input, group: null, option: o.key, }); for (const ch of o.input === "select" ? (o.choices ?? []) : []) { const nameKey = `${o.key}=${ch}`; rows.push({ level: 2, nameKey, key: ch, label: names[nameKey] ?? ch, tag: null, group: null, option: null, }); } } }; const loose = options.filter((o) => !inGroup.has(o.key)).map((o) => o.key); if (loose.length) { rows.push({ level: 0, nameKey: "", key: "", label: "", tag: null, group: null, option: null }); optionRows(loose); } for (const g of groups) { rows.push({ level: 0, nameKey: `#${g.id}`, key: g.id, label: g.이름, tag: null, group: g.id, option: null, }); optionRows(g.옵션); } return rows; } /** 이름 하나를 사본에 씀 — 그룹에서 온 제목은 그 그룹 이름 · 옵션 칸은 옵션 이름 · 나머지는 이름 표. */ export function setSpecName(def: StructDef, typeId: string, row: SpecNameRow, value: string): void { const option = row.option ? def.types.find((t) => t.type_id === typeId)?.options.find((o) => o.key === row.option) : undefined; if (option) { option.label = value; return; } const group = row.group ? def.그룹[typeId]?.find((g) => g.id === row.group) : undefined; if (group) { group.이름 = value; return; } const kind = (def.종류[typeId] ??= {}); (kind.이름 ??= {})[row.nameKey] = value; }