From 76461627677d3adbf7de8449d011882d3be5f0d0 Mon Sep 17 00:00:00 2001 From: Wild Karl-Heinz Date: Thu, 10 Sep 2026 00:16:10 +0200 Subject: [PATCH] Add per-table "hide chrome/headers when unfocused" clean-look option New Settings checkbox: while a table isn't the selected canvas object, its title bar, column letters and row numbers disappear entirely -- not just visually, their reserved space collapses too, so the table shrinks to just its cell grid. Clicking back into it (e.g. any cell) reselects it and brings everything back. TableObject.hideChromeUnfocused (store/types.ts) + setTableHideChromeUnfocused (document.ts) + io/json.ts normalization, following the existing per-object appearance-setting pattern. CanvasObject.vue gates its title bar on a new showChrome computed and passes its existing `selected` prop down to TableGrid.vue (previously only took tableId) so it can make the same call for its own headers. TableGrid.vue's HEAD_W (a plain constant) becomes a `headW` computed, and the existing `headerH` computed now returns 0 for both whenever hidden -- sufficient on its own since rowTop()/colLeft() already sum those two values as their base offset and are the single source every other position calculation in the file (sticky-frozen offsets, the in-cell editor overlay, clip/fill marquees, scroll-into-view, row virtualization) already goes through. Co-Authored-By: Claude Sonnet 5 --- README.md | 11 ++++++++-- src/components/CanvasObject.vue | 21 ++++++++++++++++--- src/components/SettingsPanel.vue | 8 ++++++++ src/components/TableGrid.vue | 29 ++++++++++++++++++--------- src/io/json.ts | 1 + src/store/__tests__/document.spec.ts | 30 ++++++++++++++++++++++++++++ src/store/document.ts | 8 ++++++++ src/store/types.ts | 7 +++++++ 8 files changed, 101 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ead5f17..bc78039 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,13 @@ scroll- und zoombare Fläche, die **Objekte** trägt: Canvas-Objekt, unabhängig vom DOM. Nur Zeilen, nicht Spalten (in der Praxis wird eher die Zeilenzahl groß). Spaltenbreite per Drag am Spaltenkopf (ab **24 px**), **Zeilenhöhe** per Drag - an der Unterkante des Zeilenkopfs (Standard + je Zeile). + an der Unterkante des Zeilenkopfs (Standard + je Zeile). **„Ohne Rahmen/ + Kopfzeilen, wenn nicht ausgewählt"** (Einstellungen → Objekt, je Tabelle): + solange die Tabelle nicht das ausgewählte Canvas-Objekt ist, verschwinden + Titelleiste, Spaltenbuchstaben und Zeilennummern komplett — nicht nur + optisch, ihr reservierter Platz fällt mit weg, die Tabelle schrumpft auf + reines Zellraster. Ein Klick hinein (z. B. auf eine Zelle) wählt sie + wieder aus und blendet alles zurück ein. - **Diagramm-Objekt** — Chart.js (Balken/Linie), frei positionier- **und** skalierbar. Datenquelle = ein Tabellen-Objekt; jede Zelländerung aktualisiert es sofort. @@ -646,7 +652,8 @@ wichtigen Punkte: - **Objekt** (das gewählte): - Tabelle → Name, Zeilen/Spalten (exakt), Darstellung, Viewport-Größe, fixierte Kopfzeilen/Spalten, Position, Zahlenformat-Locale, **Standard- - Zeilenhöhe**, **Tabellen-Hintergrund/-Textfarbe**, und – bei aktiver + Zeilenhöhe**, **Tabellen-Hintergrund/-Textfarbe**, **„Ohne Rahmen/ + Kopfzeilen, wenn nicht ausgewählt"** (s. u.), und – bei aktiver Zellauswahl – die **Auswahl**-Formatierung (s. o.). - Diagramm → Name, Ansicht, Quelltabelle, Größe, Position, Zahlenformat-Locale, **Hintergrund**, **Text-/Achsenfarbe**. diff --git a/src/components/CanvasObject.vue b/src/components/CanvasObject.vue index 7bb53c4..977bc7f 100644 --- a/src/components/CanvasObject.vue +++ b/src/components/CanvasObject.vue @@ -221,6 +221,16 @@ function onResizeUp() { window.removeEventListener('mouseup', onResizeUp); } +/** A table with `hideChromeUnfocused` set shows its title bar (and, via the + * `selected` prop passed to TableGrid below, its row/column headers) only + * while it's the selected object -- a "clean look" presentation option + * ("Ohne Rahmen/Kopfzeilen, wenn nicht ausgewählt" in Settings). Every + * other object kind is unaffected -- always shown, today's behaviour. */ +const showChrome = computed(() => { + const o = props.object; + return props.selected || !(o.kind === 'table' && o.hideChromeUnfocused); +}); + const icon = computed(() => { switch (props.object.kind) { case 'table': @@ -253,7 +263,7 @@ const style = computed(() => { :style="style" @mousedown="emit('select')" > -
+
{
-
- +
+ @@ -357,6 +367,11 @@ const style = computed(() => { overflow: hidden; border-radius: 0 0 10px 10px; } +/* the chrome bar is what normally rounds the top corners -- without it + (hideChromeUnfocused, currently unfocused), .content fills the whole box */ +.content.full-radius { + border-radius: 10px; +} .canvas-object.flex .content { flex: 1; min-height: 0; diff --git a/src/components/SettingsPanel.vue b/src/components/SettingsPanel.vue index 9b7b30d..80b9292 100644 --- a/src/components/SettingsPanel.vue +++ b/src/components/SettingsPanel.vue @@ -161,6 +161,14 @@ function resetDoc() { Design +
diff --git a/src/components/TableGrid.vue b/src/components/TableGrid.vue index d233824..2d4ba94 100644 --- a/src/components/TableGrid.vue +++ b/src/components/TableGrid.vue @@ -19,12 +19,11 @@ import { useKeyboardPointMode } from '../composables/useKeyboardPointMode'; import ContextMenu from './ContextMenu.vue'; import FormulaSuggest from './FormulaSuggest.vue'; -const props = defineProps<{ tableId: string }>(); +const props = defineProps<{ tableId: string; selected: boolean }>(); const store = useDocument(); const table = computed(() => store.tableById(props.tableId)); -const HEAD_W = 44; const DEFAULT_W = 104; /** How far the in-cell editor may grow beyond the cell before it wraps/scrolls * -- an approximation of "the visible display width/height" rather than @@ -33,7 +32,18 @@ const EDIT_MAX_W = 560; const EDIT_MAX_H = 320; const colWidth = (c: number) => table.value?.colWidths[c] ?? DEFAULT_W; const rowH = (r: number) => store.rowHeightOf(props.tableId, r); -const headerH = computed(() => table.value?.rowHeight ?? DEFAULT_ROW_H); +/** `hideChromeUnfocused` (Settings panel "Ohne Rahmen/Kopfzeilen, wenn nicht + * ausgewählt") hides the row/column headers -- not just visually, their + * reserved space collapses to 0 too -- whenever this table isn't the + * selected canvas object (CanvasObject.vue hides its title bar the same + * way, via its own `showChrome`). `headW`/`headerH` below are what + * actually implement the collapse: every position calculation in this file + * already goes through them (or through `rowTop`/`colLeft`, which do), so + * making them 0 when hidden is the entire fix -- cells simply start at + * (0,0) instead of past a header that isn't there. */ +const showHeaders = computed(() => props.selected || table.value?.hideChromeUnfocused !== true); +const headW = computed(() => (showHeaders.value ? 44 : 0)); +const headerH = computed(() => (showHeaders.value ? (table.value?.rowHeight ?? DEFAULT_ROW_H) : 0)); /** Prefix sums of row heights (`rowCumHeights[i]` = total height of rows * `0..i-1`, excluding the header) -- lets `rowTop()` and the row @@ -127,7 +137,7 @@ const scrollStyle = computed(() => /** px from the table's left edge to the start of column `c` (incl. row header). */ function colLeft(c: number): number { - return HEAD_W + (colCumWidths.value[c] ?? 0); + return headW.value + (colCumWidths.value[c] ?? 0); } /** The internal scroll element of a fixed table (null for auto-size tables). */ @@ -1284,8 +1294,8 @@ function onRowResizeUp() { const totalWidth = computed(() => { const t = table.value; - if (!t) return HEAD_W; - let w = HEAD_W; + if (!t) return headW.value; + let w = headW.value; for (let c = 0; c < t.cols; c++) w += colWidth(c); return w; }); @@ -1320,10 +1330,10 @@ defineExpose({
- + - +