Source

Tab.js

import events from '@devoinc/applications-builder/libs/events';
import scroll from '@devoinc/applications-builder/utils/scroll';
import DataNode from '@devoinc/applications-builder/data/DataNode';

/**
 * @class
 * This class involve a set of [Sections](Section.html).
 *
 */
class Tab {
  /**
   * @param {string} id - Id of the tab.
   * @param {object} settigns - Settings of the tab.
   * @param {boolean} settigns.loadOnDemand - Process the section on init. Default false.
   * @param {object} settigns.transition - Process the section on init. Default false.
   * @param {string} settigns.transition.effect - Effect to apply on tab transitions. One of _fade_, _slide_ or _none_.
   * @param {string} settigns.transition.duration - Duration of the transition in milliseconds,
   */
  constructor(id, settings = {}) {
    this.id = id;
    this.sections = [];
    this.data = new DataNode(id);
    this.settings = Object.assign(
      {
        loadOnDemand: false,
      },
      settings
    );
    this.stats = {
      show: 0,
    };
  }

  /**
   * Init tab
   * This is part of the initialization process
   * @param {object} settings - Settings for the tab
   */
  init(settings) {
    this.vapp = document.querySelector('.lt-vapp');
    this.selector = this.vapp.querySelector(`li[main="${this.id}"]`);
    if (this.selector) {
      this.settings = Object.assign({}, this.settings, settings);
      this.el = this.vapp.querySelector(`#${this.id}`);
      for (let sec of this.sections) sec.init();
      this._initSubMenu();
      this._addListeners();
    } else {
      console.error(`Tab "${this.id}" does not exists on HTML.`);
    }
  }

  // If the class has submenu add the class
  _initSubMenu() {
    if (this.selector.querySelector('.lt-vapp-menu-scroll')) {
      this.selector.classList.add('hasMenu');
    } else {
      this.selector.classList.remove('hasMenu');
    }
  }

  // Add listeners
  _addListeners() {
    this.selector.addEventListener('click', () => this.select());
    events.listen('tab-changed', (evt) => {
      if (evt.detail.id !== this.id) this.unselect();
    });
    if (this.selector.querySelector('[section]') != null) {
      for (let sec of this.selector.querySelectorAll('[section]')) {
        sec.addEventListener('click', (evt) => {
          evt.preventDefault();
          evt.stopPropagation();
          this.toSection(evt.currentTarget);
        });
      }
    }
  }

  /**
   * Set transition properties
   * For transition we can use the next keys:
   * - effect[=none] - Effect to apply on tab transitions: Fade, Slide or None.
   * - duration[=0] - Duration of the transition (in milliseconds).
   *
   * @param {object} trans - Transition options
   */
  setTransition(trans) {
    this.trans = trans;
  }

  /**
   * Go To Section
   */
  toSection(sectionTag) {
    this.closeSubmenu();
    let toSection = sectionTag.getAttribute('section');
    scroll.scrollToElement(`#${toSection}`, 300, 90);
  }

  /**
   * Close the submenu
   */
  closeSubmenu() {
    this.selector.classList.remove('scroll-menu-open');
    this.selector.classList.remove('hasMenuOpen');
  }

  /**
   * Add section to the tab
   * @param {Section} section
   */
  addSection(section) {
    this.data.append(section.data);
    this.sections.push(section);
  }

  /**
   * Select tab
   */
  select() {
    if (this.isSelected()) {
      // Only if have submenu
      if (this.selector.classList.contains('hasMenu')) {
        // TODO Use only one class
        this.selector.classList.toggle('scroll-menu-open');
        this.selector.classList.toggle('hasMenuOpen');
      }
    } else {
      events.trigger('tab-changed', this);
      this._selectEffect(() => {
        // After select effect redraw all widgets for adjustments
        // only if the loadOnDemand is false
        if (!this.settings.loadOnDemand) {
          for (let sec of this.sections) {
            if (!sec.settings.hidden) sec.show();
            for (let wid of sec.widgets) wid.resize();
          }
        } else {
          this.settings.loadOnDemand = false;
        }
      });

      this.selector.classList.add('active');

      if (this.onSelect) this.onSelect();
      this.stats.show++;
    }
  }

  // Make the effect on select
  _selectEffect(callback = null) {
    let ops = { fade: 'fadeIn', slide: 'slideDown' };
    if (Object.keys(ops).includes(this.trans.effect)) {
      $(this.el)
        .delay(this.trans.duration)
        [ops[this.trans.effect]](this.trans.duration, callback);
    } else {
      this.el.style.display = 'block';
      if (callback) callback();
    }
  }

  /**
   * Refresh the sections in tab
   */
  refresh() {
    if (this.isSelected()) {
      for (let sec of this.sections) sec.refresh();
    }
  }

  /**
   * Check if the tab is selected
   * @return {boolean}
   */
  isSelected() {
    return this.selector.classList.contains('active');
  }

  /**
   * Unselect tab
   */
  unselect() {
    for (let sec of this.sections) sec.hide();
    this._unselectEffect();
    this.selector.classList.remove('active');
    this.closeSubmenu();
  }

  // Unselect effect
  _unselectEffect() {
    let ops = { fade: 'fadeOut', slide: 'slideUp' };
    if (Object.keys(ops).includes(this.trans.effect)) {
      $(this.el)[ops[this.trans.effect]](this.trans.duration);
    } else this.el.style.display = 'none';
  }
}

export default Tab;