diff --git a/M02_MasterTemplete/DataFlow/M02_DF_Detail.ts b/M02_MasterTemplete/DataFlow/M02_DF_Detail.ts index 30b1312f8..2f4cf9fc2 100644 --- a/M02_MasterTemplete/DataFlow/M02_DF_Detail.ts +++ b/M02_MasterTemplete/DataFlow/M02_DF_Detail.ts @@ -69,11 +69,15 @@ export function evalSafe(lg: CfLogic, o: DetailOptions = {}): CfEvalResult | nul export const valueOf = (ev: CfEvalResult | null, p: CfPort): CfValue | undefined => ev?.ports[p.box]?.[p.name]; +/** 숫자 글 — 천 단위 쉼표 · 군더더기 소수는 뗌 */ +export const commas = (n: number): string => + Number(n.toPrecision(12)).toLocaleString("ko-KR", { maximumFractionDigits: 6 }); + /** 화면에 보일 값 글 — 표는 줄 수 */ export function showValue(v: CfValue | undefined): string { if (v === undefined || v === null) return td("Empty"); if (Array.isArray(v)) return td("Table", { n: Math.max(0, v.length - 1) }); - return typeof v === "number" ? String(Number(v.toPrecision(12))) : String(v); + return typeof v === "number" ? commas(v) : String(v); } export function unitOf(lg: CfLogic, p: CfPort): string { diff --git a/M02_MasterTemplete/DataFlow/M02_DF_LinkPanel.ts b/M02_MasterTemplete/DataFlow/M02_DF_LinkPanel.ts index 1d7c4043c..5b4be1dc6 100644 --- a/M02_MasterTemplete/DataFlow/M02_DF_LinkPanel.ts +++ b/M02_MasterTemplete/DataFlow/M02_DF_LinkPanel.ts @@ -19,6 +19,7 @@ import { } from "./M02_DF_Model"; import { GATE_OPS, + commas, SCALE_OPS, evalSafe, labelOf, @@ -127,6 +128,7 @@ function select( return s; } +/** 숫자 칸 — 보일 때만 천 단위 쉼표 · 고치는 동안은 쉼표 없이 */ function numberBox( ctx: Ctx, value: number | string, @@ -135,10 +137,13 @@ function numberBox( ): HTMLElement { const i = el("input", { className: "ui-input ui-input--sm m02-dflink-num", - attrs: { type: "text", "aria-label": aria }, + attrs: { type: "text", inputmode: "decimal", "aria-label": aria }, }); - i.value = String(value); + const shown = (): string => (typeof value === "number" ? commas(value) : String(value)); + i.value = shown(); i.readOnly = ctx.readOnly; + i.addEventListener("focus", () => (i.value = String(value))); + i.addEventListener("blur", () => (i.value = shown())); i.addEventListener("change", () => { const t = i.value.trim().replace(/,/g, ""); onCommit(t !== "" && Number.isFinite(Number(t)) ? Number(t) : t); diff --git a/M02_MasterTemplete/DataFlow/M02_DF_PillPanel.ts b/M02_MasterTemplete/DataFlow/M02_DF_PillPanel.ts index dcd3a4aa3..deff36790 100644 --- a/M02_MasterTemplete/DataFlow/M02_DF_PillPanel.ts +++ b/M02_MasterTemplete/DataFlow/M02_DF_PillPanel.ts @@ -47,6 +47,7 @@ const line = (label: string, ...value: (HTMLElement | string)[]): HTMLElement => children: [el("span", { className: "m02-dflink-label", text: label }), ...value], }); +/** 한 줄 = 선 하나면 「보낸 알약 → (규칙 글) → 받는 알약」 · 함수 안 걸음이면 알약 하나 */ function steps( lg: CfLogic, list: CfStep[], @@ -55,21 +56,30 @@ function steps( onBox?: (id: string) => void, ): HTMLElement { if (!list.length) return el("p", { className: "m02-dflink-note", text: empty }); + const pill = (box: string, name: string): HTMLElement | string => + onBox + ? createButton({ + label: portText(lg, { box, name }), + variant: "ghost", + size: "sm", + onClick: () => onBox(box), + }) + : portText(lg, { box, name }); return el("ol", { className: "m02-dfpill-list", children: list.map((s) => { - const parts: (HTMLElement | string)[] = [ - arrow, - onBox - ? createButton({ - label: portText(lg, { box: s.box, name: s.port }), - variant: "ghost", - size: "sm", - onClick: () => onBox(s.box), - }) - : portText(lg, { box: s.box, name: s.port }), - ]; - if (s.rule) parts.push(el("span", { className: "m02-dfpill-rule", text: labelOf(s.rule) })); + const link = s.link ? lg.links.find((l) => l.id === s.link) : undefined; + const parts: (HTMLElement | string)[] = link + ? [ + arrow, + pill(link.from.box, link.from.name), + "→", + ...(s.rule + ? [el("span", { className: "m02-dfpill-rule", text: `(${labelOf(s.rule)})` }), "→"] + : []), + pill(link.to.box, link.to.name), + ] + : [arrow, pill(s.box, s.port)]; return el("li", { className: "m02-dfpill-step", children: parts }); }), });