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