- 시험판 ▾ 목록 「단가산출 흐름도(시험)」 · main.py DataFlow 서버 등록 줄 - 페이지 제목 「단가산출 흐름도」 · 단추 공용 작은 크기(size "sm") · 큰 단추 css 덮기 없앰 - 상세 내용(옛 「고른 상자」) — 머리 왼쪽 「상세 내용」 · 오른쪽 고른 상자 제목 · 알약이면 알약 이름 · 설정 · 본문 · 경고 차례 - 고름 = 상자 · 선 · 알약 — 종류마다 판을 바꿔 끼움(선 = 보내는 곳 · 받는 곳 · 선 규칙 글 · 알약 = 온 길 · 가는 길) - 경고 = 검사 · 계산 글 가운데 고른 것 몫만 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
184 lines
6.9 KiB
TypeScript
184 lines
6.9 KiB
TypeScript
/* =============================================================================
|
|
* M02_DF_Entry_Detail.ts
|
|
* 단가산출 흐름도 — 「상세 내용」 칸 틀(정함 9): 머리(왼쪽 「상세 내용」 · 오른쪽 고른 상자 제목 · 알약이면 그 옆 알약 이름)
|
|
* · 설정 · 본문 · 경고 차례. 상자 · 알약 · 선 무엇을 골라도 바뀜 — 본문은 Entry 가 종류별 판을 끼움.
|
|
* 선 · 알약 본문은 규칙 판 · 알약 판(sub2)이 오기 전까지 이 파일이 흐름 줄(보내는 곳 → 받는 곳 · 출처 사슬 · 가는 곳)만 보임.
|
|
* ========================================================================== */
|
|
|
|
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
|
import { el } from "@ui/ui_template_elements";
|
|
import type { CfLogic, CfPort, CfStep } from "./M02_DF_Model";
|
|
|
|
const TEXT = {
|
|
Detail: ["상세 내용", "Details"],
|
|
Settings: ["설정", "Settings"],
|
|
Body: ["본문", "Content"],
|
|
Warn: ["경고", "Warnings"],
|
|
WarnNone: ["경고 없음", "No warnings"],
|
|
PickSome: [
|
|
"다이어그램에서 상자 · 알약 · 선을 고르면 여기에 그 상세 내용이 보임",
|
|
"Choose a box, pill or line in the diagram",
|
|
],
|
|
Line: ["선", "Line"],
|
|
From: ["보내는 곳", "From"],
|
|
To: ["받는 곳", "To"],
|
|
Rule: ["선 규칙", "Line rule"],
|
|
RuleNone: ["없음 — 값을 그대로 보냄", "None — sends the value as is"],
|
|
Back: ["온 길(출처)", "Came from"],
|
|
Forward: ["가는 길", "Goes to"],
|
|
NoTrace: ["아직 따라갈 수 없음", "Can't trace yet"],
|
|
Nowhere: ["이어진 곳 없음", "Not connected"],
|
|
In: ["받는 알약", "Incoming pill"],
|
|
Out: ["내보내는 알약", "Outgoing pill"],
|
|
} as const;
|
|
|
|
const tt = (key: keyof typeof TEXT): string =>
|
|
(TEXT[key][currentLanguageIndex as 0 | 1] ?? TEXT[key][0]) as string;
|
|
|
|
/** 고른 것 — 상자 · 선 · 알약 */
|
|
export type DfPick =
|
|
| { kind: "box"; id: string }
|
|
| { kind: "link"; id: string }
|
|
| { kind: "pill"; box: string; name: string; dir: "in" | "out" }
|
|
| null;
|
|
|
|
export interface DetailParts {
|
|
root: HTMLElement;
|
|
title: HTMLElement;
|
|
pill: HTMLElement;
|
|
hint: HTMLElement;
|
|
settings: HTMLElement;
|
|
body: HTMLElement;
|
|
warn: HTMLElement;
|
|
}
|
|
|
|
const row = (label: string, host: HTMLElement): HTMLElement =>
|
|
el("div", {
|
|
className: "m02-df__detail-row",
|
|
children: [el("h4", { text: label }), host],
|
|
});
|
|
|
|
/** 상세 내용 칸 — 머리 + 설정 · 본문 · 경고 */
|
|
export function detailFrame(): DetailParts {
|
|
const title = el("strong", { className: "m02-df__detail-title" });
|
|
const pill = el("span", { className: "m02-df__detail-pill" });
|
|
const hint = el("p", { className: "m02-df__muted", text: tt("PickSome") });
|
|
const settings = el("div", { className: "m02-df__detail-settings" });
|
|
const body = el("div", { className: "m02-df__detail-body" });
|
|
const warn = el("ul", { className: "m02-df__detail-warn" });
|
|
const root = el("section", {
|
|
className: "m02-df__part m02-df__detail",
|
|
children: [
|
|
el("div", {
|
|
className: "m02-df__detail-head",
|
|
children: [
|
|
el("h3", { text: tt("Detail") }),
|
|
el("div", { className: "m02-df__detail-name", children: [title, pill] }),
|
|
],
|
|
}),
|
|
el("div", {
|
|
className: "m02-df__part-body",
|
|
children: [
|
|
hint,
|
|
row(tt("Settings"), settings),
|
|
row(tt("Body"), body),
|
|
row(tt("Warn"), warn),
|
|
],
|
|
}),
|
|
],
|
|
});
|
|
return { root, title, pill, hint, settings, body, warn };
|
|
}
|
|
|
|
const boxName = (lg: CfLogic, id: string): string => lg.boxes.find((b) => b.id === id)?.name ?? id;
|
|
const portText = (lg: CfLogic, p: CfPort): string => `${boxName(lg, p.box)} · ${p.name}`;
|
|
|
|
/** 머리 · 빈 자리 — 고른 것에 맞춤 · 설정 · 경고 줄은 Entry 가 채움 */
|
|
export function drawHead(d: DetailParts, lg: CfLogic, pick: DfPick): void {
|
|
d.hint.hidden = !!pick;
|
|
for (const r of [d.settings, d.body, d.warn]) (r.parentElement as HTMLElement).hidden = !pick;
|
|
const link = pick?.kind === "link" ? lg.links.find((l) => l.id === pick.id) : undefined;
|
|
d.title.textContent =
|
|
pick?.kind === "box"
|
|
? boxName(lg, pick.id)
|
|
: pick?.kind === "pill"
|
|
? boxName(lg, pick.box)
|
|
: link
|
|
? `${tt("Line")} · ${boxName(lg, link.from.box)} → ${boxName(lg, link.to.box)}`
|
|
: "";
|
|
d.pill.textContent =
|
|
pick?.kind === "pill" ? `${tt(pick.dir === "in" ? "In" : "Out")} · ${pick.name}` : "";
|
|
d.pill.hidden = pick?.kind !== "pill";
|
|
}
|
|
|
|
/** 경고 — 화면 계산 · 검사 글 가운데 고른 것(상자 이름 · 알약 이름)이 든 줄만 */
|
|
export function drawWarnings(d: DetailParts, lg: CfLogic, pick: DfPick, all: string[]): void {
|
|
const names: string[] = [];
|
|
if (pick?.kind === "box") names.push(`「${boxName(lg, pick.id)}」`);
|
|
if (pick?.kind === "pill") names.push(`「${boxName(lg, pick.box)}」`);
|
|
if (pick?.kind === "link") {
|
|
const l = lg.links.find((x) => x.id === pick.id);
|
|
if (l) names.push(`「${boxName(lg, l.from.box)}」`, `「${boxName(lg, l.to.box)}」`);
|
|
}
|
|
const mine = [...new Set(all)].filter(
|
|
(w) => names.some((n) => w.includes(n)) && (pick?.kind !== "pill" || w.includes(pick.name)),
|
|
);
|
|
d.warn.replaceChildren(
|
|
...(mine.length
|
|
? mine.map((w) => el("li", { text: w }))
|
|
: [el("li", { className: "m02-df__muted", text: tt("WarnNone") })]),
|
|
);
|
|
}
|
|
|
|
/** 선 본문 — 보내는 곳 → 받는 곳 · 선 규칙 글(규칙 판이 오면 그쪽이 그림) */
|
|
export function lineBody(
|
|
host: HTMLElement,
|
|
lg: CfLogic,
|
|
id: string,
|
|
ruleText: string | null,
|
|
): void {
|
|
const l = lg.links.find((x) => x.id === id);
|
|
if (!l) return void host.replaceChildren();
|
|
host.replaceChildren(
|
|
el("dl", {
|
|
className: "m02-df__detail-dl",
|
|
children: [
|
|
el("dt", { text: tt("From") }),
|
|
el("dd", { text: portText(lg, l.from) }),
|
|
el("dt", { text: tt("To") }),
|
|
el("dd", { text: portText(lg, l.to) }),
|
|
el("dt", { text: tt("Rule") }),
|
|
el("dd", { text: ruleText ?? tt("RuleNone") }),
|
|
],
|
|
}),
|
|
);
|
|
}
|
|
|
|
/** 알약 본문 — 온 길(출처 사슬) · 가는 길 · 흐름 따라가기(Model traceBack · traceForward)가 아직이면 한 줄 */
|
|
export function pillBody(
|
|
host: HTMLElement,
|
|
lg: CfLogic,
|
|
back: () => CfStep[],
|
|
forward: () => CfStep[],
|
|
): void {
|
|
const list = (steps: () => CfStep[]): HTMLElement => {
|
|
let got: CfStep[];
|
|
try {
|
|
got = steps();
|
|
} catch {
|
|
return el("p", { className: "m02-df__muted", text: tt("NoTrace") });
|
|
}
|
|
if (!got.length) return el("p", { className: "m02-df__muted", text: tt("Nowhere") });
|
|
return el("ol", {
|
|
className: "m02-df__detail-trace",
|
|
children: got.map((s) => el("li", { text: portText(lg, { box: s.box, name: s.port }) })),
|
|
});
|
|
};
|
|
host.replaceChildren(
|
|
el("h5", { text: tt("Back") }),
|
|
list(back),
|
|
el("h5", { text: tt("Forward") }),
|
|
list(forward),
|
|
);
|
|
}
|