Files
Aislo/M02_MasterTemplete/DataFlow/M02_DF_PillPanel.ts
T
eomsangdonandClaude Opus 5.5 09134bab84 refactor(M02): 띠 알림 · 빈 상태 · 보조 글 · 배지 · 탭 · 표 · 목록 줄 · 도구줄 · 체크를 부품으로 (PLAN 553-5)
- DataFlow: 표 넷 = 데이터표 · 함수 판 알림 · 선/알약 경고 · 끊긴 곳 = 띠 알림 · 알약 · 칸 종류 = 배지 · 고르는 줄 = 목록 줄 · 입력 넷 = 입력 · 도구줄 여섯 · 날 색 → 공통 값
- 구조물 옵션 · 구조물 · 도면 · 면적 · 품목 고르기: 판 띠 = 띠 알림 · 한눈/종류별/부품 = 탭 · 표 둘 = 데이터표 · 체크 둘 = 체크 · 카드 · 번호 = 카드 · 배지 · 도면 도구줄
- 일위대가 · 호표 · 실무자료 · 본 화면 · 서고: 알림 = 띠 알림 · 빈 상태 · 보조 글 · 도구줄 여섯 · 옛 보라 별칭 0
- M01 로직 입력칸 = 입력 부품(작은 칸) · 일위대가 상세 줄 수 고정 시험 693 · 지킴 시험 M02 몫 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 23:44:19 +09:00

170 lines
5.8 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";
import "@ui/ui_template_text.css";
import "@ui/ui_template_banner.css";
import "@ui/ui_template_badge.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: "ui-text ui-text--muted", 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: "ui-badge ui-badge--pill ui-badge--neutral",
attrs: { "data-size": "20" },
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: "ui-badge ui-badge--pill ui-badge--neutral",
attrs: { "data-size": "20" },
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: "ui-banner ui-banner--warning m02-dflink-warn",
text: `⚠ ${b}`,
}),
)
: [el("p", { className: "ui-text ui-text--muted", text: tp("NoBreak") })]),
],
}),
);
}