refactor(785-7): 옛 맞춤 화면 TS 지움 · 옛 [저장] · [모두 고정] 단추 걷음 · 390 줄 판 높이

- 지움(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>
This commit is contained in:
2026-11-05 01:32:17 +09:00
co-authored by Claude Opus 5.5
parent 48848bb287
commit cf4e2a0339
45 changed files with 126 additions and 5791 deletions
@@ -1,36 +1,33 @@
/* =============================================================================
* M02_MasterTemplete_Hopyo_Sheet.ts
* 일위대가 실무자료 엑셀 보기 몸(PLAN 329 · 338-3 · 359-1 · 359-2 · 785-4) — 본 칸 불러오기 보기(`Hopyo_Import`)가 바로 담음 ·
* 머리 막대 = 시트 · 호표 수 · 검산 · 제안 n / 줄 m · 숫자 판정 셈 중 + 부르는 쪽 덧붙임(`headExtra` — 층 · 안 이은 줄 · [끝내기]) ·
* 머리 막대 = 시트 · 호표 수 · 검산 · 제안 n / 줄 m(1차 판정) · 블록 풀이 셈 중 + 부르는 쪽 덧붙임(`headExtra` — 층 · [끝내기]) ·
* 왼쪽 = 올린 엑셀을 연 것처럼 읽기 전용 시트(A00 산출근거 스프레드시트 · 탭 셋 일위대가표 · 단가산출표 · 단가산출 상세 ·
* 칠 · 범례 · 뛰기 · 뒤로 = `Hopyo_SheetNav` · 779-4 · 같은 파일 몸은 다시 씀 = 그 자리로 뜀 · 779-6) ·
* 오른쪽 = 호표 목록(체크 · 제N호표 [수정] · 찾기 · 제안 n/m — 다 연결이면 녹색 · 뒤 셈 중이면 3 초마다 다시) + [저장] ·
* 칠 · 범례 · 뛰기 · 뒤로 = `Hopyo_SheetNav` · 779-4 · 같은 파일 격자 다시 씀 · 779-6) ·
* 오른쪽 = 호표 목록(제N호표 [수정] · 찾기 · 제안 n/m — 다 제안이면 녹색 · 뒤 셈 중이면 3 초마다 다시) ·
* 단가산출표 · 상세 탭이면 단가산출 목록으로 바뀜(780-2 · `Hopyo_SheetSk`).
* · 목록 누름 → 시트에서 그 호표 영역(B열 ~ 끝 열) 고름 · 칸 누름 → 그 행 호표를 목록에서 강조 · 머리 행이면 영역 고름
* · [수정] = 시트 아래 줄 판(785-6 `Hopyo_Rows` · 줄 누름과 따로) · 단가산출 탭 블록 = 같은 자리 블록 판 ·
* 오른쪽 판 아래 [옮기기](확인한 줄 · 블록 · 고름 캐시 `createJudgeState` 한 벌)
* · [저장] · [모두 고정으로 들여오기] = `Hopyo_SheetMake`
* · 격자 = 서버 `/hopyo-grid/{키}` · 옛 파일(격자 없음)은 시트 자리에 까닭 글 · 목록 · 저장은 됨
* · `buildHopyoSheet` = 몸(머리 · 띠 · 시트 + 목록) · `openHopyoSheet` = 모달 껍데기([로직 추가] 「실무 호표」 길 — 785-7 이 지움)
* · 격자 = 서버 `/hopyo-grid/{키}` · 옛 파일(격자 없음)은 시트 자리에 까닭 글 · 목록은 됨
* · `buildHopyoSheet` = 몸(머리 · 띠 · 시트 + 목록) · 옛 [저장] · [모두 고정으로 들여오기] · 모달 껍데기는 785-7 에서 지움
* (만들기 = [옮기기] · 모두 고정 서버 길은 B09 · 붙여넣기가 씀)
* ⚠ 부르는 쪽은 `import()` 로 엶 · 스프레드시트도 열 때 불러옴(다른 화면 번들에 안 섞이게).
* ========================================================================== */
import { createButton, el } from "@ui/ui_template_elements";
import { createCheckbox } from "@ui/ui_template_checkbox";
import { createInputField } from "@ui/ui_template_input";
import { openModal } from "@ui/ui_template_modal";
import type { Sheet } from "../A00_Common/spreadsheet/spreadsheet_types";
import type { SpreadsheetHandle } from "../A00_Common/spreadsheet/spreadsheet";
import {
POLL_MS,
readHopyo,
type HopyoBrief,
type HopyoBuilt,
type HopyoGrid,
type HopyoSpan,
type HopyoView,
type HopyoScope,
} from "./M02_MasterTemplete_Hopyo_Api";
import { POLL_MS } from "./M02_MasterTemplete_Hopyo_Check";
import {
createJudgeMove,
createJudgeRows,
@@ -39,17 +36,18 @@ import {
import { lineOf } from "./logic/M02_MasterTemplete_Hopyo_Picks";
import * as Nav from "./M02_MasterTemplete_Hopyo_SheetNav";
import { createSheetSk, type SheetSk } from "./M02_MasterTemplete_Hopyo_SheetSk";
import { note, why, wireMake } from "./M02_MasterTemplete_Hopyo_SheetMake";
import "./M02_MasterTemplete_Hopyo.css";
export { buildOrder } from "./M02_MasterTemplete_Hopyo_SheetMake";
const why = (error: unknown): string => (error instanceof Error ? error.message : String(error));
const note = (text: string): HTMLElement =>
el("span", { className: "ui-text ui-text--muted", text, attrs: { "data-text": "14" } });
export interface HopyoSheetOptions {
/** 실무자료 파일 층 */
scope: HopyoScope;
key: string;
파일: string;
/** 만들 층 쓰기 권한 — 없으면 체크 · 만들기 없음(보기만) */
/** 만들 층 쓰기 권한 — 없으면 옮기기 없음(보기만) */
canWrite: boolean;
/** 사전조합을 만들 층(없으면 `scope` · [로직 추가] = 문서 층) */
target?: HopyoScope;
@@ -57,8 +55,6 @@ export interface HopyoSheetOptions {
호표?: number;
/** 처음 보일 단가산출 행(330-3 근거 [엑셀 보기]) */
단산행?: number | [number, number];
/** 호표 하나를 만들었음 — [로직 추가] 참조 줄 */
onImport?: (got: HopyoBuilt, hopyo: HopyoBrief) => void;
/** 창을 닫았음(좌측 파일 줄 고름 표시) */
onClose?: () => void;
/** 머리 막대 끝에 덧붙일 것(본 칸 불러오기 보기 — 층 · 안 이은 줄 · [끝내기]) */
@@ -74,8 +70,6 @@ export interface HopyoSheetBody {
parts: HTMLElement[];
/** 몸을 걷음(마지막 창 손잡이 · 격자 · 스프레드시트) */
destroy: () => void;
/** 모달에서 앞으로 꺼낸 몸을 제자리로 — 했으면 참(닫기 대신) */
lower: () => boolean;
/** 안 옮긴 단가산출 블록 수(판정 전 null) */
skCount: () => { 블록: number; 밖: number; 줄: number } | null;
/** 그 단가산출 블록으로 뜀 + 줄 판 엶 */
@@ -84,8 +78,6 @@ export interface HopyoSheetBody {
const ILWI = "일위대가";
const DANSAN = ["단가산출", "단가산출근거"]; // 형식별 블록 시트(EST · STmate · 역할 id 없는 옛 격자)
/** 열린 창 손잡이(779-6) — 파일마다 · 다시 부르면 그 창이 앞으로 와 그 자리로 뜀 */
const opened = new Map<string, (o: HopyoSheetOptions) => void>();
const sheetId = (o: HopyoSheetOptions): string =>
`${o.scope.layer}:${o.scope.owner ?? ""}:${o.key}`;
@@ -125,32 +117,12 @@ export function lastColumn(sheet: Pick<Sheet, "칸" | "열">): string {
export const rowsText = (행: number | [number, number]): string =>
typeof 행 === "number" ? `B${행}:F${행}` : `B${행[0]}:F${행[1]}`;
/** 모달 껍데기 — 같은 파일 몸이 이미 있으면(본 칸 · 모달) 그 몸을 그 자리로 뜀 */
export function openHopyoSheet(opt: HopyoSheetOptions): void {
const id = sheetId(opt);
if (opened.has(id)) return opened.get(id)!(opt);
const view = buildHopyoSheet(opt);
openModal({
title: `엑셀 보기 — ${opt.파일}`,
closeLabel: "닫기",
wide: true,
dialogClass: "m02-modal m02-hopyo-sheet__dialog",
beforeClose: async () => {
if (view.lower()) return false; // 꺼낸 창 = 닫기 대신 제자리로
view.destroy();
return true;
},
mount: (body) => body.append(...view.parts),
});
}
export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
const id = sheetId(opt);
let sheet: SpreadsheetHandle | null = null;
let nav: Nav.SheetNav | null = null;
let sk: SheetSk | null = null; // 단가산출 탭 오른쪽 판(780-2)
let detail = ""; // 단가산출 상세 시트 id
let above: Element[] = []; // 앞으로 꺼냈을 때 원래 위에 있던 창
let closed = false;
let briefs: HopyoBrief[] = [];
let spans: HopyoSpan[] = [];
@@ -158,7 +130,6 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
let last = "O";
let at = { 시트: "", 칸: "", 범위: "" }; // 시트 고름
let current: number | null = opt.호표 ?? null;
const checked = new Set<number>();
const items = new Map<number, HTMLElement>();
const target = opt.target ?? opt.scope;
const state = createJudgeState(opt.scope, opt.key); // 판정 고름 캐시(785-6)
@@ -203,13 +174,6 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
const search = searchField.input;
const list = el("div", { className: "m02-hopyo-sheet__list" });
const count = note("");
const go = createButton({ label: "저장", variant: "filled", size: "sm" });
go.disabled = true;
go.setAttribute("data-hopyo-save", "");
const fixAll = createButton({ label: "모두 고정으로 들여오기", size: "sm" });
fixAll.setAttribute("data-hopyo-all-fixed", "");
fixAll.disabled = true; // 엑셀을 다 읽은 뒤 켬
const result = el("div", { className: "m02-hopyo-sheet__result" });
const move = createJudgeMove({
file: opt.scope,
key: opt.key,
@@ -224,10 +188,9 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
searchField.root,
el("div", {
className: "ui-toolbar m02-hopyo__bar",
children: [count, ...(opt.canWrite ? [fixAll, go] : [])],
children: [count],
}),
list,
result,
move.root,
],
});
@@ -235,7 +198,6 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
const finish = (): void => {
if (closed) return;
closed = true;
opened.delete(id);
closeLines();
offPaint();
move.destroy();
@@ -245,25 +207,16 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
opt.onClose?.();
};
const paintGo = (): void => {
go.querySelector(".ui-btn__label")!.textContent = checked.size
? `저장 (${checked.size})`
: "저장";
go.disabled = !checked.size;
};
/** 머리 막대 — 시트 · 호표 수 · 검산 · 제안 · 셈 중 + 덧붙임 */
function paintHead(file: HopyoView): void {
const wrong = file.검산.어긋남;
const c = file.셈 && !file.셈.끝 ? file.셈 : null;
const busy = !!file.셈 && !file.셈.끝;
head.replaceChildren(
note(`시트 ${file.시트}`),
note(`호표 ${file.호표수}`),
note(wrong.length ? `검산 어긋남 ${wrong.length}` : "검산 맞음"),
note(`제안 ${file.맞춤[0]} / 줄 ${file.맞춤[1]}`),
...(c
? [note(`숫자 판정 셈 중 ${c.한}/${c.모두 ?? "?"}${c.까닭 ? ` · ${c.까닭}` : ""}`)]
: []),
...(busy ? [note(`${file.셈?.까닭 ?? "블록 풀이"} 셈 중`)] : []),
...(opt.headExtra ?? []),
);
opt.onRead?.(file);
@@ -288,11 +241,6 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
const text = spanText(span, last);
if (at.시트 !== ilwi || at.범위 !== text) sheet.select(text, ilwi);
}
opened.set(id, (o) => {
above = Nav.raise(left); // 모달이 아니면 [] — 그 자리로만 뜀
if (o.단산행 !== undefined && detail) nav?.jumpTo(detail, o.단산행);
else if (o.호표 !== undefined) focus(o.호표);
});
/** [수정] — 그 호표 자리 + 시트 아래 줄 판(줄 누름과 따로) */
const editButton = (h: HopyoBrief): HTMLElement => {
@@ -330,23 +278,12 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
);
list.replaceChildren(
...shown.map((h) => {
const box = createCheckbox("", {
checked: checked.has(h.번),
onChange: (on) => {
if (on) checked.add(h.번);
else checked.delete(h.번);
paintGo();
},
});
box.root.hidden = !opt.canWrite;
box.root.addEventListener("click", (e) => e.stopPropagation());
const linked = note(linkText(h));
linked.classList.toggle("is-linked", allLinked(h));
const row = el("div", {
className: `m02-hopyo-sheet__item${h.번 === current ? " is-current" : ""}`,
attrs: { "data-hopyo-no": String(h.번), title: `${h.이름} ${h.규격}`.trim() },
children: [
box.root,
el("span", { className: "m02-hopyo-sheet__no", text: `제${h.번}호표` }),
editButton(h),
el("span", { className: "m02-hopyo-sheet__name", text: `${h.이름} ${h.규격}`.trim() }),
@@ -438,7 +375,6 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
]);
if (closed) return;
briefs = file.호표;
fixAll.disabled = !briefs.length;
spans = grid instanceof Error ? [] : grid.호표;
paintHead(file);
paintList();
@@ -456,25 +392,10 @@ export function buildHopyoSheet(opt: HopyoSheetOptions): HopyoSheetBody {
}
}
wireMake({
scope: opt.scope,
key: opt.key,
파일: opt.파일,
target,
...(opt.onImport ? { onImport: opt.onImport } : {}),
checked,
briefs: () => briefs,
closed: () => closed,
go,
fixAll,
result,
paintGo,
});
void load();
return {
parts: [head, warn, el("div", { className: "m02-hopyo-sheet", children: [col, right] })],
destroy: finish,
lower: () => Nav.lower(left, above.splice(0)),
skCount: () => sk?.count() ?? null,
showSk: (번) => sk?.show(번),
};