- 고름 대기 칸에 후보(앞 셋)마다 줄 비교 표(줄 · 엑셀 품 · 기초 품 · 꼴 배지) · 후보 고름 칸과 동기 · 칸 글에 같음 · 품 다름 수 · 판정 · 미리 고름 그대로 - 못옮김 잇기 창 · 안 이은 줄 창에 M01 요소 후보 셋 단추(누르면 이음 고름) · 안 이은 줄 창 기초 후보 칸에는 요소 후보가 안 섞이게 가름 - 새 파일 Hopyo_Compare.ts · 새 지킴 시험 test_746_2_hopyo_compare_screen Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
128 lines
4.8 KiB
TypeScript
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;
|
|
}
|