/* ============================================================================= * M01_MasterData_UI_LogicLab_EquationVar.ts * 수식 탭 변수 창(PLAN 796-2 · 요구 657) — 변수 고침 · 변수 더함 · 변수 지움(쓰는 곳 있으면 막고 목록 보임). * 고침 창 = 이름 · 기호 · 위첨자 · 아래첨자 · 단위 · 값(입력 변수만 · 시험 값 · 저장 안 함) · [적용] · [변수 지움] * 더함 창 = 갈래(입력 · 중간) · 열쇠 · 이름 · 기호 · 첨자 · 단위 · 기본값(입력) | 식 · 자리(중간) * 적용 = 부르는 쪽 `commit` 이 서버 미리 셈(`/logic/equation/preview`)을 거쳐 되면 [] · 안 되면 까닭 — 까닭은 창 안 띠에 그대로 * 부품 = 모달 · 입력 · 고르기 · 단추 · 띠 알림 · 열쇠 · 식 검사 · 이름 겹침은 서버 몫 * ========================================================================== */ import type { EqVarRow } from "@ui/ui_template_equation"; import { createBanner } from "@ui/ui_template_banner"; import { createButton } from "@ui/ui_template_button"; import { el } from "@ui/ui_template_elements_base"; import { createInputField, createSelectField } from "@ui/ui_template_input"; import { currentLanguageIndex } from "@ui/ui_template_locale"; import { openModal } from "@ui/ui_template_modal"; import { addEdit, varEdits, type EqEdit, type VarForm } from "./logic/M01_Logic_Equation_Model"; const TEXT = { VarTitle: ["변수 고침", "Edit variable"], AddTitle: ["변수 더함", "Add variable"], Close: ["닫기", "Close"], Apply: ["적용", "Apply"], Drop: ["변수 지움", "Delete variable"], Key: ["열쇠 (식이 부르는 이름 · 안 바뀜)", "Key (used by formulas · fixed)"], Name: ["이름", "Name"], Sign: ["기호", "Symbol"], Up: ["위첨자", "Superscript"], Down: ["아래첨자", "Subscript"], Unit: ["단위", "Unit"], Value: ["값 (시험 값 · 저장 안 함)", "Value (trial · not saved)"], Digits: ["보이는 자리 (0~6 · 빈 칸 = 전체 자리)", "Digits (0-6 · blank = full)"], Kind: ["갈래", "Kind"], Input: ["입력", "Input"], Mid: ["중간", "Intermediate"], Default: ["기본값", "Default"], Formula: ["식", "Formula"], After: ["자리", "Place"], End: ["맨 끝", "End"], Top: ["맨 위", "Top"], Why: ["안 됨", "Not applied"], Hint: ["이름 · 기호 · 첨자를 바꾸면 쓰는 모든 식에 함께 보임", "Renames show in every formula"], } as const satisfies Record; const t = (k: keyof typeof TEXT): string => TEXT[k][currentLanguageIndex as 0 | 1] ?? TEXT[k][0]; export type Commit = (edits: EqEdit[]) => Promise; const grid = (...kids: HTMLElement[]): HTMLElement => el("div", { className: "m01-eq__form", children: kids }); /** 까닭 띠 — 있으면 보임 · 없으면 비움 */ function reasonBox(): { root: HTMLElement; set: (why: string[]) => void } { const root = el("div", { className: "m01-eq__why" }); return { root, set: (why) => root.replaceChildren( ...(why.length ? [createBanner({ tone: "danger", title: t("Why"), text: why.join(" · ") })] : []), ), }; } export interface VarDialog { row: EqVarRow; /** 입력 변수 시험 값(옵션 칸 글) */ value: string; commit: Commit; /** 시험 값을 바꿈(저장 안 함) */ onValue: (value: string) => void; /** 변수를 지운 뒤 — 시험 값 비움 */ onDropped: (key: string) => void; } export function openVarDialog(o: VarDialog): void { const v = o.row; const field = (label: string, value: string): ReturnType => createInputField({ label, value, size: 28 }); const name = field(t("Name"), v.이름); const sign = field(t("Sign"), v.기호); const up = field(t("Up"), v.위); const down = field(t("Down"), v.아래); const unit = field(t("Unit"), v.단위); const val = field(t("Value"), o.value); const shown = v.표시 === undefined ? "" : String(v.표시); const digits = field(t("Digits"), shown); const why = reasonBox(); openModal({ title: `${t("VarTitle")} · ${v.열쇠}`, closeLabel: t("Close"), width: "40rem", mount: (body, close, actions) => { body.append( el("p", { className: "ui-text ui-text--muted", text: t("Hint") }), grid( name.root, sign.root, up.root, down.root, unit.root, ...(v.갈래 === "입력" ? [val.root] : [digits.root]), ), why.root, ); actions.append( createButton({ label: t("Drop"), variant: "danger-ghost", onClick: async () => { const bad = await o.commit([{ 할일: "지움", 열쇠: v.열쇠 }]); why.set(bad); if (bad.length) return; o.onDropped(v.열쇠); close(); }, }), createButton({ label: t("Apply"), onClick: async () => { const now: VarForm = { 열쇠: v.열쇠, 이름: name.input.value.trim(), 기호: sign.input.value.trim(), 위: up.input.value.trim(), 아래: down.input.value.trim(), 단위: unit.input.value.trim(), 표시: digits.input.value.trim(), }; const edits = varEdits( { 열쇠: v.열쇠, 이름: v.이름, 기호: v.기호, 위: v.위, 아래: v.아래, 단위: v.단위, 표시: shown, }, now, ); const bad = edits.length ? await o.commit(edits) : []; why.set(bad); if (bad.length) return; if (v.갈래 === "입력" && val.input.value.trim() !== o.value) o.onValue(val.input.value.trim()); close(); }, }), ); }, }); } export interface AddDialog { /** 중간 줄 열쇠 차례(자리 고르기) */ mids: string[]; commit: Commit; } export function openAddDialog(o: AddDialog): void { const field = (label: string, value = ""): ReturnType => createInputField({ label, value, size: 28 }); const kind = createSelectField({ label: t("Kind"), options: [ { value: "입력", text: t("Input") }, { value: "중간", text: t("Mid") }, ], value: "입력", size: 28, }); const key = field(t("Key")); const name = field(t("Name")); const sign = field(t("Sign")); const up = field(t("Up")); const down = field(t("Down")); const unit = field(t("Unit")); const dflt = field(t("Default")); const expr = field(t("Formula")); const place = createSelectField({ label: t("After"), options: [ { value: "", text: t("End") }, { value: "\u0000top", text: t("Top") }, ...o.mids.map((m) => ({ value: m, text: m })), ], value: "", size: 28, }); const why = reasonBox(); const sync = (): void => { const mid = kind.select.value === "중간"; dflt.root.hidden = mid; expr.root.hidden = place.root.hidden = !mid; }; kind.select.addEventListener("change", sync); sync(); openModal({ title: t("AddTitle"), closeLabel: t("Close"), width: "40rem", mount: (body, close, actions) => { body.append( grid( kind.root, key.root, name.root, sign.root, up.root, down.root, unit.root, dflt.root, expr.root, place.root, ), why.root, ); actions.append( createButton({ label: t("Apply"), onClick: async () => { const mid = kind.select.value === "중간"; const at = place.select.value; const edit = addEdit( { 열쇠: key.input.value, 이름: name.input.value, 기호: sign.input.value, 위: up.input.value, 아래: down.input.value, 단위: unit.input.value, 갈래: mid ? "중간" : "입력", 기본값: dflt.input.value.trim(), 식: expr.input.value.trim(), }, at === "\u0000top" ? "" : at || undefined, ); const bad = await o.commit([edit]); why.set(bad); if (!bad.length) close(); }, }), ); }, }); }