From e331be0572f4ced69ee58850cc564b07f40c8ea1 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 2 Jun 2026 08:49:03 -0400 Subject: [PATCH] fix: Exclude nested tables from sticky scrollbar Share the nested-table check between the sticky header and sticky scrollbar via a memoized isNestedTable() helper, so a nested table that overflows horizontally no longer shows a viewport-pinned floating scrollbar from the constructor tick or update(). Co-Authored-By: Claude Opus 4.8 --- shared/editor/nodes/TableView.ts | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/shared/editor/nodes/TableView.ts b/shared/editor/nodes/TableView.ts index bb4c1d0f3b..b020126fd2 100644 --- a/shared/editor/nodes/TableView.ts +++ b/shared/editor/nodes/TableView.ts @@ -148,6 +148,8 @@ export class TableView extends ProsemirrorTableView { private syncingScroll = false; + private nestedTable: boolean | null = null; + private scrollHandler: ((event: Event) => void) | null = null; private resizeHandler: (() => void) | null = null; @@ -168,7 +170,7 @@ export class TableView extends ProsemirrorTableView { // Defer setup to ensure DOM is fully rendered setTimeout(() => { // Skip sticky behavior for nested tables - if (this.dom.closest(`table .${EditorStyleHelper.table}`)) { + if (this.isNestedTable()) { return; } @@ -298,11 +300,13 @@ export class TableView extends ProsemirrorTableView { const viewportHeight = window.innerHeight || document.documentElement.clientHeight; - // Only show the floating scrollbar when the browser renders persistent - // scrollbars (otherwise the table can be scrolled by gesture and a floating - // bar would look out of place), the table overflows horizontally, is within - // view, and its real scrollbar sits below the bottom of the viewport. + // Only show the floating scrollbar when this is not a nested table, the + // browser renders persistent scrollbars (otherwise the table can be + // scrolled by gesture and a floating bar would look out of place), the + // table overflows horizontally, is within view, and its real scrollbar sits + // below the bottom of the viewport. const shouldShow = + !this.isNestedTable() && hasVisibleScrollbars() && overflows && rect.top < viewportHeight && @@ -340,6 +344,22 @@ export class TableView extends ProsemirrorTableView { } } + /** + * Returns whether this table is nested within another table. Nested tables + * are excluded from sticky header and sticky scrollbar behavior. The result + * is memoized once the DOM is connected, as nesting is structural and stable. + * + * @returns whether the table is nested within another table. + */ + private isNestedTable(): boolean { + if (this.nestedTable === null && isBrowser && this.dom.isConnected) { + this.nestedTable = !!this.dom.closest( + `table .${EditorStyleHelper.table}` + ); + } + return this.nestedTable ?? false; + } + /** * Gets the current header offset from the CSS variable. *