Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Hopyo_Compare.ts
T
eomsangdonandClaude Sonnet 5.5 6732842a8e feat(746-2): 고르기 창 후보 블록 줄 비교 표 · 못옮김 · 안 이은 기계 줄 M01 요소 후보 단추
- 고름 대기 칸에 후보(앞 셋)마다 줄 비교 표(줄 · 엑셀 품 · 기초 품 · 꼴 배지) · 후보 고름 칸과 동기 · 칸 글에 같음 · 품 다름 수 · 판정 · 미리 고름 그대로
- 못옮김 잇기 창 · 안 이은 줄 창에 M01 요소 후보 셋 단추(누르면 이음 고름) · 안 이은 줄 창 기초 후보 칸에는 요소 후보가 안 섞이게 가름
- 새 파일 Hopyo_Compare.ts · 새 지킴 시험 test_746_2_hopyo_compare_screen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 22:17:48 +09:00

128 lines
4.8 KiB
TypeScript

/* =============================================================================
* M02_MasterTemplete_Hopyo_Compare.ts
* 불러오기 고름 창 — 후보 블록 줄 비교 · M01 요소 후보(PLAN 746-2 · 요구 585 · 서버 746-1 `줄비교` · `후보`) —
* · `compareView` = 고름 대기 후보(앞 셋)마다 줄 비교 표(줄 · 엑셀 품 · 기초 품 · 꼴 배지) + 못옮김 수 · 후보 바꿈 단추
* · `elemChoices` = 못옮김 · 안 이은 기계 줄의 M01 요소 후보 셋 단추(누르면 부르는 쪽이 이음)
* 보이기만 — 판정 · 순위 · 미리 고름은 서버 그대로 · 고르기는 부르는 쪽.
* ========================================================================== */
import { createBadge, type BadgeTone } from "@ui/ui_template_badge";
import { createButton, el } from "@ui/ui_template_elements";
import { t as L, type LocaleKey } from "@ui/ui_template_locale";
import { createSegment } from "@ui/ui_template_segment";
import { createTable } from "@ui/ui_template_table";
import { createText } from "@ui/ui_template_text";
import type {
CompareLook,
ElemCand,
SangeunCand,
SangeunCompare,
} from "./M02_MasterTemplete_Hopyo_Api";
const LOOK: Record<CompareLook, { tone: BadgeTone; key: LocaleKey }> = {
같음: { tone: "success", key: "M02_CmpSame" },
품다름: { tone: "warning", key: "M02_CmpDiff" },
엑셀만: { tone: "info", key: "M02_CmpExcel" },
기초만: { tone: "neutral", key: "M02_CmpBase" },
};
const LOOKS = Object.keys(LOOK) as CompareLook[];
const num = (n: number | null): string => (n === null ? "—" : String(Number(n.toPrecision(4))));
/** 선택 칸 글 뒤 요약 — 「같음 3 · 품 다름 1」 · 견줌 못함 = "" */
export const compareBrief = (c: SangeunCompare | null | undefined): string =>
c
? LOOKS.filter((k) => c.수[k])
.map((k) => `${L(LOOK[k].key)} ${c.수[k]}`)
.join(" · ")
: "";
/** 줄 비교 표 하나 */
function compareTable(c: SangeunCompare): HTMLElement {
const rows = c.줄.map((x) => [
`${x.갈래} ${x.이름}${x.규격 ? ` ${x.규격}` : ""}`,
`${num(x.엑셀)} ${x.단위 ?? ""}`.trim(),
`${num(x.기초)} ${x.단위 ?? ""}`.trim(),
createBadge(L(LOOK[x.꼴].key), LOOK[x.꼴].tone),
]);
return createTable(
[
{ label: L("M02_SangeunColName") },
{ label: L("M02_CmpColExcel"), width: "112px", align: "end" },
{ label: L("M02_CmpColBase"), width: "112px", align: "end" },
{ label: L("M02_CmpColLook"), width: "84px" },
],
rows,
{ density: "data", empty: L("M02_CmpNone") },
);
}
/**
* 고름 대기 후보 줄 비교 칸 — 앞 후보 줄 비교를 후보 단추로 바꿔 봄 · `show(로직)` 으로 밖(고르기 칸)이 바꿈.
* 줄 비교 있는 후보가 하나도 없으면 null.
*/
export function compareView(
cands: SangeunCand[],
): { root: HTMLElement; show: (로직: string) => void } | null {
const has = cands.filter((c) => c.줄비교);
if (!has.length) return null;
const root = el("div", { className: "m02-hopyo__cmp", attrs: { "data-cmp": "" } });
const body = el("div");
const seg = createSegment({
options: has.map((c, i) => ({ value: c.로직, label: `${i + 1}. ${c.이름}`, title: c.이름 })),
value: has[0]!.로직,
size: 24,
ariaLabel: L("M02_CmpTitle").replace("{name}", ""),
onChange: (v) => paint(v),
});
const paint = (로직: string): void => {
const c = has.find((x) => x.로직 === 로직) ?? has[0]!;
const cmp = c.줄비교!;
body.dataset.cmpOf = c.로직;
const lost = cmp.못옮김.length;
body.replaceChildren(
createText(
[
L("M02_CmpTitle").replace("{name}", c.이름),
compareBrief(cmp),
lost ? L("M02_CmpLost").replace("{n}", String(lost)) : "",
]
.filter(Boolean)
.join(" · "),
"muted",
12,
),
compareTable(cmp),
);
};
root.append(seg.root, body);
paint(has[0]!.로직);
return {
root,
show: (로직) => {
if (!has.some((c) => c.로직 === 로직)) return;
seg.set(로직);
paint(로직);
},
};
}
/** 요소 후보 단추 줄(셋까지) — 누르면 `pick(후보)` */
export function elemChoices(cands: ElemCand[], pick: (c: ElemCand) => void): HTMLElement | null {
if (!cands.length) return null;
const box = el("div", { className: "ui-toolbar", attrs: { "data-elem": "" } });
box.title = L("M02_ElemTitle");
for (const c of cands.slice(0, 3)) {
const price = c.단가 ? ` · ${c.단가.toLocaleString()}` : "";
const b = createButton({
label: `${c.이름}${c.규격 ? ` ${c.규격}` : ""} [${c.단위 ?? ""}]${price}`,
size: 24,
variant: "ghost",
onClick: () => pick(c),
});
b.dataset.elemKey = c.키;
b.title = `${c.키} · ${c.갈래}`;
box.append(b);
}
return box;
}