Skip to content

Commit 5357c02

Browse files
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.
1 parent ddfb5eb commit 5357c02

File tree

2 files changed

+295
-0
lines changed

2 files changed

+295
-0
lines changed

src/plugins/section/README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
Section plugin
2+
==============
3+
4+
Sections for impress.js presentations
5+
6+
Usage
7+
-----
8+
9+
Add `<section>` and `<h1>` tags to group your steps as you can see here:
10+
11+
```html
12+
<div id="title" class="step">
13+
<h1>Title of Presentation</h1>
14+
15+
<p>Our agenda for today:</p>
16+
<ul id="impress-section-agenda"></ul>
17+
</div>
18+
19+
<section>
20+
<h1>Section Title</h1>
21+
<div id="first-slide" class="step">
22+
23+
</div>
24+
25+
<!-- Nested sections are supported as well -->
26+
<section>
27+
<h1>Sub Section Title</h1>
28+
<div id="second-slide" class="step">
29+
30+
</div>
31+
</section>
32+
</section>
33+
```
34+
35+
The section name and the current index of the section will be displayed in your presentation.
36+
Therefore, add a div for displaying the current section and/or progress as you can see it here:
37+
38+
39+
```html
40+
<div id="impress-section-overview">
41+
<div class="impress-section-numbers"></div>
42+
<div class="impress-current-section"></div>
43+
</div>
44+
```
45+
46+
```css
47+
#impress-section-overview {
48+
display: flex;
49+
align-items: flex-end;
50+
justify-content: flex-end;
51+
}
52+
53+
#impress-section-overview .impress-section-numbers {
54+
display: inline-block;
55+
margin-top: .25em;
56+
padding: .1em .25em;
57+
color: white;
58+
background: #aaa;
59+
}
60+
61+
#impress-section-overview .impress-current-section {
62+
padding-left: 5px;
63+
}
64+
65+
#impress-section-overview .impress-current-section .section-spacer {
66+
padding-left: 0.3rem;
67+
padding-right: 0.3rem;
68+
}
69+
```
70+
71+
Feel free to change the style of your section overview as you like by editing the CSS file.
72+
73+
Additionally, the plugin will generate an agenda outline for you if you want it to. Therefore, add a `ul` tag with the
74+
id `impress-section-agenda` to any of your divs of the presentation (as shown in the aforementioned HTML snippet).
75+
76+
Furthermore, this plugin adds the class `active-section` to all steps of the action section and `hidden-section` to all
77+
steps that do not belong to this section. You can use this classes, e.g. to hide the steps of another section:
78+
79+
```css
80+
.impress-enabled .step .hidden-section {
81+
opacity: 0;
82+
}
83+
```
84+
85+
Author
86+
------
87+
88+
Copyright 2020: Marc Schreiber (@schrieveslaach)

