8 * A DropButton presents an HTML list as a button with a primary action.
10 * All secondary actions beyond the first in the list are presented in a
11 * dropdown list accessible through a toggle arrow associated with the button.
13 * @constructor Drupal.DropButton
15 * @param {HTMLElement} dropbutton
17 * @param {object} settings
18 * A list of options including:
19 * @param {string} settings.title
20 * The text inside the toggle link element. This text is hidden
23 function DropButton(dropbutton, settings) {
24 // Merge defaults with settings.
25 const options = $.extend(
26 { title: Drupal.t('List additional actions') },
29 const $dropbutton = $(dropbutton);
34 this.$dropbutton = $dropbutton;
39 this.$list = $dropbutton.find('.dropbutton');
42 * Find actions and mark them.
46 this.$actions = this.$list.find('li').addClass('dropbutton-action');
48 // Add the special dropdown only if there are hidden actions.
49 if (this.$actions.length > 1) {
50 // Identify the first element of the collection.
51 const $primary = this.$actions.slice(0, 1);
52 // Identify the secondary actions.
53 const $secondary = this.$actions.slice(1);
54 $secondary.addClass('secondary-action');
56 $primary.after(Drupal.theme('dropbuttonToggle', options));
58 this.$dropbutton.addClass('dropbutton-multiple').on({
60 * Adds a timeout to close the dropdown on mouseleave.
64 'mouseleave.dropbutton': $.proxy(this.hoverOut, this),
67 * Clears timeout when mouseout of the dropdown.
71 'mouseenter.dropbutton': $.proxy(this.hoverIn, this),
74 * Similar to mouseleave/mouseenter, but for keyboard navigation.
78 'focusout.dropbutton': $.proxy(this.focusOut, this),
83 'focusin.dropbutton': $.proxy(this.focusIn, this),
86 this.$dropbutton.addClass('dropbutton-single');
91 * Delegated callback for opening and closing dropbutton secondary actions.
93 * @function Drupal.DropButton~dropbuttonClickHandler
95 * @param {jQuery.Event} e
96 * The event triggered.
98 function dropbuttonClickHandler(e) {
101 .closest('.dropbutton-wrapper')
102 .toggleClass('open');
106 * Process elements with the .dropbutton class on page load.
108 * @type {Drupal~behavior}
110 * @prop {Drupal~behaviorAttach} attach
111 * Attaches dropButton behaviors.
113 Drupal.behaviors.dropButton = {
114 attach(context, settings) {
115 const $dropbuttons = $(context)
116 .find('.dropbutton-wrapper')
118 if ($dropbuttons.length) {
119 // Adds the delegated handler that will toggle dropdowns on click.
120 const $body = $('body').once('dropbutton-click');
122 $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
124 // Initialize all buttons.
125 const il = $dropbuttons.length;
126 for (let i = 0; i < il; i++) {
127 DropButton.dropbuttons.push(
128 new DropButton($dropbuttons[i], settings.dropbutton),
136 * Extend the DropButton constructor.
140 /** @lends Drupal.DropButton */ {
142 * Store all processed DropButtons.
144 * @type {Array.<Drupal.DropButton>}
151 * Extend the DropButton prototype.
154 DropButton.prototype,
155 /** @lends Drupal.DropButton# */ {
157 * Toggle the dropbutton open and closed.
159 * @param {bool} [show]
160 * Force the dropbutton to open by passing true or to close by
164 const isBool = typeof show === 'boolean';
165 show = isBool ? show : !this.$dropbutton.hasClass('open');
166 this.$dropbutton.toggleClass('open', show);
173 // Clear any previous timer we were using.
175 window.clearTimeout(this.timerID);
183 // Wait half a second before closing.
184 this.timerID = window.setTimeout($.proxy(this, 'close'), 500);
202 * @param {jQuery.Event} e
203 * The event triggered.
206 this.hoverOut.call(this, e);
210 * @param {jQuery.Event} e
211 * The event triggered.
214 this.hoverIn.call(this, e);
221 /** @lends Drupal.theme */ {
223 * A toggle is an interactive element often bound to a click handler.
225 * @param {object} options
227 * @param {string} [options.title]
231 * A string representing a DOM fragment.
233 dropbuttonToggle(options) {
234 return `<li class="dropbutton-toggle"><button type="button"><span class="dropbutton-arrow"><span class="visually-hidden">${
236 }</span></span></button></li>`;
241 // Expose constructor in the public space.
242 Drupal.DropButton = DropButton;