From 5357c02502be9063ec3214e3fc84e19349451b47 Mon Sep 17 00:00:00 2001 From: Marc Schreiber Date: Wed, 1 May 2019 19:12:11 +0200 Subject: [PATCH 1/2] Add sections plugin - Use section and h1 tags to assign steps to sections - Optional divs showing the section progress - active-section and hidden-section are classes to mark steps as active and hidden section element. --- src/plugins/section/README.md | 88 ++++++++++++++ src/plugins/section/section.js | 207 +++++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 src/plugins/section/README.md create mode 100644 src/plugins/section/section.js diff --git a/src/plugins/section/README.md b/src/plugins/section/README.md new file mode 100644 index 000000000..9ef0b0840 --- /dev/null +++ b/src/plugins/section/README.md @@ -0,0 +1,88 @@ +Section plugin +============== + +Sections for impress.js presentations + +Usage +----- + +Add `
` and `

` tags to group your steps as you can see here: + +```html +
+

Title of Presentation

+ +

Our agenda for today:

+
    +
    + +
    +

    Section Title

    +
    + +
    + + +
    +

    Sub Section Title

    +
    + +
    +
    +
    +``` + +The section name and the current index of the section will be displayed in your presentation. +Therefore, add a div for displaying the current section and/or progress as you can see it here: + + +```html +
    +
    +
    +
    +``` + +```css +#impress-section-overview { + display: flex; + align-items: flex-end; + justify-content: flex-end; +} + +#impress-section-overview .impress-section-numbers { + display: inline-block; + margin-top: .25em; + padding: .1em .25em; + color: white; + background: #aaa; +} + +#impress-section-overview .impress-current-section { + padding-left: 5px; +} + +#impress-section-overview .impress-current-section .section-spacer { + padding-left: 0.3rem; + padding-right: 0.3rem; +} +``` + +Feel free to change the style of your section overview as you like by editing the CSS file. + +Additionally, the plugin will generate an agenda outline for you if you want it to. Therefore, add a `ul` tag with the +id `impress-section-agenda` to any of your divs of the presentation (as shown in the aforementioned HTML snippet). + +Furthermore, this plugin adds the class `active-section` to all steps of the action section and `hidden-section` to all +steps that do not belong to this section. You can use this classes, e.g. to hide the steps of another section: + +```css +.impress-enabled .step .hidden-section { + opacity: 0; +} +``` + +Author +------ + +Copyright 2020: Marc Schreiber (@schrieveslaach) diff --git a/src/plugins/section/section.js b/src/plugins/section/section.js new file mode 100644 index 000000000..885af950f --- /dev/null +++ b/src/plugins/section/section.js @@ -0,0 +1,207 @@ +/** + * Sections Plugin + * + * Copyright 2019 Marc Schreiber (@schrieveslaach) + * Released under the MIT license. + */ +/* global document */ + +( function( document ) { + "use strict"; + let root, api, gc; + + let indexedSteps = null; + + const agenda = document.querySelector( "#impress-section-agenda" ); + const currentSection = document.querySelector( + "#impress-section-overview .impress-current-section" ); + const sectionNumbers = document.querySelector( + "#impress-section-overview .impress-section-numbers" ); + + function findSection( element ) { + if ( element.parentNode == null ) { + return null; + } + if ( element.parentNode.tagName === "SECTION" ) { + const sectionElement = element.parentNode; + + let title = ""; + const headingElements = sectionElement.getElementsByTagName( "H1" ); + if ( headingElements.length > 0 ) { + title = headingElements[ 0 ].textContent; + } + + return { + element: element, + sectionElement: sectionElement, + sectionTitle: title + }; + } + return findSection( element.parentNode ); + } + + function indexSteps() { + return Array.prototype.slice.call( root.querySelectorAll( ".step" ) ) + .map( function( step ) { + return findSection( step ); + } ) + .filter( function( section ) { + return section != null; + } ) + .map( function( step, index ) { + step.index = index + 1; + return step; + } ); + } + + document.addEventListener( "impress:init", function( event ) { + root = event.target; + api = event.detail.api; + gc = api.lib.gc; + + if ( agenda !== null ) { + indexedSteps = indexSteps(); + + function depth( heading, n = 0 ) { + const parent = heading.parentElement; + if ( parent !== null ) { + if ( parent.tagName === "SECTION" ) { + return depth( parent, n + 1 ); + } + return depth( parent, n ); + } + return n; + } + + const headings = [].slice.call( document.querySelectorAll( "section h1" ) ).map( function ( heading ) { + return { text: heading.innerText, depth: depth( heading ) }; + } ); + + headings.reduce( function ( dom, heading, index ) { + if ( index === 0 ) { + const li = document.createElement( "li" ); + li.innerText = heading.text; + + dom.appendChild( li ); + + return { + ul: dom, + depth: heading.depth + }; + } + + if ( dom.depth === heading.depth ) { + const li = document.createElement( "li" ); + li.innerText = heading.text; + + dom.ul.appendChild( li ); + } else if ( dom.depth < heading.depth ) { + const ul = document.createElement( "ul" ); + const li = document.createElement( "li" ); + li.innerText = heading.text; + ul.appendChild( li ); + + const parentLi = dom.ul.lastChild; + parentLi.appendChild( ul ); + + return { + ul, + depth: heading.depth + }; + } else { + const ul = dom.ul.parentElement.parentElement; + + const li = document.createElement( "li" ); + li.innerText = heading.text; + ul.appendChild( li ); + + return { + ul, + depth: heading.depth + }; + } + + return dom; + }, agenda ); + } + } ); + + document.addEventListener( "impress:steprefresh", function( event ) { + updateSectionOverview( event.target ); + } ); + + function updateSectionOverview( targetElement ) { + if ( indexedSteps === null ) { + return; + } + + const indexedStep = indexedSteps.find( function( step ) { + return step.element.isSameNode( targetElement ); + } ); + + if ( sectionNumbers !== null ) { + if ( indexedStep === undefined ) { + sectionNumbers.innerText = ""; + } else { + sectionNumbers.innerText = indexedStep.index + "/" + indexedSteps.length; + } + } + + if ( currentSection !== null ) { + currentSection.innerHTML = ""; + + if ( indexedStep !== undefined ) { + function findSectionTitles( sectionElement, titles = [] ) { + if ( sectionElement.tagName === "SECTION" ) { + const headingElements = sectionElement.getElementsByTagName( "H1" ); + if ( headingElements.length > 0 ) { + titles = titles.concat( headingElements[ 0 ].textContent ); + } + } + + if ( sectionElement.parentElement !== null ) { + return findSectionTitles( sectionElement.parentElement, titles ); + } + + return titles; + } + + const titles = findSectionTitles( indexedStep.sectionElement ).reverse(); + titles.forEach( function( title, index ) { + if ( index > 0 ) { + const span = document.createElement( "span" ); + span.classList.add( "section-spacer" ); + span.innerText = "❯"; + currentSection.appendChild( span ); + } + + const span = document.createElement( "span" ); + span.innerText = title; + currentSection.appendChild( span ); + } ); + } + } + + const currentSectionElement = indexedStep !== undefined ? indexedStep.sectionElement : null; + Array.prototype.slice.call( root.querySelectorAll( ".step" ) ).forEach( function( step ) { + const sectionOfStep = findSection( step ); + + let activeSection = false; + if ( currentSectionElement === null && sectionOfStep === null ) { + activeSection = true; + } + if ( sectionOfStep !== null && sectionOfStep.sectionElement.isSameNode( currentSectionElement ) ) { + activeSection = true; + } + + if ( activeSection ) { + step.classList.add( "active-section" ); + step.classList.remove( "hidden-section" ); + } else { + step.classList.remove( "active-section" ); + step.classList.add( "hidden-section" ); + } + } ); + } + +} )( document ); From a71d984b1edd4c580adbdd85ebdb6051485acba8 Mon Sep 17 00:00:00 2001 From: Marc Schreiber Date: Fri, 17 Jul 2020 09:51:44 +0200 Subject: [PATCH 2/2] Fix hidden section class for subsections --- src/plugins/section/README.md | 2 +- src/plugins/section/section.js | 39 ++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/plugins/section/README.md b/src/plugins/section/README.md index 9ef0b0840..829a8e3aa 100644 --- a/src/plugins/section/README.md +++ b/src/plugins/section/README.md @@ -77,7 +77,7 @@ Furthermore, this plugin adds the class `active-section` to all steps of the act steps that do not belong to this section. You can use this classes, e.g. to hide the steps of another section: ```css -.impress-enabled .step .hidden-section { +.step.hidden-section { opacity: 0; } ``` diff --git a/src/plugins/section/section.js b/src/plugins/section/section.js index 885af950f..4112f54cd 100644 --- a/src/plugins/section/section.js +++ b/src/plugins/section/section.js @@ -147,26 +147,26 @@ } } - if ( currentSection !== null ) { - currentSection.innerHTML = ""; + function findSectionTitles( sectionElement, titles = [] ) { + if ( sectionElement.tagName === "SECTION" ) { + const headingElements = sectionElement.getElementsByTagName( "H1" ); + if ( headingElements.length > 0 ) { + titles = titles.concat( headingElements[ 0 ].textContent ); + } + } - if ( indexedStep !== undefined ) { - function findSectionTitles( sectionElement, titles = [] ) { - if ( sectionElement.tagName === "SECTION" ) { - const headingElements = sectionElement.getElementsByTagName( "H1" ); - if ( headingElements.length > 0 ) { - titles = titles.concat( headingElements[ 0 ].textContent ); - } - } + if ( sectionElement.parentElement !== null ) { + return findSectionTitles( sectionElement.parentElement, titles ); + } - if ( sectionElement.parentElement !== null ) { - return findSectionTitles( sectionElement.parentElement, titles ); - } + return titles; + } + const titles = indexedStep !== undefined ? findSectionTitles( indexedStep.sectionElement ).reverse() : []; - return titles; - } + if ( currentSection !== null ) { + currentSection.innerHTML = ""; - const titles = findSectionTitles( indexedStep.sectionElement ).reverse(); + if ( indexedStep !== undefined ) { titles.forEach( function( title, index ) { if ( index > 0 ) { const span = document.createElement( "span" ); @@ -190,8 +190,11 @@ if ( currentSectionElement === null && sectionOfStep === null ) { activeSection = true; } - if ( sectionOfStep !== null && sectionOfStep.sectionElement.isSameNode( currentSectionElement ) ) { - activeSection = true; + if ( sectionOfStep !== null && titles.length > 0 ) { + const titlesOfStep = findSectionTitles( sectionOfStep.sectionElement ).reverse(); + if ( titlesOfStep.length > 0 && titlesOfStep[ 0 ] === titles[ 0 ] ) { + activeSection = true; + } } if ( activeSection ) {