1 | 'use strict';
|
2 |
|
3 | import $ from 'jquery';
|
4 | import { Keyboard } from './foundation.util.keyboard';
|
5 | import { Nest } from './foundation.util.nest';
|
6 | import { Box } from './foundation.util.box';
|
7 | import { rtl as Rtl } from './foundation.util.core';
|
8 | import { Plugin } from './foundation.plugin';
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | class DropdownMenu extends Plugin {
|
20 | |
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | _setup(element, options) {
|
29 | this.$element = element;
|
30 | this.options = $.extend({}, DropdownMenu.defaults, this.$element.data(), options);
|
31 | this.className = 'DropdownMenu';
|
32 |
|
33 | Nest.Feather(this.$element, 'dropdown');
|
34 | this._init();
|
35 |
|
36 | Keyboard.register('DropdownMenu', {
|
37 | 'ENTER': 'open',
|
38 | 'SPACE': 'open',
|
39 | 'ARROW_RIGHT': 'next',
|
40 | 'ARROW_UP': 'up',
|
41 | 'ARROW_DOWN': 'down',
|
42 | 'ARROW_LEFT': 'previous',
|
43 | 'ESCAPE': 'close'
|
44 | });
|
45 | }
|
46 |
|
47 | |
48 |
|
49 |
|
50 |
|
51 |
|
52 | _init() {
|
53 | var subs = this.$element.find('li.is-dropdown-submenu-parent');
|
54 | this.$element.children('.is-dropdown-submenu-parent').children('.is-dropdown-submenu').addClass('first-sub');
|
55 |
|
56 | this.$menuItems = this.$element.find('[role="menuitem"]');
|
57 | this.$tabs = this.$element.children('[role="menuitem"]');
|
58 | this.$tabs.find('ul.is-dropdown-submenu').addClass(this.options.verticalClass);
|
59 |
|
60 | if (this.options.alignment === 'auto') {
|
61 | if (this.$element.hasClass(this.options.rightClass) || Rtl() || this.$element.parents('.top-bar-right').is('*')) {
|
62 | this.options.alignment = 'right';
|
63 | subs.addClass('opens-left');
|
64 | } else {
|
65 | this.options.alignment = 'left';
|
66 | subs.addClass('opens-right');
|
67 | }
|
68 | } else {
|
69 | if (this.options.alignment === 'right') {
|
70 | subs.addClass('opens-left');
|
71 | } else {
|
72 | subs.addClass('opens-right');
|
73 | }
|
74 | }
|
75 | this.changed = false;
|
76 | this._events();
|
77 | };
|
78 |
|
79 | _isVertical() {
|
80 | return this.$tabs.css('display') === 'block' || this.$element.css('flex-direction') === 'column';
|
81 | }
|
82 |
|
83 | _isRtl() {
|
84 | return this.$element.hasClass('align-right') || (Rtl() && !this.$element.hasClass('align-left'));
|
85 | }
|
86 |
|
87 | |
88 |
|
89 |
|
90 |
|
91 |
|
92 | _events() {
|
93 | var _this = this,
|
94 | hasTouch = 'ontouchstart' in window || (typeof window.ontouchstart !== 'undefined'),
|
95 | parClass = 'is-dropdown-submenu-parent';
|
96 |
|
97 |
|
98 | var handleClickFn = function(e) {
|
99 | var $elem = $(e.target).parentsUntil('ul', `.${parClass}`),
|
100 | hasSub = $elem.hasClass(parClass),
|
101 | hasClicked = $elem.attr('data-is-click') === 'true',
|
102 | $sub = $elem.children('.is-dropdown-submenu');
|
103 |
|
104 | if (hasSub) {
|
105 | if (hasClicked) {
|
106 | if (!_this.options.closeOnClick || (!_this.options.clickOpen && !hasTouch) || (_this.options.forceFollow && hasTouch)) { return; }
|
107 | else {
|
108 | e.stopImmediatePropagation();
|
109 | e.preventDefault();
|
110 | _this._hide($elem);
|
111 | }
|
112 | } else {
|
113 | e.preventDefault();
|
114 | e.stopImmediatePropagation();
|
115 | _this._show($sub);
|
116 | $elem.add($elem.parentsUntil(_this.$element, `.${parClass}`)).attr('data-is-click', true);
|
117 | }
|
118 | }
|
119 | };
|
120 |
|
121 | if (this.options.clickOpen || hasTouch) {
|
122 | this.$menuItems.on('click.zf.dropdownmenu touchstart.zf.dropdownmenu', handleClickFn);
|
123 | }
|
124 |
|
125 |
|
126 | if(_this.options.closeOnClickInside){
|
127 | this.$menuItems.on('click.zf.dropdownmenu', function(e) {
|
128 | var $elem = $(this),
|
129 | hasSub = $elem.hasClass(parClass);
|
130 | if(!hasSub){
|
131 | _this._hide();
|
132 | }
|
133 | });
|
134 | }
|
135 |
|
136 | if (!this.options.disableHover) {
|
137 | this.$menuItems.on('mouseenter.zf.dropdownmenu', function(e) {
|
138 | var $elem = $(this),
|
139 | hasSub = $elem.hasClass(parClass);
|
140 |
|
141 | if (hasSub) {
|
142 | clearTimeout($elem.data('_delay'));
|
143 | $elem.data('_delay', setTimeout(function() {
|
144 | _this._show($elem.children('.is-dropdown-submenu'));
|
145 | }, _this.options.hoverDelay));
|
146 | }
|
147 | }).on('mouseleave.zf.dropdownmenu', function(e) {
|
148 | var $elem = $(this),
|
149 | hasSub = $elem.hasClass(parClass);
|
150 | if (hasSub && _this.options.autoclose) {
|
151 | if ($elem.attr('data-is-click') === 'true' && _this.options.clickOpen) { return false; }
|
152 |
|
153 | clearTimeout($elem.data('_delay'));
|
154 | $elem.data('_delay', setTimeout(function() {
|
155 | _this._hide($elem);
|
156 | }, _this.options.closingTime));
|
157 | }
|
158 | });
|
159 | }
|
160 | this.$menuItems.on('keydown.zf.dropdownmenu', function(e) {
|
161 | var $element = $(e.target).parentsUntil('ul', '[role="menuitem"]'),
|
162 | isTab = _this.$tabs.index($element) > -1,
|
163 | $elements = isTab ? _this.$tabs : $element.siblings('li').add($element),
|
164 | $prevElement,
|
165 | $nextElement;
|
166 |
|
167 | $elements.each(function(i) {
|
168 | if ($(this).is($element)) {
|
169 | $prevElement = $elements.eq(i-1);
|
170 | $nextElement = $elements.eq(i+1);
|
171 | return;
|
172 | }
|
173 | });
|
174 |
|
175 | var nextSibling = function() {
|
176 | if (!$element.is(':last-child')) {
|
177 | $nextElement.children('a:first').focus();
|
178 | e.preventDefault();
|
179 | }
|
180 | }, prevSibling = function() {
|
181 | $prevElement.children('a:first').focus();
|
182 | e.preventDefault();
|
183 | }, openSub = function() {
|
184 | var $sub = $element.children('ul.is-dropdown-submenu');
|
185 | if ($sub.length) {
|
186 | _this._show($sub);
|
187 | $element.find('li > a:first').focus();
|
188 | e.preventDefault();
|
189 | } else { return; }
|
190 | }, closeSub = function() {
|
191 |
|
192 | var close = $element.parent('ul').parent('li');
|
193 | close.children('a:first').focus();
|
194 | _this._hide(close);
|
195 | e.preventDefault();
|
196 |
|
197 | };
|
198 | var functions = {
|
199 | open: openSub,
|
200 | close: function() {
|
201 | _this._hide(_this.$element);
|
202 | _this.$menuItems.eq(0).children('a').focus();
|
203 | e.preventDefault();
|
204 | },
|
205 | handled: function() {
|
206 | e.stopImmediatePropagation();
|
207 | }
|
208 | };
|
209 |
|
210 | if (isTab) {
|
211 | if (_this._isVertical()) {
|
212 | if (_this._isRtl()) {
|
213 | $.extend(functions, {
|
214 | down: nextSibling,
|
215 | up: prevSibling,
|
216 | next: closeSub,
|
217 | previous: openSub
|
218 | });
|
219 | } else {
|
220 | $.extend(functions, {
|
221 | down: nextSibling,
|
222 | up: prevSibling,
|
223 | next: openSub,
|
224 | previous: closeSub
|
225 | });
|
226 | }
|
227 | } else {
|
228 | if (_this._isRtl()) {
|
229 | $.extend(functions, {
|
230 | next: prevSibling,
|
231 | previous: nextSibling,
|
232 | down: openSub,
|
233 | up: closeSub
|
234 | });
|
235 | } else {
|
236 | $.extend(functions, {
|
237 | next: nextSibling,
|
238 | previous: prevSibling,
|
239 | down: openSub,
|
240 | up: closeSub
|
241 | });
|
242 | }
|
243 | }
|
244 | } else {
|
245 | if (_this._isRtl()) {
|
246 | $.extend(functions, {
|
247 | next: closeSub,
|
248 | previous: openSub,
|
249 | down: nextSibling,
|
250 | up: prevSibling
|
251 | });
|
252 | } else {
|
253 | $.extend(functions, {
|
254 | next: openSub,
|
255 | previous: closeSub,
|
256 | down: nextSibling,
|
257 | up: prevSibling
|
258 | });
|
259 | }
|
260 | }
|
261 | Keyboard.handleKey(e, 'DropdownMenu', functions);
|
262 |
|
263 | });
|
264 | }
|
265 |
|
266 | |
267 |
|
268 |
|
269 |
|
270 |
|
271 | _addBodyHandler() {
|
272 | var $body = $(document.body),
|
273 | _this = this;
|
274 | $body.off('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu')
|
275 | .on('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu', function(e) {
|
276 | var $link = _this.$element.find(e.target);
|
277 | if ($link.length) { return; }
|
278 |
|
279 | _this._hide();
|
280 | $body.off('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu');
|
281 | });
|
282 | }
|
283 |
|
284 | |
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 | _show($sub) {
|
292 | var idx = this.$tabs.index(this.$tabs.filter(function(i, el) {
|
293 | return $(el).find($sub).length > 0;
|
294 | }));
|
295 | var $sibs = $sub.parent('li.is-dropdown-submenu-parent').siblings('li.is-dropdown-submenu-parent');
|
296 | this._hide($sibs, idx);
|
297 | $sub.css('visibility', 'hidden').addClass('js-dropdown-active')
|
298 | .parent('li.is-dropdown-submenu-parent').addClass('is-active');
|
299 | var clear = Box.ImNotTouchingYou($sub, null, true);
|
300 | if (!clear) {
|
301 | var oldClass = this.options.alignment === 'left' ? '-right' : '-left',
|
302 | $parentLi = $sub.parent('.is-dropdown-submenu-parent');
|
303 | $parentLi.removeClass(`opens${oldClass}`).addClass(`opens-${this.options.alignment}`);
|
304 | clear = Box.ImNotTouchingYou($sub, null, true);
|
305 | if (!clear) {
|
306 | $parentLi.removeClass(`opens-${this.options.alignment}`).addClass('opens-inner');
|
307 | }
|
308 | this.changed = true;
|
309 | }
|
310 | $sub.css('visibility', '');
|
311 | if (this.options.closeOnClick) { this._addBodyHandler(); }
|
312 | |
313 |
|
314 |
|
315 |
|
316 | this.$element.trigger('show.zf.dropdownmenu', [$sub]);
|
317 | }
|
318 |
|
319 | |
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 | _hide($elem, idx) {
|
327 | var $toClose;
|
328 | if ($elem && $elem.length) {
|
329 | $toClose = $elem;
|
330 | } else if (idx !== undefined) {
|
331 | $toClose = this.$tabs.not(function(i, el) {
|
332 | return i === idx;
|
333 | });
|
334 | }
|
335 | else {
|
336 | $toClose = this.$element;
|
337 | }
|
338 | var somethingToClose = $toClose.hasClass('is-active') || $toClose.find('.is-active').length > 0;
|
339 |
|
340 | if (somethingToClose) {
|
341 | $toClose.find('li.is-active').add($toClose).attr({
|
342 | 'data-is-click': false
|
343 | }).removeClass('is-active');
|
344 |
|
345 | $toClose.find('ul.js-dropdown-active').removeClass('js-dropdown-active');
|
346 |
|
347 | if (this.changed || $toClose.find('opens-inner').length) {
|
348 | var oldClass = this.options.alignment === 'left' ? 'right' : 'left';
|
349 | $toClose.find('li.is-dropdown-submenu-parent').add($toClose)
|
350 | .removeClass(`opens-inner opens-${this.options.alignment}`)
|
351 | .addClass(`opens-${oldClass}`);
|
352 | this.changed = false;
|
353 | }
|
354 | |
355 |
|
356 |
|
357 |
|
358 | this.$element.trigger('hide.zf.dropdownmenu', [$toClose]);
|
359 | }
|
360 | }
|
361 |
|
362 | |
363 |
|
364 |
|
365 |
|
366 | _destroy() {
|
367 | this.$menuItems.off('.zf.dropdownmenu').removeAttr('data-is-click')
|
368 | .removeClass('is-right-arrow is-left-arrow is-down-arrow opens-right opens-left opens-inner');
|
369 | $(document.body).off('.zf.dropdownmenu');
|
370 | Nest.Burn(this.$element, 'dropdown');
|
371 | }
|
372 | }
|
373 |
|
374 |
|
375 |
|
376 |
|
377 | DropdownMenu.defaults = {
|
378 | |
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 | disableHover: false,
|
385 | |
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 | autoclose: true,
|
392 | |
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 | hoverDelay: 50,
|
399 | |
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 | clickOpen: false,
|
406 | |
407 |
|
408 |
|
409 |
|
410 |
|
411 |
|
412 |
|
413 | closingTime: 500,
|
414 | |
415 |
|
416 |
|
417 |
|
418 |
|
419 |
|
420 | alignment: 'auto',
|
421 | |
422 |
|
423 |
|
424 |
|
425 |
|
426 |
|
427 | closeOnClick: true,
|
428 | |
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 | closeOnClickInside: true,
|
435 | |
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 | verticalClass: 'vertical',
|
442 | |
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 | rightClass: 'align-right',
|
449 | |
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 | forceFollow: true
|
456 | };
|
457 |
|
458 | export {DropdownMenu};
|