257 lines
6.9 KiB
JavaScript
257 lines
6.9 KiB
JavaScript
import { SLIDES_SELECTOR } from "../utils/constants.js";
|
|
import { extend, queryAll, transformElement } from "../utils/util.js";
|
|
|
|
/**
|
|
* Handles all logic related to the overview mode
|
|
* (birds-eye view of all slides).
|
|
*/
|
|
export default class Overview {
|
|
constructor(Reveal) {
|
|
this.Reveal = Reveal;
|
|
|
|
this.active = false;
|
|
|
|
this.onSlideClicked = this.onSlideClicked.bind(this);
|
|
}
|
|
|
|
/**
|
|
* Displays the overview of slides (quick nav) by scaling
|
|
* down and arranging all slide elements.
|
|
*/
|
|
activate() {
|
|
// Only proceed if enabled in config
|
|
if (this.Reveal.getConfig().overview && !this.isActive()) {
|
|
this.active = true;
|
|
|
|
this.Reveal.getRevealElement().classList.add("overview");
|
|
|
|
// Don't auto-slide while in overview mode
|
|
this.Reveal.cancelAutoSlide();
|
|
|
|
// Move the backgrounds element into the slide container to
|
|
// that the same scaling is applied
|
|
this.Reveal.getSlidesElement().appendChild(
|
|
this.Reveal.getBackgroundsElement()
|
|
);
|
|
|
|
// Clicking on an overview slide navigates to it
|
|
queryAll(this.Reveal.getRevealElement(), SLIDES_SELECTOR).forEach(
|
|
(slide) => {
|
|
if (!slide.classList.contains("stack")) {
|
|
slide.addEventListener("click", this.onSlideClicked, true);
|
|
}
|
|
}
|
|
);
|
|
|
|
// Calculate slide sizes
|
|
const margin = 70;
|
|
const slideSize = this.Reveal.getComputedSlideSize();
|
|
this.overviewSlideWidth = slideSize.width + margin;
|
|
this.overviewSlideHeight = slideSize.height + margin;
|
|
|
|
// Reverse in RTL mode
|
|
if (this.Reveal.getConfig().rtl) {
|
|
this.overviewSlideWidth = -this.overviewSlideWidth;
|
|
}
|
|
|
|
this.Reveal.updateSlidesVisibility();
|
|
|
|
this.layout();
|
|
this.update();
|
|
|
|
this.Reveal.layout();
|
|
|
|
const indices = this.Reveal.getIndices();
|
|
|
|
// Notify observers of the overview showing
|
|
this.Reveal.dispatchEvent({
|
|
type: "overviewshown",
|
|
data: {
|
|
indexh: indices.h,
|
|
indexv: indices.v,
|
|
currentSlide: this.Reveal.getCurrentSlide(),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Uses CSS transforms to position all slides in a grid for
|
|
* display inside of the overview mode.
|
|
*/
|
|
layout() {
|
|
// Layout slides
|
|
this.Reveal.getHorizontalSlides().forEach((hslide, h) => {
|
|
hslide.setAttribute("data-index-h", h);
|
|
transformElement(
|
|
hslide,
|
|
"translate3d(" + h * this.overviewSlideWidth + "px, 0, 0)"
|
|
);
|
|
|
|
if (hslide.classList.contains("stack")) {
|
|
queryAll(hslide, "section").forEach((vslide, v) => {
|
|
vslide.setAttribute("data-index-h", h);
|
|
vslide.setAttribute("data-index-v", v);
|
|
|
|
transformElement(
|
|
vslide,
|
|
"translate3d(0, " + v * this.overviewSlideHeight + "px, 0)"
|
|
);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Layout slide backgrounds
|
|
Array.from(this.Reveal.getBackgroundsElement().childNodes).forEach(
|
|
(hbackground, h) => {
|
|
transformElement(
|
|
hbackground,
|
|
"translate3d(" + h * this.overviewSlideWidth + "px, 0, 0)"
|
|
);
|
|
|
|
queryAll(hbackground, ".slide-background").forEach((vbackground, v) => {
|
|
transformElement(
|
|
vbackground,
|
|
"translate3d(0, " + v * this.overviewSlideHeight + "px, 0)"
|
|
);
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Moves the overview viewport to the current slides.
|
|
* Called each time the current slide changes.
|
|
*/
|
|
update() {
|
|
const vmin = Math.min(window.innerWidth, window.innerHeight);
|
|
const scale = Math.max(vmin / 5, 150) / vmin;
|
|
const indices = this.Reveal.getIndices();
|
|
|
|
this.Reveal.transformSlides({
|
|
overview: [
|
|
"scale(" + scale + ")",
|
|
"translateX(" + -indices.h * this.overviewSlideWidth + "px)",
|
|
"translateY(" + -indices.v * this.overviewSlideHeight + "px)",
|
|
].join(" "),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Exits the slide overview and enters the currently
|
|
* active slide.
|
|
*/
|
|
deactivate() {
|
|
// Only proceed if enabled in config
|
|
if (this.Reveal.getConfig().overview) {
|
|
this.active = false;
|
|
|
|
this.Reveal.getRevealElement().classList.remove("overview");
|
|
|
|
// Temporarily add a class so that transitions can do different things
|
|
// depending on whether they are exiting/entering overview, or just
|
|
// moving from slide to slide
|
|
this.Reveal.getRevealElement().classList.add("overview-deactivating");
|
|
|
|
setTimeout(() => {
|
|
this.Reveal.getRevealElement().classList.remove(
|
|
"overview-deactivating"
|
|
);
|
|
}, 1);
|
|
|
|
// Move the background element back out
|
|
this.Reveal.getRevealElement().appendChild(
|
|
this.Reveal.getBackgroundsElement()
|
|
);
|
|
|
|
// Clean up changes made to slides
|
|
queryAll(this.Reveal.getRevealElement(), SLIDES_SELECTOR).forEach(
|
|
(slide) => {
|
|
transformElement(slide, "");
|
|
|
|
slide.removeEventListener("click", this.onSlideClicked, true);
|
|
}
|
|
);
|
|
|
|
// Clean up changes made to backgrounds
|
|
queryAll(
|
|
this.Reveal.getBackgroundsElement(),
|
|
".slide-background"
|
|
).forEach((background) => {
|
|
transformElement(background, "");
|
|
});
|
|
|
|
this.Reveal.transformSlides({ overview: "" });
|
|
|
|
const indices = this.Reveal.getIndices();
|
|
|
|
this.Reveal.slide(indices.h, indices.v);
|
|
this.Reveal.layout();
|
|
this.Reveal.cueAutoSlide();
|
|
|
|
// Notify observers of the overview hiding
|
|
this.Reveal.dispatchEvent({
|
|
type: "overviewhidden",
|
|
data: {
|
|
indexh: indices.h,
|
|
indexv: indices.v,
|
|
currentSlide: this.Reveal.getCurrentSlide(),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggles the slide overview mode on and off.
|
|
*
|
|
* @param {Boolean} [override] Flag which overrides the
|
|
* toggle logic and forcibly sets the desired state. True means
|
|
* overview is open, false means it's closed.
|
|
*/
|
|
toggle(override) {
|
|
if (typeof override === "boolean") {
|
|
override ? this.activate() : this.deactivate();
|
|
} else {
|
|
this.isActive() ? this.deactivate() : this.activate();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the overview is currently active.
|
|
*
|
|
* @return {Boolean} true if the overview is active,
|
|
* false otherwise
|
|
*/
|
|
isActive() {
|
|
return this.active;
|
|
}
|
|
|
|
/**
|
|
* Invoked when a slide is and we're in the overview.
|
|
*
|
|
* @param {object} event
|
|
*/
|
|
onSlideClicked(event) {
|
|
if (this.isActive()) {
|
|
event.preventDefault();
|
|
|
|
let element = event.target;
|
|
|
|
while (element && !element.nodeName.match(/section/gi)) {
|
|
element = element.parentNode;
|
|
}
|
|
|
|
if (element && !element.classList.contains("disabled")) {
|
|
this.deactivate();
|
|
|
|
if (element.nodeName.match(/section/gi)) {
|
|
let h = parseInt(element.getAttribute("data-index-h"), 10),
|
|
v = parseInt(element.getAttribute("data-index-v"), 10);
|
|
|
|
this.Reveal.slide(h, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|