/* ============================================================================= * e_harness.ts — E(도구 모음 · 탭 · 메뉴 · 클립보드) 화면 시험 틀. * A(주소 · 명령 · 엔진 · 참조 옮김) · B(숫자 형식) 가 이제 실제로 있어 진짜 모듈을 그대로 씀 * (`vite.harness.config.ts` 는 `@ui` 별칭만 · 가짜 없음). dispatch 는 진짜 `applyCommand` → * 진짜 `createCalcEngine` → 화면 갱신 순서로 실제 앱과 같은 길을 탐. * ========================================================================== */ import { mountToolbar } from "../../../A00_Common/spreadsheet/spreadsheet_toolbar"; import { mountTabs } from "../../../A00_Common/spreadsheet/spreadsheet_tabs"; import { attachMenu } from "../../../A00_Common/spreadsheet/spreadsheet_menu"; import { attachClipboard, parseClipboard, } from "../../../A00_Common/spreadsheet/spreadsheet_clipboard"; import { applyCommand, emptyWorkbook } from "../../../A00_Common/spreadsheet/spreadsheet_commands"; import { createCalcEngine } from "../../../A00_Common/spreadsheet/spreadsheet_graph"; import { createHistory } from "../../../A00_Common/spreadsheet/spreadsheet_history"; import type { Workbook } from "../../../A00_Common/spreadsheet/spreadsheet_types"; import type { GridHandle, Selection, SpreadsheetContext, } from "../../../A00_Common/spreadsheet/spreadsheet_view_types"; const log: string[] = []; const logEl = document.querySelector("#log")!; function say(msg: string): void { log.push(msg); logEl.textContent = log.join("\n"); } const book: Workbook = emptyWorkbook("test", "측구수로400"); book.서식 = [{}, { 굵게: true }]; book.시트[0].칸 = { A1: { 값: "터파기 계산서", 서식: 1 }, B1: { 값: 12.5 }, C1: { 식: "B1*2" }, }; // 탭 이름 바꾸기가 다른 시트 참조 글도 고치는지 보는 둘째 시트. book.시트.push({ id: "s2", 이름: "둘째", 칸: { A1: { 식: "측구수로400!B1+1" } }, }); const engine = createCalcEngine(book); const history = createHistory(); const selection: Selection = { 시트: "s1", 범위: [{ r0: 0, c0: 0, r1: 0, c1: 0 }], 활성: { r: 0, c: 0 }, 기준: { r: 0, c: 0 }, }; const root = document.querySelector("#ss-root")!; const grid: GridHandle = { root, render() {}, invalidate() {}, renderSelection() {}, setRefHighlights() {}, cellBox: () => ({ x: 0, y: 0, w: 60, h: 20 }), hitTest: (clientX) => { const box = root.getBoundingClientRect(); const x = clientX - box.left; if (x < 30) return { kind: "rowHead", r: 0, c: 0 }; if (x < 60) return { kind: "colHead", r: 0, c: 0 }; return { kind: "cell", r: selection.활성.r, c: selection.활성.c }; }, reveal() {}, visibleRange: () => ({ r0: 0, c0: 0, r1: 10, c1: 10 }), editorSlot: () => ({ layer: root, box: { x: 0, y: 0, w: 60, h: 20 } }), destroy() {}, }; function applyAndRefresh(command: import("../../../A00_Common/spreadsheet/spreadsheet_types").Command): void { const effect = applyCommand(book, command); if (effect.rebuild) engine.rebuild(book); else engine.update(effect.cells); toolbar.refresh(); tabs.refresh(); } const ctx: SpreadsheetContext = { root, book, readOnly: false, engine, history, selection, sheet: () => book.시트.find((s) => s.id === selection.시트)!, dispatch(command) { say(`dispatch: ${JSON.stringify(command)}`); const effect = applyCommand(book, command); history.push(command, effect.undo); if (effect.rebuild) engine.rebuild(book); else engine.update(effect.cells); toolbar.refresh(); tabs.refresh(); }, undo() { const cmd = history.undo(); say(`undo: ${cmd ? JSON.stringify(cmd) : "(없음)"}`); if (cmd) applyAndRefresh(cmd); }, redo() { const cmd = history.redo(); say(`redo: ${cmd ? JSON.stringify(cmd) : "(없음)"}`); if (cmd) applyAndRefresh(cmd); }, select(sel) { Object.assign(selection, sel); say(`select: ${JSON.stringify(sel)}`); toolbar.refresh(); }, showSheet(sheetId) { book.활성 = sheetId; selection.시트 = sheetId; selection.범위 = [{ r0: 0, c0: 0, r1: 0, c1: 0 }]; selection.활성 = { r: 0, c: 0 }; say(`showSheet: ${sheetId}`); tabs.refresh(); toolbar.refresh(); }, grid, editor: { editing: () => false, begin() {}, commit() {}, cancel() {}, sync() {}, }, }; const toolbar = mountToolbar(ctx); const tabs = mountTabs(ctx); attachMenu(ctx); attachClipboard(ctx); document.querySelector("#toolbar-slot")!.append(toolbar.root!); document.querySelector("#tabs-slot")!.append(tabs.root!); say("mounted — 도구 모음 · 탭 · 메뉴 · 클립보드 붙음(진짜 A · B 엔진)"); (window as unknown as { __ctx: unknown; __book: unknown }).__ctx = ctx; (window as unknown as { __ctx: unknown; __book: unknown }).__book = book; // 클립보드 밖 → 안 시험 — 자리 표시자에 실제로 붙여넣기(Ctrl+V)를 받아 결과를 보임. const pasteZone = document.querySelector("#paste-zone")!; const pasteOut = document.querySelector("#paste-out")!; pasteZone.addEventListener("paste", (ev) => { ev.preventDefault(); const html = ev.clipboardData?.getData("text/html") ?? ""; const text = ev.clipboardData?.getData("text/plain") ?? ""; const block = parseClipboard(html, text, { r: 0, c: 0 }); pasteOut.textContent = `html:\n${html}\n\ntext:\n${text}\n\nblock:\n${JSON.stringify(block, null, 2)}`; });