Source: components/pomo-finish/pomo-finish.js

/**
 * @module PomoFinish
 */

class PomoFinish extends HTMLElement {
  constructor() {
    super();

    const shadow = this.attachShadow({ mode: 'open' });

    // event listener for opening finish page
    this.openEvent = new CustomEvent('openEvent', {
      bubbles: true,
      composed: true,
    });

    // event listener for closing finish page
    this.closeEvent = new CustomEvent('closeEvent', {
      bubbles: true,
      composed: true,
    });

    // the component wrapper
    const wrapper = document.createElement('div');
    const statsStyle = document.createElement('link');
    statsStyle.setAttribute('id', 'statistics-styles');
    statsStyle.setAttribute('rel', 'stylesheet');
    statsStyle.setAttribute('href', './components/pomo-finish/pomo-finish.css');
    shadow.append(statsStyle);

    // button to finish session and display statistics
    const finishButton = document.createElement('button');
    finishButton.setAttribute('id', 'finish-button');

    const finishIcon = document.createElement('img');
    finishIcon.setAttribute('id', 'finish-button-icon');
    finishIcon.setAttribute('src', './assets/images/bar_chart_stats.png');
    finishIcon.textContent = 'Statistics';

    finishButton.appendChild(finishIcon);

    finishButton.onclick = () => {
      shadow.dispatchEvent(this.event);
    };

    // the lightbox
    const modal = document.createElement('div');
    modal.setAttribute('class', 'modal');
    modal.setAttribute('id', 'statistics-modal');
    modal.onclick = (event) => {
      // close lightbox when click outside of the content area
      if (event.target === modal) {
        shadow.dispatchEvent(this.closeEvent);
        modal.style.display = 'none';
      }
    };

    // wrapper for the content inside the lightbox
    const modalContent = document.createElement('div');
    modalContent.setAttribute('class', 'modal-content');
    modalContent.setAttribute('id', 'statistics-modal-content');

    // button to close the lightbox
    const closeButton = document.createElement('div');
    closeButton.setAttribute('id', 'statistics-close-button');
    closeButton.setAttribute('class', 'button-off');
    closeButton.innerHTML = '×';
    closeButton.onclick = () => {
      shadow.dispatchEvent(this.closeEvent);
      modal.style.display = 'none';
    };

    /* mimic a button hover event */
    closeButton.addEventListener('mouseover', () => {
      closeButton.setAttribute('class', 'button-on');
    });

    closeButton.addEventListener('mouseout', () => {
      closeButton.setAttribute('class', 'button-off');
    });

    modalContent.appendChild(closeButton);

    // the main content to be display in the lightbox

    const modalTitle = document.createElement('h3');
    modalTitle.setAttribute('class', 'modal-title');
    modalTitle.setAttribute('id', 'statistics-modal-title');
    modalTitle.textContent = 'Session Statistics';
    modalContent.appendChild(modalTitle);

    const statsContent = document.createElement('div');
    statsContent.setAttribute('class', 'stats-content');

    const workContainer = document.createElement('div');
    workContainer.setAttribute('class', 'stats-entry');
    const workSquares = document.createElement('div');
    workSquares.setAttribute('class', 'stats-squares');
    workContainer.appendChild(workSquares);
    const workTitle = document.createElement('h4');
    workTitle.setAttribute('class', 'stats-subtitle');
    workTitle.textContent = 'Work';
    workContainer.appendChild(workTitle);
    statsContent.appendChild(workContainer);

    const shortContainer = document.createElement('div');
    shortContainer.setAttribute('class', 'stats-entry');
    const shortSquares = document.createElement('div');
    shortSquares.setAttribute('class', 'stats-squares');
    shortContainer.appendChild(shortSquares);
    const shortTitle = document.createElement('h4');
    shortTitle.setAttribute('class', 'stats-subtitle');
    shortTitle.textContent = 'Short Breaks';
    shortContainer.appendChild(shortTitle);
    statsContent.appendChild(shortContainer);

    const longContainer = document.createElement('div');
    longContainer.setAttribute('class', 'stats-entry');
    const longSquares = document.createElement('div');
    longSquares.setAttribute('class', 'stats-squares');
    longContainer.appendChild(longSquares);
    const longTitle = document.createElement('h4');
    longTitle.setAttribute('class', 'stats-subtitle');
    longTitle.textContent = 'Long Breaks';
    longContainer.appendChild(longTitle);
    statsContent.appendChild(longContainer);

    const interruptContainer = document.createElement('div');
    interruptContainer.setAttribute('class', 'stats-entry');
    const interruptSquares = document.createElement('div');
    interruptSquares.setAttribute('class', 'stats-squares');
    interruptContainer.appendChild(interruptSquares);
    const interruptTitle = document.createElement('h4');
    interruptTitle.setAttribute('class', 'stats-subtitle');
    interruptTitle.textContent = 'Interruptions';
    interruptContainer.appendChild(interruptTitle);
    statsContent.appendChild(interruptContainer);

    modalContent.appendChild(statsContent);
    modal.appendChild(modalContent);
    wrapper.appendChild(modal);
    wrapper.append(finishButton);

    shadow.appendChild(wrapper);

    // custom event for modal display
    this.event = new CustomEvent('modalRequest', {
      bubbles: true,
      composed: true,
    });

    // Enabled determines if this component can be opened
    this.enabled = true;

    /**
     * @method
     * Allows the control to open the finish page
     */
    this.enableFinish = () => {
      this.enabled = true;
      finishButton.disabled = false;
    };

    /**
     * @method
     * Prevent the control from open the finish page
     */
    this.disableFinish = () => {
      this.enabled = false;
      finishButton.disabled = true;
    };

    /**
     * @method
     * Modify elements' data-mode to dark-mode or light-mode
     * @param {Boolean} dark  indicate whether or not the setting is in dark mode
     */

    this.setDark = (dark) => {
      if (dark) {
        statsStyle.setAttribute('href', './components/pomo-finish/pomo-finish.css');
        finishIcon.setAttribute('src', './assets/images/bar_chart_stats.png');
      } else {
        statsStyle.setAttribute('href', './components/pomo-finish/pomo-finish-light.css');
        finishIcon.setAttribute('src', './assets/images/bar_chart_stats_light.png');
      }
    };

    function generateGrid(container, count, type) {
      // clear old content
      while (container.firstChild) {
        container.removeChild(container.firstChild);
      }

      for (let row = 3; row >= 0; row -= 1) {
        const rowContainer = document.createElement('div');
        rowContainer.setAttribute('class', 'stats-row');

        for (let col = 0; col < 3; col += 1) {
          if (row * 3 + col + 1 <= count) {
            const fillSquare = document.createElement('div');
            fillSquare.setAttribute('class', `square ${type}`);
            rowContainer.appendChild(fillSquare);
          } else {
            const emptySquare = document.createElement('div');
            emptySquare.setAttribute('class', 'square');
            rowContainer.appendChild(emptySquare);
          }
        }

        container.appendChild(rowContainer);
      }

      if (count > 12) {
        const fancySquare = document.createElement('div');
        fancySquare.setAttribute('class', `fancy ${type}`);
        fancySquare.textContent = count;
        container.appendChild(fancySquare);
      }
    }

    /**
     * @method
     * Render session's statistics to the screen
     * @param {Number} workCount            the number of pomodoro sessions completed
     * @param {Number} shortBreakCount      the number of short breaks
     * @param {Number} longBreakCount       the number of long breaks
     * @param {Number} interruptedCount     the number of time reset because of interruptions
     * @return {void}
     */
    this.showModal = (workCount, shortBreakCount, longBreakCount, interruptedCount) => {
      generateGrid(workSquares, workCount, 'work');
      generateGrid(shortSquares, shortBreakCount, 'short');
      generateGrid(longSquares, longBreakCount, 'long');
      generateGrid(interruptSquares, interruptedCount, 'interrupt');

      // show the statistics panel
      modal.style.display = 'block';

      shadow.dispatchEvent(this.openEvent);
    };

    /**
     * @method
     * For transforming the whole object
     * @param {String} transformText the text to put in transform css
     */
    this.changeTransform = (transformText) => {
      finishButton.style.transform = transformText;
    };

    // Assessible determines if the keyboard shortcuts should run
    this.accessible = true;

    /**
     * @method
     * For CONTROL to determine whether we can open info, setting, stats
     * @param {Boolean} enabled true for being able to open, false otherwise
     */
    this.setAccessibility = (accessible) => {
      this.accessible = accessible;
    };

    /**
     * @method
     * Functions that opens and closes the finish page with the f key
     */
    document.addEventListener('keydown', (e) => {
      if (e.key === 'f' && this.accessible === true) {
        if (modal.style.display === 'block') {
          closeButton.onclick();
        } else if (this.enabled === true) {
          finishButton.onclick();
        }
      }
    });
  }
}

customElements.define('pomo-finish', PomoFinish);

export default PomoFinish;