src/plugins/section/section.js

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/**
2+
* Sections Plugin
3+
*
4+
* Copyright 2019 Marc Schreiber (@schrieveslaach)
5+
* Released under the MIT license.
6+
*/
7+
/* global document */
8+
9+
( function( document ) {
10+
"use strict";
11+
let root, api, gc;
12+
13+
let indexedSteps = null;
14+
15+
const agenda = document.querySelector( "#impress-section-agenda" );
16+
const currentSection = document.querySelector(
17+
"#impress-section-overview .impress-current-section" );
18+
const sectionNumbers = document.querySelector(
19+
"#impress-section-overview .impress-section-numbers" );
20+
21+
function findSection( element ) {
22+
if ( element.parentNode == null ) {
23+
return null;
24+
}
25+
if ( element.parentNode.tagName === "SECTION" ) {
26+
const sectionElement = element.parentNode;
27+
28+
let title = "";
29+
const headingElements = sectionElement.getElementsByTagName( "H1" );
30+
if ( headingElements.length > 0 ) {
31+
title = headingElements[ 0 ].textContent;
32+
}
33+
34+
return {
35+
element: element,
36+
sectionElement: sectionElement,
37+
sectionTitle: title
38+
};
39+
}
40+
return findSection( element.parentNode );
41+
}
42+
43+
function indexSteps() {
44+
return Array.prototype.slice.call( root.querySelectorAll( ".step" ) )
45+
.map( function( step ) {
46+
return findSection( step );
47+
} )
48+
.filter( function( section ) {
49+
return section != null;
50+
} )
51+
.map( function( step, index ) {
52+
step.index = index + 1;
53+
return step;
54+
} );
55+
}
56+
57+
document.addEventListener( "impress:init", function( event ) {
58+
root = event.target;
59+
api = event.detail.api;
60+
gc = api.lib.gc;
61+
62+
if ( agenda !== null ) {
63+
indexedSteps = indexSteps();
64+
65+
function depth( heading, n = 0 ) {
66+
const parent = heading.parentElement;
67+
if ( parent !== null ) {
68+
if ( parent.tagName === "SECTION" ) {
69+
return depth( parent, n + 1 );
70+
}
71+
return depth( parent, n );
72+
}
73+
return n;
74+
}
75+
76+
const headings = [].slice.call( document.querySelectorAll( "section h1" ) ).map( function ( heading ) {
77+
return { text: heading.innerText, depth: depth( heading ) };
78+
} );
79+
80+
headings.reduce( function ( dom, heading, index ) {
81+
if ( index === 0 ) {
82+
const li = document.createElement( "li" );
83+
li.innerText = heading.text;
84+
85+
dom.appendChild( li );
86+
87+
return {
88+
ul: dom,
89+
depth: heading.depth
90+
};
91+
}
92+
93+
if ( dom.depth === heading.depth ) {
94+
const li = document.createElement( "li" );
95+
li.innerText = heading.text;
96+
97+
dom.ul.appendChild( li );
98+
} else if ( dom.depth < heading.depth ) {
99+
const ul = document.createElement( "ul" );
100+
const li = document.createElement( "li" );
101+
li.innerText = heading.text;
102+
ul.appendChild( li );
103+
104+
const parentLi = dom.ul.lastChild;
105+
parentLi.appendChild( ul );
106+
107+
return {
108+
ul,
109+
depth: heading.depth
110+
};
111+
} else {
112+
const ul = dom.ul.parentElement.parentElement;
113+
114+
const li = document.createElement( "li" );
115+
li.innerText = heading.text;
116+
ul.appendChild( li );
117+
118+
return {
119+
ul,
120+
depth: heading.depth
121+
};
122+
}
123+
124+
return dom;
125+
}, agenda );
126+
}
127+
} );
128+
129+
document.addEventListener( "impress:steprefresh", function( event ) {
130+
updateSectionOverview( event.target );
131+
} );
132+
133+
function updateSectionOverview( targetElement ) {
134+
if ( indexedSteps === null ) {
135+
return;
136+
}
137+
138+
const indexedStep = indexedSteps.find( function( step ) {
139+
return step.element.isSameNode( targetElement );
140+
} );
141+
142+
if ( sectionNumbers !== null ) {
143+
if ( indexedStep === undefined ) {
144+
sectionNumbers.innerText = "";
145+
} else {
146+
sectionNumbers.innerText = indexedStep.index + "/" + indexedSteps.length;
147+
}
148+
}
149+
150+
if ( currentSection !== null ) {
151+
currentSection.innerHTML = "";
152+
153+
if ( indexedStep !== undefined ) {
154+
function findSectionTitles( sectionElement, titles = [] ) {
155+
if ( sectionElement.tagName === "SECTION" ) {
156+
const headingElements = sectionElement.getElementsByTagName( "H1" );
157+
if ( headingElements.length > 0 ) {
158+
titles = titles.concat( headingElements[ 0 ].textContent );
159+
}
160+
}
161+
162+
if ( sectionElement.parentElement !== null ) {
163+
return findSectionTitles( sectionElement.parentElement, titles );
164+
}
165+
166+
return titles;
167+
}
168+
169+
const titles = findSectionTitles( indexedStep.sectionElement ).reverse();
170+
titles.forEach( function( title, index ) {
171+
if ( index > 0 ) {
172+
const span = document.createElement( "span" );
173+
span.classList.add( "section-spacer" );
174+
span.innerText = "❯";
175+
currentSection.appendChild( span );
176+
}
177+
178+
const span = document.createElement( "span" );
179+
span.innerText = title;
180+
currentSection.appendChild( span );
181+
} );
182+
}
183+
}
184+
185+
const currentSectionElement = indexedStep !== undefined ? indexedStep.sectionElement : null;
186+
Array.prototype.slice.call( root.querySelectorAll( ".step" ) ).forEach( function( step ) {
187+
const sectionOfStep = findSection( step );
188+
189+
let activeSection = false;
190+
if ( currentSectionElement === null && sectionOfStep === null ) {
191+
activeSection = true;
192+
}
193+
if ( sectionOfStep !== null && sectionOfStep.sectionElement.isSameNode( currentSectionElement ) ) {
194+
activeSection = true;
195+
}
196+
197+
if ( activeSection ) {
198+
step.classList.add( "active-section" );
199+
step.classList.remove( "hidden-section" );
200+
} else {
201+
step.classList.remove( "active-section" );
202+
step.classList.add( "hidden-section" );
203+
}
204+
} );
205+
}
206+
207+
} )( document );

0 commit comments

Comments
 (0)