- 텍스트 수식 줄 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>
72 lines
2.8 KiB
TypeScript
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;
|
|
}),
|
|
});
|
|
}
|