- 옛 「옵션 보이기 · 그룹」 화면을 정의 편집 화면으로 넓힘(시스템 관리자 고쳐 씀 · 다른 역할 읽기만) - 자료 손질 = M02_SO_Def_Model.ts(정의 틀 48-1 꼴) · 시험 test_m02_so_def_model.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
/* =============================================================================
|
|
* 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<string, Spot>): 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<Spot>): 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),
|
|
],
|
|
}),
|
|
],
|
|
});
|
|
}
|