/* ============================================================================= * M02_MasterTemplete_Sangeun_Side.ts * 단가산출(SK) 상세 오른쪽 원문 패널(PLAN 752-1 · 요구 588-3) — 일위대가 상세(47-4 · 85-4)와 같은 부품: * 오른쪽 서랍(`createSidePanels`) + 원문 보기(`createSourceView` · M02 로그인 길 `/m02/logic-lib/source`) * 기초마다 탭 — 바탕 로직 · 둘째 기초(더한줄 로직) 각각의 출처 · 기초가 하나면 탭 없음 · 없으면 안내 * 처음 = 넓은 화면만 열림(좁으면 세로 띠로 접힘) · 열림 · 폭 기억 = `m02-sangeun-side` * 맨 위 「단가산출 기초 찾기」(807-1 · M01 원문 패널과 같은 `LogicLab_SourceFind`) — 고르면 그 기초 원문(보기만 · 기억 안 함) · * 지우면 처음 화면 · 처음 화면 = 우리 SK 는 기초 원문 · 가져온 식 SK 는 「어디서 왔나」 카드 * (프로그램 · 파일 · 블록 · 행 · 가져온 때 · 표지 요약 + 엑셀 글의 품셈 절 글만 — 찾기 · 잇기 없음) * 카드 맨 위 = 우리 기초 판정 띠(810-4 · `Sangeun_MatchBand`) · 우리 기초 사본으로 바꾼 SK = 보관한 가져온 식 카드 * 일위대가 줄 모달 안 상세는 안 만듦(부르는 쪽이 `source` 를 안 켬). * ========================================================================== */ import { createSegment } from "@ui/ui_template_segment"; import { el } from "@ui/ui_template_elements"; import { currentLanguageIndex, t as L } from "@ui/ui_template_locale"; import { createSidePanels } from "@ui/ui_template_side_panels"; import { createText } from "@ui/ui_template_text"; import { createButton } from "@ui/ui_template_button"; import { createCard } from "@ui/ui_template_card"; import { createKv } from "@ui/ui_template_kv"; import { createSourceView } from "../M01_MasterData/M01_MasterData_UI_LogicLab_Source"; import { createSourceFind } from "../M01_MasterData/M01_MasterData_UI_LogicLab_SourceFind"; import { originOf, type Origin, type OriginMeta } from "./logic/M02_Sangeun_Origin"; import { tc } from "./M02_MasterTemplete_Labor_Text"; import { createMatchBand, type MatchCtx } from "./M02_MasterTemplete_Sangeun_MatchBand"; export interface SourceTab { label: string; src: string; } /** 기초 로직 → 원문 탭(「원문번호 이름」 · 출처 글) */ export const tabOf = (logic: { 키: string; 원문번호?: string; 이름: string; 출처?: string; }): SourceTab => ({ label: `${logic.원문번호 || logic.키} ${logic.이름}`, src: logic.출처 ?? "", }); /** 이 폭 이상일 때만 처음부터 열림 — 상세 표 여섯 칸이 좁아지지 않게 */ const OPEN_FROM = 1500; const TEXT = { Origin: ["가져온 단가산출 — 어디서 왔나", "Imported build-up — where from"], Program: ["프로그램", "Program"], File: ["파일", "File"], Block: ["블록", "Block"], Rows: ["행", "Rows"], When: ["가져온 때", "Imported"], Marks: ["표지", "Flags"], MarksN: ["{n} 개", "{n}"], Clauses: ["엑셀 글의 품셈 정보", "Standard clauses in the Excel text"], NoClause: ["엑셀 글에 품셈 절 글 없음", "No standard clause in the Excel text"], Hint: ["우리 기초 원문은 위 찾기로", "Find our base above to see its source"], More: ["표지 모두 보기", "Show all flags"], Sure: ["원파일 확정", "Source-file read"], } as const; const tx = (k: keyof typeof TEXT, n = 0): string => TEXT[k][currentLanguageIndex === 1 ? 1 : 0].replace("{n}", String(n)); /** 「어디서 왔나」 카드 — 표지는 셋까지(더 = 단추로 펼침) */ function originCard(o: Origin, band: HTMLElement | null): HTMLElement { const marks = el("div"); const showMarks = (all: boolean): void => marks.replaceChildren( ...(all ? o.표지 : o.표지.slice(0, 3)).map((m) => createText(m, "muted", 12)), ...(!all && o.표지.length > 3 ? [createButton({ label: tx("More"), variant: "ghost", size: 24, onClick: () => showMarks(true) })] // prettier-ignore : []), ); showMarks(false); const clauses = o.품셈.length ? o.품셈.map((c) => createKv([{ label: `${c.책} ${c.절}`, value: c.행 !== undefined ? `${c.행}행 · ${c.글}` : c.글 }]), ) // prettier-ignore : [createText(tx("NoClause"), "muted", 12)]; const card = createCard({ title: tx("Origin"), pad: 12, body: [ ...(band ? [band] : []), ...(o.요약 ? [el("strong", { text: o.요약, attrs: { "data-origin-native": "" } })] : []), createKv([ { label: tx("Program"), value: o.프로그램 }, { label: tx("File"), value: o.파일 }, { label: tx("Block"), value: o.블록 }, { label: tx("Rows"), value: o.행 }, { label: tx("When"), value: o.때 }, { label: tx("Marks"), value: tx("MarksN", o.표지.length) }, ...(o.확정 ? [{ label: tx("Sure"), value: o.확정 }] : []), ]), marks, el("strong", { text: tx("Clauses") }), ...clauses, createText(tx("Hint"), "muted", 12), ], }); card.setAttribute("data-sangeun-origin", ""); return card; } export function createSangeunSide(match?: MatchCtx): { wrap: (main: HTMLElement) => HTMLElement; /** 원문 탭 · SK 문서(`가져온` · 바꾼 SK `가져온보관` — 있으면 첫 화면 = 카드 + 판정 띠) */ set: (tabs: SourceTab[], sk?: unknown) => void; } { const view = createSourceView({ path: "/m02/logic-lib/source", pdf: "/m02/logic-lib/source-pdf", }); const content = el("div"); let found: string | null = null; // 찾기로 고른 기초 출처(지우면 처음 화면) const find = createSourceFind({ onPick: (x) => ((found = x.출처 ?? ""), paint()), onClear: () => ((found = null), paint()), }); const host = el("div", { className: "m01lab-src", attrs: { "data-sangeun-source": "" }, children: [find.root, content], }); let tabs: SourceTab[] = []; let origin: Origin | null = null; let skDoc: unknown = null; let at = 0; const side = createSidePanels({ prefKey: "m02-sangeun-side", follow: false, // 일위대가와 같음 — 상세 고정 높이 칸을 채움 · 본문과 따로 스크롤 defaultOpen: window.innerWidth >= OPEN_FROM ? "source" : null, panels: [{ id: "source", title: tc("Labor_Source"), body: host }], onChange: () => paint(), }); function paint(): void { if (side.current() !== "source") return; // 접힘 = 찾지 않음 if (found !== null) { view.show(found); return void content.replaceChildren(view.root); } if (origin) return void content.replaceChildren(originCard(origin, createMatchBand(skDoc, match))); if (!tabs.length) return void content.replaceChildren(createText(L("M02_SangeunSourceNone"), "muted")); at = Math.min(at, tabs.length - 1); view.show(tabs[at].src); const bar = tabs.length > 1 ? [ createSegment({ options: tabs.map((x, i) => ({ value: String(i), label: x.label })), value: String(at), size: 28, ariaLabel: tc("Labor_Source"), onChange: (v) => ((at = Number(v)), paint()), }).root, ] : []; content.replaceChildren(...bar, view.root); } return { wrap: (main) => el("div", { className: "ui-side-panels-row", children: [main, side.root] }), set: (next, sk) => { const one = sk as { 가져온?: unknown; 가져온보관?: { 가져온?: unknown } } | null; const card = originOf((one?.가져온 ?? one?.가져온보관?.가져온) as OriginMeta | null); const same = JSON.stringify([next, card, sk]) === JSON.stringify([tabs, origin, skDoc]); if (same) return; [tabs, origin, skDoc] = [next, card, sk ?? null]; paint(); }, }; }