Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ export class PresentationEditor extends EventEmitter {
#editorListeners: Array<{ event: string; handler: (...args: unknown[]) => void }> = [];
#scrollHandler: (() => void) | null = null;
#scrollContainer: Element | Window | null = null;
#scrollContainerValidated = false;
#sectionMetadata: SectionMetadata[] = [];
#documentMode: 'editing' | 'viewing' | 'suggesting' = 'editing';
#inputBridge: PresentationInputBridge | null = null;
Expand Down Expand Up @@ -3064,6 +3065,60 @@ export class PresentationEditor extends EventEmitter {
return win;
}

/**
* Re-validates the detected scroll container after the first layout completes.
*
* At setup time, #findScrollableAncestor picks the first ancestor with
* overflow-y: auto|scroll — but it can't verify the element actually constrains
* content height (content isn't laid out yet). A consumer may set overflow:auto
* on the SuperDoc container without constraining its height, causing the element
* to expand to fit all content instead of scrolling.
*
* After layout, we can check scrollHeight vs clientHeight. If the detected
* container isn't actually scrollable AND it grew beyond the viewport (ruling
* out properly constrained containers that simply don't have enough content
* yet), we walk further up to find one that actually scrolls, or fall back
* to window.
*/
#revalidateScrollContainer(): void {
if (this.#scrollContainerValidated) return;
this.#scrollContainerValidated = true;

if (!(this.#scrollContainer instanceof Element)) return;
if (this.#scrollContainer.scrollHeight > this.#scrollContainer.clientHeight + 1) return;
Comment thread
tupizz marked this conversation as resolved.

// A properly constrained container (e.g. height:600px; overflow:auto) may
// not be overflowing yet if the document is short. Its clientHeight stays
// within viewport bounds. Only switch when the container grew beyond the
// viewport — a clear sign its height is unconstrained.
const win = this.#scrollContainer.ownerDocument?.defaultView;
const viewportHeight = win?.innerHeight ?? 0;
if (this.#scrollContainer.clientHeight <= viewportHeight) return;

let el: Element | null = this.#scrollContainer.parentElement;
let next: Element | Window | null = win ?? null;

while (el) {
const { overflowY } = getComputedStyle(el);
if ((overflowY === 'auto' || overflowY === 'scroll') && el.scrollHeight > el.clientHeight + 1) {
next = el;
break;
}
el = el.parentElement;
}

if (!next || next === this.#scrollContainer) return;

const prev = this.#scrollContainer;
prev.removeEventListener('scroll', this.#scrollHandler!);
this.#scrollContainer = next;

if (next instanceof Element) {
next.addEventListener('scroll', this.#scrollHandler!, { passive: true });
}
this.#domPainter?.setScrollContainer?.(next instanceof HTMLElement ? next : null);
}

/**
* Sets up drag and drop handlers for field annotations and image files.
*/
Expand Down Expand Up @@ -3798,6 +3853,7 @@ export class PresentationEditor extends EventEmitter {
this.#epochMapper.onLayoutComplete(layoutEpoch);
this.#selectionSync.onLayoutComplete(layoutEpoch);
layoutCompleted = true;
this.#revalidateScrollContainer();
this.#updatePermissionOverlay();

// Reset error state on successful layout
Expand Down
Loading