feat(M02-흐름도): 공용 단위 고르기 UnitPick · 끝수 줄 · 함수 판 단위 고르기로 바꿈

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D2nUMUMTsqm93PbQtipo6c
This commit is contained in:
2026-10-01 19:42:27 +09:00
co-authored by Claude Sonnet 5.5
parent 4cd2930ff3
commit 348003cf5a
6 changed files with 411 additions and 25 deletions
+63 -16
View File
@@ -15,6 +15,9 @@ import type {
Workbook,
} from "../../A00_Common/spreadsheet/spreadsheet";
import type { CfBox, CfFunc, CfLogic, CfVariable } from "./M02_DF_Model";
import { parseRound, wrapRound, type RoundSpec } from "./M02_DF_Round";
import { createRoundRow } from "./M02_DF_RoundRow";
import { createUnitPick, suggestPlaces } from "./M02_DF_UnitPick";
import "./M02_DF_FuncPanel.css";
const TEXT = {
@@ -126,6 +129,8 @@ interface State {
json: string;
cell: string;
form: VarForm | null;
/** 단위 표준이 제안한 출력 소수자리 */
suggest: number | null;
parts: {
head: HTMLElement;
got: HTMLElement;
@@ -317,20 +322,19 @@ function applyMarks(st: State): void {
function drawHead(st: State): void {
const box = boxOf(st);
const unit = el("input", {
className: "ui-input m02-dfpanel-unit",
attrs: { type: "text", "aria-label": tf("Unit") },
});
unit.value = box?.unit ?? "-";
if (st.readOnly) unit.readOnly = true;
unit.addEventListener("change", () => {
const v = unit.value.trim() || "-";
unit.value = v;
patchBox(st, (b) => ({
...b,
unit: v,
func: b.func ? { ...b.func, output: { ...b.func.output, unit: v } } : b.func,
}));
const unit = createUnitPick({
value: box?.unit ?? "-",
readOnly: st.readOnly,
extra: st.lg.resultUnit ? [st.lg.resultUnit] : [],
ariaLabel: tf("Unit"),
onPick: (v) => {
patchBox(st, (x) => ({
...x,
unit: v,
func: x.func ? { ...x.func, output: { ...x.func.output, unit: v } } : x.func,
}));
void suggestPlaces(v).then((n) => ((st.suggest = n), drawBar(st)));
},
});
st.parts.head.replaceChildren(
el("strong", { className: "m02-dfpanel-title", text: box?.name ?? "" }),
@@ -372,8 +376,21 @@ function formField(
cls = "",
): HTMLElement {
const form = st.form!;
if (key === "unit") {
return el("label", {
className: "m02-dfpanel-fieldbox",
children: [
el("span", { className: "m02-dfpanel-note", text: tf(label) }),
createUnitPick({
value: form.unit || "-",
ariaLabel: tf(label),
onPick: (u) => ((form.unit = u), drawVars(st)),
}),
],
});
}
const input = el("input", {
className: `ui-input m02-dfpanel-field ${cls}`,
className: `ui-input ui-input--sm m02-dfpanel-field ${cls}`,
attrs: { type: "text", "aria-label": tf(label) },
});
input.value = form[key];
@@ -443,7 +460,7 @@ function drawVars(st: State): void {
);
} else {
const type = el("select", {
className: "ui-select m02-dfpanel-field",
className: "ui-select ui-select--sm m02-dfpanel-field",
attrs: { "aria-label": tf("VarType") },
});
for (const [v, k] of [
@@ -520,6 +537,7 @@ function drawBar(st: State): void {
el("span", { className: "m02-dfpanel-note", text: tf("OutOnly") }),
],
}),
...[roundRow(st)].filter((x): x is HTMLElement => !!x),
el("div", {
className: "m02-dfpanel-row",
children: [
@@ -538,6 +556,34 @@ function drawBar(st: State): void {
);
}
/** 출력 칸 수식에 끝수 씌움(칸 값만 있으면 그 값을 감쌈) */
function writeRound(st: State, spec: RoundSpec | null): void {
const f = funcOf(st);
const cell = f?.sheet.칸[f.output.cell];
if (!f || !cell) return;
const text = cell.식 ?? String(cell.값 ?? "");
const inner = parseRound(text)?.inner ?? text;
patchFunc(st, (x) => {
const sheet = structuredClone(x.sheet);
sheet.칸[x.output.cell] = { 식: wrapRound(inner, spec) };
return { ...x, sheet };
});
}
function roundRow(st: State): HTMLElement | null {
const f = funcOf(st);
const cell = f?.sheet.칸[f.output.cell];
const formula = cell
? (cell.식 ?? (cell.값 === undefined ? undefined : String(cell.값)))
: undefined;
return createRoundRow({
formula,
suggest: st.suggest,
readOnly: st.readOnly,
onApply: (spec) => writeRound(st, spec),
});
}
/** 출력 칸 하나 정하기 — 다시 고르면 바뀜 */
function setOutput(st: State, cell: string): void {
const f = funcOf(st);
@@ -681,6 +727,7 @@ export function renderFuncPanel(
json: "",
cell: "A1",
form: null,
suggest: null,
parts,
observer: null,
redraw: () => {