Files
Aislo/ui_template/cad_host/cad_host_area.ts
T
eomsangdonandClaude Opus 5.5 3e5b7cf21f feat(b07): 도면 작도 영역 가이드 선 · 숫자 칸 공용 부품 · 끌기 · 도각 편집 영역 저장 (169-1·169-3·169-4)
- 그림칸 = 양식 drawing_area 그대로(여백 2 % 없앰) · 도각 편집이 영역 싣고 [완료] 때 저장
- 도면 응답 guide(작도 영역 도면 좌표) · M02 구조물 도각 응답 area
- CAD 그리기 겹판 점선 사각형(저장 · DXF · 인쇄 제외) · 모서리 · 변 끌기(도각 편집 · M02 양식만)
- 숫자 칸 공용 부품(왼쪽 · 위 · 폭 · 높이 mm) — M02 양식 · B07 도각 편집 · 끌기와 한 벌
- B08 구조물도 · M02 도면 카드 = 얹은 도각 영역 가이드(보기만)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL
2026-10-10 08:35:31 +09:00

78 lines
3.3 KiB
TypeScript

/* =============================================================================
* 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 };
}