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;
|
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;
|
height: 80vh;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
background: lightgray;
|
background: lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#footer-left {
|
slide-controls {
|
||||||
vertical-align: middle;
|
|
||||||
height: 15vh;
|
height: 15vh;
|
||||||
|
width: 33vw;
|
||||||
|
text-align: center;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#footer-left img {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#footer-right {
|
|
||||||
font-size: 2em;
|
|
||||||
height: 15vh;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextslide {
|
nextslide {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
13
index.html
13
index.html
@ -15,14 +15,15 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<script type="module" src="./js/app.js"></script>
|
<script type="module" src="./js/app.js"></script>
|
||||||
<div id="main">
|
<slide-deck id="main" start="001-title">
|
||||||
<h1>DevNexus</h1>
|
<h1>DevNexus | Vanilla.js: Modern 1st Party JavaScript</h1>
|
||||||
<h2>We are getting things ready...</h2>
|
<h2>Setting things up ...</h2>
|
||||||
</div>
|
</slide-deck>
|
||||||
<div id="footer-left">
|
<div class="footer left">
|
||||||
<img src="./images/devnexus.png" alt="DevNexus logo" title="DevNexus logo" />
|
<img src="./images/devnexus.png" alt="DevNexus logo" title="DevNexus logo" />
|
||||||
</div>
|
</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>
|
<a href="https://twitter.com/JeremyLikness">@JeremyLikness</a>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
21
js/app.js
21
js/app.js
@ -1,23 +1,10 @@
|
|||||||
import { getJson } from "./jsonLoader.js"
|
import { registerDeck } from "./navigator.js"
|
||||||
import { loadSlides } from "./slideLoader.js"
|
import { registerControls } from "./controls.js"
|
||||||
import { Navigator } from "./navigator.js"
|
|
||||||
|
|
||||||
const state = {
|
|
||||||
manifest: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = async () => {
|
const app = async () => {
|
||||||
|
|
||||||
state.deck = document.getElementById("main");
|
registerDeck();
|
||||||
|
registerControls();
|
||||||
// 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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", app);
|
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) {
|
class Navigator extends HTMLElement {
|
||||||
this._deck = deck;
|
|
||||||
this._slides = slides;
|
constructor() {
|
||||||
this.jumpTo(0);
|
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() {
|
get currentIndex() {
|
||||||
@ -11,11 +45,11 @@ export class Navigator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get currentSlide() {
|
get currentSlide() {
|
||||||
return this._slides[this._currentIndex];
|
return this._slides ? this._slides[this._currentIndex] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalSlides() {
|
get totalSlides() {
|
||||||
return this._slides.length;
|
return this._slides ? this._slides.length : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasPrevious() {
|
get hasPrevious() {
|
||||||
@ -29,8 +63,11 @@ export class Navigator {
|
|||||||
jumpTo(slideIdx) {
|
jumpTo(slideIdx) {
|
||||||
if (slideIdx >= 0 && slideIdx < this.totalSlides) {
|
if (slideIdx >= 0 && slideIdx < this.totalSlides) {
|
||||||
this._currentIndex = slideIdx;
|
this._currentIndex = slideIdx;
|
||||||
this._deck.innerHTML = '';
|
this.innerHTML = '';
|
||||||
this._deck.appendChild(this.currentSlide.html);
|
this.appendChild(this.currentSlide.html);
|
||||||
|
this._router.setRoute(slideIdx+1);
|
||||||
|
this._route = this._router.getRoute();
|
||||||
|
this.dispatchEvent(this.slidesChangedEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,3 +83,5 @@ export class Navigator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 slides = [];
|
||||||
const cycle = {};
|
const cycle = {};
|
||||||
while (next) {
|
while (next) {
|
||||||
|
if (!cycle[next]) {
|
||||||
|
cycle[next] = true;
|
||||||
const nextSlide = await loadSlide(next);
|
const nextSlide = await loadSlide(next);
|
||||||
if (!cycle[nextSlide.title]) {
|
|
||||||
slides.push(nextSlide);
|
slides.push(nextSlide);
|
||||||
cycle[nextSlide.title] = nextSlide;
|
|
||||||
next = nextSlide.nextSlide;
|
next = nextSlide.nextSlide;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
<title>More Stuff</title>
|
<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