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
62 lines
3.0 KiB
TypeScript
62 lines
3.0 KiB
TypeScript
/* =============================================================================
|
|
* spreadsheet_autofit.ts (2단계 · 주인 sub_laptop_1)
|
|
* 열 머리 경계를 두 번 눌러 폭 자동 맞춤 — 그 열 안 글자 수로 어림(엑셀 「글자 단위」).
|
|
* 진짜 픽셀 값은 C(격자)가 쓰는 글꼴로 재야 더 정확 — `measure` 자리에 C 의 실제 재기 함수를
|
|
* 나중에 꽂을 수 있게 열어 둠(안 주면 아래 어림 규칙을 씀).
|
|
*
|
|
* 잇는 법(D · sub7 · C 격자 머리) — 열 머리 경계에서 두 번 누르면(dblclick)
|
|
* `autofitColumn(ctx, col)` 을 부름. C 가 완성되면 `measure` 자리에 캔버스 `measureText` 를 넘겨 더 정밀하게.
|
|
*
|
|
* ── 이미 계약에 있어 새 파일이 필요 없는 것(붙일 자리만 확인) ──────────────────────
|
|
* · 여러 범위(Ctrl+누르기) — `spreadsheet_view_types.ts` `Selection.범위` 가 이미 배열(D `spreadsheet_selection.ts` 가 씀).
|
|
* · 눈금선 끄기 — `Command`(`종류:"눈금선"`) · `Sheet.보기?.눈금선` 이미 있음(A 가 처리).
|
|
* · 확대 · 축소 — 값 자체를 저장하지 않는 화면 전용 CSS 배율(C 격자 뿌리에 transform) · 계약 밖.
|
|
* ========================================================================== */
|
|
|
|
import type { Workbook } from "./spreadsheet_types";
|
|
import type { SpreadsheetContext } from "./spreadsheet_view_types";
|
|
|
|
export type TextMeasurer = (text: string) => number;
|
|
|
|
const PADDING = 2;
|
|
const MIN_WIDTH = 4;
|
|
|
|
/** 글꼴을 모를 때 어림 — 한글 · 한자 · 전각은 1.9 배 폭 · 나머지는 1 배. */
|
|
const defaultMeasure: TextMeasurer = (text) => {
|
|
let width = 0;
|
|
for (const ch of text) width += ch.codePointAt(0)! >= 0x1100 ? 1.9 : 1;
|
|
return width;
|
|
};
|
|
|
|
const colFromKey = (key: string): number => {
|
|
const m = /^([A-Z]+)\d+$/.exec(key);
|
|
if (!m) return -1;
|
|
let col = 0;
|
|
for (const ch of m[1]) col = col * 26 + (ch.charCodeAt(0) - 64);
|
|
return col - 1;
|
|
};
|
|
|
|
/** 그 열에 든 칸 글자(식 칸은 식 글로 어림 — 계산값은 여기서 모름) 중 가장 넓은 것 + 여백. */
|
|
export function estimateColumnWidth(
|
|
book: Workbook,
|
|
sheetId: string,
|
|
col: number,
|
|
measure: TextMeasurer = defaultMeasure,
|
|
): number {
|
|
const sheet = book.시트.find((s) => s.id === sheetId);
|
|
if (!sheet) return MIN_WIDTH;
|
|
let widest = 0;
|
|
for (const [key, cell] of Object.entries(sheet.칸)) {
|
|
if (colFromKey(key) !== col) continue;
|
|
const text = cell.식 !== undefined ? cell.식 : cell.값 !== undefined ? String(cell.값) : "";
|
|
if (text) widest = Math.max(widest, measure(text));
|
|
}
|
|
return Math.max(MIN_WIDTH, Math.ceil(widest + PADDING));
|
|
}
|
|
|
|
/** 폭을 자동 맞춤 값으로 바꿈(열넓이 명령 하나). */
|
|
export function autofitColumn(ctx: SpreadsheetContext, col: number, measure?: TextMeasurer): void {
|
|
const width = estimateColumnWidth(ctx.book, ctx.selection.시트, col, measure);
|
|
ctx.dispatch({ 종류: "열폭", 시트: ctx.selection.시트, 열: [col], 폭: width });
|
|
}
|