/* ============================================================================= * M02_MasterTemplete_Hopyo_SheetSk.ts * 엑셀 보기 창 오른쪽 판 바꾸기(PLAN 780-2 · 809-1 · 요구 627 · 702) — 탭 따라: * · 일위대가표 = 호표 목록(`Hopyo_Sheet` 판 그대로) · 단가산출표 · 단가산출 상세 = 단가산출 목록 * (블록 줄 = 단산 N 이름 · 단위 · 부름 수 / 일위대가 밖 · 만든 가져온 식 SK · 재료 = `hopyo-marks`) * · 블록 줄 누름 → 지금 탭의 그 블록 자리 · 줄 끝 [가져온 식으로 열기](만들 층) · [내 단가산출로](개인 층 · 807-1) = `Hopyo_Imported` 모달 * (경쟁사 로직 그대로 · 엑셀 단가 · 요소 이음 · 소요량 이음) * · 시트 칸 고름 → 그 행 블록을 목록에서 강조(`SheetNav.blockAt`) * · 목록은 처음 단가산출 탭에 올 때 읽음 · 기초 판정 · 블록 판 · [옮기기] 없음(요구 703) * ========================================================================== */ import { createBadge } from "@ui/ui_template_badge"; import { createButton, el } from "@ui/ui_template_elements"; import { t as L } from "@ui/ui_template_locale"; import { createListRow } from "@ui/ui_template_list_row"; import { createText } from "@ui/ui_template_text"; import type { HopyoScope } from "./M02_MasterTemplete_Hopyo_Api"; import { openImported } from "./M02_MasterTemplete_Hopyo_Imported"; import { readHopyoMarks, type SheetNav } from "./M02_MasterTemplete_Hopyo_SheetNav"; import type { BlockMark } from "./logic/M02_MasterTemplete_Hopyo_SheetMap"; export interface SheetSkOptions { scope: HopyoScope; key: string; target: HopyoScope; canWrite: boolean; nav: SheetNav; /** 호표 목록 판 — 단가산출 탭이면 숨김 · 그 뒤에 단가산출 판을 붙임 */ side: HTMLElement; /** 목록 읽은 뒤(부르는 쪽 끝내기 글) */ onChange?: () => void; } const STRIPE = { done: "var(--color-success)", ready: "var(--color-info)", out: "var(--color-border)" } as const; // prettier-ignore export function createSheetSk(opt: SheetSkOptions) { const list = el("div", { className: "m02-hopyo-sheet__list" }); const root = el("div", { className: "m02-hopyo-sheet__side m02-hopyo-sheet__sk", children: [list] }); // prettier-ignore root.setAttribute("data-hopyo-sk-side", ""); root.hidden = true; opt.side.after(root); let blocks: BlockMark[] | null = null; let asked = false; let sheet = ""; let current: number | null = null; const mineAt = { layer: "personal", owner: null } as const; // [내 단가산출로](807-1) = 늘 개인 층 const open = (no: number, mine: boolean): void => openImported({ file: opt.scope, key: opt.key, no, target: mine ? mineAt : opt.target, canWrite: mine || opt.canWrite }); // prettier-ignore function draw(): void { if (!blocks) return; list.replaceChildren( ...blocks.map((b) => { const calls = b.부름?.length ?? 0; const sub = [b.단위 ?? "", calls ? `${L("M02_JudgeCalls")} ${calls}` : L("M02_JudgeOutside")].filter(Boolean).join(" · "); // prettier-ignore const go = createButton({ label: L("M02_CheckImported"), size: 24, variant: "ghost", onClick: (e) => { e.stopPropagation(); open(b.번, false); } }); // prettier-ignore go.dataset.skOpen = String(b.번); go.hidden = opt.target.layer === "personal" && !opt.target.owner; // 만들 층 = 개인이면 [내 단가산출로]와 같은 일 const mine = createButton({ label: L("M02_ToMySk"), size: 24, variant: "ghost", onClick: (e) => { e.stopPropagation(); open(b.번, true); } }); // prettier-ignore mine.dataset.skMine = String(b.번); const tail = el("span", { className: "ui-toolbar", children: [...(b.SK ? [createBadge(`${L("M02_JudgeMade")} ${b.SK}`, "success")] : []), go, mine] }); // prettier-ignore const row = createListRow({ label: `${L("M02_JudgeBlock").replace("{n}", String(b.번))} ${b.이름 ?? ""}`, sub, trailing: tail, stripe: b.SK ? STRIPE.done : calls ? STRIPE.ready : STRIPE.out, active: b.번 === current, size: 36, onClick: () => pick(b.번), }); row.dataset.judgeSk = String(b.번); return row; }), ); } /** 목록 — 처음 단가산출 탭에 올 때 한 번 */ function ensure(): void { if (asked) return; asked = true; if (!blocks) list.replaceChildren(createText(L("M02_JudgeLoading"), "hint")); void readHopyoMarks(opt.scope, opt.key, opt.target) .then((got) => { blocks = got; draw(); opt.onChange?.(); }) .catch((e: Error) => list.replaceChildren(createText(e.message, "error"))); } /** 블록 고름 — 그 자리 뜀 */ function pick(no: number): void { current = no; draw(); const list = opt.nav.role(sheet) === "단가산출표"; const spot = list ? (opt.nav.spotOf(no, "단가산출표") ?? opt.nav.spotOf(no, "단가산출상세")) : (opt.nav.spotOf(no, "단가산출상세") ?? opt.nav.spotOf(no, "단가산출표")); if (spot) opt.nav.jumpTo(spot.시트, spot.행); } return { root, /** 아직 SK 안 만든 블록 수 · 그중 일위대가 밖 — 목록 전 null */ count: (): { 블록: number; 밖: number; 줄: number } | null => { if (!blocks) return null; const left = blocks.filter((b) => !b.SK); return { 블록: left.length, 밖: left.filter((b) => !b.부름?.length).length, 줄: 0 }; }, /** 그 블록으로 뜀 */ show(번: number): void { ensure(); pick(번); }, /** 시트 고름 · 탭 바꿈 — 판 바꾸기 · 강조 */ at(sel: { 시트: string; 칸: string }): void { sheet = sel.시트; const role = opt.nav.role(sel.시트); const sk = role === "단가산출표" || role === "단가산출상세"; opt.side.hidden = sk; root.hidden = !sk; opt.side.parentElement?.classList.toggle("is-sk", sk); if (!sk) return; ensure(); const no = opt.nav.blockAt(sel.시트, Number(/\d+/.exec(sel.칸)?.[0] ?? 0)); if (no !== null && no !== current) { current = no; draw(); } }, destroy(): void {}, }; } export type SheetSk = ReturnType;