/* ============================================================================= * M02_SO_Def_Layout.ts * 구조물 정의 편집 화면(PLAN 48-2) — 고른 종류의 **배치 격자**(B05 · B06 좌측 구조물 칸 = 두 칸) * 옵션마다 줄 · 칸 · 너비 + 미리보기(기준 측점은 맨 위 한 줄 · 겹치면 빨강). * 자리를 안 정한 옵션은 차례대로 빈 칸을 채움 · 숫자를 고치면 그 옵션 자리만 정의 `배치` 에 남음. * ========================================================================== */ import { el } from "@ui/ui_template_elements"; import { baseShown, GRID_COLS, live, overlaps, placeAll, type Spot, type StructDef, type TypeDef, } from "./M02_SO_Def_Model"; import { head, numberBox } from "./M02_SO_Def_Ui"; import { to } from "./M02_SO_Text"; export interface LayoutBlockOptions { def: StructDef; type: TypeDef; edit: boolean; changed: () => void; } const ROW_MAX = 60; /** 미리보기 — 두 칸 격자 · 옵션마다 상자 */ function preview(def: StructDef, type: TypeDef, cells: Map): HTMLElement { const bad = new Set(overlaps(cells)); const base = baseShown(def, type.type_id); const offset = base ? 1 : 0; const boxes: HTMLElement[] = []; if (base) boxes.push( el("div", { className: "m02-sod__cell is-base", text: to("BaseStation"), attrs: { style: `grid-row: 1; grid-column: 1 / span ${GRID_COLS}` }, }), ); for (const o of live(type.options)) { const c = cells.get(o.key)!; boxes.push( el("div", { className: `m02-sod__cell${bad.has(o.key) ? " is-bad" : ""}`, text: o.label, attrs: { "data-sod-cell": o.key, title: o.label, style: `grid-row: ${c.줄 + offset}; grid-column: ${c.칸} / span ${c.너비}`, }, }), ); } return el("div", { className: "m02-sod__grid", attrs: { "data-sod-preview": "", style: `grid-template-columns: repeat(${GRID_COLS}, minmax(0, 1fr))`, }, children: boxes, }); } export function buildLayoutBlock(opt: LayoutBlockOptions): HTMLElement { const { def, type, edit, changed } = opt; const cells = placeAll(type.options, def.배치[type.type_id]); const rows = live(type.options).map((o) => { const c = cells.get(o.key)!; const fix = (patch: Partial): void => { (def.배치[type.type_id] ??= {})[o.key] = { ...c, ...patch }; changed(); }; return el("div", { className: "m02-sod__place", attrs: { "data-sod-place-row": o.key }, children: [ el("span", { className: "m02-sod__placename", text: o.label }), el("small", { text: to("GridRow") }), numberBox(c.줄, (n) => fix({ 줄: n }), { edit, min: 1, max: ROW_MAX, attr: "data-sod-row", }), el("small", { text: to("GridCol") }), numberBox(c.칸, (n) => fix({ 칸: n }), { edit, min: 1, max: GRID_COLS, attr: "data-sod-col", }), el("small", { text: to("GridWidth") }), numberBox(c.너비, (n) => fix({ 너비: n }), { edit, min: 1, max: GRID_COLS, attr: "data-sod-width", }), ], }); }); return el("div", { className: "m02-sod__block", children: [ ...head(to("LayoutHead"), to("LayoutNote")), el("div", { className: "m02-sod__layout", children: [ el("div", { className: "m02-sod__places", children: rows }), preview(def, type, cells), ], }), ], }); }