Files
Aislo/M02_MasterTemplete/DataFlow/M02_DF_PillPanel.ts
T

148 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* 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 { CfLogic, CfStep } from "./M02_DF_Model";
import {
boxOf,
breakLines,
evalSafe,
isOut,
labelOf,
portText,
showValue,
trace,
unitOf,
valueOf,
type DetailOptions,
} 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[],
arrow: string,
empty: string,
onBox?: (id: string) => void,
): HTMLElement {
if (!list.length) return el("p", { className: "m02-dflink-note", text: empty });
const pill = (box: string, name: string): HTMLElement | string =>
onBox
? createButton({
label: portText(lg, { box, name }),
variant: "ghost",
size: "sm",
onClick: () => onBox(box),
})
: portText(lg, { box, name });
return el("ol", {
className: "m02-dfpill-list",
children: list.map((s) => {
const link = s.link ? lg.links.find((l) => l.id === s.link) : undefined;
const parts: (HTMLElement | string)[] = link
? [
arrow,
pill(link.from.box, link.from.name),
"→",
...(s.rule
? [el("span", { className: "m02-dfpill-rule", text: `(${labelOf(s.rule)})` }), "→"]
: []),
pill(link.to.box, link.to.name),
]
: [arrow, pill(s.box, s.port)];
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: DetailOptions = {},
): void {
host.classList.add("m02-dfpill");
const box = boxOf(lg, boxId);
if (!box) return host.replaceChildren();
const ev = evalSafe(lg, opts);
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 })
: (ev?.arrived?.[boxId]?.[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, opts);
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") })]),
],
}),
);
}