The first part of this is the slides-holder
webcomponent. This is
responsible for creating the content-slide
components, which represent
each page. We aren't using the shadow dom so we get the global
styling.
In the constructor we copy the inner html to use as a template, and
then recreate everything from the window hash. This is a JSON object
which has all of the elements on each of the sections.
newSection
creates a new section on the bottom of the document.
nextSlide
and prevSlide
attempt to move forward and backwards on the
page depending on what is visible. There's probably a more elegant
way.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
| import {deflateToBase64, inflateFromBase64} from './compress.js'
class SlidesHolder extends HTMLElement {
constructor() {
super();
// Treat the whole thing as a template
this.template = `${this.innerHTML}`;
const header = `<header>
<button id="saveState">Save State</button>
<button id="newSlide">New Slide</button>
<button id="nextSlide">Next Slide</button>
</header>`;
// default
let sections = [`<section>${this.template}</section>`];
// load from cache if exists
const hash = window.location.hash;
if( hash != '' ) {
sections = [];
const state = JSON.parse(inflateFromBase64( hash.substring( 1 ) ))
for( let section of state ) {
let frag = `<section><content-slide>`
for( let elem of section ) {
frag += `<${elem.nodeName}>${elem.text}</${elem.nodeName}>`
}
frag += "</content-slide></section>"
sections.push( frag );
}
}
this.innerHTML = `${header}${sections.join( "\n")}`
}
connectedCallback() {
this.querySelector( "#nextSlide" ).
addEventListener( "click", () => this.nextSlide() );
this.querySelector( "#newSlide" ).
addEventListener( "click", () => this.newSection() );
this.querySelector( "#saveState" ).
addEventListener( "click", () => this.getState() );
}
getState() {
const state = []
for( let child of this.children ) {
const slide = child.querySelector( "content-slide" );
if( slide ) {
state.push(slide.getState())
}
}
const json = JSON.stringify(state);
window.location.hash = deflateToBase64( json );
}
newSection() {
this.innerHTML += `<section>${this.template}</section>`;
this.connectedCallback();
}
nextSlide() {
let first = true;
let lastneg = false
for( let section of this.querySelectorAll("section" ) ) {
let top = section.getBoundingClientRect().top
if( top > 0 && lastneg ) {
section.scrollIntoView()
return;
}
lastneg = top <= 0
if( first && top > 0 ) {
lastneg = true;
}
first = false;
}
}
prevSlide() {
let last = undefined
for( let section of this.querySelectorAll("section" ) ) {
let top = section.getBoundingClientRect().top
if( top >= 0 && last) {
last.scrollIntoView();
return;
}
last = section;
}
}
}
customElements.define("slides-holder", SlidesHolder);
|