From 4860e11794cd16a5110cfa7636317611db8b3b3c Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 27 Oct 2026 21:21:01 +0900 Subject: [PATCH] =?UTF-8?q?feat(E03):=20=ED=83=90=EC=83=89=20=ED=95=9C?= =?UTF-8?q?=EA=B3=84=20=EA=B3=A0=EB=A5=B4=EA=B8=B0=EB=A5=BC=20=EC=99=BC?= =?UTF-8?q?=EC=AA=BD=20=ED=8C=90=20=EB=B6=84=ED=95=A0=20=EA=B3=A0=EB=A5=B4?= =?UTF-8?q?=EA=B8=B0=EB=A1=9C=20=C2=B7=20=E3=80=8C=EC=9D=B4=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=ED=83=90=EC=83=89=20=ED=95=9C=EA=B3=84=E3=80=8D=20?= =?UTF-8?q?=ED=95=9C=20=EC=A4=84=20=C2=B7=20=EB=B0=94=EA=BE=BC=20=EB=92=A4?= =?UTF-8?q?=20=ED=95=B4=EC=84=9D=20=EC=A0=84=20=ED=91=9C=EC=8B=9C=20(PLAN?= =?UTF-8?q?=20567-4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- E03_StabSection/E03_StabSection_UI_Page.ts | 73 +++++++++++++++++----- ui_template/ui_template_locale_e03.ts | 5 ++ 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/E03_StabSection/E03_StabSection_UI_Page.ts b/E03_StabSection/E03_StabSection_UI_Page.ts index d1d20cf5c..4bae2313d 100644 --- a/E03_StabSection/E03_StabSection_UI_Page.ts +++ b/E03_StabSection/E03_StabSection_UI_Page.ts @@ -22,6 +22,7 @@ import { showToast, } from "@ui/ui_template_elements"; import { t, type LocaleKey } from "@ui/ui_template_locale"; +import { createSegment } from "@ui/ui_template_segment"; import { mountGroupStep } from "../A00_Common/group_workflow"; import { buildMissingBanner } from "../E01_StabRegister/E01_StabRegister_UI_Missing"; import { buildList } from "./E03_StabSection_UI_List"; @@ -142,28 +143,56 @@ export function renderE03StabSection(root: HTMLElement): void { paintList(); }, }); - const reachField = createSelectField({ - label: t("E03_Reach"), + type ReachPick = "model_end" | "2h" | "m"; + const reachSegment = createSegment({ options: [ - { value: "model_end", text: t("E03_Reach_model_end") }, - { value: "2h", text: t("E03_Reach_2h") }, - { value: "m", text: t("E03_Reach_m") }, + { value: "model_end", label: t("E03_Reach_model_end") }, + { value: "2h", label: t("E03_Reach_2h_Short"), title: t("E03_Reach_2h") }, + { value: "m", label: t("E03_Reach_m_Short"), title: t("E03_Reach_m") }, ], - compact: true, + value: "model_end", + size: 24, + ariaLabel: t("E03_Reach"), onChange: () => paintReach(), }); - reachField.select.dataset.role = "reach"; - reachField.select.title = t("E03_Reach_Hint"); - const reachMeters = createInputField({ type: "number", min: 0.1, compact: true, unit: "m" }); + reachSegment.root.dataset.role = "reach"; + reachSegment.root.title = t("E03_Reach_Hint"); + const reachMeters = createInputField({ + type: "number", + min: 0.1, + compact: true, + unit: "m", + onInput: () => paintReach(), + }); reachMeters.input.dataset.role = "reach-m"; reachMeters.input.title = t("E03_Reach_Hint"); + const reachLabelNode = muted(t("E03_Reach")); + const reachNote = muted("", "reach-note"); + const reachLabel = (reach: string | number | undefined): string => + typeof reach === "number" + ? t("E03_Reach_m_Value").replace("{v}", String(reach)) + : t(reach === "2h" ? "E03_Reach_2h" : "E03_Reach_model_end"); + /** 고른 값(서버로 보낼 꼴) — 정한 m 이 비면 null */ + const reachPicked = (): string | number | null => { + const pick = reachSegment.get() as ReachPick; + if (pick !== "m") return pick; + const meters = Number(reachMeters.input.value); + return meters > 0 ? meters : null; + }; + /** 정한 m 칸 보임 · 「이 결과」 한 줄 — 고른 값이 결과와 다르면 「해석 전」 */ const paintReach = (): void => { - reachMeters.root.hidden = reachField.select.value !== "m"; + reachMeters.root.hidden = reachSegment.get() !== "m"; + if (result?.status !== "ok") return void (reachNote.textContent = ""); + const used = result.search_reach ?? "model_end"; + const picked = reachPicked(); + reachNote.textContent = + `${t("E03_Reach_Used")} ${reachLabel(used)}` + + (picked !== null && picked !== used ? ` · ${t("E03_Reach_Pending")}` : ""); }; /** 서버 저장값을 칸에 맞춤 */ const showReach = (reach: string | number | undefined): void => { const custom = typeof reach === "number"; - reachField.select.value = custom ? "m" : reach === "2h" ? "2h" : "model_end"; + reachSegment.set(custom ? "m" : reach === "2h" ? "2h" : "model_end"); if (custom) reachMeters.input.value = String(reach); paintReach(); }; @@ -190,7 +219,17 @@ export function renderE03StabSection(root: HTMLElement): void { controls.append(condField.root, sortField.root); const actions = el("div", { className: "ui-sidebar-actions" }); actions.append(view3dButton, analyzeButton); - panel.append(controls, reachField.root, reachMeters.root, ngBox, status, listHost, actions); + panel.append( + controls, + reachLabelNode, + reachSegment.root, + reachMeters.root, + reachNote, + ngBox, + status, + listHost, + actions, + ); // ---- 본문 ---- const missingHost = el("div"); @@ -239,7 +278,8 @@ export function renderE03StabSection(root: HTMLElement): void { if (result && !result.editable) lines.push(t("E03_ReadOnly")); status.textContent = lines.join("\n"); analyzeButton.disabled = !result?.editable; - reachField.select.disabled = reachMeters.input.disabled = !result?.editable; + reachMeters.input.disabled = !result?.editable; + reachSegment.root.querySelectorAll("button").forEach((b) => (b.disabled = !result?.editable)); view3dButton.disabled = result?.status !== "ok"; }; @@ -503,9 +543,8 @@ export function renderE03StabSection(root: HTMLElement): void { } async function analyze(): Promise { - const pick = reachField.select.value; - const meters = Number(reachMeters.input.value); - if (pick === "m" && !(meters > 0)) { + const reach = reachPicked(); + if (reach === null) { reachMeters.setError(t("E03_Reach_Invalid")); return; } @@ -513,7 +552,7 @@ export function renderE03StabSection(root: HTMLElement): void { try { const response = await api<{ status: string }>(`${base}/analyze2d`, { method: "POST", - body: JSON.stringify({ search_reach: pick === "m" ? meters : pick }), + body: JSON.stringify({ search_reach: reach }), }); if (response.status === "fresh") { showToast(t("E03_Fresh"), "info"); diff --git a/ui_template/ui_template_locale_e03.ts b/ui_template/ui_template_locale_e03.ts index bf4bbe43a..b835ba663 100644 --- a/ui_template/ui_template_locale_e03.ts +++ b/ui_template/ui_template_locale_e03.ts @@ -76,6 +76,11 @@ export const ui_locales_e03 = { E03_Reach_model_end: ["모형 끝", "Model edge"], E03_Reach_2h: ["비탈 끝 + 2H", "Slope end + 2H"], E03_Reach_m: ["비탈 끝 + 정한 m", "Slope end + set m"], + E03_Reach_2h_Short: ["+ 2H", "+ 2H"], + E03_Reach_m_Short: ["+ 정한 m", "+ set m"], + E03_Reach_m_Value: ["비탈 끝 + {v} m", "Slope end + {v} m"], + E03_Reach_Used: ["이 결과 탐색 한계 —", "This result's reach —"], + E03_Reach_Pending: ["지금 고른 값은 해석 전", "current pick not analyzed yet"], E03_Reach_Hint: [ "[해석]을 누르면 이 값으로 셈 · 저장됨", "Applied and saved when you press Analyze",