globular-mvc
Version:
Generic template to create web-application that made use of globular as backend and materialize as css (wrap in web-component's)
414 lines (340 loc) • 13.2 kB
JavaScript
import '@polymer/paper-card/paper-card.js';
import '@polymer/paper-ripple/paper-ripple.js';
import '@polymer/paper-icon-button/paper-icon-button.js';
import "@polymer/iron-icons/av-icons";
import { Model } from '../Model';
import { Countdown } from './Countdown';
export class SlideShow extends HTMLElement {
constructor(delay) {
super()
// Set the default delay values...
if(delay==undefined){
delay = 15
}
this.attachShadow({ mode: 'open' });
this.interval = null;
this.isRunning = false;
this.countdown = null;
this.startBtn = null;
this.lastActiveSlide = null;
// Settings event
Model.eventHub.subscribe(
"settings_event_",
(uuid) => {
this.settings_event_listener = uuid;
},
() => {
this.stop();
},
true, this
);
// Settings event
Model.eventHub.subscribe(
"save_settings_evt",
(uuid) => {
this.save_settings_event_listener = uuid;
},
(saveSetting) => {
this.start()
},
true, this
);
// default is fiteen seconds.
this.delay = 1000 * delay // transform it in milisecond.
if (this.hasAttribute("delay")) {
this.delay = parseInt(this.getAttribute("delay")) * 1000
}
this.shadowRoot.innerHTML = `
<style>
:host {
--slidewidth: 1080px;
--footerHeight: 40px;
--slideContainerHeight: 1920px;
--slideHeight: calc(var(--slideContainerHeight) - var(--footerHeight));
--inScreen: 0px;
--offScreen: -1080px;
--offScreenRight: var(--slidewidth);
}
.slides-container {
position: relative;
width: var(--slidewidth);
height: var(--slideContainerHeight);
overflow: hidden;
margin: 0 auto;
}
footer {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
height: var(--footerHeight);
z-index: 99;
}
footer > span {
position: relative;
border-radius: 2rem;
border: solid white;
border-width: 3px;
margin: 0 1rem;
}
#container{
display: flex;
flex-direction: column;
}
#countdown{
position: absolute;
top: 1px;
left: 1px;
}
.marker:hover {
cursor: pointer;
}
#start-btn{
display: none;
position: absolute;
color: var(--palette-text-accent);
--paper-icon-button-ink-color: var(--palette-text-accent);
}
#slides{
position: relative;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<paper-icon-button id="start-btn" icon="av:play-circle-filled"></paper-icon-button>
<paper-card id="container" class="container slides-container">
<div id="slides" >
<slot></slot>
</div>
<footer id="footer">
</footer>
</paper-card>
`
this.startBtn = this.shadowRoot.getElementById("start-btn")
this.startBtn.parentNode.removeChild(this.startBtn)
// equivalent to <globular-count-down id="countdown" countdown="${this.delay / 1000}" diameter="38" stroke="3" ></globular-count-down>
this.countdown = new Countdown(this.delay / 1000, 38, 3)
this.countdown.id = "countdown"
this.countdown.oncountdone = ()=>{
if (this.isRunning) {
this.rotateSlide();
this.start()
}
}
this.startBtn.onclick = (evt) => {
evt.stopPropagation();
this.startBtn.style.display = "none"
this.startBtn.parentNode.removeChild(this.startBtn)
let markers = this.shadowRoot.querySelectorAll(".marker")
for (var i = 0; i < markers.length; i++) {
markers[i].style.backgroundColor = "";
}
this.start()
}
if (this.hasAttribute("backgroundColor")) {
this.shadowRoot.getElementById("start-btn").getElementById("container").style.backgroundColor = this.getAttribute("backgroundColor")
}
}
connectedCallback() {
// Here I will add each slides in order to create theire marker...
for(var i=0; i < this.childNodes.length; i++){
let slide = this.childNodes[i]
// That will create the slide marker
this.createMarker(slide.id)
}
}
setDelay(delay){
this.delay = delay * 1000
// The countdown must be recreate...
let parentNode = this.countdown.parentNode;
if(parentNode != null){
parentNode.removeChild(this.countdown)
}
this.countdown.stop();
this.countdown = new Countdown(this.delay / 1000, 38, 3)
this.countdown.id = "countdown"
this.countdown.oncountdone = ()=>{
if (this.isRunning) {
this.rotateSlide();
this.start()
}
}
if(parentNode != null){
parentNode.appendChild(this.countdown)
this.countdown.start()
}
}
/**
* Append a slide into the slideshow.
* @param {*} html
*/
appendSlide(html) {
// Here I will create the slide.
let range = document.createRange()
let slide = range.createContextualFragment(html)
let id = slide.children[0].id
// In the case of existing slide I will only set
// the slide itself.
if (this.querySelector("#" + id) != undefined) {
let toDelete = this.querySelector("#" + id)
this.replaceChild(slide, toDelete)
}else{
this.appendChild(slide)
}
// Create the marker.
this.createMarker(id)
// Set the slide order...
this.orderSlides();
}
createMarker(id){
// if the marker already exist I will do nothing.
if( this.shadowRoot.getElementById(id) !=undefined){
return
}
let marker = document.createElement("span");
marker.style.position = "relative";
let ripple = document.createElement("paper-ripple");
ripple.classList.add("circle")
ripple.setAttribute("recenters", "")
marker.appendChild(ripple)
marker.classList.add("marker")
marker.id = id
marker.style.borderColor = this.lastChild.marker
this.shadowRoot.getElementById("footer").appendChild(marker)
marker.onclick = () => {
this.stop();
// Here I will rotate the slide.
while (this.childNodes[1].id != id) {
let firstChild = this.firstChild
this.removeChild(firstChild)
this.appendChild(firstChild)
}
let markers = this.shadowRoot.querySelectorAll(".marker")
for (var i = 0; i < markers.length; i++) {
markers[i].style.backgroundColor = "";
}
let marker = this.shadowRoot.getElementById(id)
marker.style.backgroundColor = marker.style.borderColor
// Here I will append the start button into the marker.
marker.appendChild(this.startBtn)
this.orderSlides();
}
}
// set slice position
orderSlides() {
let slides = this.getSlides();
for (var i = 0; i < slides.length; i++) {
let s = slides[i]
s.style.left = (i - 1) * s.offsetWidth + "px"
let marker = this.shadowRoot.getElementById(s.id)
if (i == 1) {
this.countdown.setColor(marker.style.borderColor);
marker.appendChild(this.countdown)
}
}
}
getSlides() {
return this.children; // empty array
}
// Rotate the slide to one position
rotateSlide() {
if (this.children.length == 0) {
return
}
this.orderSlides();
// rotate the slides.
let w = this.firstElementChild.offsetWidth;
this.shadowRoot.getElementById("slides").style.transition = "all 1s ease-out"
this.shadowRoot.getElementById("slides").style.transform = `translateX(${-1 * w}px)`
// Wait the time of animation delay and set back the div at it start position.
setTimeout(() => {
if (this.children.length == 0) {
return
}
this.countdown.style.display = "none";
this.shadowRoot.getElementById("slides").style.transition = 'none';
this.shadowRoot.getElementById("slides").style.transform = `none`;
let firstChild = this.firstElementChild
this.removeChild(firstChild)
this.appendChild(firstChild)
this.orderSlides();
this.countdown.start();
this.countdown.style.display = "block";
}, 1000)
}
/**
* Start the slide show
*/
start() {
if (!this.isRunning) {
if (this.startBtn.parentNode != undefined) {
return;
}
this.countdown.style.display = "block"
this.isRunning = true;
if (this.countdown.parentNode == undefined && this.lastActiveSlide != undefined) {
this.lastActiveSlide.appendChild(this.countdown);
}
this.countdown.start();
}
}
/**
* Stop the slideshow
*/
stop() {
// Stop the running loop.
this.countdown.stop()
this.countdown.style.display = "none"
if (this.countdown.parentNode != undefined) {
this.lastActiveSlide = this.countdown.parentNode;
this.countdown.parentNode.removeChild(this.countdown);
}
this.isRunning = false
this.startBtn.style.display = "block";
}
}
customElements.define('globular-slide-show', SlideShow);
/**
* Accept contact button.
*/
export class Slide extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor() {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
this.marker = "";
}
// The connection callback.
connectedCallback() {
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
:host {
--offWhite: white;
--slidewidth: 1080px;
--footerHeight: 40px;
--slideContainerHeight: 1970px;
--slideHeight: var(--slideContainerHeight);
--offScreen: -1080px;
--offScreenRight: var(--slidewidth);
}
</style>
<slot name="content"></slot>
`
this.style.position = "absolute";
this.style.backgroundColor = "var(--offWhite)";
this.style.boxSizing = "border-box";
this.style.zIndex = "0"
this.style.width = "var(--slidewidth)"
this.style.height = "var(--slideHeight)"
this.marker = this.getAttribute("marker")
}
}
customElements.define('globular-slide', Slide)