mirror of
https://github.com/JeremyLikness/vanillajs-deck
synced 2024-12-04 19:27:37 +00:00
Add controls and SPA routing
This commit is contained in:
parent
3ef6273404
commit
d66f8c5db4
@ -20,28 +20,39 @@ img {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
div#main {
|
||||
.footer {
|
||||
float: left;
|
||||
height: 15vh;
|
||||
font-size: 1.5em;
|
||||
vertical-align: middle;
|
||||
width: 32vw;
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
slide-deck div {
|
||||
height: 80vh;
|
||||
padding: 1em;
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
div#footer-left {
|
||||
vertical-align: middle;
|
||||
slide-controls {
|
||||
height: 15vh;
|
||||
width: 33vw;
|
||||
text-align: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div#footer-left img {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div#footer-right {
|
||||
font-size: 2em;
|
||||
height: 15vh;
|
||||
float: right;
|
||||
}
|
||||
|
||||
nextslide {
|
||||
display: none;
|
||||
}
|
13
index.html
13
index.html
@ -15,14 +15,15 @@
|
||||
|
||||
<body>
|
||||
<script type="module" src="./js/app.js"></script>
|
||||
<div id="main">
|
||||
<h1>DevNexus</h1>
|
||||
<h2>We are getting things ready...</h2>
|
||||
</div>
|
||||
<div id="footer-left">
|
||||
<slide-deck id="main" start="001-title">
|
||||
<h1>DevNexus | Vanilla.js: Modern 1st Party JavaScript</h1>
|
||||
<h2>Setting things up ...</h2>
|
||||
</slide-deck>
|
||||
<div class="footer left">
|
||||
<img src="./images/devnexus.png" alt="DevNexus logo" title="DevNexus logo" />
|
||||
</div>
|
||||
<div id="footer-right">
|
||||
<slide-controls deck="main" class="footer center">---</slide-controls>
|
||||
<div class="footer right">
|
||||
<a href="https://twitter.com/JeremyLikness">@JeremyLikness</a>
|
||||
</div>
|
||||
</body>
|
||||
|
21
js/app.js
21
js/app.js
@ -1,23 +1,10 @@
|
||||
import { getJson } from "./jsonLoader.js"
|
||||
import { loadSlides } from "./slideLoader.js"
|
||||
import { Navigator } from "./navigator.js"
|
||||
|
||||
const state = {
|
||||
manifest: {}
|
||||
};
|
||||
import { registerDeck } from "./navigator.js"
|
||||
import { registerControls } from "./controls.js"
|
||||
|
||||
const app = async () => {
|
||||
|
||||
state.deck = document.getElementById("main");
|
||||
|
||||
// load the manifest
|
||||
state.manifest = await getJson("slides/manifest.json");
|
||||
|
||||
// load the slides
|
||||
state.slides = await loadSlides(state.manifest.start);
|
||||
|
||||
// initialize the navigation
|
||||
state.navigator = new Navigator(state.slides, state.deck);
|
||||
registerDeck();
|
||||
registerControls();
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", app);
|
||||
|
54
js/controls.js
vendored
Normal file
54
js/controls.js
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
class Controls extends HTMLElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._controlRef = null;
|
||||
this._deck = null;
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
const response = await fetch("/templates/controls.html");
|
||||
const template = await response.text();
|
||||
this.innerHTML = "";
|
||||
const host = document.createElement("div");
|
||||
host.innerHTML = template;
|
||||
this.appendChild(host);
|
||||
this._controlRef = {
|
||||
first: document.getElementById("ctrlFirst"),
|
||||
prev: document.getElementById("ctrlPrevious"),
|
||||
next: document.getElementById("ctrlNext"),
|
||||
last: document.getElementById("ctrlLast"),
|
||||
pos: document.getElementById("position")
|
||||
};
|
||||
this._controlRef.first.addEventListener("click", () => this._deck.jumpTo(0));
|
||||
this._controlRef.prev.addEventListener("click", () => this._deck.previous());
|
||||
this._controlRef.next.addEventListener("click", () => this._deck.next());
|
||||
this._controlRef.last.addEventListener("click", () => this._deck.jumpTo(this._deck.totalSlides - 1));
|
||||
this.refreshState();
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ["deck"];
|
||||
}
|
||||
|
||||
async attributeChangedCallback(attrName, oldVal, newVal) {
|
||||
if (attrName === "deck") {
|
||||
if (oldVal !== newVal) {
|
||||
this._deck = document.getElementById(newVal);
|
||||
this._deck.addEventListener("slideschanged", () => this.refreshState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshState() {
|
||||
const next = this._deck.hasNext;
|
||||
const prev = this._deck.hasPrevious;
|
||||
this._controlRef.first.disabled = !prev;
|
||||
this._controlRef.prev.disabled = !prev;
|
||||
this._controlRef.next.disabled = !next;
|
||||
this._controlRef.last.disabled = !next;
|
||||
this._controlRef.pos.innerText = `${this._deck.currentIndex + 1} / ${this._deck.totalSlides}`;
|
||||
}
|
||||
}
|
||||
|
||||
export const registerControls = () => customElements.define('slide-controls', Controls);
|
@ -1,9 +1,43 @@
|
||||
export class Navigator {
|
||||
import { loadSlides } from "./slideLoader.js"
|
||||
import { Router } from "./router.js"
|
||||
|
||||
constructor(slides, deck) {
|
||||
this._deck = deck;
|
||||
this._slides = slides;
|
||||
this.jumpTo(0);
|
||||
class Navigator extends HTMLElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._router = new Router();
|
||||
this._route = this._router.getRoute();
|
||||
this.slidesChangedEvent = new CustomEvent("slideschanged", {
|
||||
bubbles: true,
|
||||
cancelable: false
|
||||
});
|
||||
this._router.eventSource.addEventListener("routechanged", () => {
|
||||
if (this._route !== this._router.getRoute()) {
|
||||
this._route = this._router.getRoute();
|
||||
if (this._route) {
|
||||
var slide = parseInt(this._route) - 1;
|
||||
this.jumpTo(slide);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ["start"];
|
||||
}
|
||||
|
||||
async attributeChangedCallback(attrName, oldVal, newVal) {
|
||||
if (attrName === "start") {
|
||||
if (oldVal !== newVal) {
|
||||
this._slides = await loadSlides(newVal);
|
||||
this._route = this._router.getRoute();
|
||||
var slide = 0;
|
||||
if (this._route) {
|
||||
slide = parseInt(this._route) - 1;
|
||||
}
|
||||
this.jumpTo(slide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get currentIndex() {
|
||||
@ -11,11 +45,11 @@ export class Navigator {
|
||||
}
|
||||
|
||||
get currentSlide() {
|
||||
return this._slides[this._currentIndex];
|
||||
return this._slides ? this._slides[this._currentIndex] : null;
|
||||
}
|
||||
|
||||
get totalSlides() {
|
||||
return this._slides.length;
|
||||
return this._slides ? this._slides.length : 0;
|
||||
}
|
||||
|
||||
get hasPrevious() {
|
||||
@ -29,8 +63,11 @@ export class Navigator {
|
||||
jumpTo(slideIdx) {
|
||||
if (slideIdx >= 0 && slideIdx < this.totalSlides) {
|
||||
this._currentIndex = slideIdx;
|
||||
this._deck.innerHTML = '';
|
||||
this._deck.appendChild(this.currentSlide.html);
|
||||
this.innerHTML = '';
|
||||
this.appendChild(this.currentSlide.html);
|
||||
this._router.setRoute(slideIdx+1);
|
||||
this._route = this._router.getRoute();
|
||||
this.dispatchEvent(this.slidesChangedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,4 +82,6 @@ export class Navigator {
|
||||
this.jumpTo(this.currentIndex - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const registerDeck = () => customElements.define('slide-deck', Navigator);
|
30
js/router.js
Normal file
30
js/router.js
Normal file
@ -0,0 +1,30 @@
|
||||
export class Router {
|
||||
|
||||
constructor() {
|
||||
this._eventSource = document.createElement("div");
|
||||
this._routeChanged = new CustomEvent("routechanged", {
|
||||
bubbles: true,
|
||||
cancelable: false
|
||||
});
|
||||
this._route = null;
|
||||
window.addEventListener("popstate", () => {
|
||||
if (this.getRoute() !== this._route) {
|
||||
this._route = this.getRoute();
|
||||
this._eventSource.dispatchEvent(this._routeChanged);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get eventSource() {
|
||||
return this._eventSource;
|
||||
}
|
||||
|
||||
setRoute(route) {
|
||||
window.location.hash = route;
|
||||
this._route = route;
|
||||
}
|
||||
|
||||
getRoute() {
|
||||
return window.location.hash.substr(1).replace(/\//ig, "/");
|
||||
}
|
||||
}
|
@ -11,10 +11,10 @@ export async function loadSlides(start) {
|
||||
const slides = [];
|
||||
const cycle = {};
|
||||
while (next) {
|
||||
const nextSlide = await loadSlide(next);
|
||||
if (!cycle[nextSlide.title]) {
|
||||
if (!cycle[next]) {
|
||||
cycle[next] = true;
|
||||
const nextSlide = await loadSlide(next);
|
||||
slides.push(nextSlide);
|
||||
cycle[nextSlide.title] = nextSlide;
|
||||
next = nextSlide.nextSlide;
|
||||
}
|
||||
else {
|
||||
|
@ -1,2 +1,3 @@
|
||||
<title>More Stuff</title>
|
||||
<h1>More Stuff</h1>
|
||||
<h1>More Stuff</h1>
|
||||
<nextslide>003-morestuff</nextslide>
|
7
slides/003-morestuff.html
Normal file
7
slides/003-morestuff.html
Normal file
@ -0,0 +1,7 @@
|
||||
<title>More Stuff</title>
|
||||
<h1>This is even more stuff</h1>
|
||||
<p>Lorem jalskdjflkasdjflkasd jkjdsaflkdsj kldsjflksdjdlk klsdjflkasjlksj kjdsf
|
||||
asdklfjlkfjk laskdjfladskjf
|
||||
asldfkjlkddj
|
||||
asldfjlkfjjkjlff
|
||||
</p>
|
5
templates/controls.html
Normal file
5
templates/controls.html
Normal file
@ -0,0 +1,5 @@
|
||||
<button id="ctrlFirst">⏮</button>
|
||||
<button id="ctrlPrevious">⏪</button>
|
||||
<span id="position">?/?</span>
|
||||
<button id="ctrlNext">▶</button>
|
||||
<button id="ctrlLast">⏭</button>
|
Loading…
Reference in New Issue
Block a user