18F/identity-idp

View on GitHub
app/javascript/packages/countdown/countdown-element.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { t } from '@18f/identity-i18n';

export class CountdownElement extends HTMLElement {
  #pollIntervalId?: number;

  static observedAttributes = ['data-expiration'];

  connectedCallback() {
    if (this.startImmediately) {
      this.start();
    } else {
      this.setTimeRemaining();
    }
  }

  disconnectedCallback() {
    this.stop();
  }

  attributeChangedCallback() {
    this.setTimeRemaining();
  }

  get expiration(): Date {
    return new Date(this.getAttribute('data-expiration')!);
  }

  set expiration(expiration: Date) {
    this.setAttribute('data-expiration', expiration.toISOString());
  }

  get timeRemaining(): number {
    return Math.max(this.expiration.getTime() - Date.now(), 0);
  }

  get updateInterval(): number {
    return Number(this.getAttribute('data-update-interval'));
  }

  get startImmediately(): boolean {
    return this.getAttribute('data-start-immediately') === 'true';
  }

  get #textNode(): Text {
    if (!this.firstChild) {
      this.appendChild(this.ownerDocument.createTextNode(''));
    }

    return this.firstChild as Text;
  }

  start(): void {
    this.stop();
    this.setTimeRemaining();
    this.#pollIntervalId = window.setInterval(() => this.tick(), this.updateInterval);
  }

  stop(): void {
    window.clearInterval(this.#pollIntervalId);
  }

  tick(): void {
    this.setTimeRemaining();
    this.dispatchEvent(new window.CustomEvent('lg:countdown:tick', { bubbles: true }));

    if (this.timeRemaining <= 0) {
      this.stop();
    }
  }

  setTimeRemaining(): void {
    const { timeRemaining } = this;

    const minutes = Math.floor(timeRemaining / 60000);
    const seconds = Math.floor(timeRemaining / 1000) % 60;

    this.#textNode.nodeValue = [
      minutes && t('datetime.dotiw.minutes', { count: minutes }),
      t('datetime.dotiw.seconds', { count: seconds }),
    ]
      .filter(Boolean)
      .join(t('datetime.dotiw.two_words_connector'));
  }
}

if (!customElements.get('lg-countdown')) {
  customElements.define('lg-countdown', CountdownElement);
}