UNPKG

15.6 kBJavaScriptView Raw
1(function (global, factory) {
2 if (typeof define === "function" && define.amd) {
3 define(["exports", "../../globals/js/misc/mixin", "../../globals/js/mixins/create-component", "../../globals/js/mixins/init-component-by-search", "../../globals/js/mixins/handles", "../../globals/js/misc/on", "../../globals/js/settings", "../../globals/js/misc/event-matches"], factory);
4 } else if (typeof exports !== "undefined") {
5 factory(exports, require("../../globals/js/misc/mixin"), require("../../globals/js/mixins/create-component"), require("../../globals/js/mixins/init-component-by-search"), require("../../globals/js/mixins/handles"), require("../../globals/js/misc/on"), require("../../globals/js/settings"), require("../../globals/js/misc/event-matches"));
6 } else {
7 var mod = {
8 exports: {}
9 };
10 factory(mod.exports, global.mixin, global.createComponent, global.initComponentBySearch, global.handles, global.on, global.settings, global.eventMatches);
11 global.headerSubmenu = mod.exports;
12 }
13})(this, function (_exports, _mixin2, _createComponent, _initComponentBySearch, _handles, _on, _settings, _eventMatches) {
14 "use strict";
15
16 Object.defineProperty(_exports, "__esModule", {
17 value: true
18 });
19 _exports.default = void 0;
20 _mixin2 = _interopRequireDefault(_mixin2);
21 _createComponent = _interopRequireDefault(_createComponent);
22 _initComponentBySearch = _interopRequireDefault(_initComponentBySearch);
23 _handles = _interopRequireDefault(_handles);
24 _on = _interopRequireDefault(_on);
25 _settings = _interopRequireDefault(_settings);
26 _eventMatches = _interopRequireDefault(_eventMatches);
27
28 function _interopRequireDefault(obj) {
29 return obj && obj.__esModule ? obj : {
30 default: obj
31 };
32 }
33
34 function _typeof(obj) {
35 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
36 _typeof = function _typeof(obj) {
37 return typeof obj;
38 };
39 } else {
40 _typeof = function _typeof(obj) {
41 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
42 };
43 }
44
45 return _typeof(obj);
46 }
47
48 function _slicedToArray(arr, i) {
49 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
50 }
51
52 function _nonIterableRest() {
53 throw new TypeError("Invalid attempt to destructure non-iterable instance");
54 }
55
56 function _iterableToArrayLimit(arr, i) {
57 var _arr = [];
58 var _n = true;
59 var _d = false;
60 var _e = undefined;
61
62 try {
63 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
64 _arr.push(_s.value);
65
66 if (i && _arr.length === i) break;
67 }
68 } catch (err) {
69 _d = true;
70 _e = err;
71 } finally {
72 try {
73 if (!_n && _i["return"] != null) _i["return"]();
74 } finally {
75 if (_d) throw _e;
76 }
77 }
78
79 return _arr;
80 }
81
82 function _arrayWithHoles(arr) {
83 if (Array.isArray(arr)) return arr;
84 }
85
86 function _classCallCheck(instance, Constructor) {
87 if (!(instance instanceof Constructor)) {
88 throw new TypeError("Cannot call a class as a function");
89 }
90 }
91
92 function _defineProperties(target, props) {
93 for (var i = 0; i < props.length; i++) {
94 var descriptor = props[i];
95 descriptor.enumerable = descriptor.enumerable || false;
96 descriptor.configurable = true;
97 if ("value" in descriptor) descriptor.writable = true;
98 Object.defineProperty(target, descriptor.key, descriptor);
99 }
100 }
101
102 function _createClass(Constructor, protoProps, staticProps) {
103 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
104 if (staticProps) _defineProperties(Constructor, staticProps);
105 return Constructor;
106 }
107
108 function _possibleConstructorReturn(self, call) {
109 if (call && (_typeof(call) === "object" || typeof call === "function")) {
110 return call;
111 }
112
113 return _assertThisInitialized(self);
114 }
115
116 function _assertThisInitialized(self) {
117 if (self === void 0) {
118 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
119 }
120
121 return self;
122 }
123
124 function _getPrototypeOf(o) {
125 _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
126 return o.__proto__ || Object.getPrototypeOf(o);
127 };
128 return _getPrototypeOf(o);
129 }
130
131 function _inherits(subClass, superClass) {
132 if (typeof superClass !== "function" && superClass !== null) {
133 throw new TypeError("Super expression must either be null or a function");
134 }
135
136 subClass.prototype = Object.create(superClass && superClass.prototype, {
137 constructor: {
138 value: subClass,
139 writable: true,
140 configurable: true
141 }
142 });
143 if (superClass) _setPrototypeOf(subClass, superClass);
144 }
145
146 function _setPrototypeOf(o, p) {
147 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
148 o.__proto__ = p;
149 return o;
150 };
151
152 return _setPrototypeOf(o, p);
153 }
154
155 var forEach =
156 /* #__PURE__ */
157 function () {
158 return Array.prototype.forEach;
159 }();
160
161 var toArray = function toArray(arrayLike) {
162 return Array.prototype.slice.call(arrayLike);
163 };
164
165 var HeaderSubmenu =
166 /*#__PURE__*/
167 function (_mixin) {
168 _inherits(HeaderSubmenu, _mixin);
169
170 function HeaderSubmenu(element, options) {
171 var _this;
172
173 _classCallCheck(this, HeaderSubmenu);
174
175 _this = _possibleConstructorReturn(this, _getPrototypeOf(HeaderSubmenu).call(this, element, options));
176
177 _this._getAction = function (event) {
178 var isFlyoutMenu = (0, _eventMatches.default)(event, _this.options.selectorFlyoutMenu);
179
180 if (isFlyoutMenu) {
181 return _this.constructor.actions.DELEGATE_TO_FLYOUT_MENU;
182 }
183
184 switch (event.type) {
185 case 'keydown':
186 return {
187 32: _this.constructor.actions.TOGGLE_SUBMENU_WITH_FOCUS,
188 // space bar
189 13: _this.constructor.actions.TOGGLE_SUBMENU_WITH_FOCUS,
190 // enter
191 27: _this.constructor.actions.CLOSE_SUBMENU // esc
192 // possible arrow keys
193
194 }[event.which];
195
196 case 'click':
197 return (0, _eventMatches.default)(event, _this.options.selectorItem) ? _this.constructor.actions.CLOSE_SUBMENU : null;
198
199 case 'blur':
200 case 'focusout':
201 {
202 var isOfSelf = _this.element.contains(event.relatedTarget);
203
204 return isOfSelf ? null : _this.constructor.actions.CLOSE_SUBMENU;
205 }
206
207 case 'mouseenter':
208 return _this.constructor.actions.OPEN_SUBMENU;
209
210 case 'mouseleave':
211 return _this.constructor.actions.CLOSE_SUBMENU;
212
213 default:
214 return null;
215 }
216 };
217
218 _this._getNewState = function (action) {
219 var trigger = _this.element.querySelector(_this.options.selectorTrigger);
220
221 var isExpanded = trigger.getAttribute(_this.options.attribExpanded) === 'true';
222
223 switch (action) {
224 case _this.constructor.actions.CLOSE_SUBMENU:
225 return false;
226
227 case _this.constructor.actions.OPEN_SUBMENU:
228 return true;
229
230 case _this.constructor.actions.TOGGLE_SUBMENU_WITH_FOCUS:
231 return !isExpanded;
232
233 default:
234 return isExpanded;
235 }
236 };
237
238 _this._setState = function (_ref) {
239 var shouldBeExpanded = _ref.shouldBeExpanded,
240 shouldFocusOnOpen = _ref.shouldFocusOnOpen;
241
242 var trigger = _this.element.querySelector(_this.options.selectorTrigger);
243
244 trigger.setAttribute(_this.options.attribExpanded, shouldBeExpanded);
245 forEach.call(_this.element.querySelectorAll(_this.options.selectorItem), function (item) {
246 item.tabIndex = shouldBeExpanded ? 0 : -1;
247 }); // focus first submenu item
248
249 if (shouldBeExpanded && shouldFocusOnOpen) {
250 _this.element.querySelector(_this.options.selectorItem).focus();
251 }
252 };
253
254 _this.getCurrentNavigation = function () {
255 var focused = _this.element.ownerDocument.activeElement;
256 return focused.nodeType === Node.ELEMENT_NODE && focused.matches(_this.options.selectorItem) ? focused : null;
257 };
258
259 _this.navigate = function (direction) {
260 var items = toArray(_this.element.querySelectorAll(_this.options.selectorItem));
261
262 var start = _this.getCurrentNavigation() || _this.element.querySelector(_this.options.selectorItemSelected);
263
264 var getNextItem = function getNextItem(old) {
265 var handleUnderflow = function handleUnderflow(index, length) {
266 return index + (index >= 0 ? 0 : length);
267 };
268
269 var handleOverflow = function handleOverflow(index, length) {
270 return index - (index < length ? 0 : length);
271 }; // `items.indexOf(old)` may be -1 (Scenario of no previous focus)
272
273
274 var index = Math.max(items.indexOf(old) + direction, -1);
275 return items[handleUnderflow(handleOverflow(index, items.length), items.length)];
276 };
277
278 for (var current = getNextItem(start); current && current !== start; current = getNextItem(current)) {
279 if (!current.matches(_this.options.selectorItemHidden) && !current.parentNode.matches(_this.options.selectorItemHidden) && !current.matches(_this.options.selectorItemSelected)) {
280 current.focus();
281 break;
282 }
283 }
284 };
285
286 _this._handleEvent = function (event) {
287 var trigger = _this.element.querySelector(_this.options.selectorTrigger);
288
289 if (!trigger) {
290 return;
291 }
292
293 var action = _this._getAction(event);
294
295 if (action) {
296 var shouldBeExpanded = _this._getNewState(action);
297
298 _this._setState({
299 shouldBeExpanded: shouldBeExpanded
300 });
301 }
302 };
303
304 _this._handleKeyDown = function (event) {
305 var trigger = _this.element.querySelector(_this.options.selectorTrigger);
306
307 if (!trigger) {
308 return;
309 }
310
311 var action = _this._getAction(event);
312
313 if (event.which === 32) {
314 event.preventDefault();
315 }
316
317 switch (action) {
318 case _this.constructor.actions.DELEGATE_TO_FLYOUT_MENU:
319 // currently we do not have a scenario that handles flyout menu
320 // handleFlyoutMenu
321 break;
322 // currently we do not have a scenario that opens a submenu on keydown
323 // case this.constructor.actions.OPEN_SUBMENU:
324
325 case _this.constructor.actions.CLOSE_SUBMENU:
326 {
327 var shouldBeExpanded = _this._getNewState(action);
328
329 _this._setState({
330 shouldBeExpanded: shouldBeExpanded
331 });
332
333 break;
334 }
335
336 case _this.constructor.actions.TOGGLE_SUBMENU_WITH_FOCUS:
337 {
338 var _shouldBeExpanded = _this._getNewState(action);
339
340 _this._setState({
341 shouldBeExpanded: _shouldBeExpanded,
342 shouldFocusOnOpen: true
343 });
344
345 break;
346 }
347
348 default:
349 {
350 var expanded = trigger.getAttribute(_this.options.attribExpanded) === 'true';
351
352 if (expanded) {
353 var direction = {
354 38: _this.constructor.NAVIGATE.BACKWARD,
355 40: _this.constructor.NAVIGATE.FORWARD
356 }[event.which];
357
358 switch (event.which) {
359 case 35:
360 {
361 // end key
362 event.preventDefault(); // prevents key from scrolling page
363
364 var menuItems = _this.element.querySelectorAll(_this.options.selectorItem);
365
366 var lastMenuItem = menuItems[menuItems.length - 1];
367
368 if (lastMenuItem) {
369 lastMenuItem.focus();
370 }
371
372 break;
373 }
374
375 case 36:
376 {
377 // home key
378 event.preventDefault(); // prevents key from scrolling page
379
380 var _this$element$querySe = _this.element.querySelectorAll(_this.options.selectorItem),
381 _this$element$querySe2 = _slicedToArray(_this$element$querySe, 1),
382 firstMenuItem = _this$element$querySe2[0];
383
384 if (firstMenuItem) {
385 firstMenuItem.focus();
386 }
387
388 break;
389 }
390
391 case 38: // up arrow
392
393 case 40:
394 // down arrow
395 _this.navigate(direction);
396
397 event.preventDefault(); // prevents keys from scrolling page
398
399 break;
400
401 default:
402 break;
403 }
404 }
405
406 break;
407 }
408 }
409 };
410
411 var hasFocusOut = 'onfocusout' in window;
412
413 _this.manage((0, _on.default)(_this.element, hasFocusOut ? 'focusout' : 'blur', _this._handleEvent, !hasFocusOut));
414
415 _this.manage((0, _on.default)(_this.element, 'mouseenter', _this._handleEvent));
416
417 _this.manage((0, _on.default)(_this.element, 'mouseleave', _this._handleEvent));
418
419 _this.manage((0, _on.default)(_this.element, 'click', _this._handleEvent));
420
421 _this.manage((0, _on.default)(_this.element, 'keydown', _this._handleKeyDown));
422
423 return _this;
424 }
425 /**
426 * The map associating DOM element and HeaderSubmenu instance.
427 * @member HeaderSubmenu.components
428 * @type {WeakMap}
429 */
430
431
432 _createClass(HeaderSubmenu, null, [{
433 key: "options",
434
435 /**
436 * The component options.
437 * If `options` is specified in the constructor,
438 * {@linkcode HeaderSubmenu.create .create()}, or
439 * {@linkcode HeaderSubmenu.init .init()},
440 * properties in this object are overriden for the instance being create and
441 * how {@linkcode HeaderSubmenu.init .init()} works.
442 * @member HeaderSubmenu.options
443 * @type {Object}
444 * @property {string} selectorInit The data attribute to find side navs.
445 */
446 get: function get() {
447 var prefix = _settings.default.prefix;
448 return {
449 selectorInit: '[data-header-submenu]',
450 selectorTrigger: ".".concat(prefix, "--header__menu-title"),
451 selectorItem: ".".concat(prefix, "--header__menu .").concat(prefix, "--header__menu-item"),
452 attribExpanded: 'aria-expanded'
453 };
454 }
455 /**
456 * Enum for navigating backward/forward.
457 * @readonly
458 * @member HeaderSubmenu.NAVIGATE
459 * @type {Object}
460 * @property {number} BACKWARD Navigating backward.
461 * @property {number} FORWARD Navigating forward.
462 */
463
464 }]);
465
466 HeaderSubmenu.components = new WeakMap();
467 HeaderSubmenu.actions = {
468 CLOSE_SUBMENU: 'CLOSE_SUBMENU',
469 OPEN_SUBMENU: 'OPEN_SUBMENU',
470 TOGGLE_SUBMENU_WITH_FOCUS: 'TOGGLE_SUBMENU_WITH_FOCUS',
471 DELEGATE_TO_FLYOUT_MENU: 'DELEGATE_TO_FLYOUT_MENU'
472 };
473 HeaderSubmenu.NAVIGATE = {
474 BACKWARD: -1,
475 FORWARD: 1
476 };
477 return HeaderSubmenu;
478 }((0, _mixin2.default)(_createComponent.default, _initComponentBySearch.default, _handles.default));
479
480 _exports.default = HeaderSubmenu;
481});
\No newline at end of file