PLAN 11-8 스프레드시트 2단계. 0 계약(spreadsheet_types.ts · spreadsheet_view_types.ts) 타입만 보고 새 파일 다섯 짬 — 잇기는 1단계 끝에 D(sub7)가 파일 머리 주석대로: - spreadsheet_find.ts 찾기 · 바꾸기(대소문자 · 전체칸 · 수식 안) - spreadsheet_paste_special.ts 붙여넣기 골라서(값만 · 서식만 · 수식만) - spreadsheet_format_painter.ts 서식 붓(칸→행→열→기본 차례로 서식을 찾음) - spreadsheet_autofit.ts 열 머리 두 번 눌러 폭 자동 맞춤(어림 재기 · 실제 재기 자리 열어 둠) - spreadsheet_func_help.ts 함수 자동 완성 · 인자 도움말(1단계 함수 48 개) 여러 범위 · 확대 · 눈금선 끄기는 이미 계약에 있어 새 파일 없이 붙일 자리만 확인(autofit.ts 머리). 시험 = resources/tester/spreadsheet/test_stage2_*.ts(공용 견본 · 가짜 컨텍스트는 test_stage2_helpers.ts). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K7JS47SacjPxZ718QsZKyr
96 lines
3.4 KiB
TypeScript
96 lines
3.4 KiB
TypeScript
/* =============================================================================
|
|
* 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<string, { 서식?: number }>;
|
|
열?: Record<string, { 서식?: number }>;
|
|
행?: Record<string, { 서식?: number }>;
|
|
},
|
|
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;
|
|
},
|
|
};
|
|
}
|