Untitled

mail@pastecode.io avatar
unknown
javascript
a year ago
8.5 kB
4
Indexable
Never
/* eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */

// import Logger from '../legacy-core/logger';

const navActiveClass = 'nav--active';
const navButtonClass = 'nav--button--active';
const navBodyClass = 'overlay-lock';

const OVERLAY_CLASSES = ['search--active', 'property-finder--active', 'login--active'];

class Navigation {

  /**
   * Navigation Constructor
   */
  constructor() {
    this.menuActive = false;

    this.searchToggles = document.querySelectorAll('.js-search-toggle');
    this.propertyFinderToggles = document.querySelectorAll('.js-property-finder-toggle');
    this.loginToggles = document.querySelectorAll('.js-login-toggle');
    this.body = document.querySelector('body');
    this.navMain = document.querySelector('.js-nav-main');
    this.navToggle = document.querySelector('.js-nav-toggle');
    this.hasChildren = this.navMain.querySelectorAll('.has-children');
    this.navButtons = document.querySelectorAll('.js-btn-nav');
    this.navBackButtons = document.querySelectorAll('.js-btn-nav-back');

    this.attachEventListeners();

    window.addEventListener('resize', () => this.toggleNavigationOff());
  }

  /**
   * Attaches event listeners to the corresponding elements in the DOM.
   * @returns {undefined}
   */
  attachEventListeners() {
    // Nav toggle
    this.navToggle.addEventListener('click', (event) => this.navToggleClick(event));

    // Search toggles
    this.searchToggles.forEach((searchToggle) => {
      searchToggle.addEventListener('click', () => this.overlayToggleClick('search'));
    });

    // Login toggles
    this.loginToggles.forEach((loginToggle) => {
      loginToggle.addEventListener('click', () => this.overlayToggleClick('login'));
    });

    // Property finder toggles
    this.propertyFinderToggles.forEach((propertyFinderToggle) => {
      propertyFinderToggle.addEventListener('click', ({ target }) => {
        this.updatePropertyFinderDisplay(target);
        this.overlayToggleClick('property-finder');
      });
    });

    // Nav buttons
    this.navButtons.forEach((navButton) => {
      navButton.addEventListener('click', ({ target }) => this.navButtonClick(target));
    });

    // Nav back buttons
    this.navBackButtons.forEach((navBackButton) => {
      navBackButton.addEventListener('click', ({ target }) => this.navBackButtonClick(target));
    });
  }

  /**
   * Toggles the navigation off by setting the menuActive property to false,
   * adding the navBodyClass to the body element, and removing all active
   * classes from navigation elements and buttons
   * @return {void} - No return value
   */
  toggleNavigationOff() {
    this.menuActive = false;
    this.body.classList.add(navBodyClass);
    this.removeAllNavActiveClasses();
    this.removeAllButtonActiveClasses();
  }

  /**
   * Toggles the navigation menu on by setting the required classes
   * @method toggleNavigationOn
   *
   * @return {void} - Does not return any value
   */
  toggleNavigationOn() {
    this.menuActive = true;
    this.navToggle.classList.add(navActiveClass);
    this.body.classList.remove(navBodyClass);
    this.navMain.classList.add(navActiveClass);
  }

  /**
   * Removes specified classes from the document body.
   *
   * @param {Array} classes - An array of classes to remove from the document body.
   */
  removeClassesFromBody(classes) {
    classes.forEach((classToRemove) => {
      if (this.body.classList.contains(classToRemove)) {
        this.body.classList.remove(classToRemove);
      }
    });
  }

  /**
   * Removes the "navActiveClass" from all elements that have it.
   *
   * @returns {void}
   */
  removeAllNavActiveClasses() {
    document.querySelectorAll(`.${navActiveClass}`).forEach((elem) => elem.classList.remove(navActiveClass));
  }

  /**
   * Removes the "active" classes from all buttons with a specific class.
   *
   * @param {string} navButtonClass - The class name of the buttons to remove the "active" class from.
   * @returns {void}
   */
  removeAllButtonActiveClasses() {
    document.querySelectorAll(`.${navButtonClass}`).forEach((elem) => elem.classList.remove(navButtonClass));
  }

