@ucd-lib/theme-elements 0.0.2 → 0.0.3

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.
@@ -1,7 +1,7 @@
1
1
  import { LitElement } from 'lit';
2
2
  import {render, styles} from "./ucd-theme-header.tpl.js";
3
3
 
4
- import { Mixin, MutationObserverElement, BreakPoints } from "../../utils/index.js";
4
+ import { Mixin, MutationObserverElement, BreakPoints, Wait } from "../../utils/index.js";
5
5
 
6
6
  /**
7
7
  * @class UcdThemeHeader
@@ -36,7 +36,7 @@ import { Mixin, MutationObserverElement, BreakPoints } from "../../utils/index.j
36
36
  *
37
37
  */
38
38
  export default class UcdThemeHeader extends Mixin(LitElement)
39
- .with(MutationObserverElement, BreakPoints) {
39
+ .with(MutationObserverElement, BreakPoints, Wait) {
40
40
 
41
41
  static get properties() {
42
42
  return {
@@ -105,7 +105,7 @@ export default class UcdThemeHeader extends Mixin(LitElement)
105
105
 
106
106
  this.opened = true;
107
107
  this._transitioning = true;
108
- await this._waitForAnimation();
108
+ await this.waitForAnimation();
109
109
  this._transitioning = false;
110
110
  return true;
111
111
 
@@ -121,7 +121,7 @@ export default class UcdThemeHeader extends Mixin(LitElement)
121
121
 
122
122
  this.opened = false;
123
123
  this._transitioning = true;
124
- await this._waitForAnimation();
124
+ await this.waitForAnimation();
125
125
  this._transitioning = false;
126
126
  return true;
127
127
 
@@ -215,18 +215,6 @@ export default class UcdThemeHeader extends Mixin(LitElement)
215
215
  }
216
216
  }
217
217
 
218
- /**
219
- * @method _waitForAnimation
220
- * @private
221
- * @description Wait for time designated for open/close animation
222
- * @returns {Promise}
223
- */
224
- async _waitForAnimation() {
225
- return new Promise(resolve => {
226
- setTimeout(resolve, this._animationDuration);
227
- });
228
- }
229
-
230
218
  }
231
219
 
232
220
  customElements.define('ucd-theme-header', UcdThemeHeader);
@@ -4,7 +4,7 @@ import { styleMap } from 'lit/directives/style-map.js';
4
4
  import { classMap } from 'lit/directives/class-map.js';
5
5
  import { ifDefined } from 'lit/directives/if-defined.js';
6
6
 
7
- import { Mixin, MutationObserverElement, BreakPoints } from "../../utils/index.js";
7
+ import { Mixin, MutationObserverElement, BreakPoints, NavElement } from "../../utils/index.js";
8
8
 
9
9
  /**
10
10
  * @class UcdThemePrimaryNav
@@ -33,7 +33,7 @@ import { Mixin, MutationObserverElement, BreakPoints } from "../../utils/index.j
33
33
  * </ucd-theme-primary-nav>
34
34
  */
35
35
  export default class UcdThemePrimaryNav extends Mixin(LitElement)
36
- .with(MutationObserverElement, BreakPoints) {
36
+ .with(NavElement, MutationObserverElement, BreakPoints) {
37
37
 
38
38
  static get properties() {
39
39
  return {
@@ -58,8 +58,6 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
58
58
  this.styleModifiers = "";
59
59
  this.hoverDelay = 300;
60
60
  this.animationDuration = 300;
61
- this.navItems = [];
62
- this.maxDepth = 2;
63
61
 
64
62
  this._classPrefix = "primary-nav";
65
63
  this._acceptedNavTypes = ['superfish', 'mega'];
@@ -108,14 +106,14 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
108
106
  navItem.isTransitioning = true;
109
107
 
110
108
  // Get expanded height
111
- navItem.mobileStyles.display = "block";
112
- navItem.mobileStyles.height = 0 + "px";
109
+ navItem.inlineStyles.display = "block";
110
+ navItem.inlineStyles.height = 0 + "px";
113
111
  this.requestUpdate();
114
112
  await this.updateComplete;
115
113
  const expandedHeight = ul.scrollHeight + "px";
116
114
 
117
115
  // Set expanded height
118
- navItem.mobileStyles.height = expandedHeight;
116
+ navItem.inlineStyles.height = expandedHeight;
119
117
  this.requestUpdate();
120
118
  await this.updateComplete;
121
119
 
@@ -131,7 +129,7 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
131
129
  return;
132
130
  }
133
131
 
134
- this.clearMobileAnimationStyles(navItem);
132
+ this.clearItemInlineStyles(navItem);
135
133
  if ( navItem.isClosing ) {
136
134
  navItem.isClosing = false;
137
135
  this.requestUpdate();
@@ -171,15 +169,15 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
171
169
  navItem.isTransitioning = true;
172
170
 
173
171
  // Set expanded height
174
- navItem.mobileStyles.height = ul.scrollHeight + "px";
175
- navItem.mobileStyles.display = "block";
172
+ navItem.inlineStyles.height = ul.scrollHeight + "px";
173
+ navItem.inlineStyles.display = "block";
176
174
  this.requestUpdate();
177
175
  await this.updateComplete;
178
176
 
179
177
  // Set height to 0 by requesting all of the animation frames :-(
180
178
  requestAnimationFrame(() => {
181
179
  requestAnimationFrame(() => {
182
- navItem.mobileStyles.height = "0px";
180
+ navItem.inlineStyles.height = "0px";
183
181
  this.requestUpdate();
184
182
 
185
183
  requestAnimationFrame(() => {
@@ -200,7 +198,7 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
200
198
  }
201
199
 
202
200
 
203
- this.clearMobileAnimationStyles(navItem);
201
+ this.clearItemInlineStyles(navItem);
204
202
  if ( navItem.timeout ) clearTimeout(navItem.timeout);
205
203
  if ( !navItem.isOpen ) return;
206
204
 
@@ -234,22 +232,6 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
234
232
  });
235
233
  }
236
234
 
237
- /**
238
- * @method clearMobileAnimationStyles
239
- * @description Removes inline styles on a nav element (used for mobile transition animation)
240
- * @param {Object} navItem - Member of the this.navItems array
241
- */
242
- clearMobileAnimationStyles(navItem){
243
- if (
244
- navItem &&
245
- navItem.mobileStyles &&
246
- Object.keys(navItem.mobileStyles).length > 0
247
- ) {
248
- navItem.mobileStyles = {};
249
- this.requestUpdate();
250
- }
251
- }
252
-
253
235
  /**
254
236
  * @method isMegaMenu
255
237
  * @description Does this element use the mega menu?
@@ -260,20 +242,6 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
260
242
  return false;
261
243
  }
262
244
 
263
- /**
264
- * @method getNavItem
265
- * @description Retrieves an item from the navItems array.
266
- * @param {Array} location - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
267
- * @returns {Object}
268
- */
269
- getNavItem(location){
270
- let accessor = "this.navItems";
271
- if ( location && location.length > 0) {
272
- accessor += "[" + location.join("].subItems[") + "]";
273
- }
274
- return eval(accessor);
275
- }
276
-
277
245
  /**
278
246
  * @method _getNavClasses
279
247
  * @private
@@ -299,42 +267,10 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
299
267
  * Sets the 'navItems' property.
300
268
  */
301
269
  _onChildListMutation(){
302
- const children = Array.from(this.children);
303
- let navItems = children.map((child) => this._makeNavItemTree(child)).filter(navItem => navItem.linkText);
270
+ let navItems = this.parseNavChildren();
304
271
  if ( navItems.length ) this.navItems = navItems;
305
272
  }
306
273
 
307
- /**
308
- * @method _makeNavItemTree
309
- * @private
310
- * @description Extracts menu item data from DOM Element
311
- * @param {Element} ele - Element
312
- * @returns {Object} Formatted object describing the menu item and its children
313
- */
314
- _makeNavItemTree(ele){
315
- let linkText, href, subItems = [], isOpen=false, mobileStyles={};
316
- if ( ele.tagName === 'LI' && ele.children.length > 0) ele = ele.children[0];
317
-
318
- if ( ele.tagName === 'A' ) {
319
- linkText = ele.innerText;
320
- href = ele.href;
321
- } else if ( ele.tagName === 'LI' ) {
322
- linkText = ele.innerText;
323
- } else if ( ele.tagName === 'OL' || ele.tagName === 'UL' ) {
324
- linkText = ele.getAttribute('link-text');
325
- href = ele.getAttribute('href');
326
-
327
- for (const child of Array.from(ele.children)) {
328
- let childItem = this._makeNavItemTree(child);
329
- if ( childItem.linkText ) subItems.push(childItem);
330
- }
331
- }
332
-
333
- if ( linkText ) linkText = linkText.trim();
334
- return {linkText, href, subItems, isOpen, mobileStyles};
335
-
336
- }
337
-
338
274
  /**
339
275
  * @method _renderNavItem
340
276
  * @private
@@ -347,7 +283,7 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
347
283
  const depth = location.length - 1;
348
284
 
349
285
  // Render item and its subnav
350
- if ( this._hasSubNav(navItem) && depth < this.maxDepth) {
286
+ if ( this.itemHasSubNav(navItem) && depth < this.maxDepth) {
351
287
  return html`
352
288
  <li
353
289
  id="nav--${location.join("-")}"
@@ -561,7 +497,7 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
561
497
  */
562
498
  _completeMobileTransition(navItem){
563
499
  navItem.timeout = setTimeout(() => {
564
- navItem.mobileStyles = {};
500
+ navItem.inlineStyles = {};
565
501
  navItem.isOpen = !navItem.isOpen;
566
502
  navItem.isTransitioning = false;
567
503
  this.requestUpdate();
@@ -630,18 +566,6 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
630
566
 
631
567
  }
632
568
 
633
- /**
634
- * @method _hasSubNav
635
- * @private
636
- * @description Utility function for determining if a menu has subitems
637
- * @param {Object} navItem - A member of the navItems array.
638
- * @returns {Boolean}
639
- */
640
- _hasSubNav(navItem){
641
- if ( navItem && navItem.subItems && navItem.subItems.length) return true;
642
- return false;
643
- }
644
-
645
569
  /**
646
570
  * @method _getItemMobileStyles
647
571
  * @private
@@ -652,8 +576,8 @@ export default class UcdThemePrimaryNav extends Mixin(LitElement)
652
576
  _getItemMobileStyles(location) {
653
577
  if ( this.isDesktop() ) return {};
654
578
  let navItem = this.getNavItem(location);
655
- if ( !navItem.mobileStyles ) return {};
656
- return navItem.mobileStyles;
579
+ if ( !navItem.inlineStyles ) return {};
580
+ return navItem.inlineStyles;
657
581
  }
658
582
 
659
583
  }
@@ -1,7 +1,7 @@
1
1
  import { LitElement, html } from 'lit';
2
2
  import {render, styles} from "./ucd-theme-quick-links.tpl.js";
3
3
 
4
- import { Mixin, BreakPoints } from "../../utils/index.js";
4
+ import { Mixin, MutationObserverElement, BreakPoints, Wait } from "../../utils/index.js";
5
5
 
6
6
  /**
7
7
  * @class UcdThemeQuickLinks
@@ -18,7 +18,7 @@ import { Mixin, BreakPoints } from "../../utils/index.js";
18
18
  * @property {Number} animationDuration - Length of animation when opening/closing menu
19
19
  */
20
20
  export default class UcdThemeQuickLinks extends Mixin(LitElement)
21
- .with(BreakPoints) {
21
+ .with(MutationObserverElement, BreakPoints, Wait) {
22
22
 
23
23
  static get properties() {
24
24
  return {
@@ -67,7 +67,7 @@ export default class UcdThemeQuickLinks extends Mixin(LitElement)
67
67
  this._openedHeight = this.renderRoot.getElementById('menu').scrollHeight + "px";
68
68
  await this.updateComplete;
69
69
 
70
- await this._waitForAnimation();
70
+ await this.waitForAnimation();
71
71
  this._transitioning = false;
72
72
  this.opened = true;
73
73
  return true;
@@ -84,11 +84,11 @@ export default class UcdThemeQuickLinks extends Mixin(LitElement)
84
84
 
85
85
  this._openedHeight = this.renderRoot.getElementById('menu').scrollHeight + "px";
86
86
  await this.updateComplete;
87
- await this._waitForFrames(2);
87
+ await this.waitForFrames(2);
88
88
  this._openedHeight = 0;
89
89
  await this.updateComplete;
90
90
 
91
- await this._waitForAnimation();
91
+ await this.waitForAnimation();
92
92
 
93
93
  this._transitioning = false;
94
94
  this.opened = false;
@@ -262,33 +262,6 @@ export default class UcdThemeQuickLinks extends Mixin(LitElement)
262
262
  return html``;
263
263
  }
264
264
 
265
- /**
266
- * @method _waitForAnimation
267
- * @private
268
- * @description Wait for time designated for open/close animation
269
- * @returns {Promise}
270
- */
271
- async _waitForAnimation() {
272
- return new Promise(resolve => {
273
- setTimeout(resolve, this.animationDuration);
274
- });
275
- }
276
-
277
- /**
278
- * @method _waitForFrames
279
- * @private
280
- * @description Wait for specified number of animation frames
281
- * @param {Number} ct Number of frames
282
- */
283
- async _waitForFrames(ct=1) {
284
- for (let i = 0; i < ct; i++) {
285
- await new Promise(resolve => {
286
- requestAnimationFrame(resolve);
287
- });
288
- }
289
- }
290
-
291
-
292
265
  }
293
266
 
294
267
  customElements.define('ucd-theme-quick-links', UcdThemeQuickLinks);
@@ -0,0 +1,192 @@
1
+ import { LitElement, html } from 'lit';
2
+ import {render, styles} from "./ucd-theme-subnav.tpl.js";
3
+ import { ifDefined } from 'lit/directives/if-defined.js';
4
+
5
+ import { styleMap } from 'lit/directives/style-map.js';
6
+
7
+ import {Mixin, MutationObserverElement, NavElement, Wait} from "../../utils";
8
+
9
+ /**
10
+ * @class UcdThemeSubnav
11
+ * @classdesc Component class for displaying a subnav
12
+ *
13
+ * Patternlab url:
14
+ * - http://dev.webstyleguide.ucdavis.edu/redesign/?p=molecules-sub-nav
15
+ * - http://dev.webstyleguide.ucdavis.edu/redesign/?p=molecules-sub-nav-linked-title
16
+ *
17
+ * @property {String} navTitle - Subnav header text
18
+ * @property {String} titleHref - Link for subnav header (optional)
19
+ *
20
+ * @example
21
+ * <ucd-theme-subnav nav-title="A subnav">
22
+ * <li><a href="#">Link 1</a></li>
23
+ * <li><a href="#">Link 2</a></li>
24
+ * <ul link-text="Link with Children" href="#">
25
+ * <li><a href="#">Child 1</a></li>
26
+ * <li><a href="#">Child 2</a></li>
27
+ * <li><a href="#">Child 3</a></li>
28
+ * </ul>
29
+ * </ucd-theme-subnav>
30
+ */
31
+ export default class UcdThemeSubnav extends Mixin(LitElement)
32
+ .with(NavElement, MutationObserverElement, Wait) {
33
+
34
+ static get properties() {
35
+ return {
36
+ navTitle: {type: String, attribute: "nav-title"},
37
+ titleHref: {type: String, attribute: "title-href"},
38
+ navItems: {type: Array},
39
+ animationDuration: {type: Number, attribute: "animation-duration"}
40
+ };
41
+ }
42
+
43
+ static get styles() {
44
+ return styles();
45
+ }
46
+
47
+ constructor() {
48
+ super();
49
+ this.render = render.bind(this);
50
+
51
+ this.navTitle = "";
52
+ this.titleHref = "";
53
+ this.animationDuration = 300;
54
+ }
55
+
56
+ /**
57
+ * @method openNavItem
58
+ * @description Shows children of a nav item (if applicable)
59
+ * @param {Array} navLocation - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
60
+ * @returns {Boolean}
61
+ */
62
+ async openNavItem(navLocation){
63
+ let navItem = this.getNavItem(navLocation);
64
+ if ( !navItem || navItem.isTransitioning ) return false;
65
+
66
+ let navEle = this.renderRoot.getElementById(`nav--${navLocation.join("-")}`);
67
+ if ( !navEle ) return false;
68
+ let navUL = navEle.querySelector('ul');
69
+ if ( !navUL ) return false;
70
+ navItem.isTransitioning = true;
71
+
72
+ // Get expanded height
73
+ navItem.inlineStyles.display = "block";
74
+ navItem.inlineStyles.height = "0px";
75
+ await this.waitForUpdate();
76
+ const expandedHeight = navUL.scrollHeight + "px";
77
+
78
+ // Set expanded height
79
+ navItem.inlineStyles.height = expandedHeight;
80
+ await this.waitForUpdate();
81
+
82
+ // Complete animation
83
+ await this.waitForAnimation();
84
+ navItem.inlineStyles = {};
85
+ navItem.isOpen = true;
86
+ navItem.isTransitioning = false;
87
+ this.requestUpdate();
88
+
89
+ return true;
90
+ }
91
+
92
+ /**
93
+ * @method closeNavItem
94
+ * @description Hides children of a nav item (if applicable)
95
+ * @param {Array} navLocation - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
96
+ * @returns {Boolean}
97
+ */
98
+ async closeNavItem(navLocation){
99
+ let navItem = this.getNavItem(navLocation);
100
+ if ( !navItem || navItem.isTransitioning ) return false;
101
+
102
+ let navEle = this.renderRoot.getElementById(`nav--${navLocation.join("-")}`);
103
+ if ( !navEle ) return false;
104
+ let navUL = navEle.querySelector('ul');
105
+ if ( !navUL ) return false;
106
+ navItem.isTransitioning = true;
107
+
108
+ // Set expanded height
109
+ navItem.inlineStyles.height = navUL.scrollHeight + "px";
110
+ navItem.inlineStyles.display = "block";
111
+ await this.waitForUpdate();
112
+
113
+ // Set height to zero
114
+ await this.waitForFrames(2);
115
+ navItem.inlineStyles.height = "0px";
116
+ await this.waitForUpdate();
117
+
118
+ // Complete animation
119
+ await this.waitForAnimation();
120
+ navItem.inlineStyles = {};
121
+ navItem.isOpen = false;
122
+ navItem.isTransitioning = false;
123
+ this.requestUpdate();
124
+
125
+ return true;
126
+
127
+ }
128
+
129
+ /**
130
+ * @method _onChildListMutation
131
+ * @private
132
+ * @description Fires when light dom child list changes. Injected by MutationObserverElement mixin.
133
+ * Sets the 'navItems' property.
134
+ */
135
+ _onChildListMutation(){
136
+ let navItems = this.parseNavChildren();
137
+ if ( navItems.length ) this.navItems = navItems;
138
+ }
139
+
140
+ /**
141
+ * @method _renderNavItem
142
+ * @private
143
+ * @description Renders a menu item and all its children to the specified max depth
144
+ * @param {Object} item - An item from the 'navItems' element property
145
+ * @param {Array} location - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4]
146
+ * @returns {TemplateResult}
147
+ */
148
+ _renderNavItem(item, location){
149
+ const depth = location.length - 1;
150
+
151
+ if ( this.itemHasSubNav(item) && depth < this.maxDepth) {
152
+ return html`
153
+ <li id="nav--${location.join("-")}">
154
+ <div class="submenu-toggle__wrapper">
155
+ <a href=${ifDefined(item.href ? item.href : null)}>${item.linkText}</a>
156
+ <button
157
+ @click=${() => this._toggleItemMenu(location)}
158
+ class="submenu-toggle ${item.isOpen ? "submenu-toggle--open" : ""}"
159
+ ?disabled=${item.isTransitioning}
160
+ aria-label="Toggle Submenu">
161
+ <span class="submenu-toggle__icon"></span>
162
+ </button>
163
+ </div>
164
+ <ul style=${styleMap(item.inlineStyles)} class="${item.isOpen ? "is-open": "" }">
165
+ ${item.subItems.map((subItem, i) => this._renderNavItem(subItem, location.concat([i])))}
166
+ </ul>
167
+ </li>
168
+ `;
169
+ }
170
+ return html`
171
+ <li id="nav--${location.join("-")}"><a href=${item.href}>${item.linkText}</a></li>
172
+ `;
173
+ }
174
+
175
+ /**
176
+ * @method _toggleItemMenu
177
+ * @private
178
+ * @description Attached to nav item button click. Shows/hides children.
179
+ * @param {Array} navLocation - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4]
180
+ */
181
+ _toggleItemMenu(navLocation){
182
+ let navItem = this.getNavItem(navLocation);
183
+ if ( navItem.isOpen ) {
184
+ this.closeNavItem(navLocation);
185
+ } else {
186
+ this.openNavItem(navLocation);
187
+ }
188
+ }
189
+
190
+ }
191
+
192
+ customElements.define('ucd-theme-subnav', UcdThemeSubnav);
@@ -0,0 +1,60 @@
1
+ import { html, css } from 'lit';
2
+
3
+ import normalizeStyles from "@ucd-lib/theme-sass/normalize.css.js";
4
+ import headerStyles from "@ucd-lib/theme-sass/1_base_html/_headings.css.js";
5
+ import formStyles from "@ucd-lib/theme-sass/1_base_html/_forms.css.js";
6
+ import menuStyles from "@ucd-lib/theme-sass/2_base_class/_misc.css.js";
7
+ import subNavStyles from "@ucd-lib/theme-sass/4_component/_nav-sub.css.js";
8
+ import subNavToggleStyles from "@ucd-lib/theme-sass/4_component/_submenu-toggle.css.js";
9
+
10
+ export function styles() {
11
+ const elementStyles = css`
12
+ :host {
13
+ display: block;
14
+ }
15
+ ul.sub-nav__menu ul {
16
+ display: none;
17
+ overflow-y: hidden;
18
+ visibility: visible;
19
+ height: auto;
20
+ border-top-width: 0px;
21
+ border-bottom-width: 0px;
22
+ padding-top: 0px;
23
+ padding-bottom: 0px;
24
+ }
25
+ ul.sub-nav__menu ul.is-open {
26
+ display: block;
27
+ }
28
+ `;
29
+
30
+ return [
31
+ normalizeStyles,
32
+ headerStyles,
33
+ formStyles,
34
+ menuStyles,
35
+ subNavStyles,
36
+ subNavToggleStyles,
37
+ elementStyles
38
+ ];
39
+ }
40
+
41
+ export function render() {
42
+ return html`
43
+ <style>
44
+ ul.sub-nav__menu ul {
45
+ transition: height ${this.animationDuration + "ms"};
46
+ }
47
+
48
+ </style>
49
+ <nav class="sub-nav">
50
+ ${this.navTitle ? html`
51
+ <h2 class="sub-nav__title${this.titleHref ? "-linked" : ""}">
52
+ ${this.titleHref ? html`<a href=${this.titleHref}>${this.navTitle}</a>` : this.navTitle}
53
+ </h2>
54
+ ` : html``}
55
+ <ul class="sub-nav__menu">
56
+ ${this.navItems.map((item, i) => this._renderNavItem(item, [i]))}
57
+ </ul>
58
+ </nav>
59
+
60
+ `;}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ucd-lib/theme-elements",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Custom elements for the UCD brand theme",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,7 +1,7 @@
1
1
  import "../ucdlib-iconset/ucdlib-iconset";
2
2
  import "../ucdlib-icon/ucdlib-icon";
3
3
 
4
- import { html, render } from "lit";
4
+ import { html, render } from "lit-html";
5
5
 
6
6
  /**
7
7
  * @description Creates iconset and moves to document head
package/utils/index.js CHANGED
@@ -1,6 +1,14 @@
1
1
  import Mixin from './mixin.js';
2
- import {MutationObserverElement} from './mutation-observer.js';
3
- import {MainDomElement} from './main-dom-element.js';
2
+ import { MutationObserverElement } from './mutation-observer.js';
3
+ import { MainDomElement } from './main-dom-element.js';
4
4
  import { BreakPoints } from './break-points.js';
5
+ import { NavElement } from './nav-element.js';
6
+ import { Wait } from './wait.js';
5
7
 
6
- export {Mixin, MutationObserverElement, MainDomElement, BreakPoints};
8
+ export {
9
+ Mixin,
10
+ MutationObserverElement,
11
+ MainDomElement,
12
+ BreakPoints,
13
+ Wait,
14
+ NavElement};
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @function NavElement
3
+ * @param {Class} superClass - LitElement or child class.
4
+ * @description Adds utilities for navigation to a LitElement
5
+ *
6
+ * @returns {Class} LitElement with Nav utilities attached
7
+ */
8
+ const NavElement = (superClass) => class extends superClass {
9
+
10
+ constructor() {
11
+ super();
12
+ this.navItems = [];
13
+ this.maxDepth = 2;
14
+ }
15
+
16
+ /**
17
+ * @method parseChildren
18
+ * @description Creates a tree-like nav Array structure from element children
19
+ * @param {HTMLCollection} children - Element children (non-shadow)
20
+ * @returns {Array}
21
+ */
22
+ parseNavChildren( children=this.children ){
23
+ if ( !children ) return [];
24
+ children = Array.from(this.children);
25
+ let navItems = children.map((child) => this._makeNavItemTree(child)).filter(navItem => navItem.linkText);
26
+ return navItems;
27
+ }
28
+
29
+ /**
30
+ * @method _makeNavItemTree
31
+ * @private
32
+ * @description Extracts menu item data from DOM Element
33
+ * @param {Element} ele - Element
34
+ * @returns {Object} Formatted object describing the menu item and its children
35
+ */
36
+ _makeNavItemTree(ele){
37
+ let linkText, href, subItems = [], isOpen=false, inlineStyles={};
38
+ if ( ele.tagName === 'LI' && ele.children.length > 0) ele = ele.children[0];
39
+
40
+ if ( ele.tagName === 'A' ) {
41
+ linkText = ele.innerText;
42
+ href = ele.href;
43
+ } else if ( ele.tagName === 'LI' ) {
44
+ linkText = ele.innerText;
45
+ } else if ( ele.tagName === 'OL' || ele.tagName === 'UL' ) {
46
+ linkText = ele.getAttribute('link-text');
47
+ href = ele.getAttribute('href');
48
+
49
+ for (const child of Array.from(ele.children)) {
50
+ let childItem = this._makeNavItemTree(child);
51
+ if ( childItem.linkText ) subItems.push(childItem);
52
+ }
53
+ }
54
+
55
+ if ( linkText ) linkText = linkText.trim();
56
+ return {linkText, href, subItems, isOpen, inlineStyles};
57
+ }
58
+
59
+ /**
60
+ * @method getNavItem
61
+ * @description Retrieves an item from the navItems array.
62
+ * @param {Array} location - Coordinates of the item in the 'navItems' array. i.e. [0, 1, 4].
63
+ * @returns {Object}
64
+ */
65
+ getNavItem(location){
66
+ let accessor = "this.navItems";
67
+ if ( location && location.length > 0) {
68
+ accessor += "[" + location.join("].subItems[") + "]";
69
+ }
70
+ return eval(accessor);
71
+ }
72
+
73
+ /**
74
+ * @method itemHasSubNav
75
+ * @description Utility function for determining if a menu has subitems
76
+ * @param {Object} navItem - A member of the navItems array.
77
+ * @returns {Boolean}
78
+ */
79
+ itemHasSubNav(navItem){
80
+ if ( navItem && navItem.subItems && navItem.subItems.length) return true;
81
+ return false;
82
+ }
83
+
84
+ /**
85
+ * @method clearMobileAnimationStyles
86
+ * @description Removes inline styles on a nav element (used for mobile transition animation)
87
+ * @param {Object} navItem - Member of the this.navItems array
88
+ */
89
+ clearItemInlineStyles(navItem){
90
+ if (
91
+ navItem &&
92
+ navItem.inlineStyles &&
93
+ Object.keys(navItem.inlineStyles).length > 0
94
+ ) {
95
+ navItem.inlineStyles = {};
96
+ this.requestUpdate();
97
+ }
98
+ }
99
+ };
100
+
101
+ export {NavElement};
package/utils/wait.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @function Wait
3
+ * @param {Class} superClass - LitElement or child class.
4
+ * @description Adds wait methods to Lit element
5
+ *
6
+ * @returns {Class} LitElement with Wait methods
7
+ */
8
+ const Wait = (superClass) => class extends superClass {
9
+
10
+ /**
11
+ * @method wait
12
+ * @description Wait for the specified amount of time
13
+ * @param {Number} time - Time to wait (ms)
14
+ * @returns
15
+ */
16
+ async wait(time){
17
+ return new Promise(resolve => {
18
+ setTimeout(resolve, time);
19
+ });
20
+ }
21
+
22
+ /**
23
+ * @method waitForUpdate
24
+ * @description Requests and waits for Lit update.
25
+ */
26
+ async waitForUpdate(){
27
+ this.requestUpdate();
28
+ await this.updateComplete;
29
+ }
30
+
31
+ /**
32
+ * @method waitForFrames
33
+ * @description Wait for specified number of animation frames
34
+ * @param {Number} ct Number of frames
35
+ */
36
+ async waitForFrames(ct=1) {
37
+ for (let i = 0; i < ct; i++) {
38
+ await new Promise(resolve => {
39
+ requestAnimationFrame(resolve);
40
+ });
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @method waitForAnimation
46
+ * @description Wait for animation time designated by element
47
+ * @returns {Promise}
48
+ */
49
+ async waitForAnimation() {
50
+
51
+ let animationDuration = 0;
52
+ if ( this.animationDuration ) {
53
+ animationDuration = this.animationDuration;
54
+ } else if (this._animationDuration) {
55
+ animationDuration = this._animationDuration;
56
+ } else {
57
+ console.warn("animationDuration property not set!");
58
+ }
59
+
60
+ return new Promise(resolve => {
61
+ setTimeout(resolve, animationDuration);
62
+ });
63
+ }
64
+ };
65
+ export {Wait};