diff --git a/apps/viewer-demo/scripts/verify-pptx-slideshow.mjs b/apps/viewer-demo/scripts/verify-pptx-slideshow.mjs index fded59a86..f9e6bbd13 100644 --- a/apps/viewer-demo/scripts/verify-pptx-slideshow.mjs +++ b/apps/viewer-demo/scripts/verify-pptx-slideshow.mjs @@ -287,9 +287,15 @@ await page.waitForFunction( { timeout: 5_000 } ) assert.equal(await call('overlayCount'), 1, 'shadow viewer should be presenting') +await page.waitForFunction( + () => window.__slideshowTest.presentationIsVisible('shadow'), + null, + { timeout: 5_000 } +) +assert.equal(await call('presentationIsVisible', 'shadow'), true, 'shadow viewer should show its active slide') await call('exitFullscreen') await waitOverlay(0, 5_000) -console.log('13) shadow-root native fullscreen exit closes the overlay') +console.log('13) shadow-root fullscreen shows the slide and browser exit closes the overlay') // 14) Enter/Space on the focused exit button activates it, not the slide. await call('enter', 'default') diff --git a/apps/viewer-demo/src/slideshow-test.ts b/apps/viewer-demo/src/slideshow-test.ts index 0f58cab33..60b2972f1 100644 --- a/apps/viewer-demo/src/slideshow-test.ts +++ b/apps/viewer-demo/src/slideshow-test.ts @@ -12,6 +12,7 @@ interface SlideshowTestApi { presenting: (name: ViewerName) => boolean activeNumber: (name: ViewerName) => number activeSlideHasContent: (name: ViewerName) => boolean + presentationIsVisible: (name: ViewerName) => boolean counter: (name: ViewerName) => string transform: (name: ViewerName) => string overlayCount: () => number @@ -177,6 +178,39 @@ const init = async () => { : active return Boolean(slide && slide.classList.contains('slide') && slide.textContent?.trim()) }, + presentationIsVisible: name => { + const overlay = overlayFor(name) as HTMLElement | null + const viewer = viewers.get(name) + if (!overlay || !viewer) { + return false + } + const containers = Array.from( + viewer.content.querySelectorAll(':scope > .flyfish-pptx-slide-slot, :scope > .slide') + ) + const active = containers.find(container => container.classList.contains('is-active-slide')) + const slide = active?.classList.contains('flyfish-pptx-slide-slot') + ? active.firstElementChild as HTMLElement | null + : active + if (!slide) { + return false + } + const root = overlay.getRootNode() + const hasViewerStyles = root instanceof ShadowRoot + ? Boolean(root.querySelector('#flyfish-pptx-native-style')) + : Boolean(document.getElementById('flyfish-pptx-native-style')) + const overlayRect = overlay.getBoundingClientRect() + const slideRect = slide.getBoundingClientRect() + const slideStyle = getComputedStyle(slide) + const intersectsOverlay = slideRect.right > overlayRect.left && + slideRect.left < overlayRect.right && + slideRect.bottom > overlayRect.top && + slideRect.top < overlayRect.bottom + return hasViewerStyles && + slideStyle.display !== 'none' && + slideRect.width > 0 && + slideRect.height > 0 && + intersectsOverlay + }, counter: name => overlayFor(name)?.querySelector('.flyfish-pptx-presentation-counter')?.textContent ?? '', transform: name => viewers.get(name)?.content.style.transform ?? '', overlayCount: () => deepAll('.flyfish-pptx-presentation').length, diff --git a/packages/renderers/pptx/src/viewer.ts b/packages/renderers/pptx/src/viewer.ts index 978e1c6e6..95e9be6f4 100644 --- a/packages/renderers/pptx/src/viewer.ts +++ b/packages/renderers/pptx/src/viewer.ts @@ -238,19 +238,27 @@ export class PptxViewer { return this.presentation?.slideNumber ?? 1; } + private get styleRoot(): Document | ShadowRoot { + if (this.options.styleRoot) { + return this.options.styleRoot; + } + const documentRef = this.target.ownerDocument || document; + const root = this.target.getRootNode(); + const ShadowRootCtor = documentRef.defaultView?.ShadowRoot; + return ShadowRootCtor && root instanceof ShadowRootCtor + ? root as ShadowRoot + : documentRef; + } + /** * Where the slideshow overlay is mounted. It has to share a root with the injected slide styles, * or the engine's scoped CSS stops applying once the slides move into the overlay. */ get presentationRoot(): ShadowRoot | HTMLElement { const documentRef = this.target.ownerDocument || document; - if (this.options.styleRoot) { - return this.options.styleRoot; - } - const root = this.target.getRootNode(); - const ShadowRootCtor = documentRef.defaultView?.ShadowRoot; - if (ShadowRootCtor && root instanceof ShadowRootCtor) { - return root as ShadowRoot; + const styleRoot = this.styleRoot; + if (styleRoot !== documentRef) { + return styleRoot as ShadowRoot; } return documentRef.body || documentRef.documentElement; } @@ -325,7 +333,7 @@ export class PptxViewer { async open() { ensureZipWithinLimits(this.buffer, this.options); - ensurePptxViewerStyles(this.target.ownerDocument || document, this.options.styleRoot); + ensurePptxViewerStyles(this.target.ownerDocument || document, this.styleRoot); this.target.replaceChildren(this.scaleBox); this.attachResizeObserver(); this.attachSlideWindowListeners();