/* ============================================================================= * spreadsheet_format_painter.ts (2단계 · 주인 sub_laptop_1) * 서식 붓 — 활성 칸의 서식을 묻혀 다른 범위에 바름. 한 번 누르면 한 번만 · 두 번 누르면(sticky) * 끌 때까지 계속. 서식 찾기는 칸 → 행 → 열 → 기본(0) 차례(`spreadsheet_types.ts` `Cell.서식` 규칙). * * 잇는 법(D · sub7 · E 도구 모음) — 도구 모음에 버튼을 두고 `createFormatPainter(ctx)` 하나를 붙임. * 한 번 누르면 `pick()` → 커서를 붓 모양으로 → 범위를 고르면(mouseup) `paint(range)`. * 두 번 누르면 `pick()` 뒤 `sticky=true` 로 두고 Esc 나 버튼을 다시 누르면 꺼짐(`active()` 로 표시). * ========================================================================== */ import type { CellRange, CellStyle, Workbook } from "./spreadsheet_types"; import type { PartHandle, SpreadsheetContext } from "./spreadsheet_view_types"; const EMPTY_STYLE: CellStyle = {}; /** 칸 하나의 실제 서식 — 칸 자기 서식 없으면 행 서식 · 없으면 열 서식 · 없으면 기본(0). */ export function resolveCellStyle(book: Workbook, sheetId: string, r: number, c: number): CellStyle { const sheet = book.시트.find((s) => s.id === sheetId); if (!sheet) return EMPTY_STYLE; const idx = cellStyleIndex(sheet, r, c); return book.서식[idx] ?? EMPTY_STYLE; } function cellStyleIndex( sheet: { 칸: Record; 열?: Record; 행?: Record; }, r: number, c: number, ): number { const key = a1(r, c); const cellIdx = sheet.칸[key]?.서식; if (cellIdx !== undefined) return cellIdx; const rowIdx = sheet.행?.[String(r + 1)]?.서식; if (rowIdx !== undefined) return rowIdx; const colIdx = sheet.열?.[colLetters(c)]?.서식; if (colIdx !== undefined) return colIdx; return 0; } function colLetters(c: number): string { let col = c + 1; let letters = ""; while (col > 0) { const rem = (col - 1) % 26; letters = String.fromCharCode(65 + rem) + letters; col = Math.floor((col - 1) / 26); } return letters; } const a1 = (r: number, c: number): string => `${colLetters(c)}${r + 1}`; export interface FormatPainterHandle extends PartHandle { /** 지금 활성 칸의 서식을 붓에 묻힘 */ pick(): void; /** 묻힌 서식이 있으면 범위에 바름 · `sticky` 가 아니면 한 번 쓰고 스스로 끔 */ paint(range: CellRange, sticky?: boolean): void; active(): boolean; clear(): void; } export function createFormatPainter(ctx: SpreadsheetContext): FormatPainterHandle { let picked: CellStyle | null = null; let sticky = false; return { root: null, pick(): void { const at = ctx.selection.활성; picked = resolveCellStyle(ctx.book, ctx.selection.시트, at.r, at.c); }, paint(range: CellRange, keepOn = false): void { if (!picked) return; sticky = keepOn; ctx.dispatch({ 종류: "서식", 시트: ctx.selection.시트, 범위: [range], 바꿀: { ...picked } }); if (!sticky) picked = null; }, active(): boolean { return picked !== null; }, clear(): void { picked = null; sticky = false; }, refresh(): void { /* 상태 없는 표시만 씀 — 버튼 눌림은 active() 로 E 가 그림 */ }, destroy(): void { picked = null; }, }; }