6fe2b67db7be68849fec587c0b814f9367baea6a
[yaffs-website] / web / core / misc / dropbutton / dropbutton.es6.js
1 /**
2  * @file
3  * Dropbutton feature.
4  */
5
6 (function ($, Drupal) {
7   /**
8    * Process elements with the .dropbutton class on page load.
9    *
10    * @type {Drupal~behavior}
11    *
12    * @prop {Drupal~behaviorAttach} attach
13    *   Attaches dropButton behaviors.
14    */
15   Drupal.behaviors.dropButton = {
16     attach(context, settings) {
17       const $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
18       if ($dropbuttons.length) {
19         // Adds the delegated handler that will toggle dropdowns on click.
20         const $body = $('body').once('dropbutton-click');
21         if ($body.length) {
22           $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
23         }
24         // Initialize all buttons.
25         const il = $dropbuttons.length;
26         for (let i = 0; i < il; i++) {
27           DropButton.dropbuttons.push(new DropButton($dropbuttons[i], settings.dropbutton));
28         }
29       }
30     },
31   };
32
33   /**
34    * Delegated callback for opening and closing dropbutton secondary actions.
35    *
36    * @function Drupal.DropButton~dropbuttonClickHandler
37    *
38    * @param {jQuery.Event} e
39    *   The event triggered.
40    */
41   function dropbuttonClickHandler(e) {
42     e.preventDefault();
43     $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
44   }
45
46   /**
47    * A DropButton presents an HTML list as a button with a primary action.
48    *
49    * All secondary actions beyond the first in the list are presented in a
50    * dropdown list accessible through a toggle arrow associated with the button.
51    *
52    * @constructor Drupal.DropButton
53    *
54    * @param {HTMLElement} dropbutton
55    *   A DOM element.
56    * @param {object} settings
57    *   A list of options including:
58    * @param {string} settings.title
59    *   The text inside the toggle link element. This text is hidden
60    *   from visual UAs.
61    */
62   function DropButton(dropbutton, settings) {
63     // Merge defaults with settings.
64     const options = $.extend({ title: Drupal.t('List additional actions') }, settings);
65     const $dropbutton = $(dropbutton);
66
67     /**
68      * @type {jQuery}
69      */
70     this.$dropbutton = $dropbutton;
71
72     /**
73      * @type {jQuery}
74      */
75     this.$list = $dropbutton.find('.dropbutton');
76
77     /**
78      * Find actions and mark them.
79      *
80      * @type {jQuery}
81      */
82     this.$actions = this.$list.find('li').addClass('dropbutton-action');
83
84     // Add the special dropdown only if there are hidden actions.
85     if (this.$actions.length > 1) {
86       // Identify the first element of the collection.
87       const $primary = this.$actions.slice(0, 1);
88       // Identify the secondary actions.
89       const $secondary = this.$actions.slice(1);
90       $secondary.addClass('secondary-action');
91       // Add toggle link.
92       $primary.after(Drupal.theme('dropbuttonToggle', options));
93       // Bind mouse events.
94       this.$dropbutton
95         .addClass('dropbutton-multiple')
96         .on({
97
98           /**
99            * Adds a timeout to close the dropdown on mouseleave.
100            *
101            * @ignore
102            */
103           'mouseleave.dropbutton': $.proxy(this.hoverOut, this),
104
105           /**
106            * Clears timeout when mouseout of the dropdown.
107            *
108            * @ignore
109            */
110           'mouseenter.dropbutton': $.proxy(this.hoverIn, this),
111
112           /**
113            * Similar to mouseleave/mouseenter, but for keyboard navigation.
114            *
115            * @ignore
116            */
117           'focusout.dropbutton': $.proxy(this.focusOut, this),
118
119           /**
120            * @ignore
121            */
122           'focusin.dropbutton': $.proxy(this.focusIn, this),
123         });
124     }
125     else {
126       this.$dropbutton.addClass('dropbutton-single');
127     }
128   }
129
130   /**
131    * Extend the DropButton constructor.
132    */
133   $.extend(DropButton, /** @lends Drupal.DropButton */{
134     /**
135      * Store all processed DropButtons.
136      *
137      * @type {Array.<Drupal.DropButton>}
138      */
139     dropbuttons: [],
140   });
141
142   /**
143    * Extend the DropButton prototype.
144    */
145   $.extend(DropButton.prototype, /** @lends Drupal.DropButton# */{
146
147     /**
148      * Toggle the dropbutton open and closed.
149      *
150      * @param {bool} [show]
151      *   Force the dropbutton to open by passing true or to close by
152      *   passing false.
153      */
154     toggle(show) {
155       const isBool = typeof show === 'boolean';
156       show = isBool ? show : !this.$dropbutton.hasClass('open');
157       this.$dropbutton.toggleClass('open', show);
158     },
159
160     /**
161      * @method
162      */
163     hoverIn() {
164       // Clear any previous timer we were using.
165       if (this.timerID) {
166         window.clearTimeout(this.timerID);
167       }
168     },
169
170     /**
171      * @method
172      */
173     hoverOut() {
174       // Wait half a second before closing.
175       this.timerID = window.setTimeout($.proxy(this, 'close'), 500);
176     },
177
178     /**
179      * @method
180      */
181     open() {
182       this.toggle(true);
183     },
184
185     /**
186      * @method
187      */
188     close() {
189       this.toggle(false);
190     },
191
192     /**
193      * @param {jQuery.Event} e
194      *   The event triggered.
195      */
196     focusOut(e) {
197       this.hoverOut.call(this, e);
198     },
199
200     /**
201      * @param {jQuery.Event} e
202      *   The event triggered.
203      */
204     focusIn(e) {
205       this.hoverIn.call(this, e);
206     },
207   });
208
209   $.extend(Drupal.theme, /** @lends Drupal.theme */{
210
211     /**
212      * A toggle is an interactive element often bound to a click handler.
213      *
214      * @param {object} options
215      *   Options object.
216      * @param {string} [options.title]
217      *   The HTML anchor title attribute and text for the inner span element.
218      *
219      * @return {string}
220      *   A string representing a DOM fragment.
221      */
222     dropbuttonToggle(options) {
223       return `<li class="dropbutton-toggle"><button type="button"><span class="dropbutton-arrow"><span class="visually-hidden">${options.title}</span></span></button></li>`;
224     },
225   });
226
227   // Expose constructor in the public space.
228   Drupal.DropButton = DropButton;
229 }(jQuery, Drupal));