From 86d5e5dc17c5081a8cea56e8bba3b1cb7b92f21b Mon Sep 17 00:00:00 2001 From: Marc Schreiber Date: Wed, 1 May 2019 19:12:11 +0200 Subject: [PATCH] Add sections plugin - Use data-section to assign steps to sections - Use data-sub-section to assign steps to sub-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/sections/README.md | 76 +++++++++++++++ src/plugins/sections/sections.js | 153 +++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 src/plugins/sections/README.md create mode 100644 src/plugins/sections/sections.js diff --git a/src/plugins/sections/README.md b/src/plugins/sections/README.md new file mode 100644 index 000000000..acaf208cb --- /dev/null +++ b/src/plugins/sections/README.md @@ -0,0 +1,76 @@ +Sections plugin +=============== + +Sections for impress.js presentations + +Usage +----- + +Add `data-section="Intro"` to your steps as you can see here: + +```html +
+

Title of Slide

+
+ +
+ +
+ +
+ +
+``` + +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-numbers { + display: inline-block; + margin-top: .25em; + padding: .1em .25em; + color: white; + background: #aaa; +} + +.impress-current-section { + padding-left: 5px; +} +``` + +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 the a class `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 .hidden-section { + opacity: 0 !important; +} +``` + +The sections plugin supports also sub section. Therefore, add the attribute `data-sub-section="…"` to your subsection. +The agenda list and also the section overview will show the sub sections. + +Author +------ + +Copyright 2019: Marc Schreiber (@schrieveslaach) diff --git a/src/plugins/sections/sections.js b/src/plugins/sections/sections.js new file mode 100644 index 000000000..358da608f --- /dev/null +++ b/src/plugins/sections/sections.js @@ -0,0 +1,153 @@ +/** + * Sections Plugin + * + * Copyright 2019 Marc Schreiber (@schrieveslaach) + * Released under the MIT license. + */ +/* global document */ + +(function (document) { + "use strict"; + var root, api, gc; + + var indexedSteps; + + var agenda = document.querySelector("div.agenda ul"); + var currentSection = document.querySelector("div.impress-section-overview .impress-current-section"); + var sectionNumbers = document.querySelector("div.impress-section-overview .impress-section-numbers"); + + function indexSteps() { + var slides = Array.prototype.slice.call(root.querySelectorAll(".step")) + .map(function (step) { + return { + id: step.id, + section: step.dataset.section, + subSection: step.dataset.subSection + }; + }) + .reduce(function (accumulatedSlides, slide, currentIndex, slides) { + if (currentIndex === 0) { + return [slide]; + } + + let previousSlide = slides[currentIndex - 1]; + if (slide.section == null) { + var currentSection = previousSlide.section; + slide.section = currentSection; + } + + if (slide.subSection == null && previousSlide.section === slide.section) { + var currentSubSection = previousSlide.subSection; + slide.subSection = currentSubSection; + } + + return accumulatedSlides.concat([slide]); + }, []); + + return { + sectionIndices: slides.reduce(function (sectionIndices, slide) { + var section = sectionIndices[slide.section]; + + if (section == null) { + section = { + index: Object.keys(sectionIndices).length + 1, + steps: [slide.id], + subSections: [] + }; + sectionIndices[slide.section] = section; + } else { + section.steps = section.steps.concat([slide.id]); + } + + if (slide.subSection != null && section.subSections.indexOf(slide.subSection) < 0) { + section.subSections = section.subSections.concat([slide.subSection]); + } + + return sectionIndices; + }, {}), + + slideSectionMapping: slides.reduce(function (slideSectionMapping, slide) { + slideSectionMapping[slide.id] = { + section: slide.section, + subSection: slide.subSection + }; + return slideSectionMapping; + }, {}) + }; + } + + document.addEventListener("impress:init", function (event) { + root = event.target; + api = event.detail.api; + gc = api.lib.gc; + + indexedSteps = indexSteps(); + + if (agenda != null) { + Object.keys(indexedSteps.sectionIndices).forEach(function (section) { + var li = document.createElement("li"); + agenda.appendChild(li); + li.innerText = section; + + var subSections = indexedSteps.sectionIndices[section].subSections; + if (subSections.length > 0) { + var ul = document.createElement("ul"); + li.appendChild(ul); + + subSections.forEach(function (subSection) { + var li = document.createElement("li"); + ul.appendChild(li); + li.innerText = subSection; + }); + } + }); + } + }); + + + document.addEventListener("impress:stepleave", function (event) { + updateSectionOverview(event.detail.next.id); + }); + + document.addEventListener("impress:steprefresh", function (event) { + updateSectionOverview(event.target.id); + }); + + function updateSectionOverview(slideId) { + if (indexedSteps == null) { + return; + } + + var section = { + section: indexedSteps.slideSectionMapping[slideId].section, + subSection: indexedSteps.slideSectionMapping[slideId].subSection, + indices: indexedSteps.sectionIndices[indexedSteps.slideSectionMapping[slideId].section] + }; + + if (currentSection != null) { + if (section.subSection == null) { + currentSection.innerText = section.section; + } else { + currentSection.innerText = section.section + " \u2022 " + section.subSection; + } + } + + if (sectionNumbers != null) { + sectionNumbers.innerText = section.indices.index + "/" + Object.keys(indexedSteps.sectionIndices).length; + } + + Object.keys(indexedSteps.slideSectionMapping).forEach(function (stepId) { + var step = document.getElementById(stepId); + + var subSection = indexedSteps.slideSectionMapping[stepId].subSection; + if (section.indices.steps.indexOf(stepId) >= 0 && section.subSection === subSection) { + step.classList.add("active-section"); + step.classList.remove("hidden-section"); + } else { + step.classList.remove("active-section"); + step.classList.add("hidden-section"); + } + }); + } + +})(document);