Source: modules/quickedit/js/views/FieldToolbarView.es6.js

/**
 * @file
 * A Backbone View that provides an interactive toolbar (1 per in-place editor).
 */

(function ($, _, Backbone, Drupal) {
  Drupal.quickedit.FieldToolbarView = Backbone.View.extend(
    /** @lends Drupal.quickedit.FieldToolbarView# */ {
      /**
       * The edited element, as indicated by EditorView.getEditedElement.
       *
       * @type {jQuery}
       */
      $editedElement: null,

      /**
       * A reference to the in-place editor.
       *
       * @type {Drupal.quickedit.EditorView}
       */
      editorView: null,

      /**
       * @type {string}
       */
      _id: null,

      /**
       * @constructs
       *
       * @augments Backbone.View
       *
       * @param {object} options
       *   Options object to construct the field toolbar.
       * @param {jQuery} options.$editedElement
       *   The element being edited.
       * @param {Drupal.quickedit.EditorView} options.editorView
       *   The EditorView the toolbar belongs to.
       */
      initialize(options) {
        this.$editedElement = options.$editedElement;
        this.editorView = options.editorView;

        /**
         * @type {jQuery}
         */
        this.$root = this.$el;

        // Generate a DOM-compatible ID for the form container DOM element.
        this._id = `quickedit-toolbar-for-${this.model.id.replace(
          /[/[\]]/g,
          '_',
        )}`;

        this.listenTo(this.model, 'change:state', this.stateChange);
      },

      /**
       * {@inheritdoc}
       *
       * @return {Drupal.quickedit.FieldToolbarView}
       *   The current FieldToolbarView.
       */
      render() {
        // Render toolbar and set it as the view's element.
        this.setElement(
          $(
            Drupal.theme('quickeditFieldToolbar', {
              id: this._id,
            }),
          ),
        );

        // Attach to the field toolbar $root element in the entity toolbar.
        this.$el.prependTo(this.$root);

        return this;
      },

      /**
       * Determines the actions to take given a change of state.
       *
       * @param {Drupal.quickedit.FieldModel} model
       *   The quickedit FieldModel
       * @param {string} state
       *   The state of the associated field. One of
       *   {@link Drupal.quickedit.FieldModel.states}.
       */
      stateChange(model, state) {
        const from = model.previous('state');
        const to = state;
        switch (to) {
          case 'inactive':
            break;

          case 'candidate':
            // Remove the view's existing element if we went to the 'activating'
            // state or later, because it will be recreated. Not doing this would
            // result in memory leaks.
            if (from !== 'inactive' && from !== 'highlighted') {
              this.$el.remove();
              this.setElement();
            }
            break;

          case 'highlighted':
            break;

          case 'activating':
            this.render();

            if (this.editorView.getQuickEditUISettings().fullWidthToolbar) {
              this.$el.addClass('quickedit-toolbar-fullwidth');
            }

            if (this.editorView.getQuickEditUISettings().unifiedToolbar) {
              this.insertWYSIWYGToolGroups();
            }
            break;

          case 'active':
            break;

          case 'changed':
            break;

          case 'saving':
            break;

          case 'saved':
            break;

          case 'invalid':
            break;
        }
      },

      /**
       * Insert WYSIWYG markup into the associated toolbar.
       */
      insertWYSIWYGToolGroups() {
        this.$el
          .append(
            Drupal.theme('quickeditToolgroup', {
              id: this.getFloatedWysiwygToolgroupId(),
              classes: [
                'wysiwyg-floated',
                'quickedit-animate-slow',
                'quickedit-animate-invisible',
                'quickedit-animate-delay-veryfast',
              ],
              buttons: [],
            }),
          )
          .append(
            Drupal.theme('quickeditToolgroup', {
              id: this.getMainWysiwygToolgroupId(),
              classes: [
                'wysiwyg-main',
                'quickedit-animate-slow',
                'quickedit-animate-invisible',
                'quickedit-animate-delay-veryfast',
              ],
              buttons: [],
            }),
          );

        // Animate the toolgroups into visibility.
        this.show('wysiwyg-floated');
        this.show('wysiwyg-main');
      },

      /**
       * Retrieves the ID for this toolbar's container.
       *
       * Only used to make sane hovering behavior possible.
       *
       * @return {string}
       *   A string that can be used as the ID for this toolbar's container.
       */
      getId() {
        return `quickedit-toolbar-for-${this._id}`;
      },

      /**
       * Retrieves the ID for this toolbar's floating WYSIWYG toolgroup.
       *
       * Used to provide an abstraction for any WYSIWYG editor to plug in.
       *
       * @return {string}
       *   A string that can be used as the ID.
       */
      getFloatedWysiwygToolgroupId() {
        return `quickedit-wysiwyg-floated-toolgroup-for-${this._id}`;
      },

      /**
       * Retrieves the ID for this toolbar's main WYSIWYG toolgroup.
       *
       * Used to provide an abstraction for any WYSIWYG editor to plug in.
       *
       * @return {string}
       *   A string that can be used as the ID.
       */
      getMainWysiwygToolgroupId() {
        return `quickedit-wysiwyg-main-toolgroup-for-${this._id}`;
      },

      /**
       * Finds a toolgroup.
       *
       * @param {string} toolgroup
       *   A toolgroup name.
       *
       * @return {jQuery}
       *   The toolgroup element.
       */
      _find(toolgroup) {
        return this.$el.find(`.quickedit-toolgroup.${toolgroup}`);
      },

      /**
       * Shows a toolgroup.
       *
       * @param {string} toolgroup
       *   A toolgroup name.
       */
      show(toolgroup) {
        const $group = this._find(toolgroup);
        // Attach a transitionEnd event handler to the toolbar group so that
        // update events can be triggered after the animations have ended.
        $group.on(Drupal.quickedit.util.constants.transitionEnd, (event) => {
          $group.off(Drupal.quickedit.util.constants.transitionEnd);
        });
        // The call to remove the class and start the animation must be started in
        // the next animation frame or the event handler attached above won't be
        // triggered.
        window.setTimeout(() => {
          $group.removeClass('quickedit-animate-invisible');
        }, 0);
      },
    },
  );
})(jQuery, _, Backbone, Drupal);