@@ -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() {
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({
diff --git a/src/io/json.ts b/src/io/json.ts
index d6b6efc..810854d 100644
--- a/src/io/json.ts
+++ b/src/io/json.ts
@@ -341,6 +341,7 @@ function normaliseObject(o: SheetObject): SheetObject {
locale: localeOrUndef(o.locale),
bg: colorOrUndef(o.bg),
fg: colorOrUndef(o.fg),
+ hideChromeUnfocused: boolOrUndef(o.hideChromeUnfocused),
};
}
if (o.kind === 'text') {
diff --git a/src/store/__tests__/document.spec.ts b/src/store/__tests__/document.spec.ts
index 158f1f6..a6e8a53 100644
--- a/src/store/__tests__/document.spec.ts
+++ b/src/store/__tests__/document.spec.ts
@@ -2650,6 +2650,34 @@ describe('per-object background & text colour', () => {
});
});
+describe('table "hide chrome when unfocused" (clean-look presentation option)', () => {
+ it('toggles hideChromeUnfocused and clears the field when turned off', () => {
+ const s = fresh();
+ const id = tableId(s, 'Umsatz');
+ expect(s.tableById(id)!.hideChromeUnfocused).toBeUndefined();
+ s.setTableHideChromeUnfocused(id, true);
+ expect(s.tableById(id)!.hideChromeUnfocused).toBe(true);
+ s.setTableHideChromeUnfocused(id, false);
+ expect(s.tableById(id)!.hideChromeUnfocused).toBeUndefined(); // "unset", not stored as false
+ });
+
+ it('survives a JSON round-trip; a non-boolean value on import is dropped', () => {
+ const s = fresh();
+ const id = tableId(s, 'Umsatz');
+ s.setTableHideChromeUnfocused(id, true);
+ const before = s.snapshot();
+ s.replaceDocument(parseDocument(serializeDocument(before)));
+ expect(s.snapshot()).toEqual(before);
+ expect(s.tableById(id)!.hideChromeUnfocused).toBe(true);
+
+ const doc = JSON.parse(serializeDocument(s.snapshot()));
+ const tbl = doc.sheets[0].objects.find((o: { id: string }) => o.id === id);
+ tbl.hideChromeUnfocused = 'yes';
+ s.replaceDocument(parseDocument(JSON.stringify(doc)));
+ expect(s.tableById(id)!.hideChromeUnfocused).toBeUndefined();
+ });
+});
+
describe('full-fidelity JSON export — backgrounds, formats, sheet settings', () => {
/** Populate *every* persisted setting, then verify the export string carries
* it and a round-trip is byte-for-byte identical. */
@@ -2678,6 +2706,7 @@ describe('full-fidelity JSON export — backgrounds, formats, sheet settings', (
s.setObjectLocale(umsatzId, 'fr-FR');
s.setTableView(umsatzId, { mode: 'fixed', width: 520, height: 300, frozenRows: 1, frozenCols: 1 });
s.setTableRowHeight(umsatzId, 30);
+ s.setTableHideChromeUnfocused(umsatzId, true);
s.setRowHeight(umsatzId, 4, 52);
s.setColWidth(umsatzId, 0, 130);
s.setRangeFormat(umsatzId, { col: 1, row: 1 }, { col: 3, row: 4 }, {
@@ -2751,6 +2780,7 @@ describe('full-fidelity JSON export — backgrounds, formats, sheet settings', (
rowHeights: { '4': 52 },
colWidths: { '0': 130 },
view: { mode: 'fixed', width: 520, height: 300, frozenRows: 1, frozenCols: 1 },
+ hideChromeUnfocused: true,
});
expect(tbl.formats['1,1']).toEqual({
align: 'right',
diff --git a/src/store/document.ts b/src/store/document.ts
index d8d35a0..280e625 100644
--- a/src/store/document.ts
+++ b/src/store/document.ts
@@ -895,6 +895,13 @@ function createStore() {
}
markDirty();
}
+ function setTableHideChromeUnfocused(tableId: string, hide: boolean) {
+ const t = tableById(tableId);
+ if (!t) return;
+ if (hide) t.hideChromeUnfocused = true;
+ else delete t.hideChromeUnfocused;
+ markDirty();
+ }
function setCsvDelimiter(d: ',' | ';') {
doc.csvDelimiter = d === ';' ? ';' : ',';
@@ -2944,6 +2951,7 @@ function createStore() {
selectObject,
setObjectBg,
setObjectTextColor,
+ setTableHideChromeUnfocused,
// table grid
tableById,
cellInput,
diff --git a/src/store/types.ts b/src/store/types.ts
index 40c1328..c090d3e 100644
--- a/src/store/types.ts
+++ b/src/store/types.ts
@@ -86,6 +86,13 @@ export interface TableObject {
bg?: string;
/** object default text colour override */
fg?: string;
+ /** when true, this table's title bar and row/column headers are only
+ * shown while it's the selected canvas object -- otherwise (including
+ * right after the document loads, before anything is selected) they're
+ * hidden entirely, not just visually: the table's footprint shrinks to
+ * just its cell grid. A presentation/"clean look" option; unset/false =
+ * always shown, today's behaviour. */
+ hideChromeUnfocused?: boolean;
}
/** A chart object placed on a sheet canvas. Freely positioned and resized. */