Files
Aislo/M01_MasterData/M01_MasterData_UI_LogicLab_FormulaTokens.ts
T
eomsangdonandClaude Sonnet 5.5 0c0d0ce629 feat(748-2): 텍스트 수식 — 행 고르기 걷음 · 변수 이름 · 값 하나하나 누르면 줄 모달(748-1) · 줄마다 옵션 값 칩 걷음(748-3 한 조각)
- 텍스트 수식 줄 hover · 누름 고정(행 단위 선택) 걷음
- 변수 이름 식 = 변수 이름마다 낮은 단추 · 값 든 식 = 수 하나하나 낮은 단추 → 같은 줄의 상세 모달(적용 · 삭제 · 닫기)
- 변수가 시험 입력(옵션)이면 옵션 고르기 칩으로
- 공용 상세 `lineOpener` 새로 — M02 SK 상세 · M01 단가산출 기초 같은 길(M01 = 시험 고침 고리)
- 상세 보기 줄마다 붙던 옵션 값 칩(`쓰는입력`) 걷음 — 위 옵션 칸이 대신(748-3)
- 새 파일 `LogicLab_FormulaTokens`(조각 나누기 순수 셈) · 시험 test_748_2 · 낡은 시험 고침 test_722(inputChip 줄 걷음)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 22:15:33 +09:00

72 lines
2.8 KiB
TypeScript

/* =============================================================================
* 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;
}),
});
}