Source: modules/contextual/js/models/StateModel.es6.js

/**
 * @file
 * A Backbone Model for the state of a contextual link's trigger, list & region.
 */

(function (Drupal, Backbone) {
  /**
   * Models the state of a contextual link's trigger, list & region.
   *
   * @constructor
   *
   * @augments Backbone.Model
   */
  Drupal.contextual.StateModel = Backbone.Model.extend(
    /** @lends Drupal.contextual.StateModel# */ {
      /**
       * @type {object}
       *
       * @prop {string} title
       * @prop {bool} regionIsHovered
       * @prop {bool} hasFocus
       * @prop {bool} isOpen
       * @prop {bool} isLocked
       */
      defaults: /** @lends Drupal.contextual.StateModel# */ {
        /**
         * The title of the entity to which these contextual links apply.
         *
         * @type {string}
         */
        title: '',

        /**
         * Represents if the contextual region is being hovered.
         *
         * @type {bool}
         */
        regionIsHovered: false,

        /**
         * Represents if the contextual trigger or options have focus.
         *
         * @type {bool}
         */
        hasFocus: false,

        /**
         * Represents if the contextual options for an entity are available to
         * be selected (i.e. whether the list of options is visible).
         *
         * @type {bool}
         */
        isOpen: false,

        /**
         * When the model is locked, the trigger remains active.
         *
         * @type {bool}
         */
        isLocked: false,
      },

      /**
       * Opens or closes the contextual link.
       *
       * If it is opened, then also give focus.
       *
       * @return {Drupal.contextual.StateModel}
       *   The current contextual state model.
       */
      toggleOpen() {
        const newIsOpen = !this.get('isOpen');
        this.set('isOpen', newIsOpen);
        if (newIsOpen) {
          this.focus();
        }
        return this;
      },

      /**
       * Closes this contextual link.
       *
       * Does not call blur() because we want to allow a contextual link to have
       * focus, yet be closed for example when hovering.
       *
       * @return {Drupal.contextual.StateModel}
       *   The current contextual state model.
       */
      close() {
        this.set('isOpen', false);
        return this;
      },

      /**
       * Gives focus to this contextual link.
       *
       * Also closes + removes focus from every other contextual link.
       *
       * @return {Drupal.contextual.StateModel}
       *   The current contextual state model.
       */
      focus() {
        this.set('hasFocus', true);
        const cid = this.cid;
        this.collection.each((model) => {
          if (model.cid !== cid) {
            model.close().blur();
          }
        });
        return this;
      },

      /**
       * Removes focus from this contextual link, unless it is open.
       *
       * @return {Drupal.contextual.StateModel}
       *   The current contextual state model.
       */
      blur() {
        if (!this.get('isOpen')) {
          this.set('hasFocus', false);
        }
        return this;
      },
    },
  );
})(Drupal, Backbone);