Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1

This commit is contained in:
2026-11-05 18:46:47 +09:00
17 changed files with 970 additions and 79 deletions
@@ -14,7 +14,7 @@ import { createCalcSheetRow } from "@ui/ui_template_calc_row";
import { el } from "@ui/ui_template_elements";
import { createInputField } from "@ui/ui_template_input";
import { currentLanguageIndex } from "@ui/ui_template_locale";
import { createText } from "@ui/ui_template_text";
import { createFormulaCell } from "../A00_Common/sheet_formula_cell";
import { call, type Layer } from "./M02_MasterTemplete_Api_Fetch";
import type { EditCtx } from "./M02_MasterTemplete_Sangeun_SheetEdit";
import {
@@ -28,16 +28,12 @@ import {
import { num, type SpotInfo } from "./logic/M02_Sangeun_Sheet_Model";
const TEXT = {
Formula: ["식", "Formula"],
Edit: ["식 고침", "Edit formula"],
Close: ["식 닫기", "Close formula"],
Hint: ["예) 무게 / 운반량 × (1 + 할증율 / 100)", "e.g. weight / load × 1.2"],
Var: ["변수", "Variable"],
VarName: ["변수 이름", "Variable name"],
AddVar: ["+ 변수 줄", "+ Variable"],
DropVar: ["변수 빼기", "Remove variable"],
Bad: ["식이 틀려 안 넣음", "Invalid formula — not applied"],
Checking: ["검사 중", "Checking…"],
} as const satisfies Record<string, readonly [string, string]>;
const t = (k: keyof typeof TEXT): string => TEXT[k][currentLanguageIndex as 0 | 1] ?? TEXT[k][0];
@@ -97,7 +93,7 @@ export function clearNums<T extends FormulaDoc>(doc: T, spot: string): T {
return out as T;
}
/** 식 칸 — 칠 때마다 검사(앞 요청 끊음) · 바꿀 때 넣음 · `put(엔진 식 | null)` */
/** 식 칸 — 칠 때마다 검사(앞 요청 끊음) · 바꿀 때 넣음 · `put(엔진 식 | null)` · 몸 = 공용 `sheet_formula_cell` */
function formulaBox(
c: FormulaCtx,
spot: string,
@@ -106,63 +102,14 @@ function formulaBox(
origin: string,
put: (engine: string | null) => void,
): HTMLElement {
const box = createInputField({
value: c.drafts.get(key) ?? shown,
compact: true,
size: 24,
placeholder: t("Hint"),
});
box.input.setAttribute("aria-label", t("Formula"));
box.input.dataset.sheetCell = key;
box.input.spellcheck = false;
const note = createText("", "muted", 12);
note.dataset.sheetCheck = key;
let timer = 0;
let ctl: AbortController | null = null;
let last: Promise<CheckAnswer | null> | null = null;
const run = (text: string): Promise<CheckAnswer | null> => {
ctl?.abort();
const mine = (ctl = new AbortController());
note.textContent = t("Checking");
last = checkFormula(c, spot, text, mine.signal).then(
(got) => {
if (mine !== ctl) return got;
box.setError(got.되나 ? undefined : got.까닭.join(" · "));
note.textContent = got.알림.join(" · ");
return got;
},
(error: unknown) => {
if (mine !== ctl || mine.signal.aborted) return null;
box.setError(error instanceof Error ? error.message : String(error));
note.textContent = "";
return null;
},
);
return last;
};
box.input.addEventListener("input", () => {
c.drafts.set(key, box.input.value);
window.clearTimeout(timer);
const text = box.input.value.trim();
if (!text) return void (ctl?.abort(), box.setError(undefined), (note.textContent = ""));
timer = window.setTimeout(() => void run(text), 150);
});
box.input.addEventListener("change", async () => {
window.clearTimeout(timer);
const text = box.input.value.trim();
if (!text || text === origin) {
c.drafts.delete(key);
return put(null);
}
const got = await run(text);
if (!got?.되나) return void (note.textContent = t("Bad"));
c.drafts.delete(key);
put(got.식);
});
return el("div", {
className: "m02-sks__fx",
attrs: { "data-sheet-fx": spot },
children: [box.root, note],
return createFormulaCell({
key,
spot,
shown,
origin,
drafts: c.drafts,
check: (text, signal) => checkFormula(c, spot, text, signal),
put,
});
}