eliashaeussler/typo3-badges

View on GitHub
assets/js/badge/provider/badgeProviderToggle.js

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * This file is part of the Symfony project "eliashaeussler/typo3-badges".
 *
 * Copyright (C) 2022 Elias Häußler <elias@haeussler.dev>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

import BadgeProvider from './badgeProvider';
import Url from '../../url';

/**
 * BadgeProviderToggle.
 *
 * @author Elias Häußler <elias@haeussler.dev>
 * @license GPL-3.0-or-later
 */
export default class BadgeProviderToggle {
  buttons;

  inputs;

  providers;

  constructor(element) {
    this.buttons = document.querySelectorAll(element);
    this.inputs = this.locateInputs();
    this.providers = this.buildProviders();

    const preselectedProvider = Url.getFromHash('provider');
    this.setActiveProvider(preselectedProvider);
    if (preselectedProvider !== null) {
      this.hideButtonPings();
    }

    this.addEventListeners();
  }

  addEventListeners() {
    [...this.buttons].forEach((el) => {
      ['focus', 'click'].forEach((e) => el.addEventListener(e, () => this.hideButtonPings()));
    });
    [...this.inputs].forEach((el) => {
      el.addEventListener('change', () => this.setActiveProvider(BadgeProviderToggle.getIdentifierByInput(el)));
    });
  }

  /**
   * Locate input elements used to control the current badge provider.
   *
   * @returns {Element[]} Array of located input elements
   */
  locateInputs() {
    const inputs = [];

    [...this.buttons].forEach((button) => {
      const buttonTarget = button.getAttribute('data-dropdown-toggle');
      const dropdown = document.querySelector(`#${buttonTarget}`);

      inputs.push(...dropdown.querySelectorAll('[data-badge-provider]'));
    });

    return inputs;
  }

  /**
   * Initialize badge providers from input elements.
   *
   * @returns {Object.<string, BadgeProvider>} Initialized badge providers with
   *                                           their identifiers as keys
   */
  buildProviders() {
    const providers = {};

    [...this.inputs].forEach((el) => {
      const identifier = BadgeProviderToggle.getIdentifierByInput(el);

      if (!(identifier in providers)) {
        const name = BadgeProviderToggle.getNameByInput(el);
        providers[identifier] = new BadgeProvider(identifier, name);
      }
    });

    return providers;
  }

  /**
   * Determine currently active badge provider.
   *
   * @returns {string} Identifier of the currently active badge provider
   */
  getActiveProvider() {
    const activeProvider = [...this.inputs].filter((input) => input.checked)[0];

    return BadgeProviderToggle.getIdentifierByInput(activeProvider);
  }

  /**
   * Update currently active provider, either by given provider or by detection.
   *
   * @param activeProvider {string|null} Provider to be set as active
   *                                     or `NULL` to determine active provider
   */
  setActiveProvider(activeProvider = null) {
    if (activeProvider === null || !(activeProvider in this.providers)) {
      activeProvider = this.getActiveProvider();
    }

    // Apply provider as URL fragment
    Url.setInHash('provider', activeProvider);

    // Apply "checked" state of all input elements
    [...this.inputs].forEach((el) => {
      el.checked = BadgeProviderToggle.getIdentifierByInput(el) === activeProvider;
    });

    // Apply states to dependent elements
    Object.entries(this.providers).forEach(([identifier, provider]) => {
      if (identifier === activeProvider) {
        provider.enable();

        [...this.buttons].forEach((button) => {
          BadgeProviderToggle.getButtonLabel(button).textContent = provider.name;
        });
      } else {
        provider.disable();
      }
    });
  }

  /**
   * Get all available providers.
   *
   * @returns {string[]} Identifiers of all available providers
   */
  getAllProviders() {
    return [...this.inputs].map((input) => BadgeProviderToggle.getIdentifierByInput(input));
  }

  /**
   * Hide animated pings on toggle buttons.
   */
  hideButtonPings() {
    [...this.buttons].forEach((el) => {
      BadgeProviderToggle.getButtonPing(el).classList.add('hidden');
    });
  }

  static getButtonLabel(button) {
    return button.querySelector('.badge-providers-button-label');
  }

  static getButtonPing(button) {
    return button.querySelector('.badge-providers-button-ping');
  }

  static getIdentifierByInput(input) {
    return input.getAttribute('data-badge-provider');
  }

  static getNameByInput(input) {
    return input.getAttribute('data-badge-provider-name');
  }
}