Skip to content

Commit

Permalink
Creating new countdown component.
Browse files Browse the repository at this point in the history
  • Loading branch information
jedgar1mx committed Sep 25, 2024
1 parent 4624767 commit 0cf4390
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 1 deletion.
2 changes: 1 addition & 1 deletion build/assets/js/main.js

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions src/components/atoms/Countdown/Countdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
time {
display: flex;
}

.item {
padding: 0.25em 0.5em;
}

.number,
.unit {
display: block;
}

.number.sm {
font-size: 1rem;
}

.number.md {
font-size: 2rem;
}

.number.lg {
font-size: 3rem;
}

.unit.sm {
font-size: 0.33rem;
}

.unit.md {
font-size: 0.66rem;
}

.unit.lg {
font-size: 1rem;
}

.unit,
.number {
text-align: center;
}
165 changes: 165 additions & 0 deletions src/components/atoms/Countdown/Countdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import styles from '!!raw-loader!./Countdown.css';
import varStyles from '!!raw-loader!../../../shared/variables.css';
import bootstrapStyles from '!!raw-loader!../../../shared/themed-bootstrap.css';

const template = document.createElement('template');

template.innerHTML = `
<time></time>
`;

export default class Countdown extends HTMLElement {
static get observedAttributes() {
return ['ends', 'size', 'text'];
}
constructor() {
// Always call super first in constructor
super();
// Create a shadow root
this.countdownInterval = this.initialiseClock.bind(this);
const shadow = this.attachShadow({ mode: 'open' });
shadow.appendChild(template.content.cloneNode(true));
}

connectedCallback() {
this.render();
}

attributeChangedCallback(attrName, oldValue, newValue) {
if (newValue !== oldValue) {
this[attrName] = newValue;
if (attrName !== 'ends') {
this.render();
}
}
}

render() {
const { ends } = this;
const bootStyles = document.createElement('style');
bootStyles.textContent = bootstrapStyles;
const variableStyles = document.createElement('style');
variableStyles.textContent = varStyles;
const badgeStyles = document.createElement('style');
badgeStyles.textContent = styles;
this.shadowRoot.appendChild(bootStyles);
this.shadowRoot.appendChild(variableStyles);
this.shadowRoot.appendChild(badgeStyles);

this.initialiseClock(ends);
}

get ends() {
return this.getAttribute('ends');
}

get size() {
return this.getAttribute('size');
}

get text() {
return this.getAttribute('text');
}

set ends(value) {
this.setAttribute('ends', value);
this.initialiseClock(value);
}

set size(value) {
this.setAttribute('size', value);
}

set text(value) {
this.setAttribute('text', value);
}

// Build a countdown unit
_createCountdownItem(key, value, size, text) {
const countdownItem = document.createElement('span');
countdownItem.className = 'item';
/* actual time left in days, hours, minutes or seconds */
const timeLeft = document.createElement('span');
timeLeft.className = `number ${key} ${size} text-${text}`;
timeLeft.textContent = value;
/* days, hours, minutes or seconds */
const unit = document.createElement('span');
unit.className = `unit ${size} text-${text}`;
unit.textContent = key;
countdownItem.appendChild(timeLeft);
countdownItem.appendChild(unit);
return countdownItem;
}

// Calcuate days, hours, minutes and seconds until endTime
_getTimeRemaining(endTime) {
const total = Date.parse(endTime) - Date.parse(new Date());
const seconds = Math.floor((total / 1000) % 60);
const minutes = Math.floor((total / 1000 / 60) % 60);
const hours = Math.floor((total / (1000 * 60 * 60)) % 24);
const days = Math.floor(total / (1000 * 60 * 60 * 24));
return {
total,
days,
hours,
minutes,
seconds,
};
}

// Setup and run the clock in the DOM
initialiseClock(endDate) {
const { shadowRoot, _createCountdownItem, _getTimeRemaining, size, text } =
this;
const timeRemaining = _getTimeRemaining(endDate);
const timeUnits = Object.keys(timeRemaining);
const clock = shadowRoot.querySelector('time');
let timeInterval;
clock.innerHTML = '';
timeUnits.forEach((timeUnit) => {
if (timeUnit !== 'total') {
clock.appendChild(
_createCountdownItem(timeUnit, timeRemaining[timeUnit], size, text),
);
}
});

function updateClock(ref) {
const endValue = ref.ends;
const updatedTimeRemaining = _getTimeRemaining(endValue);

const daysEl = shadowRoot.querySelector('.days');
const hoursEl = shadowRoot.querySelector('.hours');
const minutesEl = shadowRoot.querySelector('.minutes');
const secondsEl = shadowRoot.querySelector('.seconds');

daysEl.textContent = updatedTimeRemaining.days;
hoursEl.textContent = updatedTimeRemaining.hours;
minutesEl.textContent = updatedTimeRemaining.minutes;
secondsEl.textContent = updatedTimeRemaining.seconds;

/* Update the datetime attribute of the time element */
clock.setAttribute(
'datetime',
`P${updatedTimeRemaining.days}DT${updatedTimeRemaining.hours}H${updatedTimeRemaining.minutes}M${updatedTimeRemaining.seconds}S`,
);

if (updatedTimeRemaining.total <= 0) {
clearInterval(timeInterval);
}
}

function doesClockExist(ref) {
if (shadowRoot.querySelector('.days') === null) {
window.requestAnimationFrame(doesClockExist);
} else {
updateClock(ref);
timeInterval = setInterval(() => {
updateClock(ref);
}, 1000);
}
}

doesClockExist(this);
}
}
2 changes: 2 additions & 0 deletions src/components/atoms/Countdown/cod-countdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Countdown from './Countdown';
customElements.define('cod-countdown', Countdown);
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import './components/atoms/CardOverlay/cod-card-overlay';
import './components/atoms/CarouselCaption/cod-carousel-caption';
import './components/atoms/CarouselItem/cod-carousel-item';
import './components/atoms/Container/cod-container';
import './components/atoms/Countdown/cod-countdown';
import './components/atoms/DropdownMenu/cod-dropdown-menu';
import './components/atoms/FormCheck/cod-formcheck';
import './components/atoms/FormControl/cod-formcontrol';
Expand Down
31 changes: 31 additions & 0 deletions src/stories/countdown.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import '../components/atoms/Countdown/cod-countdown';
import { COMMON_STORY_ARGS } from '../shared/js/storybook/args-utils';

export default {
title: 'Components/Atoms/Countdown',
argTypes: {
size: {
control: { type: 'select' },
options: ['sm', 'md', 'lg'],
},
text: COMMON_STORY_ARGS.bootstrapColor,
},
};
// Template
const Template = (args) => {
const countdown = document.createElement('cod-countdown');
countdown.setAttribute('data-extra-classes', args.extraClasses);
countdown.setAttribute('ends', args.end);
countdown.setAttribute('size', args.size);
countdown.setAttribute('text', args.text);

return countdown;
};

export const Countdown = Template.bind({});
Countdown.args = {
extraClasses: '',
end: '2024-10-01T19:19',
size: 'sm',
text: 'dark',
};

0 comments on commit 0cf4390

Please sign in to comment.