/* ============================================================================= * ui_template/cad_host/cad_host_area.ts * 작도 영역 숫자 칸 한 벌(169-1) — M02 도면 양식 · B07 도각 편집이 함께 쓴다. * * 칸 = 왼쪽 · 위 · 폭 · 높이(mm) · 저장 꼴은 그대로 [x0, y0, x1, y1](양식 `drawing_area`). * CAD 가이드 사각형 끌기(`cad_host` `onGuideChange`) ↔ 이 칸(`set`)이 한 벌로 움직인다. * ========================================================================== */ import { createInputField } from "@ui/ui_template_elements"; import type { CadGuideArea } from "./cad_host"; /** 작도 영역 칸이 없는 양식이 쓰는 값 — 서버 `Engine_Template._A1_INNER` 와 같다. */ export const DEFAULT_DRAWING_AREA: CadGuideArea = [42, 47, 812, 567]; /** 칸 이름 — 왼쪽 x · 위 y(윗변) · 폭 · 높이 */ const LABELS = ["왼쪽 (mm)", "위 (mm)", "폭 (mm)", "높이 (mm)"]; /** 0.1 mm 까지 — 끌기 값이 긴 소수로 안 보이게 */ const show = (value: number): string => String(Math.round(value * 10) / 10); /** [x0, y0, x1, y1] → 칸 값 [왼쪽, 위, 폭, 높이] */ export const areaToFields = ([x0, y0, x1, y1]: CadGuideArea): number[] => [ x0, y1, x1 - x0, y1 - y0, ]; /** 칸 값 [왼쪽, 위, 폭, 높이] → [x0, y0, x1, y1] · 폭 · 높이가 0 이하면 null */ export const fieldsToArea = ([left, top, width, height]: number[]): CadGuideArea | null => [left, top, width, height].every(Number.isFinite) && width > 0 && height > 0 ? [left, top - height, left + width, top] : null; export interface AreaFields { /** 칸 넷 */ element: HTMLElement; /** 지금 값 — 어긋나면 null(칸마다 까닭 표시) */ read: () => CadGuideArea | null; /** 끌기 · 다시 싣기가 바꾼 값 — 칸만 고침(`onInput` 안 부름) */ set: (area: CadGuideArea) => void; } export function createAreaFields( start: CadGuideArea, options: { readOnly?: boolean; onInput?: (area: CadGuideArea | null) => void } = {}, ): AreaFields { const element = document.createElement("div"); element.className = "cad-area-fields"; const fields = LABELS.map((label) => createInputField({ label, type: "number", value: "" })); element.append(...fields.map((field) => field.root)); const read = (): CadGuideArea | null => { const raws = fields.map((field) => field.input.value.trim()); const own = raws.map((raw) => raw === "" ? "값을 넣을 것" : Number.isFinite(Number(raw)) ? undefined : "숫자만", ); const area = own.some(Boolean) ? null : fieldsToArea(raws.map(Number)); // 모두 숫자인데 어긋나면 폭 · 높이 중 0 이하인 칸만 짚음 const size = (index: number): string | undefined => own.some(Boolean) || index < 2 || Number(raws[index]) > 0 ? undefined : "0 보다 커야 함"; fields.forEach((field, index) => field.setError(own[index] ?? size(index))); return area; }; const set = (area: CadGuideArea): void => { areaToFields(area).forEach((value, index) => (fields[index].input.value = show(value))); read(); }; for (const field of fields) { field.input.disabled = options.readOnly === true; field.input.addEventListener("input", () => options.onInput?.(read())); } set(start); return { element, read, set }; }