- 오른쪽 손가락 단추(Esc · 엔터 · 삭제 · 뒤로 · 다시 · 스냅) 지움 — 끌기 · 집기 동작 그대로 - 리본 nav 마우스 누름 한 곳에서 단추 blur(키보드 초점 남김) · 단추별 blur 셋 걷음 - B07 축척 칸 = 칸 밑 오류 글 숨김 + 줄 아래 알림 한 줄(X · Y 높이 같음) - CAD 탭 줄 밑 선 --color-smoke · 도면 탭 겹판 26 → 25 - 시험: test_cad_touch · test_280_5_b07_settings 고쳐 씀 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
166 lines
6.2 KiB
TypeScript
166 lines
6.2 KiB
TypeScript
/* =============================================================================
|
|
* B07_DesignDetail_UI_Settings_Scale.ts
|
|
* ⚙ 모달 X · Y 축척 칸(485-2 · 요구 280-5) — 종단 · 횡단 · 토적 · 전개도만.
|
|
*
|
|
* 칸 = 분모 정수 · 범위 밖 = 칸 붉게 + [축척 저장] 꺼짐 · 기본이 자동인 칸(토적 X · 전개도) = 「자동」
|
|
* [축척 저장] = 그 종류 저장본이 있으면 n 장 물음(취소 = 그대로) → PUT → 화면 다시 세움(도면 다시 그림)
|
|
* [기본값] = 칸만 기본으로 채움 — 저장은 [축척 저장]
|
|
* ========================================================================== */
|
|
|
|
import { createCheckbox } from "@ui/ui_template_checkbox";
|
|
import {
|
|
createButton,
|
|
createInputField,
|
|
hideLoadingOverlay,
|
|
showConfirmDialog,
|
|
showLoadingOverlay,
|
|
showToast,
|
|
} from "@ui/ui_template_elements";
|
|
import {
|
|
type DrawingScaleAxes,
|
|
type DrawingScaleKind,
|
|
type DrawingScales,
|
|
fetchDrawingScales,
|
|
saveDrawingScales,
|
|
} from "./B07_DesignDetail_Api_Fetch";
|
|
import type { DrawingSettingsDeps } from "./B07_DesignDetail_UI_Settings";
|
|
import { readScaleAxis, scaleToSend } from "./logic/B07_DesignDetail_Scale_Check";
|
|
|
|
const LABEL: Record<DrawingScaleKind, string> = {
|
|
longitudinal: "종단도",
|
|
cross: "횡단도",
|
|
mass_haul: "토적도",
|
|
development: "전개도",
|
|
};
|
|
const AXES = [
|
|
["x", "X(가로) 1/"],
|
|
["y", "Y(세로) 1/"],
|
|
] as const;
|
|
|
|
const isScaleKind = (kind: string): kind is DrawingScaleKind => kind in LABEL;
|
|
|
|
/** ⚙ 모달 몸에 축척 칸을 붙임 — 축척 없는 종류는 아무것도 안 함. */
|
|
export function mountDrawingScale(
|
|
body: HTMLElement,
|
|
kind: string,
|
|
deps: DrawingSettingsDeps,
|
|
close: () => void,
|
|
): void {
|
|
if (!isScaleKind(kind) || !deps.plan) return;
|
|
const { projectId, onClosed } = deps.plan;
|
|
const section = document.createElement("div");
|
|
section.className = "b07-scale";
|
|
section.textContent = "축척 받는 중…";
|
|
body.append(section);
|
|
void fetchDrawingScales(projectId)
|
|
.then((data) => {
|
|
section.textContent = "";
|
|
buildScaleFields(section, kind, data, async (next) => {
|
|
const saved = data.saved[kind] ?? 0;
|
|
const ask = `${LABEL[kind]} 축척을 바꾸면 저장본 ${saved}장이 풀립니다(원본에서 다시 그림). 바꿀까요?`;
|
|
if (saved > 0 && !(await showConfirmDialog(ask, "바꿈"))) return;
|
|
showLoadingOverlay();
|
|
try {
|
|
await saveDrawingScales(projectId, { [kind]: next });
|
|
close();
|
|
showToast(`${LABEL[kind]} 축척을 바꿨습니다.`, "success");
|
|
onClosed(true, deps.current()?.id); // 목록 · 도면을 서버에서 다시 받아 다시 그림
|
|
} catch (error) {
|
|
showToast(
|
|
error instanceof Error ? error.message : "축척을 저장하지 못했습니다.",
|
|
"error",
|
|
);
|
|
} finally {
|
|
hideLoadingOverlay();
|
|
}
|
|
});
|
|
})
|
|
.catch(() => {
|
|
section.textContent = "축척을 받지 못했습니다.";
|
|
});
|
|
}
|
|
|
|
function buildScaleFields(
|
|
section: HTMLElement,
|
|
kind: DrawingScaleKind,
|
|
data: DrawingScales,
|
|
save: (next: DrawingScaleAxes) => Promise<void>,
|
|
): void {
|
|
const fields = AXES.map(([axis, label]) => {
|
|
const fallback = data.defaults[kind][axis];
|
|
const range = data.ranges[kind][axis];
|
|
const [min, max] = range;
|
|
const field = createInputField({ label, type: "number", compact: true, min, max });
|
|
field.root.classList.add("b07-scale__field");
|
|
const auto = fallback === null ? createCheckbox("자동") : undefined;
|
|
if (auto) field.root.append(auto.root);
|
|
const set = (value: number | null): void => {
|
|
field.input.value = value === null ? "" : String(value);
|
|
if (auto) auto.input.checked = value === null;
|
|
field.input.disabled = value === null && !!auto;
|
|
};
|
|
set(data.scales[kind][axis]);
|
|
const read = () => readScaleAxis(field.input.value, !!auto?.input.checked, range);
|
|
return { axis, field, auto, fallback, set, read };
|
|
});
|
|
|
|
const saveButton = createButton({
|
|
label: "축척 저장",
|
|
onClick: () => {
|
|
const [x, y] = fields.map((f) => f.read()) as [number | null, number | null];
|
|
const now = data.scales[kind];
|
|
if (x === now.x && y === now.y) {
|
|
showToast("바뀐 축척이 없습니다.", "info");
|
|
return;
|
|
}
|
|
void save({ x: scaleToSend(x, fields[0].fallback), y: scaleToSend(y, fields[1].fallback) });
|
|
},
|
|
});
|
|
// 칸 밑 오류 글은 숨기고(틀린 칸만 길어짐) 줄 아래 한 줄로 알림
|
|
const alertLine = document.createElement("p");
|
|
alertLine.className = "b07-scale__error";
|
|
alertLine.setAttribute("role", "alert");
|
|
alertLine.hidden = true;
|
|
const check = (): void => {
|
|
const errors = new Map<string, string[]>();
|
|
for (const f of fields) {
|
|
const result = f.read();
|
|
const message = typeof result === "string" ? result : undefined;
|
|
f.field.setError(message);
|
|
if (message) errors.set(message, [...(errors.get(message) ?? []), f.axis.toUpperCase()]);
|
|
}
|
|
alertLine.textContent = [...errors].map(([m, axes]) => `${axes.join(" · ")}: ${m}`).join(" / ");
|
|
alertLine.hidden = errors.size === 0;
|
|
saveButton.disabled = errors.size > 0;
|
|
};
|
|
for (const f of fields) {
|
|
f.field.input.addEventListener("input", check);
|
|
f.auto?.input.addEventListener("change", () => {
|
|
if (f.auto!.input.checked) f.set(null);
|
|
else f.field.input.disabled = false; // 빈 칸 — 숫자를 적어야 저장
|
|
check();
|
|
});
|
|
}
|
|
const reset = createButton({
|
|
label: "기본값",
|
|
variant: "ghost",
|
|
onClick: () => {
|
|
fields.forEach((f) => f.set(f.fallback));
|
|
check();
|
|
},
|
|
});
|
|
reset.title = "칸을 기본 축척으로 채움 — [축척 저장]으로 씀";
|
|
|
|
const title = document.createElement("p");
|
|
title.className = "b07-settings__note";
|
|
title.textContent =
|
|
kind === "cross" ? "축척 — 횡단도 장(표준 횡단면도 뺌)" : `축척 — ${LABEL[kind]}`;
|
|
const row = document.createElement("div");
|
|
row.className = "b07-scale__row";
|
|
row.append(...fields.map((f) => f.field.root));
|
|
const buttons = document.createElement("div");
|
|
buttons.className = "b07-scale__actions";
|
|
buttons.append(reset, saveButton);
|
|
section.append(title, row, alertLine, buttons);
|
|
}
|