UNPKG

@allensulzen/countdown-timer

Version:

A Web Component that displays a timer counting down to a specified date.

1 lines 8.05 kB
class CountdownTimer extends HTMLElement{constructor(){super()}static get observedAttributes(){return["date","heading","subheading","theme","message","link","linktext"]}attributeChangedCallback(name,oldValue,newValue){oldValue!==newValue&&(this[name]=newValue,this.render())}connectedCallback(){this.hasAttribute("message")||this.setAttribute("message","Countdown Complete!"),this.hasAttribute("linktext")||this.setAttribute("linktext","Learn More"),this.hasAttribute("theme")||this.setAttribute("theme","light"),this.render(),this.startTimer()}disconnectedCallback(){this.stopTimer()}get styles(){return"none"===this.theme?"":"\n <style>\n .countdown-timer__title {\n text-align: center;\n font-family: inherit;\n }\n\n .countdown-timer__content {\n display: flex;\n flex-direction: row;\n gap: 0.5rem;\n justify-content: center;\n align-items: center;\n font-family: inherit;\n min-height: 100px;\n }\n\n .countdown-timer__content .figure {\n font-size: 28px;\n font-weight: bold;\n font-family: inherit;\n }\n\n .countdown-timer__content__counter.skeleton {\n animation: pulse 3s ease-in-out infinite;\n }\n\n .countdown-timer__content__counter {\n position: relative;\n border: 1px solid #ccc;\n border-radius: 5px;\n background-color: #fff;\n padding: 1rem;\n min-width: 50px;\n text-align: center;\n padding-bottom: 1.5rem;\n font-family: inherit;\n }\n\n .countdown-timer__content__counter .label {\n position: absolute;\n left: 0;\n right: 0;\n margin: auto;\n bottom: 0.5rem;\n font-size: 12px;\n text-align: center;\n font-family: inherit;\n }\n\n .countdown-timer__label {\n text-align: center;\n margin-top: 1rem;\n font-family: inherit;\n }\n\n .countdown-timer__label a {\n color: inherit;\n }\n\n .countdown-timer.theme--dark .countdown-timer__content__counter {\n background-color: #000;\n color: #fff;\n border-color: #ccc;\n }\n\n .countdown-timer.theme--dark .countdown-timer__title {\n color: white;\n }\n\n .countdown-timer.theme--dark .countdown-timer__label {\n color: white;\n }\n\n @media only screen and (max-width: 386px) {\n .countdown-timer__content__counter {\n min-width: 40px;\n }\n\n .countdown-timer__content__counter .figure {\n font-size: 16px;\n }\n }\n\n @media only screen and (max-width: 346px) {\n .countdown-timer__content__counter {\n min-width: 23px;\n }\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 0.8; }\n 25% { opacity: 0.3; }\n 50% { opacity: 0.8; }\n 75% { opacity: 0.3; }\n }\n </style>\n "}get loadingState(){return void 0===this.days?'\n <div class="countdown-timer__content__counter skeleton">\n <span class="figure">--</span>\n <span class="label">days</span>\n </div>\n <div class="countdown-timer__content__counter skeleton">\n <span class="figure">--</span>\n <span class="label">hours</span>\n </div>\n <div class="countdown-timer__content__counter skeleton">\n <span class="figure">--</span>\n <span class="label">minutes</span>\n </div>\n <div class="countdown-timer__content__counter skeleton">\n <span class="figure">--</span>\n <span class="label">seconds</span>\n </div>\n ':""}get headingTemplate(){return this.heading?`\n <div class="countdown-timer__title">\n <h2>${this.heading}</h2>\n </div>\n `:""}get subheadingTemplate(){return this.subheading?`\n <div class="countdown-timer__label">\n <span>${this.subheading}</span>\n </div>\n `:""}get completeTemplate(){return`\n <div class="countdown-timer theme--${this.theme}">\n <div class="countdown-timer__title">\n <h2>${this.message}</h2>\n ${this.link?`\n <div class="countdown-timer__label">\n <a href="${this.link}">${this.linktext}</a>\n </div>\n `:""}\n </div>\n </div>\n `}counter(timeProp){return`\n <div class="countdown-timer__content__counter ${timeProp}">\n <span class="figure">${this[timeProp]}</span>\n <span class="label">${1!==this[timeProp]?timeProp:timeProp.replace(/s$/,"")}</span>\n </div>\n `}generateContent(){return`\n <div class="countdown-timer theme--${this.theme}">\n ${this.headingTemplate}\n <div class="countdown-timer__content">\n ${this.loadingState}\n ${this.days>0?this.counter("days"):""}\n ${this.days>0||this.hours>0?this.counter("hours"):""}\n ${this.days>0||this.hours>0||this.minutes>0?this.counter("minutes"):""}\n ${this.seconds>-1?this.counter("seconds"):""}\n </div>\n ${this.subheadingTemplate}\n </div>\n `}updateTime(){if(this.date&&"Invalid Date"==new Date(this.date))throw this.stopTimer(),Error("Invalid date format. Please use the format: YYYY-MM-DDTHH:MM:SS");this.endTime=new Date(this.date),this.currentTime=new Date,this.timeRemaining=this.endTime-this.currentTime,this.days=Math.floor(this.timeRemaining/864e5),this.hours=Math.floor(this.timeRemaining%864e5/36e5),this.minutes=Math.floor(this.timeRemaining%36e5/6e4),this.seconds=Math.floor(this.timeRemaining%6e4/1e3)}renderTime(){if(this.timeRemaining<=0)return this.stopTimer(),this.querySelector(".ct__container").innerHTML=this.completeTemplate,void this.dispatchEvent(new CustomEvent("countdownComplete",{bubbles:!0}));this.querySelector(".ct__container").innerHTML=this.generateContent()}stopTimer(){clearInterval(this.timerInterval),this.dispatchEvent(new CustomEvent("stopTimer",{detail:{days:this.days,hours:this.hours,minutes:this.minutes,seconds:this.seconds},bubbles:!0}))}startTimer(){this.updateTime(),this.dispatchEvent(new CustomEvent("startTimer",{bubbles:!0})),this.timerInterval=setInterval((()=>{this.updateTime(),this.renderTime(),this.dispatchEvent(new CustomEvent("updateTimer",{detail:{days:this.days,hours:this.hours,minutes:this.minutes,seconds:this.seconds},bubbles:!0}))}),1e3)}render(){this.innerHTML=`\n ${this.styles}\n <div class="ct__container">\n ${this.generateContent()}\n </div>\n `,this.renderTime()}}customElements.define("countdown-timer",CountdownTimer);