Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2nUMUMTsqm93PbQtipo6c
86 lines
3.7 KiB
TypeScript
86 lines
3.7 KiB
TypeScript
/* =============================================================================
|
|
* M02_DF_FuncExtra.ts
|
|
* 함수 판 보조 — 식 보기 그리기 · 칸 연결 표 손잡이 · 우클릭 「조건 만들기」 에 주는 것
|
|
* 판 본체(M02_DF_FuncPanel)가 700줄 안에 들도록 뗌 · 상태는 모르고 손잡이(ExtraContext)만 받음
|
|
* ========================================================================== */
|
|
|
|
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
|
import { showToast } from "@ui/ui_template_elements";
|
|
import type { ConditionOptions } from "../../A00_Common/spreadsheet/spreadsheet_condition";
|
|
import { renderCellTable } from "./M02_DF_CellTable";
|
|
import { evalSafe, type DetailOptions } from "./M02_DF_Detail";
|
|
import { cellValuesOf, formulaLines, renderFormulaView } from "./M02_DF_FormulaView";
|
|
import { TEXT } from "./M02_DF_FuncText";
|
|
import { valueChoices, type CfFunc, type CfLogic } from "./M02_DF_Model";
|
|
|
|
export interface ExtraContext {
|
|
lg: CfLogic;
|
|
boxId: string;
|
|
detail: DetailOptions;
|
|
readOnly: boolean;
|
|
place(port: string, cell: string): void;
|
|
setOutput(cell: string): void;
|
|
patch(make: (f: CfFunc) => CfFunc): void;
|
|
used(f: CfFunc): Map<string, string>;
|
|
}
|
|
|
|
const busy = (): string => TEXT.Busy[currentLanguageIndex === 1 ? 1 : 0];
|
|
const boxOf = (x: ExtraContext) => x.lg.boxes.find((b) => b.id === x.boxId);
|
|
|
|
/** 식 보기 — 칸 식을 고치면 부른 쪽이 다시 부름 */
|
|
export function drawFormula(host: HTMLElement, x: ExtraContext): void {
|
|
const box = boxOf(x);
|
|
if (!box?.func) return;
|
|
const ev = evalSafe(x.lg, x.detail);
|
|
renderFormulaView(host, formulaLines(box, cellValuesOf(box, ev?.arrived?.[x.boxId])));
|
|
}
|
|
|
|
/** 칸 연결 표 — 표에서 칸 바꾸기 · 풀기(입력/변수/결과 모두 같은 규칙: 이미 쓰는 칸 막음 · 결과 칸 하나) */
|
|
export function drawCells(host: HTMLElement, x: ExtraContext): void {
|
|
const box = boxOf(x);
|
|
if (!box?.func) return;
|
|
renderCellTable(host, x.lg, box, x.readOnly, {
|
|
move: (kind, key, cell) => {
|
|
if (kind === "in") return x.place(key, cell);
|
|
if (kind === "out") return x.setOutput(cell);
|
|
if (box.func && x.used(box.func).has(cell)) return void showToast(busy(), "error");
|
|
x.patch((f) => ({
|
|
...f,
|
|
variables: f.variables.map((v) => (v.name === key ? { ...v, cell } : v)),
|
|
}));
|
|
},
|
|
release: (kind, key) =>
|
|
x.patch((f) =>
|
|
kind === "in"
|
|
? { ...f, inputs: f.inputs.map((i) => (i.port === key ? { ...i, cell: "" } : i)) }
|
|
: kind === "var"
|
|
? { ...f, variables: f.variables.filter((v) => v.name !== key) }
|
|
: { ...f, output: { ...f.output, cell: "" } },
|
|
),
|
|
});
|
|
}
|
|
|
|
/** 우클릭 「조건 만들기」 에 주는 것 — 칸에 올 값 목록(알약이면 valueChoices · 변수면 고르기 목록) · 이름 붙은 칸 */
|
|
export function conditionsOf(x: ExtraContext): ConditionOptions {
|
|
return {
|
|
valuesFor: (a1) => {
|
|
const f = boxOf(x)?.func;
|
|
const at = a1.toUpperCase();
|
|
const v = f?.variables.find((y) => y.cell?.toUpperCase() === at);
|
|
if (v) return v.type === "choice" && v.choices?.length ? [...v.choices] : null;
|
|
const port = f?.inputs.find((i) => i.cell.toUpperCase() === at)?.port;
|
|
const link = port
|
|
? x.lg.links.find((l) => l.to.box === x.boxId && l.to.name === port)
|
|
: undefined;
|
|
return link ? valueChoices(x.lg, link.from, { values: x.detail.values }) : null;
|
|
},
|
|
pills: () => {
|
|
const f = boxOf(x)?.func;
|
|
return [
|
|
...(f?.inputs ?? []).filter((i) => i.cell).map((i) => ({ name: i.port, cell: i.cell })),
|
|
...(f?.variables ?? []).filter((v) => v.cell).map((v) => ({ name: v.name, cell: v.cell! })),
|
|
];
|
|
},
|
|
};
|
|
}
|