@ucd-lib/theme-elements 1.1.4 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/brand/ucd-theme-header/ucd-theme-header.tpl.js +5 -5
- package/brand/ucd-theme-primary-nav/ucd-theme-primary-nav.tpl.js +0 -3
- package/package.json +1 -1
- package/ucdlib/ucdlib-header/ucdlib-header.js +207 -0
- package/ucdlib/ucdlib-header/ucdlib-header.tpl.js +283 -0
- package/ucdlib/ucdlib-primary-nav/ucdlib-primary-nav.js +587 -0
- package/ucdlib/ucdlib-primary-nav/ucdlib-primary-nav.tpl.js +368 -0
- package/ucdlib/ucdlib-sils-search-redirect/ucdlib-sils-search-redirect.js +20 -1
- package/ucdlib/ucdlib-sils-search-redirect/ucdlib-sils-search-redirect.tpl.js +1 -1
- package/utils/controllers/break-points.js +2 -2
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
import { LitElement, html } from 'lit';
|
|
2
|
+
import {render, styles} from "./ucdlib-primary-nav.tpl.js";
|
|
3
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
4
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
5
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
6
|
+
|
|
7
|
+
import { Mixin, NavElement } from "../../utils/mixins";
|
|
8
|
+
import { MutationObserverController, BreakPointsController } from '../../utils/controllers';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @class UcdlibPrimaryNav
|
|
12
|
+
* @classdesc Component class for displaying a primary site nav
|
|
13
|
+
* @property {String} navType - The primary style type of the nav:
|
|
14
|
+
* 'superfish' - The default
|
|
15
|
+
* 'mega' - Hovering over any top-level link opens a single nav with all subnav links
|
|
16
|
+
* @property {String} styleModifiers - Apply alternate styles with a space-separated list.
|
|
17
|
+
* e.g. 'justify' for 'primary-nav--justify'
|
|
18
|
+
* @property {Number} hoverDelay - How long (ms) after hover will menu open/close
|
|
19
|
+
* @property {Number} animationDuration - How long (ms) for a menu to fade in/out
|
|
20
|
+
* @property {Number} maxDepth - Maximum number of submenus to show
|
|
21
|
+
* @property {Number} mobileWidth - Screen width for mobile header display, defaults to 755
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* <ucdlib-primary-nav mobile-width="42">
|
|
25
|
+
* <a href="#">link 1</a>
|
|
26
|
+
* <a href="#">link 2</a>
|
|
27
|
+
* <ul link-title="link with subnav" href="#">
|
|
28
|
+
* <li><a href="#">subnav link 1</a></li>
|
|
29
|
+
* </ul>
|
|
30
|
+
* </ucdlib-primary-nav>
|
|
31
|
+
*/
|
|
32
|
+
export default class UcdlibPrimaryNav extends Mixin(LitElement)
|
|
33
|
+
.with(NavElement) {
|
|
34
|
+
|
|
35
|
+
static get properties() {
|
|
36
|
+
return {
|
|
37
|
+
navType: {type: String, attribute: "nav-type"},
|
|
38
|
+
styleModifiers: {type: String, attribute: "style-modifiers"},
|
|
39
|
+
hoverDelay: {type: Number, attribute: "hover-delay"},
|
|
40
|
+
animationDuration: {type: Number, attribute: "animation-duration"},
|
|
41
|
+
navItems: {type: Array},
|
|
42
|
+
maxDepth: {type: Number, attribute: "max-depth"},
|
|
43
|
+
mobileWidth: {type: Number, attribute: "mobile-width"},
|
|
44
|
+
_megaIsOpen: {type: Boolean, state: true}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static get styles() {
|
|
49
|
+
return styles();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
super();
|
|
54
|
+
this.render = render.bind(this);
|
|
55
|
+
this.mutationObserver = new MutationObserverController(this, {subtree: true, childList: true});
|
|
56
|
+
this.breakPoints = new BreakPointsController(this, 755); // override default mobile screen width
|
|
57
|
+
|
|
58
|
+
this.navType = "superfish";
|
|
59
|
+
this.styleModifiers = "";
|
|
60
|
+
this.hoverDelay = 300;
|
|
61
|
+
this.animationDuration = 300;
|
|
62
|
+
this.mobileWidth = 755;
|
|
63
|
+
|
|
64
|
+
this._classPrefix = "primary-nav";
|
|
65
|
+
this._acceptedNavTypes = ['superfish', 'mega'];
|
|
66
|
+
this._megaIsOpen = false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @method openMegaNav
|
|
71
|
+
* @description Opens the meganav menu
|
|
72
|
+
*/
|
|
73
|
+
openMegaNav() {
|
|
74
|
+
this._megaIsOpen = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @method closeMegaNav
|
|
79
|
+
* @description Closes the meganav menu
|
|
80
|
+
*/
|
|
81
|
+
closeMegaNav(){
|
|
82
|
+
this._megaIsOpen = false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @method openSubNav
|
|
87
|
+
* @description Opens the specified subnav
|
|
88
|
+
* @param {Array} navLocation - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
|
|
89
|
+
*/
|
|
90
|
+
async openSubNav(navLocation){
|
|
91
|
+
|
|
92
|
+
// non-mega menu
|
|
93
|
+
if (
|
|
94
|
+
typeof navLocation !== 'object' ||
|
|
95
|
+
!Array.isArray(navLocation) ||
|
|
96
|
+
navLocation.length === 0
|
|
97
|
+
) return;
|
|
98
|
+
let navItem = this.getNavItem(navLocation);
|
|
99
|
+
if ( !navItem ) return;
|
|
100
|
+
|
|
101
|
+
// Open on mobile
|
|
102
|
+
if ( this.breakPoints.isMobile() ) {
|
|
103
|
+
let nav = this.renderRoot.getElementById(`nav--${navLocation.join("-")}`);
|
|
104
|
+
if ( !nav ) return;
|
|
105
|
+
let ul = nav.querySelector('ul');
|
|
106
|
+
if ( !ul ) return;
|
|
107
|
+
if ( navItem.isTransitioning ) return;
|
|
108
|
+
navItem.isTransitioning = true;
|
|
109
|
+
|
|
110
|
+
// Get expanded height
|
|
111
|
+
navItem.inlineStyles.display = "block";
|
|
112
|
+
navItem.inlineStyles.height = 0 + "px";
|
|
113
|
+
this.requestUpdate();
|
|
114
|
+
await this.updateComplete;
|
|
115
|
+
const expandedHeight = ul.scrollHeight + "px";
|
|
116
|
+
|
|
117
|
+
// Set expanded height
|
|
118
|
+
navItem.inlineStyles.height = expandedHeight;
|
|
119
|
+
this.requestUpdate();
|
|
120
|
+
await this.updateComplete;
|
|
121
|
+
|
|
122
|
+
// Remove transition state after animation duration
|
|
123
|
+
this._completeMobileTransition(navItem);
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
// Open on desktop
|
|
127
|
+
} else {
|
|
128
|
+
|
|
129
|
+
// mega menu
|
|
130
|
+
if ( this.isMegaMenu() ){
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.clearItemInlineStyles(navItem);
|
|
135
|
+
if ( navItem.isClosing ) {
|
|
136
|
+
navItem.isClosing = false;
|
|
137
|
+
this.requestUpdate();
|
|
138
|
+
}
|
|
139
|
+
if ( navItem.timeout ) clearTimeout(navItem.timeout);
|
|
140
|
+
if ( navItem.isOpen ) return;
|
|
141
|
+
|
|
142
|
+
navItem.timeout = setTimeout(() => {
|
|
143
|
+
navItem.isOpen = true;
|
|
144
|
+
this.requestUpdate();
|
|
145
|
+
}, this.hoverDelay);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @method closeSubNav
|
|
151
|
+
* @description Closes a subnav given its coordinates
|
|
152
|
+
* @param {Array} navLocation - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
|
|
153
|
+
*/
|
|
154
|
+
async closeSubNav(navLocation){
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
typeof navLocation !== 'object' ||
|
|
158
|
+
!Array.isArray(navLocation) ||
|
|
159
|
+
navLocation.length === 0
|
|
160
|
+
) return;
|
|
161
|
+
let navItem = this.getNavItem(navLocation);
|
|
162
|
+
if ( !navItem ) return;
|
|
163
|
+
|
|
164
|
+
// close on mobile
|
|
165
|
+
if ( this.breakPoints.isMobile() ) {
|
|
166
|
+
let nav = this.renderRoot.getElementById(`nav--${navLocation.join("-")}`);
|
|
167
|
+
if ( !nav ) return;
|
|
168
|
+
let ul = nav.querySelector('ul');
|
|
169
|
+
if ( !ul ) return;
|
|
170
|
+
if ( navItem.isTransitioning ) return;
|
|
171
|
+
navItem.isTransitioning = true;
|
|
172
|
+
|
|
173
|
+
// Set expanded height
|
|
174
|
+
navItem.inlineStyles.height = ul.scrollHeight + "px";
|
|
175
|
+
navItem.inlineStyles.display = "block";
|
|
176
|
+
this.requestUpdate();
|
|
177
|
+
await this.updateComplete;
|
|
178
|
+
|
|
179
|
+
// Set height to 0 by requesting all of the animation frames :-(
|
|
180
|
+
requestAnimationFrame(() => {
|
|
181
|
+
requestAnimationFrame(() => {
|
|
182
|
+
navItem.inlineStyles.height = "0px";
|
|
183
|
+
this.requestUpdate();
|
|
184
|
+
|
|
185
|
+
requestAnimationFrame(() => {
|
|
186
|
+
// Remove transition state after animation duration
|
|
187
|
+
this._completeMobileTransition(navItem);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
// close on desktop
|
|
195
|
+
} else {
|
|
196
|
+
|
|
197
|
+
// mega menu
|
|
198
|
+
if ( this.isMegaMenu() ){
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
this.clearItemInlineStyles(navItem);
|
|
204
|
+
if ( navItem.timeout ) clearTimeout(navItem.timeout);
|
|
205
|
+
if ( !navItem.isOpen ) return;
|
|
206
|
+
|
|
207
|
+
navItem.isClosing = true;
|
|
208
|
+
this.requestUpdate();
|
|
209
|
+
navItem.timeout = setTimeout(() => {
|
|
210
|
+
navItem.isOpen = false;
|
|
211
|
+
navItem.isClosing = false;
|
|
212
|
+
this.requestUpdate();
|
|
213
|
+
}, this.hoverDelay + this.animationDuration);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @method closeAllSubNavs
|
|
220
|
+
* @description Recursively closes all nav submenus within specified menu.
|
|
221
|
+
* @param {Array} navItems - The subItems property of any object within the 'navItems' element property.
|
|
222
|
+
* @param {Boolean} requestUpdate - Should an update be requested after each subnav closing?
|
|
223
|
+
*/
|
|
224
|
+
closeAllSubNavs(navItems, requestUpdate=true){
|
|
225
|
+
if ( !navItems ) navItems = this.navItems;
|
|
226
|
+
navItems.forEach((navItem) => {
|
|
227
|
+
if ( navItem.isOpen ) {
|
|
228
|
+
navItem.isOpen = false;
|
|
229
|
+
if ( requestUpdate ) this.requestUpdate();
|
|
230
|
+
}
|
|
231
|
+
if ( navItem.subItems ) {
|
|
232
|
+
this.closeAllSubNavs(navItem.subItems);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @method isMegaMenu
|
|
239
|
+
* @description Does this element use the mega menu?
|
|
240
|
+
* @returns {Boolean}
|
|
241
|
+
*/
|
|
242
|
+
isMegaMenu(){
|
|
243
|
+
if ( this.navType.toLowerCase().trim() === 'mega') return true;
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @method _getNavClasses
|
|
249
|
+
* @private
|
|
250
|
+
* @description Get classes to be applied to the top-level 'nav' element
|
|
251
|
+
* @returns {String}
|
|
252
|
+
*/
|
|
253
|
+
_getNavClasses(){
|
|
254
|
+
let navType = this._acceptedNavTypes[0];
|
|
255
|
+
if ( this._acceptedNavTypes.includes(this.navType.toLowerCase()) ) navType = this.navType;
|
|
256
|
+
|
|
257
|
+
let styleModifiers = "";
|
|
258
|
+
if ( this.styleModifiers ) {
|
|
259
|
+
styleModifiers = this.styleModifiers.split(" ").map(mod => `${this._classPrefix}--${mod}`).join(" ");
|
|
260
|
+
}
|
|
261
|
+
let megaIsOpen = this.isMegaMenu() && this._megaIsOpen ? 'is-hover' : '';
|
|
262
|
+
return `${this._classPrefix} ${this._classPrefix}--${navType} ${styleModifiers} ${megaIsOpen}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @method _onChildListMutation
|
|
267
|
+
* @private
|
|
268
|
+
* @description Fires when light dom child list changes. Injected by MutationObserverController.
|
|
269
|
+
* Sets the 'navItems' property.
|
|
270
|
+
*/
|
|
271
|
+
_onChildListMutation(){
|
|
272
|
+
let navItems = this.parseNavChildren();
|
|
273
|
+
if ( navItems.length ) this.navItems = navItems;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @method _renderNavItem
|
|
278
|
+
* @private
|
|
279
|
+
* @description Renders a menu item and all its children to the specified max depth
|
|
280
|
+
* @param {Object} navItem - An item from the 'navItems' element property
|
|
281
|
+
* @param {Array} location - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4]
|
|
282
|
+
* @returns {TemplateResult}
|
|
283
|
+
*/
|
|
284
|
+
_renderNavItem(navItem, location){
|
|
285
|
+
const depth = location.length - 1;
|
|
286
|
+
|
|
287
|
+
// Render item and its subnav
|
|
288
|
+
if ( this.itemHasSubNav(navItem) && depth < this.maxDepth) {
|
|
289
|
+
return html`
|
|
290
|
+
<li
|
|
291
|
+
id="nav--${location.join("-")}"
|
|
292
|
+
.key=${location}
|
|
293
|
+
.hasnav=${true}
|
|
294
|
+
@mouseenter=${this._onItemMouseenter}
|
|
295
|
+
@mouseleave=${this._onItemMouseleave}
|
|
296
|
+
class=${classMap(this._makeLiClassMap(navItem, depth))}>
|
|
297
|
+
<div class="submenu-toggle__wrapper ${depth === 0 ? `${this._classPrefix}__top-link` : ''}">
|
|
298
|
+
<a
|
|
299
|
+
href=${ifDefined(navItem.href ? navItem.href : null)}
|
|
300
|
+
tabindex=${this._setTabIndex(depth)}
|
|
301
|
+
@focus=${this._onItemFocus}>
|
|
302
|
+
${navItem.linkText}<span class="${this._classPrefix}__submenu-indicator"></span>
|
|
303
|
+
</a>
|
|
304
|
+
<button
|
|
305
|
+
@click=${() => this._toggleMobileMenu(location)}
|
|
306
|
+
class="submenu-toggle ${navItem.isOpen ? 'submenu-toggle--open' : ''}"
|
|
307
|
+
?disabled=${navItem.isTransitioning}
|
|
308
|
+
aria-label="Toggle Submenu">
|
|
309
|
+
<span class="submenu-toggle__icon"></span>
|
|
310
|
+
</button>
|
|
311
|
+
</div>
|
|
312
|
+
<ul class="menu ${navItem.isOpen ? "menu--open" : ""}" style=${styleMap(this._getItemMobileStyles(location))}>
|
|
313
|
+
${navItem.subItems.map((subItem, i) => this._renderNavItem(subItem, location.concat([i])))}
|
|
314
|
+
</ul>
|
|
315
|
+
</li>
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// render as normal link
|
|
320
|
+
return html`
|
|
321
|
+
<li id="nav--${location.join("-")}" .key=${location} class=${classMap(this._makeLiClassMap(navItem, depth))}>
|
|
322
|
+
<div class="${depth === 0 ? `${this._classPrefix}__top-link`: '' }">
|
|
323
|
+
${navItem.href ? html`
|
|
324
|
+
<a
|
|
325
|
+
href=${navItem.href}
|
|
326
|
+
@focus=${this._onItemFocus}
|
|
327
|
+
tabindex=${this._setTabIndex(depth)}>
|
|
328
|
+
${navItem.linkText}</a>
|
|
329
|
+
` : html`
|
|
330
|
+
<span class="${this._classPrefix}__nolink">${navItem.linkText}</span>
|
|
331
|
+
`}
|
|
332
|
+
</div>
|
|
333
|
+
</li>
|
|
334
|
+
`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* @method _setTabIndex
|
|
339
|
+
* @private
|
|
340
|
+
* @description Sets the tab index of menu links
|
|
341
|
+
* @param {Number} depth - Level of the menu link
|
|
342
|
+
* @returns {Number}
|
|
343
|
+
*/
|
|
344
|
+
_setTabIndex(depth=0){
|
|
345
|
+
let i = 0;
|
|
346
|
+
if (
|
|
347
|
+
this.isMegaMenu() &&
|
|
348
|
+
depth > 0 &&
|
|
349
|
+
!this._megaIsOpen &&
|
|
350
|
+
this.breakPoints.isDesktop()
|
|
351
|
+
) i = -1;
|
|
352
|
+
|
|
353
|
+
return i;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @method _makeLiClassMap
|
|
358
|
+
* @private
|
|
359
|
+
* @description Classes to be assigned to each LI element in the nav.
|
|
360
|
+
* @param {Object} navItem - An item in the navItems property.
|
|
361
|
+
* @param {Number} depth - Depth of the navItem
|
|
362
|
+
* @returns {Object}
|
|
363
|
+
*/
|
|
364
|
+
_makeLiClassMap(navItem, depth=0){
|
|
365
|
+
let classes = {};
|
|
366
|
+
classes[`depth-${depth}`] = true;
|
|
367
|
+
if ( navItem.isOpen ) classes['sf--hover'] = true;
|
|
368
|
+
if ( navItem.isClosing ) classes.closing = true;
|
|
369
|
+
if (navItem.megaFocus) classes['mega-focus'] = true;
|
|
370
|
+
return classes;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @method _toggleMobileMenu
|
|
375
|
+
* @private
|
|
376
|
+
* @description Expands/collapses mobile subnavs with animation on user click.
|
|
377
|
+
* @param {Array} navLocation - Array coordinates of corresponding nav item
|
|
378
|
+
*/
|
|
379
|
+
async _toggleMobileMenu(navLocation){
|
|
380
|
+
if ( this.breakPoints.isDesktop() ) return;
|
|
381
|
+
let navItem = this.getNavItem(navLocation);
|
|
382
|
+
if ( navItem.isOpen ) {
|
|
383
|
+
this.closeSubNav(navLocation);
|
|
384
|
+
} else {
|
|
385
|
+
this.openSubNav(navLocation);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* @method _onNavMouseenter
|
|
391
|
+
* @private
|
|
392
|
+
* @description Attached to top-level nav element. Opens mega menu in desktop view
|
|
393
|
+
*/
|
|
394
|
+
_onNavMouseenter(){
|
|
395
|
+
if (
|
|
396
|
+
this.breakPoints.isMobile() ||
|
|
397
|
+
!this.isMegaMenu() )
|
|
398
|
+
return;
|
|
399
|
+
|
|
400
|
+
if ( this._megaTimeout ) clearTimeout(this._megaTimeout);
|
|
401
|
+
this._megaTimeout = setTimeout(() => {
|
|
402
|
+
this.openMegaNav();
|
|
403
|
+
}, this.hoverDelay);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @method _onNavMouseleave
|
|
408
|
+
* @private
|
|
409
|
+
* @description Attached to top-level nav element. Closes mega menu in desktop view
|
|
410
|
+
*/
|
|
411
|
+
_onNavMouseleave(){
|
|
412
|
+
if (
|
|
413
|
+
this.breakPoints.isMobile() ||
|
|
414
|
+
!this.isMegaMenu() )
|
|
415
|
+
return;
|
|
416
|
+
|
|
417
|
+
if ( this._megaTimeout ) clearTimeout(this._megaTimeout);
|
|
418
|
+
|
|
419
|
+
this._megaTimeout = setTimeout(() => {
|
|
420
|
+
this.closeMegaNav();
|
|
421
|
+
}, this.hoverDelay);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @method _onNavFocusin
|
|
426
|
+
* @private
|
|
427
|
+
* @description Fires when focus enters the main nav element. Used to open the meganav
|
|
428
|
+
*/
|
|
429
|
+
_onNavFocusin(){
|
|
430
|
+
if (
|
|
431
|
+
this.breakPoints.isMobile() ||
|
|
432
|
+
!this.isMegaMenu() )
|
|
433
|
+
return;
|
|
434
|
+
|
|
435
|
+
if ( this._megaIsOpen ) return;
|
|
436
|
+
if ( this._megaTimeout ) clearTimeout(this._megaTimeout);
|
|
437
|
+
|
|
438
|
+
this._megaTimeout = setTimeout(() => {
|
|
439
|
+
this.openMegaNav();
|
|
440
|
+
}, this.hoverDelay);
|
|
441
|
+
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* @method _onItemMouseenter
|
|
447
|
+
* @private
|
|
448
|
+
* @description Bound to nav li items with a subnav
|
|
449
|
+
* @param {Event} e
|
|
450
|
+
*/
|
|
451
|
+
_onItemMouseenter(e){
|
|
452
|
+
if ( this.breakPoints.isMobile() ) return;
|
|
453
|
+
this.openSubNav(e.target.key);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @method _onItemFocus
|
|
458
|
+
* @private
|
|
459
|
+
* @description Bound to nav a elements
|
|
460
|
+
* @param {Event} e
|
|
461
|
+
*/
|
|
462
|
+
_onItemFocus(e){
|
|
463
|
+
if ( this.breakPoints.isMobile() ) return;
|
|
464
|
+
const LI = e.target.parentElement.parentElement;
|
|
465
|
+
|
|
466
|
+
if (LI.hasnav) {
|
|
467
|
+
this.openSubNav(LI.key);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (this.isMegaMenu() && this._megaIsOpen) {
|
|
471
|
+
this._setMegaFocus(LI.key);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* @method _setMegaFocus
|
|
477
|
+
* @private
|
|
478
|
+
* @description Displays custom styling to meganav item when focused to fix bug in sitefarm code.
|
|
479
|
+
* @param {Array} navLocation - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
|
|
480
|
+
*/
|
|
481
|
+
_setMegaFocus(navLocation){
|
|
482
|
+
this.navItems.forEach((nav) => nav.megaFocus = false);
|
|
483
|
+
if (
|
|
484
|
+
typeof navLocation !== 'object' ||
|
|
485
|
+
!Array.isArray(navLocation) ||
|
|
486
|
+
navLocation.length < 1
|
|
487
|
+
) return;
|
|
488
|
+
let navItem = this.getNavItem([navLocation[0]]);
|
|
489
|
+
navItem.megaFocus = true;
|
|
490
|
+
this.requestUpdate();
|
|
491
|
+
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @method _completeMobileTransition
|
|
496
|
+
* @private
|
|
497
|
+
* @description Sets timeout to remove animation styles from mobile transition
|
|
498
|
+
* @param {Object} navItem - Member 'navItems' element property.
|
|
499
|
+
*/
|
|
500
|
+
_completeMobileTransition(navItem){
|
|
501
|
+
navItem.timeout = setTimeout(() => {
|
|
502
|
+
navItem.inlineStyles = {};
|
|
503
|
+
navItem.isOpen = !navItem.isOpen;
|
|
504
|
+
navItem.isTransitioning = false;
|
|
505
|
+
this.requestUpdate();
|
|
506
|
+
}, this.animationDuration);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @method _onItemMouseleave
|
|
511
|
+
* @private
|
|
512
|
+
* @description Bound to nav li items with a subnav
|
|
513
|
+
* @param {Event} e
|
|
514
|
+
*/
|
|
515
|
+
_onItemMouseleave(e){
|
|
516
|
+
if ( this.breakPoints.isMobile() || this.isMegaMenu() ) return;
|
|
517
|
+
this.closeSubNav(e.target.key);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* @method _onNavFocusout
|
|
522
|
+
* @private
|
|
523
|
+
* @description Attached to the top-level nav element. Closes subnav if it doesn't contain focused link.
|
|
524
|
+
*/
|
|
525
|
+
_onNavFocusout(){
|
|
526
|
+
if ( this.breakPoints.isMobile() ) return;
|
|
527
|
+
if ( this.isMegaMenu() ) {
|
|
528
|
+
if ( this._megaTimeout ) clearTimeout(this._megaTimeout);
|
|
529
|
+
requestAnimationFrame(() => {
|
|
530
|
+
const focusedEle = this.renderRoot.activeElement;
|
|
531
|
+
if ( focusedEle ) return;
|
|
532
|
+
this._megaTimeout = setTimeout(() => {
|
|
533
|
+
this.navItems.forEach((nav) => nav.megaFocus = false);
|
|
534
|
+
this.closeMegaNav();
|
|
535
|
+
}, this.hoverDelay);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
} else {
|
|
539
|
+
requestAnimationFrame(() => {
|
|
540
|
+
const focusedEle = this.renderRoot.activeElement;
|
|
541
|
+
if ( !focusedEle ) {
|
|
542
|
+
this.closeAllSubNavs();
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
let ele = focusedEle;
|
|
547
|
+
while (
|
|
548
|
+
ele &&
|
|
549
|
+
ele.tagName !== this.tagName &&
|
|
550
|
+
!Array.isArray(ele.key)
|
|
551
|
+
){
|
|
552
|
+
ele = ele.parentElement;
|
|
553
|
+
}
|
|
554
|
+
if ( !ele.key ) return;
|
|
555
|
+
let navLocation = [...ele.key];
|
|
556
|
+
let currentIndex = navLocation.pop();
|
|
557
|
+
let navSiblings = navLocation.length == 0 ? this.navItems : this.getNavItem(navLocation).subItems;
|
|
558
|
+
navSiblings.forEach((sibling, i) => {
|
|
559
|
+
if ( i !== currentIndex) {
|
|
560
|
+
sibling.isOpen = false;
|
|
561
|
+
this.closeAllSubNavs(sibling.subItems, false);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
this.requestUpdate();
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* @method _getItemMobileStyles
|
|
573
|
+
* @private
|
|
574
|
+
* @description Returns inline styles on a nav element (used for mobile transition animation)
|
|
575
|
+
* @param {Array} location - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
|
|
576
|
+
* @returns {Object} - Style map
|
|
577
|
+
*/
|
|
578
|
+
_getItemMobileStyles(location) {
|
|
579
|
+
if ( this.breakPoints.isDesktop() ) return {};
|
|
580
|
+
let navItem = this.getNavItem(location);
|
|
581
|
+
if ( !navItem.inlineStyles ) return {};
|
|
582
|
+
return navItem.inlineStyles;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
customElements.define('ucdlib-primary-nav', UcdlibPrimaryNav);
|