diff --git a/news/changelog-1.6.md b/news/changelog-1.6.md index 0036a4e9325..27551eeb195 100644 --- a/news/changelog-1.6.md +++ b/news/changelog-1.6.md @@ -25,6 +25,7 @@ All changes included in 1.6: - Remove wrong `sourceMappingUrl` entry in SASS built css. - ([#7715](https://github.com/quarto-dev/quarto-cli/issues/7715)): Revealjs don't support anymore special Pandoc syntax making BulletList in Blockquotes become incremental list. This was confusing and unexpected behavior. Supported syntax for incremental list is documented at . - ([#9742](https://github.com/quarto-dev/quarto-cli/issues/9742)): Links to cross-referenced images correctly works. +- ([#9558](https://github.com/quarto-dev/quarto-cli/issues/9558)): To prevent default footer to show on slide, set `footer='false'` attribute on the slide header, e.g. `## Slide with no footer {footer='false'}` - ([#6012](https://github.com/quarto-dev/quarto-cli/issues/6012)): Add styling for `kbd` element in Revealjs slides. ## `typst` Format diff --git a/src/format/reveal/format-reveal.ts b/src/format/reveal/format-reveal.ts index 3633603df17..b96888bfeb8 100644 --- a/src/format/reveal/format-reveal.ts +++ b/src/format/reveal/format-reveal.ts @@ -320,7 +320,7 @@ export function revealjsFormat() { function revealMarkdownAfterBody(format: Format) { const lines: string[] = []; - lines.push("::: {.quarto-auto-generated-content}\n"); + lines.push("::: {.quarto-auto-generated-content style='display: none;'}\n"); if (format.metadata[kSlideLogo]) { lines.push( ``, @@ -396,30 +396,13 @@ const handleHashTypeNumber = ( }; const handleAutoGeneratedContent = (doc: Document) => { - // bugfix for #6800 - // if slides have content that was added by quarto then move that to the parent node - for (const slide of doc.querySelectorAll("section.slide")) { - const slideContentFromQuarto = (slide as Element).querySelector( - ".quarto-auto-generated-content", - ); - if ( - slideContentFromQuarto && - (slide as Element).getAttribute("data-visibility") === "hidden" - ) { - if (slideContentFromQuarto.childElementCount === 0) { - slideContentFromQuarto.remove(); - } else { - for (const otherSlide of doc.querySelectorAll("section.slide")) { - if ( - (otherSlide as Element).getAttribute("data-visibility") !== - "hidden" - ) { - otherSlide.appendChild(slideContentFromQuarto); - break; - } - } - } - } + // Move quarto auto-generated content outside of slides and hide it + // Content is moved with appendChild in quarto-support plugin + const slideContentFromQuarto = doc.querySelector( + ".quarto-auto-generated-content", + ); + if (slideContentFromQuarto) { + doc.querySelector("div.reveal")?.appendChild(slideContentFromQuarto); } }; diff --git a/src/resources/formats/revealjs/plugins/support/support.js b/src/resources/formats/revealjs/plugins/support/support.js index 2ca3ab1d99d..b1b2ce50615 100644 --- a/src/resources/formats/revealjs/plugins/support/support.js +++ b/src/resources/formats/revealjs/plugins/support/support.js @@ -136,34 +136,51 @@ window.QuartoSupport = function () { }) } - // add footer text - function addFooter(deck) { + // add footer text + function addFooter(deck) { const revealParent = deck.getRevealElement(); const defaultFooterDiv = document.querySelector(".footer-default"); + // Set per slide footer if any defined, + // or show default unless data-footer="false" for no footer on this slide + const setSlideFooter = (ev, defaultFooterDiv) => { + const currentSlideFooter = ev.currentSlide.querySelector(".footer"); + const onDarkBackground = deck.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-dark-background') + const onLightBackground = deck.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-light-background') + if (currentSlideFooter) { + defaultFooterDiv.style.display = "none"; + const slideFooter = currentSlideFooter.cloneNode(true); + handleLinkClickEvents(deck, slideFooter); + deck.getRevealElement().appendChild(slideFooter); + toggleBackgroundTheme(slideFooter, onDarkBackground, onLightBackground) + } else if (ev.currentSlide.getAttribute("data-footer") === "false") { + defaultFooterDiv.style.display = "none"; + } else { + defaultFooterDiv.style.display = "block"; + toggleBackgroundTheme(defaultFooterDiv, onDarkBackground, onLightBackground) + } + } if (defaultFooterDiv) { + // move default footnote to the div.reveal element revealParent.appendChild(defaultFooterDiv); handleLinkClickEvents(deck, defaultFooterDiv); + if (!isPrintView()) { + // Ready even is needed so that footer customization applies on first loaded slide + deck.on('ready', (ev) => { + // Set footer (custom, default or none) + setSlideFooter(ev, defaultFooterDiv) + }); + // Any new navigated new slide will get the custom footnote check deck.on("slidechanged", function (ev) { + // Remove presentation footer defined by previous slide const prevSlideFooter = document.querySelector( ".reveal > .footer:not(.footer-default)" ); if (prevSlideFooter) { prevSlideFooter.remove(); } - const currentSlideFooter = ev.currentSlide.querySelector(".footer"); - const onDarkBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-dark-background') - const onLightBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-light-background') - if (currentSlideFooter) { - defaultFooterDiv.style.display = "none"; - const slideFooter = currentSlideFooter.cloneNode(true); - handleLinkClickEvents(deck, slideFooter); - deck.getRevealElement().appendChild(slideFooter); - toggleBackgroundTheme(slideFooter, onDarkBackground, onLightBackground) - } else { - defaultFooterDiv.style.display = "block"; - toggleBackgroundTheme(defaultFooterDiv, onDarkBackground, onLightBackground) - } + // Set new one (custom, default or none) + setSlideFooter(ev, defaultFooterDiv) }); } } @@ -318,6 +335,13 @@ window.QuartoSupport = function () { } } + function cleanEmptyAutpGeneratedContent(deck) { + const div = document.querySelector('div.quarto-auto-generated-content') + if (div.textContent.trim() === '') { + div.remove() + } + } + return { id: "quarto-support", init: function (deck) { @@ -333,6 +357,8 @@ window.QuartoSupport = function () { handleSlideChanges(deck); workaroundMermaidDistance(deck); handleWhiteSpaceInColumns(deck); + // should stay last + cleanEmptyAutpGeneratedContent(deck); }, }; }; diff --git a/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown-2.qmd b/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown-2.qmd index e77978341dc..3d8d543362c 100644 --- a/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown-2.qmd +++ b/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown-2.qmd @@ -1,5 +1,6 @@ --- title: Reproing a bug on codetools dropdown +format: html code-tools: true --- diff --git a/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown.qmd b/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown.qmd index 7ed5cd84b88..9d7e82bee6a 100644 --- a/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown.qmd +++ b/tests/docs/playwright/ojs/test-ojs-echo-false-codetools-dropdown.qmd @@ -1,5 +1,6 @@ --- title: Reproing a bug on codetools dropdown +format: html code-tools: true --- diff --git a/tests/docs/playwright/revealjs/.gitignore b/tests/docs/playwright/revealjs/.gitignore new file mode 100644 index 00000000000..762b9bc6af0 --- /dev/null +++ b/tests/docs/playwright/revealjs/.gitignore @@ -0,0 +1,3 @@ +.quarto/ +*.html +*_files/ \ No newline at end of file diff --git a/tests/docs/playwright/revealjs/logo-footer.qmd b/tests/docs/playwright/revealjs/logo-footer.qmd new file mode 100644 index 00000000000..38e0974c5b6 --- /dev/null +++ b/tests/docs/playwright/revealjs/logo-footer.qmd @@ -0,0 +1,26 @@ +--- +format: + revealjs: + logo: quarto.png + footer: "Footer text" +--- + +## Slide 1 + +Footer is shown on all slides + +## Slide 2 + +Like logo + +## Slide 3 {footer=false} + +unless `{footer=false}` is set, which will remove the footer + +## Slide 4 + +Custom footer can also be set + +::: {.footer} +A different footer +::: diff --git a/tests/docs/playwright/revealjs/quarto.png b/tests/docs/playwright/revealjs/quarto.png new file mode 100644 index 00000000000..616d17b92cb Binary files /dev/null and b/tests/docs/playwright/revealjs/quarto.png differ diff --git a/tests/docs/smoke-all/2023/09/11/6800.qmd b/tests/docs/smoke-all/2023/09/11/6800.qmd index bb8f3ffbc38..7fc5ccb5183 100644 --- a/tests/docs/smoke-all/2023/09/11/6800.qmd +++ b/tests/docs/smoke-all/2023/09/11/6800.qmd @@ -3,12 +3,11 @@ title: "test" format: revealjs: footer: "hello world" -keep-md: true _quarto: tests: revealjs: ensureHtmlElements: - - ["div.footer"] + - ["div.footer", "div.footer-default"] --- ## Quarto diff --git a/tests/integration/playwright-tests.test.ts b/tests/integration/playwright-tests.test.ts index f2354c63e1a..0dd4bb319da 100644 --- a/tests/integration/playwright-tests.test.ts +++ b/tests/integration/playwright-tests.test.ts @@ -39,7 +39,7 @@ for (const { path: fileName } of globOutput) { // mediabag inspection if we don't wait all renders // individually. This is very slow.. await execProcess({ - cmd: [quartoDevCmd(), "render", input, "--to", "html"], + cmd: [quartoDevCmd(), "render", input], }); fileNames.push(fileName); } diff --git a/tests/integration/playwright/tests/revealjs.spec.ts b/tests/integration/playwright/tests/revealjs.spec.ts new file mode 100644 index 00000000000..03d33e621f9 --- /dev/null +++ b/tests/integration/playwright/tests/revealjs.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; + +test('logo and footer are correctly shown', async ({ page }) => { + await page.goto('./revealjs/logo-footer.html#/slide-1'); + await expect(page.locator('.reveal > .footer.footer-default')).toContainText('Footer text'); + await expect(page.locator('.slide-logo')).toHaveAttribute("src", "quarto.png"); + await page.keyboard.press('ArrowRight'); // Next slide + await expect(page.locator('.reveal > .footer.footer-default')).toContainText('Footer text'); + await expect(page.locator('.slide-logo')).toHaveAttribute("src", "quarto.png"); + await page.keyboard.press('ArrowRight'); // Next slide + await expect(page.locator('.reveal > .footer')).toBeHidden(); + await expect(page.locator('.slide-logo')).toHaveAttribute("src", "quarto.png"); + await page.keyboard.press('ArrowRight'); // Next slide + await expect(page.locator('.reveal > .footer.footer-default')).toBeHidden(); + await expect(page.locator('.reveal > .footer:not(.footer-default)')).toContainText('A different footer'); + await expect(page.locator('.slide-logo')).toHaveAttribute("src", "quarto.png"); +}); \ No newline at end of file