- 지움(git rm) 15 파일 3,318 줄: Hopyo_Check · CheckRows · Choose · WizardSk · SkApi · SkLines · logic/SkPick · logic/Draft · Left · Lost · Pick · SheetMake · Kids · ToItem · Compare(모두 옛 확인 창 · 마법사 단가산출 · 안 이은 줄 길 몫 · 부르는 곳 0) - 단추 판단: 엑셀 보기 [저장](체크 호표) · [모두 고정으로 들여오기] 화면 단추 걷음 → 만들기 = [옮기기] 하나 · 모두 고정 서버 길(Builder · build_all)은 B09 · 붙여넣기가 써서 남김 · 목록 체크 칸 · 모달 껍데기(openHopyoSheet · raise · lower) 걷음 - Import: 「안 이은 줄」 띠 · [안 이은 줄 고르기] · 남은 줄 셈 걷음 · [끝내기] = 물음(확인하고 안 옮긴 수 = 판정 고름 캐시 moveBody) - Api: 옛 맞춤 타입 · 길(readHopyoOne · guessHopyo · buildHopyo · buildHopyoAll · leftHopyo · listHopyoPicks · hopyoSiblings · buildHopyoItem …) 걷음 · POLL_MS · HOPYO_BUILT 옮겨 옴 · 목록 꼴 = 1차 판정 줄상태(Judge.view) - Labor_Pick: withHopyo(실무 호표 덧줄) → withSangeun(찾아 넣기 창 표시만) · Detail · Table 부름 바꿈 - Ledger: unitField 옮겨 옴 · SheetNav: 옛 확인 캐시 칠 걷음 · UI_Page: [구조물 조합으로] 듣기 걷음 - CSS: 옛 확인 창 · 고르기 창 규칙 34 걷음(481 → 287 줄) · 390 폭 = 줄 판 열리면 위 칸 넓힘 · 줄 판 높이 바닥 16rem 시험 정리(까닭 = 지운 화면 파일만 읽음): - 지움: test_729_3_hopyo_left_screen · test_746_2_hopyo_compare_screen · test_778_5_sk_pick_screen · helper_729_3 · helper_m02_hopyo_check · pick · seam - test_m02_hopyo_screen = 좌측 판 · 끝내기 둘만 · 627 · helper_778_5 = SheetSk 판 바꿈만 · 785_4 · 721 · 717_4 · 720 · 739_1 = 지금 꼴로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
190 lines
7.3 KiB
TypeScript
190 lines
7.3 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Hopyo_SheetSk.ts
|
|
* 엑셀 보기 창 오른쪽 판 바꾸기(PLAN 780-2 · 785-6 · 요구 627 · 642) — 탭 따라:
|
|
* · 일위대가표 = 호표 목록(`Hopyo_Sheet` 판 그대로) · 단가산출표 · 단가산출 상세 = 단가산출 목록
|
|
* (새 블록 판정 `hopyo-judge` 목록 블록 줄 = 판정 · 고를 것 n · 표지 · 부름 · 만든 SK · 캐시 확인)
|
|
* · 목록 블록 줄 누름 → 지금 탭의 그 블록 자리 + 시트 아래 줄 판 = 블록 판(`Hopyo_Block.createJudgeBlock` · [확인])
|
|
* · 옮김 = 이 판 아래 [옮기기](`Hopyo_Rows.createJudgeMove` · 호표 목록 판 것과 같은 캐시 · 한 몸)
|
|
* · 시트 칸 고름 → 그 행 블록을 목록에서 강조(`SheetNav.blockAt`)
|
|
* · 목록은 처음 단가산출 탭에 올 때 읽음 · 만들 층 ≠ 파일 층 · 쓰기 권한 없음 = 보기만
|
|
* ========================================================================== */
|
|
|
|
import { createBadge } from "@ui/ui_template_badge";
|
|
import { 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 { POLL_MS, type HopyoScope } from "./M02_MasterTemplete_Hopyo_Api";
|
|
import { createJudgeBlock, type JudgeState } from "./M02_MasterTemplete_Hopyo_Block";
|
|
import { createJudgeMove } from "./M02_MasterTemplete_Hopyo_Rows";
|
|
import { readJudge, type JudgeBlockBrief } from "./M02_MasterTemplete_Hopyo_JudgeApi";
|
|
import type { SheetNav } from "./M02_MasterTemplete_Hopyo_SheetNav";
|
|
|
|
export interface SheetSkOptions {
|
|
scope: HopyoScope;
|
|
key: string;
|
|
target: HopyoScope;
|
|
canWrite: boolean;
|
|
nav: SheetNav;
|
|
state: JudgeState;
|
|
/** 호표 목록 판 — 단가산출 탭이면 숨김 · 그 뒤에 단가산출 판을 붙임 */
|
|
side: HTMLElement;
|
|
/** 시트 아래 줄 판 자리 열기 · 닫기(`Hopyo_Sheet` 가 쥠) */
|
|
openLines: (make: (host: HTMLElement) => { destroy: () => void }) => void;
|
|
closeLines: () => void;
|
|
/** 판정 · 옮김 뒤(부르는 쪽 끝내기 글) */
|
|
onChange?: () => void;
|
|
/** 옮긴 뒤(목록 · 칠 다시 읽음) */
|
|
onMoved?: () => void;
|
|
}
|
|
|
|
const sameLayer = (a: HopyoScope, b: HopyoScope): boolean =>
|
|
a.layer === b.layer && (a.owner ?? null) === (b.owner ?? null);
|
|
const STRIPE = {
|
|
done: "var(--color-success)",
|
|
ready: "var(--color-info)",
|
|
pick: "var(--color-warning)",
|
|
} as const;
|
|
|
|
export function createSheetSk(opt: SheetSkOptions) {
|
|
const list = el("div", { className: "m02-hopyo-sheet__list" });
|
|
const readOnly = !opt.canWrite || !sameLayer(opt.scope, opt.target);
|
|
const move = createJudgeMove({
|
|
file: opt.scope,
|
|
key: opt.key,
|
|
target: opt.target,
|
|
state: opt.state,
|
|
readOnly,
|
|
...(opt.onMoved ? { onMoved: opt.onMoved } : {}),
|
|
});
|
|
const root = el("div", {
|
|
className: "m02-hopyo-sheet__side m02-hopyo-sheet__sk",
|
|
children: [list, move.root],
|
|
});
|
|
root.setAttribute("data-hopyo-sk-side", "");
|
|
root.hidden = true;
|
|
opt.side.after(root);
|
|
let blocks: JudgeBlockBrief[] | null = null;
|
|
let asked = false;
|
|
let sheet = "";
|
|
let current: number | null = null;
|
|
|
|
function draw(): void {
|
|
if (!blocks) return;
|
|
const d = opt.state.get();
|
|
list.replaceChildren(
|
|
...blocks.map((b) => {
|
|
const ok = !!d.블록[String(b.번)]?.확인;
|
|
const sub = [
|
|
b.단위,
|
|
b.판정 ? `${b.판정}${b.판정 === "고를 것" && b.고를수 ? ` ${b.고를수}` : ""}` : "",
|
|
b.표지 ?? "",
|
|
b.부름.length ? `${L("M02_JudgeCalls")} ${b.부름.length}` : L("M02_JudgeOutside"),
|
|
].filter(Boolean).join(" · "); // prettier-ignore
|
|
const tail = b.만든
|
|
? createBadge(`${L("M02_JudgeMade")} ${b.만든}`, "success")
|
|
: ok
|
|
? createBadge(L("M02_JudgeConfirmed"), "accent")
|
|
: "";
|
|
const row = createListRow({
|
|
label: `${L("M02_JudgeBlock").replace("{n}", String(b.번))} ${b.이름}`,
|
|
sub,
|
|
trailing: tail,
|
|
stripe: b.만든 || ok ? STRIPE.done : b.판정 === "고를 것" ? STRIPE.pick : STRIPE.ready,
|
|
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 readJudge(opt.scope, opt.key, opt.target)
|
|
.then((got) => {
|
|
blocks = got.블록;
|
|
draw();
|
|
opt.onChange?.();
|
|
// 블록 풀이 뒤 셈 중 — 끝나면 판정 · 고를 것이 실림(3 초마다 · 판이 붙어 있는 동안)
|
|
if (got.셈중) setTimeout(() => list.isConnected && ((asked = false), ensure()), POLL_MS);
|
|
})
|
|
.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.행);
|
|
const detail = opt.nav.spotOf(no, "단가산출상세")?.시트;
|
|
opt.openLines((host) =>
|
|
createJudgeBlock({
|
|
host,
|
|
file: opt.scope,
|
|
key: opt.key,
|
|
target: opt.target,
|
|
state: opt.state,
|
|
no,
|
|
readOnly,
|
|
onClose: opt.closeLines,
|
|
...(detail ? { onSheet: (r: number) => opt.nav.jumpTo(detail, r) } : {}),
|
|
}),
|
|
);
|
|
}
|
|
|
|
const off = opt.state.on(draw);
|
|
|
|
return {
|
|
root,
|
|
/** 아직 안 옮긴 블록 수 · 그중 일위대가 밖 · 고를 것 합 — 목록 전 null */
|
|
count: (): { 블록: number; 밖: number; 줄: number } | null => {
|
|
if (!blocks) return null;
|
|
const left = blocks.filter((b) => !b.만든);
|
|
const 밖 = left.filter((b) => !b.부름.length).length;
|
|
return { 블록: left.length, 밖, 줄: left.reduce((a, b) => a + (b.고를수 ?? 0), 0) };
|
|
},
|
|
/** 그 블록으로 뜀 + 블록 판 */
|
|
show(번: number): void {
|
|
ensure();
|
|
pick(번);
|
|
},
|
|
/** 옮긴 뒤 — 목록 다시 읽음(만든 SK) */
|
|
reload(): void {
|
|
asked = false;
|
|
if (!root.hidden) ensure();
|
|
},
|
|
/** 시트 고름 · 탭 바꿈 — 판 바꾸기 · 강조 */
|
|
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 {
|
|
off();
|
|
move.destroy();
|
|
},
|
|
};
|
|
}
|
|
|
|
export type SheetSk = ReturnType<typeof createSheetSk>;
|