Files
Aislo/A00_Common/spreadsheet/spreadsheet_format_painter.ts
T
eomsangdonandClaude Sonnet 5 d38ef573e0 feat(spreadsheet): 2단계 다섯 — 찾기 · 붙여넣기 골라서 · 서식 붓 · 폭 자동 맞춤 · 함수 도움말
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
2026-09-27 20:20:16 +09:00

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;
},
};
}