- 명세 항목 꼴 「글」(kind text · 길이 상한 TEXT_MAX 100 · 비우기 · 이름 표 · M02 이름 나무 꼴 「글」)
- 다 빈 선택 인스턴스도 첫 칸을 빈 글로 남겨 자리 지킴 — 잇기 · 집계 줄 · 다시 열기
- 기타 = 선택 컨테이너 「상세 내용」(비고 글 · etc_detail_{n}_note · 잇기 getcdetail{n}) · 종류 단위 잇기 줄 가림 규칙 그대로
- 집계 모음: 기타 건 이름(그룹) = 상세 종류 이름 · 비면 「기타」 · 컨테이너 건 = 이름 + n · 서버 컨테이너 표에 기타 짝
- 시험: test_52_14_etc_text · 헬퍼 etc · ui_checks 52-10 에 기타 컨테이너 단계 · 52-1 스크립트는 보이는 명세 폼을 잡음
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
/* =============================================================================
|
|
* M02_SO_Def_Spec_Model.ts
|
|
* 구조물 명세(PLAN 52 · `design_structure_spec`) 이름 고치기(52-11)의 자료 손질 — 화면 그리기 없음.
|
|
*
|
|
* · 명세 = 구조 정본(키 · 컨테이너 · 필수/선택 · 꼴) — 여기서는 **이름만** 고침
|
|
* · 이름 자리 = 정의 `종류.{종류}.이름`(키 `{컨테이너}` · `{컨테이너}.{항목}` · `{컨테이너}.{항목}={값}`)
|
|
* · 필수 컨테이너 제목 = 이름 표에 없으면 정의 그룹(`그룹.{종류}` 의 `id = link`) 이름 — B05 그리기
|
|
* (`containerTitle`)와 같은 차례로 읽고 그 자리에 씀
|
|
* import 는 꼴만 — 시험이 이 파일만 옮겨 돌림.
|
|
* ========================================================================== */
|
|
|
|
import type { StructureSpec } from "../../A00_Common/design/design_structure_spec";
|
|
import type { StructDef } 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" | "text" | "auto" | null;
|
|
/** 필수 컨테이너 제목이 정의 그룹 이름에서 옴 — 고치면 그 그룹 이름에 씀 */
|
|
group: 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<string>();
|
|
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,
|
|
});
|
|
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,
|
|
});
|
|
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,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return rows;
|
|
}
|
|
|
|
/** 이름 하나를 사본에 씀 — 그룹에서 온 제목은 그 그룹 이름 · 나머지는 이름 표. */
|
|
export function setSpecName(def: StructDef, typeId: string, row: SpecNameRow, value: string): void {
|
|
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;
|
|
}
|