Source

src/Tabs.js

import $ from "jquery";
import EventEmitter from "./EventEmitter";

/**
 * @function Tabs.on
 * @param {"before-tab-hide" | "after-tab-hide" | "before-tab-show" | "after-tab-show"} event The event name
 * @param {function} listener The event listener
 */

/**
 * A class to manage tabs
 * @class
 * @extends EventEmitter
 * @fires Tabs~before-tab-hide
 * @example
 * const tabs = new Tabs($('.tabs');
 */
class Tabs extends EventEmitter {
  /**
   * @constructor
   * @param {jQuery} $el - The element contains the tabs
   */
  constructor($el) {
    super();
    this.$tabs = $el.find("[role=tab]");
    this.$panels = this.$tabs.map(function () {
      const controls = $(this).attr("aria-controls");
      const $panel = $(`#${controls}`);
      if (!$panel.length) {
        console.warn(`Tab panel '${controls}' not found`);
      }
      return $panel;
    });
    this.$tabs.on("click", this.#showTab.bind(this));

    let index = this.$tabs.filter('[aria-selected="true"]').index();

    this.$tabs.on("keydown", (e) => {
      if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") {
        return;
      }

      this.$tabs[index].setAttribute("tabindex", -1);

      if (e.key === "ArrowRight") {
        index++;
        if (index >= this.$tabs.length) {
          index = 0;
        }
      } else {
        index--;
        if (index < 0) {
          index = this.$tabs.length - 1;
        }
      }

      this.$tabs[index].setAttribute("tabindex", 0);
      this.$tabs[index].focus();
    });
  }

  #showTab(e) {
    const $currentPanel = this.$panels.filter(function () {
      const attr = $(this).attr("hidden");
      return !attr;
    });
    const $futurePanel = $(`#${$(e.target).attr("aria-controls")}`);

    // Update tabs state
    this.$tabs.attr({ "aria-selected": "false" });
    $(e.target).attr({ "aria-selected": "true" });

    /**
     * Before tab hide event
     *
     * @event Tabs#before-tab-hide
     * @property {Event} e - The event object
     * @property {jQuery} $panel - The panel which is about to hide.
     */
    this.emit("before-tab-hide", e, $currentPanel);

    // Hide all panels
    this.$panels.each(function () {
      this[0].setAttribute("hidden", "true");
    });

    /**
     * After tab hide event
     *
     * @event Tabs#after-tab-hide
     * @property {Event} e - The event object
     * @property {jQuery} $panel - The panel which  was hidden.
     */
    this.emit("after-tab-hide", e, $currentPanel);

    /**
     * Before tab show event
     *
     * @event Tabs#before-tab-show
     * @property {Event} e - The event object
     * @property {jQuery} $panel - The panel which is about to show.
     */
    this.emit("before-tab-show", e, $futurePanel);
    // Show the selected panel
    $futurePanel[0].removeAttribute("hidden");

    /**
     * After tab show event
     *
     * @event Tabs#after-tab-show
     * @property {Event} e - The event object
     * @property {jQuery} $panel - The panel which was shown.
     */
    this.emit("after-tab-show", e, $futurePanel);
  }
}

export { Tabs as default };