237 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			237 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @file
							 | 
						||
| 
								 | 
							
								 * Dropbutton feature.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								(function ($, Drupal) {
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * A DropButton presents an HTML list as a button with a primary action.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * All secondary actions beyond the first in the list are presented in a
							 | 
						||
| 
								 | 
							
								   * dropdown list accessible through a toggle arrow associated with the button.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @constructor Drupal.DropButton
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {HTMLElement} dropbutton
							 | 
						||
| 
								 | 
							
								   *   A DOM element.
							 | 
						||
| 
								 | 
							
								   * @param {object} settings
							 | 
						||
| 
								 | 
							
								   *   A list of options including:
							 | 
						||
| 
								 | 
							
								   * @param {string} settings.title
							 | 
						||
| 
								 | 
							
								   *   The text inside the toggle link element. This text is hidden
							 | 
						||
| 
								 | 
							
								   *   from visual UAs.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  function DropButton(dropbutton, settings) {
							 | 
						||
| 
								 | 
							
								    // Merge defaults with settings.
							 | 
						||
| 
								 | 
							
								    const options = $.extend(
							 | 
						||
| 
								 | 
							
								      { title: Drupal.t('List additional actions') },
							 | 
						||
| 
								 | 
							
								      settings,
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								    const $dropbutton = $(dropbutton);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @type {jQuery}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    this.$dropbutton = $dropbutton;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @type {jQuery}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    this.$list = $dropbutton.find('.dropbutton');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Find actions and mark them.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @type {jQuery}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    this.$actions = this.$list.find('li').addClass('dropbutton-action');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Add the special dropdown only if there are hidden actions.
							 | 
						||
| 
								 | 
							
								    if (this.$actions.length > 1) {
							 | 
						||
| 
								 | 
							
								      // Identify the first element of the collection.
							 | 
						||
| 
								 | 
							
								      const $primary = this.$actions.slice(0, 1);
							 | 
						||
| 
								 | 
							
								      // Identify the secondary actions.
							 | 
						||
| 
								 | 
							
								      const $secondary = this.$actions.slice(1);
							 | 
						||
| 
								 | 
							
								      $secondary.addClass('secondary-action');
							 | 
						||
| 
								 | 
							
								      // Add toggle link.
							 | 
						||
| 
								 | 
							
								      $primary.after(Drupal.theme('dropbuttonToggle', options));
							 | 
						||
| 
								 | 
							
								      // Bind mouse events.
							 | 
						||
| 
								 | 
							
								      this.$dropbutton.addClass('dropbutton-multiple').on({
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * Adds a timeout to close the dropdown on mouseleave.
							 | 
						||
| 
								 | 
							
								         *
							 | 
						||
| 
								 | 
							
								         * @ignore
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        'mouseleave.dropbutton': this.hoverOut.bind(this),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * Clears timeout when mouseout of the dropdown.
							 | 
						||
| 
								 | 
							
								         *
							 | 
						||
| 
								 | 
							
								         * @ignore
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        'mouseenter.dropbutton': this.hoverIn.bind(this),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * Similar to mouseleave/mouseenter, but for keyboard navigation.
							 | 
						||
| 
								 | 
							
								         *
							 | 
						||
| 
								 | 
							
								         * @ignore
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        'focusout.dropbutton': this.focusOut.bind(this),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * @ignore
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        'focusin.dropbutton': this.focusIn.bind(this),
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.$dropbutton.addClass('dropbutton-single');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Delegated callback for opening and closing dropbutton secondary actions.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @function Drupal.DropButton~dropbuttonClickHandler
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {jQuery.Event} e
							 | 
						||
| 
								 | 
							
								   *   The event triggered.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  function dropbuttonClickHandler(e) {
							 | 
						||
| 
								 | 
							
								    e.preventDefault();
							 | 
						||
| 
								 | 
							
								    $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Process elements with the .dropbutton class on page load.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @type {Drupal~behavior}
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @prop {Drupal~behaviorAttach} attach
							 | 
						||
| 
								 | 
							
								   *   Attaches dropButton behaviors.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  Drupal.behaviors.dropButton = {
							 | 
						||
| 
								 | 
							
								    attach(context, settings) {
							 | 
						||
| 
								 | 
							
								      const dropbuttons = once('dropbutton', '.dropbutton-wrapper', context);
							 | 
						||
| 
								 | 
							
								      if (dropbuttons.length) {
							 | 
						||
| 
								 | 
							
								        // Adds the delegated handler that will toggle dropdowns on click.
							 | 
						||
| 
								 | 
							
								        const body = once('dropbutton-click', 'body');
							 | 
						||
| 
								 | 
							
								        if (body.length) {
							 | 
						||
| 
								 | 
							
								          $(body).on('click', '.dropbutton-toggle', dropbuttonClickHandler);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // Initialize all buttons.
							 | 
						||
| 
								 | 
							
								        dropbuttons.forEach((dropbutton) => {
							 | 
						||
| 
								 | 
							
								          DropButton.dropbuttons.push(
							 | 
						||
| 
								 | 
							
								            new DropButton(dropbutton, settings.dropbutton),
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Extend the DropButton constructor.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  $.extend(
							 | 
						||
| 
								 | 
							
								    DropButton,
							 | 
						||
| 
								 | 
							
								    /** @lends Drupal.DropButton */ {
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * Store all processed DropButtons.
							 | 
						||
| 
								 | 
							
								       *
							 | 
						||
| 
								 | 
							
								       * @type {Array.<Drupal.DropButton>}
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      dropbuttons: [],
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								  );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Extend the DropButton prototype.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  $.extend(
							 | 
						||
| 
								 | 
							
								    DropButton.prototype,
							 | 
						||
| 
								 | 
							
								    /** @lends Drupal.DropButton# */ {
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * Toggle the dropbutton open and closed.
							 | 
						||
| 
								 | 
							
								       *
							 | 
						||
| 
								 | 
							
								       * @param {boolean} [show]
							 | 
						||
| 
								 | 
							
								       *   Force the dropbutton to open by passing true or to close by
							 | 
						||
| 
								 | 
							
								       *   passing false.
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      toggle(show) {
							 | 
						||
| 
								 | 
							
								        const isBool = typeof show === 'boolean';
							 | 
						||
| 
								 | 
							
								        show = isBool ? show : !this.$dropbutton.hasClass('open');
							 | 
						||
| 
								 | 
							
								        this.$dropbutton.toggleClass('open', show);
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * @method
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      hoverIn() {
							 | 
						||
| 
								 | 
							
								        // Clear any previous timer we were using.
							 | 
						||
| 
								 | 
							
								        if (this.timerID) {
							 | 
						||
| 
								 | 
							
								          window.clearTimeout(this.timerID);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * @method
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      hoverOut() {
							 | 
						||
| 
								 | 
							
								        // Wait half a second before closing.
							 | 
						||
| 
								 | 
							
								        this.timerID = window.setTimeout(this.close.bind(this), 500);
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * @method
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      open() {
							 | 
						||
| 
								 | 
							
								        this.toggle(true);
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * @method
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      close() {
							 | 
						||
| 
								 | 
							
								        this.toggle(false);
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * @param {jQuery.Event} e
							 | 
						||
| 
								 | 
							
								       *   The event triggered.
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      focusOut(e) {
							 | 
						||
| 
								 | 
							
								        this.hoverOut.call(this, e);
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * @param {jQuery.Event} e
							 | 
						||
| 
								 | 
							
								       *   The event triggered.
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      focusIn(e) {
							 | 
						||
| 
								 | 
							
								        this.hoverIn.call(this, e);
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								  );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  $.extend(
							 | 
						||
| 
								 | 
							
								    Drupal.theme,
							 | 
						||
| 
								 | 
							
								    /** @lends Drupal.theme */ {
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * A toggle is an interactive element often bound to a click handler.
							 | 
						||
| 
								 | 
							
								       *
							 | 
						||
| 
								 | 
							
								       * @param {object} options
							 | 
						||
| 
								 | 
							
								       *   Options object.
							 | 
						||
| 
								 | 
							
								       * @param {string} [options.title]
							 | 
						||
| 
								 | 
							
								       *   The button text.
							 | 
						||
| 
								 | 
							
								       *
							 | 
						||
| 
								 | 
							
								       * @return {string}
							 | 
						||
| 
								 | 
							
								       *   A string representing a DOM fragment.
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      dropbuttonToggle(options) {
							 | 
						||
| 
								 | 
							
								        return `<li class="dropbutton-toggle"><button type="button"><span class="dropbutton-arrow"><span class="visually-hidden">${options.title}</span></span></button></li>`;
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								  );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Expose constructor in the public space.
							 | 
						||
| 
								 | 
							
								  Drupal.DropButton = DropButton;
							 | 
						||
| 
								 | 
							
								})(jQuery, Drupal);
							 |