Source

src/App.js

import $ from "jquery";
import throttle from "lodash/throttle";
import CustomProperty from "./CustomProperty";
import MainMenu from "./MainMenu";

/**
 * The main application file. You can import it as many times as you want,
 * and you will get the same instance
 * @class App
 */
class App {
  #customProperties = {};
  /**
   * @private
   * @type {Object}
   */
  #resizeHandlers = {};
  /**
   * @public
   * @type {MainMenu}
   */
  mainMenu;
  constructor() {
    this.mainMenu = new MainMenu();
    this.#setCustomProperties();
    window.addEventListener(
      "resize",
      throttle(() => {
        Object.values(this.#resizeHandlers).map((fn) => fn.call(this));
      }, 250),
    );
  }
  #setCustomProperties() {
    // screen-height
    this.addCustomProperty("screen-height", innerHeight + "px");
    this.subscribeResize("screen-height", () => {
      this.#customProperties["screen-height"].value = innerHeight + "px";
    });

    // header-height
    this.addCustomProperty("header-height", $("#header").outerHeight() + "px");
    this.subscribeResize("header-height", () => {
      this.#customProperties["header-height"].value =
        $("#header").outerHeight() + "px";
    });
  }

  /**
   * Creates CustomProperty and keeps it in App instance
   * @param {string} name The CSS property name
   * @param {string|number} value The CSS property value
   * @param {HTMLElement} [ctx=document.documentElement] The DOM Element style with apply to
   * @returns this
   * @example
   * import app from 'App';
   * app.addCustomProperty('hello', 'world');
   */
  addCustomProperty(name, value, ctx = document.documentElement) {
    this.#customProperties[name] = new CustomProperty(name, value, ctx);
    return this;
  }

  /**
   * Get CSS property from App instance
   * @param {string} name The CSS property name
   * @returns {any}
   * @example
   * import app from 'App';
   * app.getCustomProperty('hello');
   */
  getCustomProperty(name) {
    return this.#customProperties[name].value;
  }

  /**
   * Add handler to window resize event
   * @param {string} name The handler identifier
   * @param {fn} handler The callback to run on window resize
   * @returns this
   * @example
   * import app from 'App';
   * app.subscribeResize('my-resize-handler', () => {console.log('I'm listening to resize')});
   */
  subscribeResize(name, handler) {
    this.#resizeHandlers[name] = handler;
    return this;
  }
  /**
   * Removes handler from window resize event
   * @param {string} name The handler identifier
   * @returns this
   * @example
   * import app from 'App';
   * app.unsubscribeResize('my-resize-handler');
   */
  unsubscribeResize(name) {
    delete this.#resizeHandlers[name];
    return this;
  }
}
let app = app || new App();
export default app;