Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_8
This commit is contained in:
@@ -34,6 +34,7 @@ import {
|
||||
readTemplate,
|
||||
saveTemplate,
|
||||
type StructureNumbers,
|
||||
type TreeEdit,
|
||||
} from "./M02_MasterTemplete_Api_Fetch";
|
||||
import {
|
||||
createItemCards,
|
||||
@@ -226,6 +227,18 @@ export function comboMerge<T>(stored: unknown, edited: T): T {
|
||||
} as unknown as T;
|
||||
}
|
||||
|
||||
/** 표 머리 이름 칸 고침 → 나무 이름 바꾸기(PLAN 31-1 · 한 정본 = 나무) — 칸이 항목 이름(가장 깊은 칸)이면
|
||||
* 항목(열 id 로 부름) · 위 칸이면 그 폴더(머리 앞마디 경로). */
|
||||
export function headRenameEdit(col: StructureColumn, level: number, name: string): TreeEdit {
|
||||
const named = (parts: (string | null)[]): string[] =>
|
||||
parts.filter((p): p is string => !!p && p.trim() !== "");
|
||||
const path = named(col.머리);
|
||||
const upto = named(col.머리.slice(0, level + 1));
|
||||
return upto.length >= path.length
|
||||
? { op: "rename", 경로: path.slice(0, -1), 키: col.id, 이름: name }
|
||||
: { op: "rename", 경로: upto, 이름: name };
|
||||
}
|
||||
|
||||
/** 구조물 이름 + 변수값 — 제목행 층을 빈칸으로 이음(빈 층은 건너뜀 · 다 비면 열 id) */
|
||||
export const columnHead = (col: StructureColumn): string =>
|
||||
col.머리.filter((part): part is string => !!part && part.trim() !== "").join(" ") || col.id;
|
||||
|
||||
@@ -110,6 +110,8 @@ export interface TreePanelHandle {
|
||||
select: (colId: string | null) => void;
|
||||
/** 「새 항목」 — 고른 폴더 안(없으면 맨 위)에 항목 추가 창 */
|
||||
newItem: () => void;
|
||||
/** 표 머리에서 온 나무 고침(이름 바꾸기 · PLAN 31-1) — 메뉴 고침과 같은 길(저장 물음 · 막힘 안내 · 다시 그림) */
|
||||
editFromTable: (body: TreeEdit, colId: string | null) => Promise<void>;
|
||||
}
|
||||
|
||||
const why = (error: unknown): string => (error instanceof Error ? error.message : "");
|
||||
@@ -685,5 +687,8 @@ export function createTreePanel(opt: TreePanelOptions): TreePanelHandle {
|
||||
void addItem(at?.kind === "folder" ? at.path : []);
|
||||
};
|
||||
|
||||
return { root, search: search.root, add: tools, refresh, select, newItem };
|
||||
const editFromTable = async (body: TreeEdit, colId: string | null): Promise<void> => {
|
||||
if (opt.canEdit && (await ready())) await edit(body, colId);
|
||||
};
|
||||
return { root, search: search.root, add: tools, refresh, select, newItem, editFromTable };
|
||||
}
|
||||
|
||||
@@ -118,6 +118,8 @@ export interface MainExtras {
|
||||
search?: HTMLElement;
|
||||
newItem?: HTMLElement;
|
||||
onColumn?: (column: string | null) => void;
|
||||
/** 구조물 조합 표 머리 이름 칸 고침 → 나무 이름 바꾸기(쓸 수 있는 층만 · PLAN 31-1) */
|
||||
onHeadRename?: (col: StructureColumn, level: number, name: string) => void;
|
||||
}
|
||||
|
||||
/** 상세 제목 줄 오른쪽 끝 「새 …」 단추 — 공용 모양 `m02-main__new`(일위대가 「새 조합」과 같게 · 30-5) */
|
||||
@@ -405,6 +407,16 @@ export function createMain(
|
||||
drawingLabel: (colId: string) => struct.numbers()[colId]?.도번 ?? "",
|
||||
areaLabel: (colId: string) => struct.numbers()[colId]?.면적번 ?? "",
|
||||
materialLabel: (colId: string) => struct.numbers()[colId]?.재료번 ?? "",
|
||||
combo: {
|
||||
...(readOnly || !extras.onHeadRename
|
||||
? {}
|
||||
: {
|
||||
onHeadEdit: (colId: string, level: number, name: string) => {
|
||||
const col = columns().find((c) => c.id === colId);
|
||||
if (col) extras.onHeadRename?.(col, level, name);
|
||||
},
|
||||
}),
|
||||
},
|
||||
onSelect: (s: { colId: string | null }) => {
|
||||
struct.show(s.colId, columns());
|
||||
extras.onColumn?.(s.colId);
|
||||
|
||||
@@ -28,7 +28,11 @@ import {
|
||||
type Root,
|
||||
} from "./M02_MasterTemplete_Layer";
|
||||
import { buildLaborLibrary, buildLaborSection } from "./M02_MasterTemplete_Labor";
|
||||
import { STRUCTURE_TABLE } from "./M02_MasterTemplete_Structure";
|
||||
import {
|
||||
headRenameEdit,
|
||||
STRUCTURE_TABLE,
|
||||
type StructureColumn,
|
||||
} from "./M02_MasterTemplete_Structure";
|
||||
import { createTreePanel } from "./M02_MasterTemplete_TreePanel";
|
||||
import {
|
||||
companyOwner,
|
||||
@@ -135,6 +139,13 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise<Mounte
|
||||
readOnlyBar?.setColumn(column);
|
||||
tree.select(column);
|
||||
},
|
||||
// 표 머리 이름 칸 고침 = 나무 이름 바꾸기(31-1) · 쓸 수 있는 층만
|
||||
...(canEdit(layer, isAdmin)
|
||||
? {
|
||||
onHeadRename: (col: StructureColumn, level: number, name: string) =>
|
||||
void tree.editFromTable(headRenameEdit(col, level, name), col.id),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
// 첫 화면 · 닫은 뒤(양식 지움 · 자재 조합 상세 닫음) = 구조물집계표 — 빈 화면 없음
|
||||
|
||||
@@ -93,7 +93,9 @@ def test_참조표는_열_머리를_못_고침():
|
||||
assert "else if (layer && editCols) editLayer(layer);" in sheet
|
||||
assert 'ro || opts.lockHead ? { hidden: "" }' in sheet
|
||||
render = _text(SHEET / "ui_template_sheet_render.ts")
|
||||
assert 'const heads = mode === "master" && !state.headLocked;' in render
|
||||
assert (
|
||||
'const heads = mode === "master" && (!state.headLocked || !!state.headEditable);' in render
|
||||
) # 31-1 머리 이름은 나무로 고침
|
||||
main = _text(M02 / "M02_MasterTemplete_UI_Main.ts")
|
||||
assert "struct ? comboView(doc) : doc" in main
|
||||
assert "getDoc: () => comboMerge(loaded, shown.getDoc())" in main
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
"""구조물 조합 표(PLAN 31-1) — 머리 이름 칸 고침 = 나무 이름 바꾸기 · 단위 줄 숨김 · 열 폭 자동 ·
|
||||
고른 열의 마지막 분류 칸 강조 · 옛 안내글 없음.
|
||||
|
||||
① `headRenameEdit`(Node) — 가장 깊은 칸이면 항목(열 id) · 위 칸이면 그 폴더 경로.
|
||||
② 표 부품 `combo` 선택 — 단위 줄 · 안내 글 없음 · 번호 글 폭 · 가장 깊은 머리 칸 ·
|
||||
머리 고침은 부른 쪽에.
|
||||
③ 잇기 — 상세(Main)가 쓸 수 있을 때만 넘기고 페이지가 나무 고침(`editFromTable`)으로 보냄.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
M02 = ROOT / "M02_MasterTemplete"
|
||||
SHEET = ROOT / "ui_template" / "sheet"
|
||||
|
||||
|
||||
def _text(path: Path) -> str:
|
||||
return path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
|
||||
def test_머리_칸_고침은_나무_이름_바꾸기():
|
||||
src = _text(M02 / "M02_MasterTemplete_Structure.ts")
|
||||
part = re.search(r"^export function headRenameEdit[\s\S]*?^}", src, re.M)
|
||||
assert part, "headRenameEdit 를 못 찾음"
|
||||
script = (
|
||||
"interface StructureColumn { id: string; 머리: (string | null)[] }\n"
|
||||
"interface TreeEdit { op: string; 경로?: string[]; 이름?: string; 키?: string }\n"
|
||||
+ part.group(0).replace("export ", "")
|
||||
+ """
|
||||
const col = { id: "item3", 머리: ["옹벽", "콘크리트", "L형", null] };
|
||||
const flat = { id: "item9", 머리: ["대피소", null, null, null] };
|
||||
console.log(JSON.stringify([
|
||||
headRenameEdit(col, 0, "옹벽공"), headRenameEdit(col, 1, "철근"),
|
||||
headRenameEdit(col, 2, "L자"), headRenameEdit(col, 3, "L자"), headRenameEdit(flat, 0, "쉼터"),
|
||||
]));
|
||||
"""
|
||||
)
|
||||
result = subprocess.run(
|
||||
["node", "--experimental-strip-types", "--no-warnings",
|
||||
"--input-type=module-typescript", "-e", script],
|
||||
capture_output=True, text=True, encoding="utf-8", timeout=60,
|
||||
) # fmt: skip
|
||||
assert result.returncode == 0, result.stderr
|
||||
assert json.loads(result.stdout) == [
|
||||
{"op": "rename", "경로": ["옹벽"], "이름": "옹벽공"}, # 맨 위 폴더
|
||||
{"op": "rename", "경로": ["옹벽", "콘크리트"], "이름": "철근"}, # 가운데 폴더
|
||||
{"op": "rename", "경로": ["옹벽", "콘크리트"], "키": "item3", "이름": "L자"}, # 항목
|
||||
{"op": "rename", "경로": ["옹벽", "콘크리트"], "키": "item3", "이름": "L자"}, # 빈 아래 칸
|
||||
{"op": "rename", "경로": [], "키": "item9", "이름": "쉼터"}, # 깊이 1 항목
|
||||
]
|
||||
|
||||
|
||||
def test_표_부품_combo_선택():
|
||||
sheet = _text(SHEET / "ui_template_sheet.ts")
|
||||
assert "combo?: { onHeadEdit?: (colId: string, level: number, name: string) => void };" in sheet
|
||||
assert '...(ro || opts.combo ? [] : [el("p", { className: "ui-sheet__hint"' in sheet
|
||||
assert (
|
||||
"filter((k) => !opts.combo || k !== KEY_UNIT)" in sheet
|
||||
) # 방향키가 숨은 단위 줄을 안 밟음
|
||||
assert 'head?.classList.add("is-head-picked")' in sheet
|
||||
assert "if (th && opts.combo?.onHeadEdit) editComboHead(th);" in sheet
|
||||
assert "if (opts.combo && sel.r === KEY_UNIT)" in sheet # F2 · Enter = 머리 이름 고침
|
||||
render = _text(SHEET / "ui_template_sheet_render.ts")
|
||||
assert "children: state.combo ? rows : [...rows, unit]" in render # 단위 줄 없음
|
||||
assert "if (state.combo) fitMeta(state, cols, mins);" in render
|
||||
assert "열너비: {}" in render # 저장 폭 안 씀 — 자동
|
||||
css = _text(SHEET / "ui_template_sheet.css")
|
||||
assert ".ui-sheet--combo .ui-sheet__meta td" in css and "th.is-head-picked" in css
|
||||
|
||||
|
||||
def test_머리_고침은_쓸_수_있는_층만_나무로():
|
||||
main = _text(M02 / "M02_MasterTemplete_UI_Main.ts")
|
||||
assert "readOnly || !extras.onHeadRename" in main
|
||||
page = _text(M02 / "M02_MasterTemplete_UI_Page.ts")
|
||||
assert "tree.editFromTable(headRenameEdit(col, level, name), col.id)" in page
|
||||
assert page.index("...(canEdit(layer, isAdmin)\n ? {\n onHeadRename") > 0
|
||||
tree = _text(M02 / "M02_MasterTemplete_TreePanel.ts")
|
||||
assert "if (opt.canEdit && (await ready())) await edit(body, colId);" in tree
|
||||
@@ -135,6 +135,16 @@
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* 구조물 조합(PLAN 31-1) — 고른 열의 가장 깊은 이름 칸 · 키 · 번호 줄바꿈 없음 */
|
||||
.ui-sheet__table th.is-head-picked {
|
||||
outline: 2px solid var(--color-focus-ring);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.ui-sheet--combo .ui-sheet__meta td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ui-sheet__table tr.is-fixed td {
|
||||
border-bottom: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
@@ -65,6 +65,10 @@ export interface SheetOptions {
|
||||
* 글을 돌려주면 일위대가 칸에 넣음 · 번호 글 줄은 부른 쪽이 가져온 뒤 `recalc()`.
|
||||
* 없으면 일위대가는 글로 고침 · 번호 글 줄은 읽기만. */
|
||||
onPick?: (sel: SheetSelection) => Promise<string | null | undefined> | void;
|
||||
/** 구조물 조합 표(PLAN 31-1) — 단위 줄 · 안내 글 없음 · 열 폭 자동 · 고른 열은 가장 깊은 이름 칸을 밝힘 ·
|
||||
* `onHeadEdit` 를 주면 이름 칸을 두 번 누르기 · F2 · Enter 로 고쳐 부른 쪽에 넘김(표 문서는 안 고침 —
|
||||
* 나무 이름 바꾸기로 · 잠긴 머리여도). */
|
||||
combo?: { onHeadEdit?: (colId: string, level: number, name: string) => void };
|
||||
}
|
||||
|
||||
export interface SheetHandle {
|
||||
@@ -89,6 +93,8 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
materialLabel: opts.materialLabel,
|
||||
pick: !!opts.onPick,
|
||||
headLocked: !!opts.lockHead,
|
||||
combo: !!opts.combo,
|
||||
headEditable: !opts.readOnly && !!opts.combo?.onHeadEdit,
|
||||
};
|
||||
const ro = !!opts.readOnly;
|
||||
const editCols = opts.mode === "master" && !ro && !opts.lockHead; // 열 · 머리 고치기
|
||||
@@ -98,11 +104,11 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
attrs: ro || opts.lockHead ? { hidden: "" } : {},
|
||||
});
|
||||
const root = el("div", {
|
||||
className: `ui-sheet ui-sheet--${opts.mode}`,
|
||||
className: `ui-sheet ui-sheet--${opts.mode}${opts.combo ? " ui-sheet--combo" : ""}`,
|
||||
attrs: { tabindex: "0" },
|
||||
children: [
|
||||
toolbar,
|
||||
...(ro ? [] : [el("p", { className: "ui-sheet__hint", text: st("Hint") })]),
|
||||
...(ro || opts.combo ? [] : [el("p", { className: "ui-sheet__hint", text: st("Hint") })]),
|
||||
scroll,
|
||||
],
|
||||
});
|
||||
@@ -178,6 +184,7 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
state.result = recalcSheet(state.doc);
|
||||
const { scrollLeft, scrollTop } = scroll;
|
||||
scroll.replaceChildren(renderSheet(state));
|
||||
if (opts.combo && state.sel) deepestHead(state.sel.c)?.classList.add("is-head-picked");
|
||||
scroll.scrollLeft = scrollLeft;
|
||||
scroll.scrollTop = scrollTop;
|
||||
syncToolbar();
|
||||
@@ -192,18 +199,31 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
`td[data-r="${CSS.escape(r)}"][data-c="${CSS.escape(c)}"]`,
|
||||
),
|
||||
];
|
||||
/** 구조물 조합 — 그 열을 덮는 머리 칸 가운데 가장 깊은 칸(마지막 분류 = 항목 이름) */
|
||||
const deepestHead = (colId: string): HTMLElement | null => {
|
||||
const at = state.doc.열.findIndex((c) => c.id === colId);
|
||||
let best: HTMLElement | null = null;
|
||||
for (const th of scroll.querySelectorAll<HTMLTableCellElement>("thead th[data-col]")) {
|
||||
const start = state.doc.열.findIndex((c) => c.id === th.dataset.col);
|
||||
if (at < start || at >= start + th.colSpan) continue;
|
||||
if (!best || Number(th.dataset.level) > Number(best.dataset.level)) best = th;
|
||||
}
|
||||
return best;
|
||||
};
|
||||
const select = (r: string, c: string, reveal = false): void => {
|
||||
for (const cell of scroll.querySelectorAll(".is-selected"))
|
||||
cell.classList.remove("is-selected");
|
||||
for (const cell of scroll.querySelectorAll(".is-selected, .is-head-picked"))
|
||||
cell.classList.remove("is-selected", "is-head-picked");
|
||||
state.sel = { r, c };
|
||||
const head = opts.combo ? deepestHead(c) : null;
|
||||
head?.classList.add("is-head-picked");
|
||||
const cells = cellsAt(r, c);
|
||||
for (const cell of cells) cell.classList.add("is-selected");
|
||||
if (reveal) cells[0]?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
||||
if (reveal) (cells[0] ?? head)?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
||||
syncToolbar();
|
||||
opts.onSelect?.(selectionOf(state.doc, r, c));
|
||||
};
|
||||
const move = (dr: number, dc: number): void => {
|
||||
const keys = navRows(state.doc, state.mode);
|
||||
const keys = navRows(state.doc, state.mode).filter((k) => !opts.combo || k !== KEY_UNIT);
|
||||
const cols = state.doc.열;
|
||||
if (!state.sel) {
|
||||
if (keys.length && cols.length) select(keys[0], cols[0].id, true);
|
||||
@@ -247,6 +267,11 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
const sel = state.sel;
|
||||
const col = sel && colOf(sel.c);
|
||||
if (!sel || !col) return;
|
||||
if (opts.combo && sel.r === KEY_UNIT) {
|
||||
const th = deepestHead(col.id); // 단위 줄 없음 — 머리(항목 이름) 고치기
|
||||
if (th && initial === undefined) editComboHead(th);
|
||||
return;
|
||||
}
|
||||
if (isBlankPick(sel.r, col)) return; // 글 열은 빈칸 · 잠김
|
||||
if (opts.onPick && state.mode === "master" && PICK_ROWS.includes(sel.r)) {
|
||||
if (initial === undefined) void pick(sel.r, col.id); // 글자를 쳐서는 안 엶
|
||||
@@ -288,6 +313,19 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
th.querySelector("input")?.addEventListener("blur", () => tip.remove());
|
||||
};
|
||||
|
||||
/** 구조물 조합 머리 이름 칸 — 고친 글을 부른 쪽(나무 이름 바꾸기)에 넘김 · 표 문서는 그대로 */
|
||||
const editComboHead = (th: HTMLElement): void => {
|
||||
const onHeadEdit = opts.combo?.onHeadEdit;
|
||||
const colId = th.dataset.col;
|
||||
const level = Number(th.dataset.level);
|
||||
if (ro || !onHeadEdit || !colId || Number.isNaN(level)) return;
|
||||
const before = th.textContent ?? "";
|
||||
startEdit(th, before, (value) => {
|
||||
render();
|
||||
if (value.trim() && value.trim() !== before) onHeadEdit(colId, level, value.trim());
|
||||
});
|
||||
};
|
||||
|
||||
// ── 사건 ──────────────────────────────────────────────────────────
|
||||
const onClick = (event: MouseEvent): void => {
|
||||
const target = event.target as HTMLElement;
|
||||
@@ -304,7 +342,8 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
const th = target.closest<HTMLElement>("th[data-col]");
|
||||
const layer = target.closest<HTMLElement>("th[data-layer]");
|
||||
if (ro) return;
|
||||
if (th && editCols) editHead(th);
|
||||
if (th && opts.combo?.onHeadEdit) editComboHead(th);
|
||||
else if (th && editCols) editHead(th);
|
||||
else if (layer && editCols) editLayer(layer);
|
||||
else if (target.closest("td[data-r]")) edit();
|
||||
};
|
||||
|
||||
@@ -47,6 +47,10 @@ export interface RenderState {
|
||||
pick?: boolean;
|
||||
/** 머리 · 층 이름을 못 고침(`lockHead`) — 칸 겉모양만. */
|
||||
headLocked?: boolean;
|
||||
/** 구조물 조합 표(`combo`) — 단위 줄 없음 · 열 폭 = 머리 · 번호 글이 한 줄에 들게(저장 폭 안 씀) ·
|
||||
* `headEditable` = 머리 이름 칸을 고칠 수 있음(나무 이름 바꾸기로). */
|
||||
combo?: boolean;
|
||||
headEditable?: boolean;
|
||||
}
|
||||
|
||||
/** 도면 · 면적산출 · 재료산출 줄 칸 글. */
|
||||
@@ -64,6 +68,18 @@ const columnWidth = (doc: SheetDoc, col: SheetColumn, mins: Map<string, number>)
|
||||
mins.get(col.id) ?? 0,
|
||||
);
|
||||
|
||||
/** 구조물 조합 — 일위대가 · 도면 · 면적산출 · 재료산출 줄 글(키 · 번호)이 줄바꿈 없이 들게 최소 폭을 넓힘
|
||||
* (`.ui-sheet__meta td` 글꼴과 같게 잼). */
|
||||
function fitMeta(state: RenderState, cols: SheetColumn[], mins: Map<string, number>): void {
|
||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
||||
const font = `400 ${rem * 0.76}px ${getComputedStyle(document.body).fontFamily}`;
|
||||
for (const col of cols) {
|
||||
const texts = [col.일위대가 ?? "", ...REF_ROWS.map((key) => refText(state, key, col.id))];
|
||||
const need = Math.ceil(Math.max(...texts.map((t) => textWidth(t, font)))) + 14;
|
||||
mins.set(col.id, Math.max(mins.get(col.id) ?? 0, need));
|
||||
}
|
||||
}
|
||||
|
||||
/** 머리 칸 글꼴 — 폭 재기용(`.ui-sheet__table thead` 와 같게). */
|
||||
function headFont(): string {
|
||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
||||
@@ -97,6 +113,8 @@ export function renderSheet(state: RenderState): HTMLElement {
|
||||
const names = columnNames(cols);
|
||||
const font = headFont();
|
||||
const mins = headMinWidths(cols, depth, font);
|
||||
if (state.combo) fitMeta(state, cols, mins);
|
||||
const widthDoc = state.combo ? { ...doc, 보기: { ...doc.보기, 열너비: {} } } : doc;
|
||||
// 줄 머리 칸 폭 — 층 이름 · 단위 · 일위대가 · 도면 · 면적산출 · 재료산출 · 합계 이름이 안 잘리게
|
||||
const GUTTER = Math.min(
|
||||
GUTTER_MAX,
|
||||
@@ -120,7 +138,7 @@ export function renderSheet(state: RenderState): HTMLElement {
|
||||
let left = GUTTER;
|
||||
for (const col of cols) {
|
||||
lefts.push(left);
|
||||
left += columnWidth(doc, col, mins);
|
||||
left += columnWidth(widthDoc, col, mins);
|
||||
}
|
||||
|
||||
const freeze = (cell: HTMLElement, from: number, span = 1): void => {
|
||||
@@ -196,7 +214,7 @@ export function renderSheet(state: RenderState): HTMLElement {
|
||||
|
||||
const thead = (): HTMLElement => {
|
||||
const rows = head.map((cells, level) => {
|
||||
const heads = mode === "master" && !state.headLocked;
|
||||
const heads = mode === "master" && (!state.headLocked || !!state.headEditable);
|
||||
const layer = gutter("th", doc.층?.[level] ?? "", heads ? "is-editable" : "");
|
||||
layer.dataset.layer = String(level);
|
||||
const tr = el("tr", { children: [layer] });
|
||||
@@ -227,7 +245,7 @@ export function renderSheet(state: RenderState): HTMLElement {
|
||||
th.append(el("span", { className: "ui-sheet__resize", attrs: { "data-resize": col.id } }));
|
||||
unit.append(th);
|
||||
});
|
||||
return el("thead", { children: [...rows, unit] });
|
||||
return el("thead", { children: state.combo ? rows : [...rows, unit] });
|
||||
};
|
||||
|
||||
const totalRows = (): HTMLElement[] =>
|
||||
@@ -251,7 +269,7 @@ ${formulaLabel(formula, names)}`
|
||||
el("col", { attrs: { style: `width:${GUTTER}px` } }),
|
||||
...cols.map((col) =>
|
||||
el("col", {
|
||||
attrs: { style: `width:${columnWidth(doc, col, mins)}px`, "data-col": col.id },
|
||||
attrs: { style: `width:${columnWidth(widthDoc, col, mins)}px`, "data-col": col.id },
|
||||
}),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user