  /**
   * Toggles the overlay of a given type.
   *
   * @param {string} type - The type of the overlay to toggle.
   * @return {void}
   */
  overlayToggleClick(type) {

    const activeClassForOverlayType = `${type}--active`;

    // Remove all overlay body classes apart from the one for this overlay type
    this.removeClassesFromBody(OVERLAY_CLASSES.filter((x) => x !== activeClassForOverlayType));

    // Toggle off navigation
    this.toggleNavigationOff();

    this.body.classList.toggle(`${type}--active`);
  }

  /**
   * Handles click event of the navigation toggle button.
   *
   * @param {Event} event - The click event.
   */
  navToggleClick(event) {
    event.stopPropagation();

    this.removeClassesFromBody(OVERLAY_CLASSES);

    // eslint-disable-next-line no-unused-expressions
    this.menuActive ? this.toggleNavigationOff() : this.toggleNavigationOn();
  }

  /**
   * Removes the active class for all grandchild elements of the navigation container
   * @function removeAllGrandchildActiveClasses
   * @returns {void}
   */
  removeAllGrandchildActiveClasses() {
    document.querySelectorAll('.js-nav-grandchild')
      .forEach((grandchild) => grandchild.classList.remove(navActiveClass));
  }

  /**
   * Removes the active class from all child elements with the class 'js-nav-child'.
   *
   * @returns {void}
   */
  removeAllChildActiveClasses() {
    document.querySelectorAll('.js-nav-child')
      .forEach((child) => child.classList.remove(navActiveClass));
  }

  /**
   * Handles clicks on a navigation button
   *
   * @param {HTMLElement} clickedButton - The button element that was clicked.
   */
  navButtonClick(clickedButton) {
    const isChildItem = clickedButton.classList.contains('nav--item__child');
    const isActiveItem = clickedButton.classList.contains(navButtonClass);

    this.removeClassesFromBody(OVERLAY_CLASSES);

    let container;

    if (isChildItem) {
      container = clickedButton.closest('.has-grandchildren');
      this.removeAllGrandchildActiveClasses();
      if (!isActiveItem) {
        container.querySelector('.js-nav-grandchild').classList.add(navActiveClass);
      }

    } else { // Is top level item
      container = clickedButton.closest('.has-children');
      this.removeAllChildActiveClasses();
      if (!isActiveItem) {
        container.querySelector('.js-nav-child').classList.add(navActiveClass);
      }
    }

    this.navButtons.forEach((button) => {
      button.classList.remove(navButtonClass);
    });

    if (isActiveItem) {
      clickedButton.classList.remove(navButtonClass);
    } else {
      clickedButton.classList.add(navButtonClass);
    }
  }

  /**
   * Handles the click event of the back buttons
   * Removes the "navActiveClass" from the closest ancestor element containing the clickedButton.
   *
   * @param {HTMLElement} clickedButton - The element representing the navigation back button that was clicked.
   * @return {undefined}
   */
  navBackButtonClick(clickedButton) {
    clickedButton.closest(`.${navActiveClass}`).classList.remove(navActiveClass);
  }

  /**
   * Updates the property finder display based on the clicked button.
   *
   * @param {Element} clickedButton - The button element that was clicked.
   */
  updatePropertyFinderDisplay(clickedButton) {
    const buttonElem = clickedButton.classList.contains('js-property-finder-toggle')
      ? clickedButton : clickedButton.closest('button');
    const finderHeading = buttonElem.getAttribute('data-property-finder-title');
    const finderDefaultType = buttonElem.getAttribute('data-property-finder-default-property-type');
    const finderButtonText = buttonElem.getAttribute('data-property-finder-button-text');

    document.getElementById('property-finder-heading').innerText = finderHeading;
    const searchTypeInput = document.querySelector('.property-finder--overlay .js-property-search-type');
    searchTypeInput.value = finderDefaultType;
    document.querySelector('#property-finder-overlay-form .js-submit-btn').value = finderButtonText;
  }
}

export default new Navigation();