Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2nUMUMTsqm93PbQtipo6c
140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
/* =============================================================================
|
||
* M02_DF_PillPanel.ts
|
||
* 상세 내용 판 — 알약: 알약을 고르면 이름 · 값 · 단위 · 온 길(출처 사슬) · 가는 곳(이어진 선 · 받는 상자) · 끊김 까닭
|
||
* 모양은 `docs/raw/M01_쉬운단가/흐름도_정함.md` 정함 8 · 9 · M02_DF_Model 의 CfStep 그대로 · 읽기만(고치기는 선 · 상자 판에서)
|
||
* 글 [한, 영] 짝은 아래 TEXT
|
||
* ========================================================================== */
|
||
|
||
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
||
import { createButton, el } from "@ui/ui_template_elements";
|
||
import type { CfValues } from "./M02_DF_Eval";
|
||
import type { CfLogic, CfStep } from "./M02_DF_Model";
|
||
import {
|
||
boxOf,
|
||
breakLines,
|
||
evalSafe,
|
||
isOut,
|
||
labelOf,
|
||
portText,
|
||
showValue,
|
||
trace,
|
||
unitOf,
|
||
valueOf,
|
||
} from "./M02_DF_Detail";
|
||
import "./M02_DF_LinkPanel.css";
|
||
|
||
const TEXT = {
|
||
Name: ["이름", "Name"],
|
||
Value: ["값", "Value"],
|
||
Unit: ["단위", "Unit"],
|
||
Kind_out: ["내보내는 알약", "Sending pill"],
|
||
Kind_in: ["받는 알약", "Receiving pill"],
|
||
From: ["온 길", "Where it came from"],
|
||
To: ["가는 곳", "Where it goes"],
|
||
Start: ["여기서 시작 — 더 거슬러 갈 곳이 없음", "It starts here — nothing before it"],
|
||
End: ["여기가 끝 — 더 가는 곳이 없음", "It ends here — nothing after it"],
|
||
Breaks: ["끊긴 곳", "Breaks"],
|
||
NoBreak: ["끊긴 곳 없음", "No breaks"],
|
||
Open: ["이 상자 보기", "Open this box"],
|
||
Line: ["선", "Line"],
|
||
} as const;
|
||
|
||
const tp = (key: keyof typeof TEXT): string => TEXT[key][currentLanguageIndex === 1 ? 1 : 0];
|
||
|
||
const line = (label: string, ...value: (HTMLElement | string)[]): HTMLElement =>
|
||
el("div", {
|
||
className: "m02-dflink-row",
|
||
children: [el("span", { className: "m02-dflink-label", text: label }), ...value],
|
||
});
|
||
|
||
function steps(
|
||
lg: CfLogic,
|
||
list: CfStep[],
|
||
empty: string,
|
||
onBox?: (id: string) => void,
|
||
): HTMLElement {
|
||
if (!list.length) return el("p", { className: "m02-dflink-note", text: empty });
|
||
return el("ol", {
|
||
className: "m02-dfpill-list",
|
||
children: list.map((s) => {
|
||
const parts: (HTMLElement | string)[] = [
|
||
s.dir === "out" ? "◀" : "▶",
|
||
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(lg, s.rule) }));
|
||
return el("li", { className: "m02-dfpill-step", children: parts });
|
||
}),
|
||
});
|
||
}
|
||
|
||
/** 알약 판 그리기 — 알약을 고르면 부름 · on.pick 이 있으면 줄의 상자 이름을 눌러 그 상자로 감 */
|
||
export function renderPillPanel(
|
||
host: HTMLElement,
|
||
lg: CfLogic,
|
||
boxId: string,
|
||
port: string,
|
||
on: { pick?(boxId: string): void } = {},
|
||
opts: { values?: CfValues } = {},
|
||
): void {
|
||
host.classList.add("m02-dfpill");
|
||
const box = boxOf(lg, boxId);
|
||
if (!box) return host.replaceChildren();
|
||
const ev = evalSafe(lg, opts.values);
|
||
const out = isOut(box, port);
|
||
// 받는 알약의 값 = 그 자리로 들어오는 선의 보낸 알약 값(규칙 지나기 전)
|
||
const incoming = lg.links.find((l) => l.to.box === boxId && l.to.name === port);
|
||
const value = out
|
||
? valueOf(ev, { box: boxId, name: port })
|
||
: incoming
|
||
? valueOf(ev, incoming.from)
|
||
: undefined;
|
||
const unit = out
|
||
? unitOf(lg, { box: boxId, name: port })
|
||
: incoming
|
||
? unitOf(lg, incoming.from)
|
||
: box.unit;
|
||
const back = trace(lg, { box: boxId, name: port }, "in");
|
||
const forward = trace(lg, { box: boxId, name: port }, "out");
|
||
const breaks = breakLines(lg, boxId, port);
|
||
host.replaceChildren(
|
||
el("div", {
|
||
className: "m02-dflink-head",
|
||
children: [
|
||
el("strong", { className: "m02-dfpill-title", text: `${box.name} › ${port}` }),
|
||
el("span", { className: "m02-dflink-note", text: tp(out ? "Kind_out" : "Kind_in") }),
|
||
],
|
||
}),
|
||
line(tp("Name"), port),
|
||
line(tp("Value"), el("span", { className: "m02-dflink-value", text: showValue(value) })),
|
||
line(tp("Unit"), unit || "-"),
|
||
el("div", {
|
||
children: [
|
||
el("div", { className: "m02-dflink-label", text: tp("From") }),
|
||
steps(lg, back, tp("Start"), on.pick),
|
||
],
|
||
}),
|
||
el("div", {
|
||
children: [
|
||
el("div", { className: "m02-dflink-label", text: tp("To") }),
|
||
steps(lg, forward, tp("End"), on.pick),
|
||
],
|
||
}),
|
||
el("div", {
|
||
children: [
|
||
el("div", { className: "m02-dflink-label", text: tp("Breaks") }),
|
||
...(breaks.length
|
||
? breaks.map((b) => el("p", { className: "m02-dflink-warn", text: `⚠ ${b}` }))
|
||
: [el("p", { className: "m02-dflink-note", text: tp("NoBreak") })]),
|
||
],
|
||
}),
|
||
);
|
||
}
|