/* ============================================================================= * M01_MasterData_UI_LogicLab_FormulaTokens.ts * 텍스트 수식 한 줄 글 → 누를 수 있는 조각(PLAN 748-2) — * 왼쪽 변수 이름 식(`이름글`) = 변수 이름 하나하나 · 오른쪽 값 든 식(`글`) = 값(수) 하나하나 * 누르면 그 줄 자료 모달(748-1) · 변수가 시험 입력(옵션)이면 옵션 고르기(`inputChip`) * 조각 나누기는 순수 셈(화면 · fetch · sessionStorage 없음) — 칸 그리기는 `tokenRow` 만 화면. * ========================================================================== */ import { createButton } from "@ui/ui_template_button"; import { el } from "@ui/ui_template_elements"; /* ── 순수 셈 ── */ export interface Tok { text: string; /** 눌러서 모달을 열 조각 */ hot: boolean; } const NAME_RE = /\s+|[*+/=()×÷,%:]|[^\s*+/=()×÷,%:]+/g; /** 변수 이름 식 → 조각 — 글자(한글 · 영문)가 든 덩이 = 변수 이름(함수 이름 「이름(」 · 수 · 기호는 아님) · 빈칸은 버림 */ export function nameTokens(text: string): Tok[] { const out: Tok[] = []; for (const m of text.matchAll(NAME_RE)) { const t = m[0]; if (/^\s+$/.test(t)) continue; const next = text[(m.index ?? 0) + t.length]; out.push({ text: t, hot: /[가-힣A-Za-z]/.test(t) && next !== "(" }); } return out; } /** 값 든 식 → 조각 — 수(콤마 · 소수점 든 것까지) 하나하나 = 값 · 나머지(기호 · 글)는 그대로 */ export function valueTokens(text: string): Tok[] { const out: Tok[] = []; let last = 0; const plain = (s: string): void => { const t = s.trim(); if (t) out.push({ text: t, hot: false }); }; for (const m of text.matchAll(/\d[\d,]*(?:\.\d+)?/g)) { plain(text.slice(last, m.index)); out.push({ text: m[0], hot: true }); last = (m.index ?? 0) + m[0].length; } plain(text.slice(last)); return out; } /* ── 순수 셈 끝 ── */ /** 조각 한 줄 — `hot` 은 낮은 단추(요소 연결 갈래 색) · `make` 가 단추를 주면 그걸로(옵션 칩) · 아니면 글자 */ export function tokenRow( toks: Tok[], open: (() => void) | undefined, make?: (text: string) => HTMLElement | null, ): HTMLElement { return el("div", { className: "m01lab-fx__toks", children: toks.map((t) => { if (!t.hot) return el("span", { text: t.text }); const own = make?.(t.text); if (own) return own; if (!open) return el("span", { text: t.text }); const b = createButton({ label: t.text, variant: "ghost", size: 20, tone: "link" }); b.classList.add("m01lab-fx__tok"); b.dataset.fxTok = t.text; b.addEventListener("click", open); return b; }), }); }