Skip to content

Commit

Permalink
Add sections plugin
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
schrieveslaach committed Jul 9, 2020
1 parent ddfb5eb commit 5357c02
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 0 deletions.
88 changes: 88 additions & 0 deletions src/plugins/section/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
Section plugin
==============

Sections for impress.js presentations

Usage
-----

Add `<section>` and `<h1>` tags to group your steps as you can see here:

```html
<div id="title" class="step">
<h1>Title of Presentation</h1>

<p>Our agenda for today:</p>
<ul id="impress-section-agenda"></ul>
</div>

<section>
<h1>Section Title</h1>
<div id="first-slide" class="step">

</div>

<!-- Nested sections are supported as well -->
<section>
<h1>Sub Section Title</h1>
<div id="second-slide" class="step">

</div>
</section>
</section>
```

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
<div id="impress-section-overview">
<div class="impress-section-numbers"></div>
<div class="impress-current-section"></div>
</div>
```

```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)
207 changes: 207 additions & 0 deletions src/plugins/section/section.js
Original file line number Diff line number Diff line change
@@ -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 );

0 comments on commit 5357c02

Please sign in to comment.