From d283f7e661e14d6ae1881fe803e5b4f1ed0689ff Mon Sep 17 00:00:00 2001 From: David Thompson Date: Mon, 24 Jun 2024 13:49:08 -0400 Subject: Add 2024 Guix social talk. --- .../reveal.js/js/controllers/overview.js | 255 +++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 2024-06-18-guix-social/reveal.js/js/controllers/overview.js (limited to '2024-06-18-guix-social/reveal.js/js/controllers/overview.js') diff --git a/2024-06-18-guix-social/reveal.js/js/controllers/overview.js b/2024-06-18-guix-social/reveal.js/js/controllers/overview.js new file mode 100644 index 0000000..4e146b6 --- /dev/null +++ b/2024-06-18-guix-social/reveal.js/js/controllers/overview.js @@ -0,0 +1,255 @@ +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.Reveal.isScrollView() && !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 ); + } + + } + } + + } + +} \ No newline at end of file -- cgit v1.2.3