feat(B08): 표 칸 최대 높이 = 브라우저 창 아래 끝까지 · 공용 표 fillViewport 옵션 (199-1)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015sDZSjFvoiDjdGpgj3muc3
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
* 고칠 칸(PLAN 121) = editable(줄, 열) 참인 칸만 글 상자 · 바꾸면 onEdit(줄, 열, 글) — 다시 그려도 커서 자리 지킴
|
||||
* 칸 누름(PLAN 144) = onCellClick(줄, 열) · 줄로 이동 = t.focusRow(줄)(스크롤 + 강조)
|
||||
* 줄 꼴(PLAN 155-2) = rowClass(줄) → 줄 class(예 is-title 굵게) · 안 주면 글 그대로
|
||||
* 창 끝까지(PLAN 199) = fillViewport → 표 칸 최대 높이를 창 아래 끝까지(줄이 늘어도 페이지 스크롤 0)
|
||||
* ========================================================================== */
|
||||
|
||||
import "./ui_template_data_table.css";
|
||||
@@ -62,6 +63,8 @@ export interface DataTableOptions {
|
||||
rowClass?: (index: number) => string | null | undefined;
|
||||
/** 칸 꼴 class(본문 줄 · 글 상자 아닌 칸만 · 예 `is-picked` = 고른 칸 강조) — 빈 값이면 안 붙임 */
|
||||
cellClass?: (index: number, key: string) => string | null | undefined;
|
||||
/** 참이면 표 칸 최대 높이 = 창 아래 끝까지(창 크기 바뀌면 다시 셈) — 안 주면 CSS 고정값 */
|
||||
fillViewport?: boolean;
|
||||
}
|
||||
|
||||
export interface DataTable {
|
||||
@@ -240,6 +243,63 @@ export function renderDataTableHtml(o: DataTableOptions, win?: RowWindow): strin
|
||||
/** 화면 위아래로 더 그려 둘 줄 수 · 구간은 이 단위로 끊어 스크롤마다 다시 그리지 않음 */
|
||||
const OVER = 40;
|
||||
|
||||
/* ── 창 아래 끝까지(PLAN 199) — 칸 최대 높이 = 창 높이 − 칸 위쪽 자리 − 아래 자리 ────────────
|
||||
* 아래 자리 = 칸 뒤 형제 요소 높이 + 윗대 요소의 아래 안쪽 여백 · 테두리 · 바깥 여백.
|
||||
* 줄이 적으면 내용만큼 · 창 크기 · 위아래 요소가 바뀌면 다시 셈 · 칸이 문서에서 떨어지면 스스로 멈춤.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
/** 칸이 이보다 작아지지 않게(아주 작은 창에서 머리만 남는 것 방지) */
|
||||
const MIN_HEIGHT = 120;
|
||||
|
||||
/** 창 기준 최대 높이(px) — `top` = 문서 맨 위에서 칸 위까지 · `below` = 칸 아래로 남겨야 할 자리 */
|
||||
export function fillHeight(viewport: number, top: number, below: number): number {
|
||||
return Math.max(MIN_HEIGHT, Math.floor(viewport - top - below));
|
||||
}
|
||||
|
||||
const px = (v: string): number => parseFloat(v) || 0;
|
||||
|
||||
/** 칸 아래로 문서 끝까지 차지하는 자리 */
|
||||
export function spaceBelow(el: HTMLElement): number {
|
||||
let sum = 0;
|
||||
for (let node: HTMLElement = el; node.parentElement; node = node.parentElement) {
|
||||
const parent = node.parentElement;
|
||||
const own = getComputedStyle(node);
|
||||
const around = getComputedStyle(parent);
|
||||
const gap = px(around.rowGap);
|
||||
for (let s = node.nextElementSibling; s; s = s.nextElementSibling) {
|
||||
const h = s.getBoundingClientRect().height;
|
||||
if (h) sum += h + gap;
|
||||
}
|
||||
sum += px(own.marginBottom) + px(around.paddingBottom) + px(around.borderBottomWidth);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
export function fillViewport(el: HTMLElement, onFit?: () => void): () => void {
|
||||
let seen = false; // 한 번이라도 문서에 붙었는지 — 붙었다가 떨어지면 멈춤
|
||||
let observer: ResizeObserver | null = null;
|
||||
const fit = (): void => {
|
||||
if (!el.isConnected) {
|
||||
if (seen) stop();
|
||||
return;
|
||||
}
|
||||
seen = true;
|
||||
const top = el.getBoundingClientRect().top + window.scrollY;
|
||||
el.style.maxHeight = `${fillHeight(window.innerHeight, top, spaceBelow(el))}px`;
|
||||
onFit?.();
|
||||
};
|
||||
const stop = (): void => {
|
||||
window.removeEventListener("resize", fit);
|
||||
observer?.disconnect();
|
||||
};
|
||||
window.addEventListener("resize", fit);
|
||||
// 칸이 붙을 때 · 줄이 늘 때 · 위아래 요소가 바뀔 때(문서 높이) 다시 셈
|
||||
observer = new ResizeObserver(fit);
|
||||
observer.observe(el);
|
||||
observer.observe(document.body);
|
||||
return stop;
|
||||
}
|
||||
|
||||
export function createDataTable(opts: DataTableOptions): DataTable {
|
||||
const root = document.createElement("div");
|
||||
root.className = "ui-data-table";
|
||||
@@ -287,6 +347,7 @@ export function createDataTable(opts: DataTableOptions): DataTable {
|
||||
state.onRowClick?.(Number(row.dataset.i));
|
||||
});
|
||||
draw(true);
|
||||
if (opts.fillViewport) fillViewport(root, () => draw(false));
|
||||
return {
|
||||
root,
|
||||
setRows(rows, total) {
|
||||
|
||||
Reference in New Issue
Block a user