Files
Aislo/M02_MasterTemplete/StructOptions/M02_SO_Def_Ui.ts
T
eomsangdonandClaude Opus 5.5 09134bab84 refactor(M02): 띠 알림 · 빈 상태 · 보조 글 · 배지 · 탭 · 표 · 목록 줄 · 도구줄 · 체크를 부품으로 (PLAN 553-5)
- DataFlow: 표 넷 = 데이터표 · 함수 판 알림 · 선/알약 경고 · 끊긴 곳 = 띠 알림 · 알약 · 칸 종류 = 배지 · 고르는 줄 = 목록 줄 · 입력 넷 = 입력 · 도구줄 여섯 · 날 색 → 공통 값
- 구조물 옵션 · 구조물 · 도면 · 면적 · 품목 고르기: 판 띠 = 띠 알림 · 한눈/종류별/부품 = 탭 · 표 둘 = 데이터표 · 체크 둘 = 체크 · 카드 · 번호 = 카드 · 배지 · 도면 도구줄
- 일위대가 · 호표 · 실무자료 · 본 화면 · 서고: 알림 = 띠 알림 · 빈 상태 · 보조 글 · 도구줄 여섯 · 옛 보라 별칭 0
- M01 로직 입력칸 = 입력 부품(작은 칸) · 일위대가 상세 줄 수 고정 시험 693 · 지킴 시험 M02 몫 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 23:44:19 +09:00

126 lines
4.0 KiB
TypeScript

/* =============================================================================
* M02_SO_Def_Ui.ts
* 구조물 정의 편집 화면(PLAN 48-2)의 작은 입력 부품 — 글 · 숫자 · 체크 · 고르기 · 위아래 단추.
* 값은 받은 손잡이로 사본만 고침 · 글은 칸을 떠날 때(`change`) 다시 그림(초점 안 끊김).
* ========================================================================== */
import { createButton, el } from "@ui/ui_template_elements";
import { step } from "./M02_SO_Def_Model";
import { to } from "./M02_SO_Text";
import { createText } from "@ui/ui_template_text";
import { createCheckbox } from "@ui/ui_template_checkbox";
/** 글 칸 — 글자마다 `set` · 떠날 때 `done` */
export function textBox(
value: string,
set: (v: string) => void,
done: () => void,
opt: { edit: boolean; attr?: string; place?: string; cls?: string; max?: number },
): HTMLInputElement {
const box = el("input", {
className: `ui-input ui-input--sm ${opt.cls ?? ""}`,
attrs: {
type: "text",
maxlength: String(opt.max ?? 40),
...(opt.attr ? { [opt.attr]: "" } : {}),
},
}) as HTMLInputElement;
box.value = value;
box.placeholder = opt.place ?? "";
box.disabled = !opt.edit;
box.addEventListener("input", () => {
set(box.value);
box.classList.toggle("is-invalid", !box.value.trim() && !opt.place);
});
box.addEventListener("change", done);
return box;
}
/** 숫자 칸 — 범위 밖 · 빈 칸은 무름 */
export function numberBox(
value: number,
set: (v: number) => void,
opt: { edit: boolean; min: number; max: number; attr?: string },
): HTMLInputElement {
const box = el("input", {
className: "ui-input ui-input--sm m02-sod__num",
attrs: {
type: "number",
min: String(opt.min),
max: String(opt.max),
...(opt.attr ? { [opt.attr]: "" } : {}),
},
}) as HTMLInputElement;
box.value = String(value);
box.disabled = !opt.edit;
box.addEventListener("change", () => {
const n = Math.round(Number(box.value));
if (box.value === "" || !Number.isFinite(n) || n < opt.min || n > opt.max) {
box.value = String(value);
return;
}
set(n);
});
return box;
}
/** 체크 + 글 */
export function checkBox(
label: string,
on: boolean,
set: (v: boolean) => void,
opt: { edit: boolean; attr?: string },
): HTMLLabelElement {
// 체크 부품 — 속성(data-sod-*)은 칸에 그대로
const { root, input } = createCheckbox(label, {
checked: on,
disabled: !opt.edit,
onChange: set,
});
if (opt.attr) input.setAttribute(opt.attr, "");
root.classList.add("m02-sod__check");
return root;
}
/** 고르기 — [값, 글] 짝 */
export function pickBox(
pairs: readonly (readonly [string, string])[],
value: string,
set: (v: string) => void,
opt: { edit: boolean; attr?: string; cls?: string },
): HTMLSelectElement {
const box = el("select", {
className: `ui-input ui-input--sm ${opt.cls ?? ""}`,
attrs: opt.attr ? { [opt.attr]: "" } : {},
}) as HTMLSelectElement;
box.append(...pairs.map(([v, t]) => new Option(t, v)));
box.value = value;
box.disabled = !opt.edit;
box.addEventListener("change", () => set(box.value));
return box;
}
/** 위로 · 아래로 단추 둘 — 고치는 사람만 · 옮겼으면 `changed` */
export function moveButtons<T>(
list: T[],
item: T,
changed: () => void,
edit: boolean,
): HTMLElement[] {
if (!edit) return [];
const index = list.indexOf(item);
const make = (label: string, delta: number, off: boolean): HTMLButtonElement => {
const b = createButton({ label, variant: "ghost", size: "sm" });
b.disabled = off;
b.addEventListener("click", () => step(list, list.indexOf(item), delta) && changed());
return b;
};
return [make(to("GroupUp"), -1, index <= 0), make(to("GroupDown"), 1, index >= list.length - 1)];
}
/** 절 제목 + 안내 한 줄 */
export const head = (title: string, note?: string): HTMLElement[] => [
el("h4", { className: "m02-opts__group", text: title }),
...(note ? [createText(note)] : []),
];