Files
Aislo/ui_template/sheet/ui_template_sheet_header.ts
T
eomsangdonandClaude Opus 5.5 20403340ed feat(sheet): 엑셀처럼 도는 표 부품 createSheet — master · project 두 모드 (PLAN 10-2)
- 여러 층 머리(null = 세로 합침 · 같은 앞머리 = 가로 합침) · 단위 줄 · 합계 줄 · 틀고정 · 열 너비 끌기
- 셀 편집은 M01 startEdit 그대로 · 화살표 · Tab · Enter · F2 · 바로 치기 · Delete 비움
- 줄 더하기 · 지우기 · master 는 열 더하기 · 지우기 · 손 입력 열 · 식 · 들어갈 것 · 일위대가 줄
- project 는 바인딩 · 계산 열 잠금 · 손 열만 입력 · 전구간 줄 맨 위 · 쪽줄 50 으로 쪽(쪽마다 머리 · 합계 마지막 쪽)
- 고칠 때마다 같은 TS 풀이로 즉시 다시 그림 · onChange 로 문서를 넘김(자동저장 없음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tjoit7rxvpLMM7cafeVTo1
2026-09-25 09:41:29 +09:00

62 lines
2.0 KiB
TypeScript

/* =============================================================================
* ui_template_sheet_header.ts
* 여러 층 머리 짜기 — 열마다 `머리`(층 수만큼 글) → 층마다 칸(colspan · rowspan).
*
* `null` = 위 칸과 합침(세로) · 모자란 층도 `null` 로 채움(위 칸이 늘어남).
* 옆 열과 그 층까지 앞머리가 모두 같고 아래 합침 모양도 같으면 가로로 합침.
* ========================================================================== */
import type { SheetColumn } from "./ui_template_sheet_types";
export interface HeadCell {
level: number;
col: number;
colspan: number;
rowspan: number;
label: string;
}
export function headDepth(cols: SheetColumn[], layers?: string[]): number {
return Math.max(1, layers?.length ?? 0, ...cols.map((c) => c.머리.length));
}
/** 층마다 칸 목록 — 앞 층부터. */
export function layoutHead(cols: SheetColumn[], depth: number): HeadCell[][] {
const paths = cols.map((c) =>
Array.from({ length: depth }, (_, i) => (i === 0 ? (c.머리[0] ?? "") : (c.머리[i] ?? null))),
);
const span = (j: number, i: number): number => {
let k = i + 1;
while (k < depth && paths[j][k] === null) k += 1;
return k - i;
};
const samePrefix = (a: number, b: number, i: number): boolean =>
paths[a].slice(0, i + 1).every((label, lv) => label === paths[b][lv]);
const levels: HeadCell[][] = [];
for (let i = 0; i < depth; i += 1) {
const cells: HeadCell[] = [];
let j = 0;
while (j < cols.length) {
if (paths[j][i] === null) {
j += 1;
continue;
}
const rowspan = span(j, i);
let colspan = 1;
while (
j + colspan < cols.length &&
paths[j + colspan][i] !== null &&
samePrefix(j, j + colspan, i) &&
span(j + colspan, i) === rowspan
) {
colspan += 1;
}
cells.push({ level: i, col: j, colspan, rowspan, label: paths[j][i] as string });
j += colspan;
}
levels.push(cells);
}
return levels;
}