@momentum-design/components 0.134.21 → 0.134.22
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/dist/browser/index.js +481 -458
- package/dist/browser/index.js.map +4 -4
- package/dist/components/datepicker/datepicker.component.d.ts +5 -0
- package/dist/components/datepicker/datepicker.component.js +96 -26
- package/dist/components/menubar/menubar.component.js +2 -1
- package/dist/components/menupopover/menupopover.component.d.ts +1 -1
- package/dist/components/menupopover/menupopover.component.js +4 -4
- package/dist/components/navmenuitem/navmenuitem.component.d.ts +32 -2
- package/dist/components/navmenuitem/navmenuitem.component.js +146 -9
- package/dist/components/navmenuitem/navmenuitem.constants.d.ts +1 -0
- package/dist/components/navmenuitem/navmenuitem.constants.js +1 -0
- package/dist/components/navmenuitem/navmenuitem.styles.js +16 -5
- package/dist/components/sidenavigation/sidenavigation.component.d.ts +43 -3
- package/dist/components/sidenavigation/sidenavigation.component.js +240 -8
- package/dist/components/sidenavigation/sidenavigation.constants.d.ts +6 -1
- package/dist/components/sidenavigation/sidenavigation.constants.js +6 -1
- package/dist/components/sidenavigation/sidenavigation.context.d.ts +6 -1
- package/dist/components/sidenavigation/sidenavigation.context.js +36 -11
- package/dist/components/sidenavigation/sidenavigation.types.d.ts +3 -2
- package/dist/components/sidenavigation/sidenavigationbase.d.ts +8 -0
- package/dist/components/sidenavigation/sidenavigationbase.js +7 -0
- package/dist/custom-elements.json +153 -10
- package/dist/models/provider/provider.component.d.ts +2 -2
- package/dist/models/provider/provider.component.js +13 -0
- package/dist/react/navmenuitem/index.d.ts +4 -2
- package/dist/react/navmenuitem/index.js +3 -1
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CSSResult, PropertyValues } from 'lit';
|
|
2
|
-
import {
|
|
3
|
-
import type { SideNavigationVariant } from './sidenavigation.types';
|
|
2
|
+
import type { SideNavigationSubmenuType, SideNavigationVariant } from './sidenavigation.types';
|
|
4
3
|
import SideNavigationContext from './sidenavigation.context';
|
|
4
|
+
import SideNavigationBase from './sidenavigationbase';
|
|
5
|
+
declare const SideNavigation_base: import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/KeyToActionMixin").KeyToActionInterface> & typeof SideNavigationBase;
|
|
5
6
|
/**
|
|
6
7
|
* The side navigation is a vertically stacked navigation landmark, typically used as a persistent or collapsible sidebar. It supports five layout variants, toggleable expand/collapse behaviour, an optional brand logo, an optional footer text, and acts as a context provider for nested `mdc-menubar` and `mdc-navmenuitem` elements.
|
|
7
8
|
*
|
|
@@ -56,7 +57,7 @@ import SideNavigationContext from './sidenavigation.context';
|
|
|
56
57
|
* @cssproperty --mdc-sidenavigation-bottom-padding - padding for the bottom of the scrollable section
|
|
57
58
|
* @cssproperty --mdc-sidenavigation-vertical-divider-button-z-index - z-index of the vertical divider button
|
|
58
59
|
*/
|
|
59
|
-
declare class SideNavigation extends
|
|
60
|
+
declare class SideNavigation extends SideNavigation_base {
|
|
60
61
|
/**
|
|
61
62
|
* Five variants of the sideNavigation
|
|
62
63
|
* - **fixed-collapsed**: Shows icons without labels and has fixed width, 4.5rem.
|
|
@@ -95,6 +96,17 @@ declare class SideNavigation extends Provider<SideNavigationContext> {
|
|
|
95
96
|
* @default false
|
|
96
97
|
*/
|
|
97
98
|
hideFixedSectionDivider: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Determines how sibling submenus are displayed. There are 2 types of submenus supported:
|
|
101
|
+
*
|
|
102
|
+
* - `flyout`: Submenus are shown with `mdc-menupopover`.
|
|
103
|
+
* - `dropdown`: A parent `mdc-navmenuitem` can render its submenu inside a sibling
|
|
104
|
+
* `div[data-trigger="<navmenuitem-id>"]` while the sidenavigation is expanded. In collapsed mode,
|
|
105
|
+
* dropdown submenus are converted to flyout menus.
|
|
106
|
+
*
|
|
107
|
+
* @default flyout
|
|
108
|
+
*/
|
|
109
|
+
submenuType: SideNavigationSubmenuType;
|
|
98
110
|
constructor();
|
|
99
111
|
connectedCallback(): void;
|
|
100
112
|
disconnectedCallback(): void;
|
|
@@ -154,6 +166,34 @@ declare class SideNavigation extends Provider<SideNavigationContext> {
|
|
|
154
166
|
* @internal
|
|
155
167
|
*/
|
|
156
168
|
private get navMenuItems();
|
|
169
|
+
/**
|
|
170
|
+
* Closes all open dropdown containers and resets their parent navmenuitems.
|
|
171
|
+
* @internal
|
|
172
|
+
*/
|
|
173
|
+
private closeAllDropdowns;
|
|
174
|
+
/**
|
|
175
|
+
* Hides all div[data-trigger] elements within the sidenavigation.
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
178
|
+
private hideAllDropdownContainers;
|
|
179
|
+
/**
|
|
180
|
+
* When collapsed with dropdown submenus, converts div[data-trigger] children into dynamically
|
|
181
|
+
* created menupopover elements for flyout behavior.
|
|
182
|
+
* @internal
|
|
183
|
+
*/
|
|
184
|
+
private convertDropdownsToFlyouts;
|
|
185
|
+
/**
|
|
186
|
+
* When expanding with dropdown submenus, converts dynamically created menupopover elements
|
|
187
|
+
* back to div[data-trigger] containers.
|
|
188
|
+
* @internal
|
|
189
|
+
*/
|
|
190
|
+
private convertFlyoutsToDropdowns;
|
|
191
|
+
/**
|
|
192
|
+
* Handles Escape key inside dropdown containers to close the dropdown
|
|
193
|
+
* and return focus to the parent trigger navmenuitem.
|
|
194
|
+
* @internal
|
|
195
|
+
*/
|
|
196
|
+
private handleDropdownKeyDown;
|
|
157
197
|
/**
|
|
158
198
|
* Sets the variant attribute for the sideNavigation component.
|
|
159
199
|
* If the provided variant is not included in the variant,
|
|
@@ -9,15 +9,17 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { html, nothing } from 'lit';
|
|
11
11
|
import { property } from 'lit/decorators.js';
|
|
12
|
-
import { Component
|
|
12
|
+
import { Component } from '../../models';
|
|
13
13
|
import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
|
|
14
14
|
import { TAG_NAME as NAVMENUITEM_TAGNAME } from '../navmenuitem/navmenuitem.constants';
|
|
15
|
+
import { TAG_NAME as MENUPOPOVER_TAGNAME } from '../menupopover/menupopover.constants';
|
|
15
16
|
import { DIRECTIONS, DIVIDER_VARIANT, DIVIDER_ORIENTATION } from '../divider/divider.constants';
|
|
16
17
|
import { ROLE } from '../../utils/roles';
|
|
17
|
-
import {
|
|
18
|
-
import { DEFAULTS, VARIANTS } from './sidenavigation.constants';
|
|
18
|
+
import { ACTIONS, KeyToActionMixin } from '../../utils/mixins/KeyToActionMixin';
|
|
19
|
+
import { DEFAULTS, SUBMENU_TYPES, VARIANTS } from './sidenavigation.constants';
|
|
19
20
|
import SideNavigationContext from './sidenavigation.context';
|
|
20
21
|
import styles from './sidenavigation.styles';
|
|
22
|
+
import SideNavigationBase from './sidenavigationbase';
|
|
21
23
|
/**
|
|
22
24
|
* @tagname mdc-sidenavigation
|
|
23
25
|
*
|
|
@@ -58,11 +60,11 @@ import styles from './sidenavigation.styles';
|
|
|
58
60
|
* @cssproperty --mdc-sidenavigation-bottom-padding - padding for the bottom of the scrollable section
|
|
59
61
|
* @cssproperty --mdc-sidenavigation-vertical-divider-button-z-index - z-index of the vertical divider button
|
|
60
62
|
*/
|
|
61
|
-
class SideNavigation extends
|
|
63
|
+
class SideNavigation extends KeyToActionMixin(SideNavigationBase) {
|
|
62
64
|
constructor() {
|
|
63
65
|
super({
|
|
64
66
|
context: SideNavigationContext.context,
|
|
65
|
-
initialValue: new SideNavigationContext(DEFAULTS.VARIANT, true),
|
|
67
|
+
initialValue: new SideNavigationContext(DEFAULTS.VARIANT, true, DEFAULTS.SUBMENU_TYPE),
|
|
66
68
|
});
|
|
67
69
|
/**
|
|
68
70
|
* Five variants of the sideNavigation
|
|
@@ -86,6 +88,17 @@ class SideNavigation extends Provider {
|
|
|
86
88
|
* @default false
|
|
87
89
|
*/
|
|
88
90
|
this.hideFixedSectionDivider = false;
|
|
91
|
+
/**
|
|
92
|
+
* Determines how sibling submenus are displayed. There are 2 types of submenus supported:
|
|
93
|
+
*
|
|
94
|
+
* - `flyout`: Submenus are shown with `mdc-menupopover`.
|
|
95
|
+
* - `dropdown`: A parent `mdc-navmenuitem` can render its submenu inside a sibling
|
|
96
|
+
* `div[data-trigger="<navmenuitem-id>"]` while the sidenavigation is expanded. In collapsed mode,
|
|
97
|
+
* dropdown submenus are converted to flyout menus.
|
|
98
|
+
*
|
|
99
|
+
* @default flyout
|
|
100
|
+
*/
|
|
101
|
+
this.submenuType = DEFAULTS.SUBMENU_TYPE;
|
|
89
102
|
this.isHovered = false;
|
|
90
103
|
this.isFocused = false;
|
|
91
104
|
/** @internal */
|
|
@@ -167,6 +180,7 @@ class SideNavigation extends Provider {
|
|
|
167
180
|
*/
|
|
168
181
|
this.findNav = (navs, navId) => navs.find(nav => nav.navId === navId);
|
|
169
182
|
this.addEventListener('activechange', this.handleNestedNavMenuItemActiveChange.bind(this));
|
|
183
|
+
this.addEventListener('keydown', this.handleDropdownKeyDown.bind(this));
|
|
170
184
|
}
|
|
171
185
|
connectedCallback() {
|
|
172
186
|
super.connectedCallback();
|
|
@@ -206,9 +220,28 @@ class SideNavigation extends Provider {
|
|
|
206
220
|
default:
|
|
207
221
|
}
|
|
208
222
|
}
|
|
209
|
-
if (changedProperties.has('variant') || changedProperties.has('expanded')) {
|
|
223
|
+
if (changedProperties.has('variant') || changedProperties.has('expanded') || changedProperties.has('submenuType')) {
|
|
210
224
|
this.updateContext();
|
|
211
225
|
}
|
|
226
|
+
// When collapsing or when dropdown submenus are turned off, close all dropdown containers
|
|
227
|
+
if (changedProperties.has('expanded') || changedProperties.has('submenuType')) {
|
|
228
|
+
if (!this.expanded || this.submenuType !== SUBMENU_TYPES.DROPDOWN) {
|
|
229
|
+
this.closeAllDropdowns();
|
|
230
|
+
}
|
|
231
|
+
// Handle dropdown-to-flyout conversion
|
|
232
|
+
if (this.submenuType === SUBMENU_TYPES.DROPDOWN) {
|
|
233
|
+
if (!this.expanded) {
|
|
234
|
+
this.convertDropdownsToFlyouts();
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
this.convertFlyoutsToDropdowns();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
// If dropdown submenus are turned off, clean up any dynamic menupopover elements
|
|
242
|
+
this.convertFlyoutsToDropdowns();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
212
245
|
}
|
|
213
246
|
firstUpdated(changedProperties) {
|
|
214
247
|
super.firstUpdated(changedProperties);
|
|
@@ -217,6 +250,12 @@ class SideNavigation extends Provider {
|
|
|
217
250
|
this.expanded = true;
|
|
218
251
|
this.updateContext();
|
|
219
252
|
}
|
|
253
|
+
// Hide all dropdown containers by default
|
|
254
|
+
this.hideAllDropdownContainers();
|
|
255
|
+
// If initially collapsed with dropdown submenus, convert to flyouts
|
|
256
|
+
if (this.submenuType === SUBMENU_TYPES.DROPDOWN && !this.expanded) {
|
|
257
|
+
this.convertDropdownsToFlyouts();
|
|
258
|
+
}
|
|
220
259
|
}
|
|
221
260
|
/**
|
|
222
261
|
* Update all observing components of this
|
|
@@ -225,9 +264,12 @@ class SideNavigation extends Provider {
|
|
|
225
264
|
* Is called on every re-render, see Provider class
|
|
226
265
|
*/
|
|
227
266
|
updateContext() {
|
|
228
|
-
if (this.context.value.variant !== this.variant ||
|
|
267
|
+
if (this.context.value.variant !== this.variant ||
|
|
268
|
+
this.context.value.expanded !== this.expanded ||
|
|
269
|
+
this.context.value.submenuType !== this.submenuType) {
|
|
229
270
|
this.context.value.variant = this.variant;
|
|
230
271
|
this.context.value.expanded = this.expanded;
|
|
272
|
+
this.context.value.submenuType = this.submenuType;
|
|
231
273
|
this.context.updateObservers();
|
|
232
274
|
}
|
|
233
275
|
}
|
|
@@ -238,6 +280,192 @@ class SideNavigation extends Provider {
|
|
|
238
280
|
get navMenuItems() {
|
|
239
281
|
return Array.from(this.querySelectorAll(`${NAVMENUITEM_TAGNAME}:not([disabled])`));
|
|
240
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Closes all open dropdown containers and resets their parent navmenuitems.
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
closeAllDropdowns() {
|
|
288
|
+
this.navMenuItems.filter(item => this.context.value.isDropDownParent(item)).forEach(item => item.closeDropdown());
|
|
289
|
+
// Also hide all dropdown containers
|
|
290
|
+
this.hideAllDropdownContainers();
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Hides all div[data-trigger] elements within the sidenavigation.
|
|
294
|
+
* @internal
|
|
295
|
+
*/
|
|
296
|
+
hideAllDropdownContainers() {
|
|
297
|
+
const containers = this.querySelectorAll('div[data-trigger]');
|
|
298
|
+
containers.forEach(container => {
|
|
299
|
+
Object.assign(container.style, {
|
|
300
|
+
display: 'none',
|
|
301
|
+
flexDirection: 'column',
|
|
302
|
+
gap: '0.25rem',
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* When collapsed with dropdown submenus, converts div[data-trigger] children into dynamically
|
|
308
|
+
* created menupopover elements for flyout behavior.
|
|
309
|
+
* @internal
|
|
310
|
+
*/
|
|
311
|
+
convertDropdownsToFlyouts() {
|
|
312
|
+
const containers = this.querySelectorAll('div[data-trigger]');
|
|
313
|
+
containers.forEach(container => {
|
|
314
|
+
var _a;
|
|
315
|
+
const triggerId = container.getAttribute('data-trigger');
|
|
316
|
+
if (!triggerId)
|
|
317
|
+
return;
|
|
318
|
+
// Check if a dynamic menupopover already exists for this trigger
|
|
319
|
+
const existingPopover = (_a = container.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector(`${MENUPOPOVER_TAGNAME}[triggerid="${CSS.escape(triggerId)}"][data-dynamic-popover]`);
|
|
320
|
+
if (existingPopover)
|
|
321
|
+
return;
|
|
322
|
+
// Create menupopover
|
|
323
|
+
const popover = document.createElement(MENUPOPOVER_TAGNAME);
|
|
324
|
+
popover.setAttribute('triggerid', triggerId);
|
|
325
|
+
popover.setAttribute('data-dynamic-popover', '');
|
|
326
|
+
popover.setAttribute('placement', 'right-start');
|
|
327
|
+
// Move children from div to menupopover
|
|
328
|
+
while (container.firstChild) {
|
|
329
|
+
popover.appendChild(container.firstChild);
|
|
330
|
+
}
|
|
331
|
+
// Insert menupopover after the div
|
|
332
|
+
container.after(popover);
|
|
333
|
+
// Hide the empty div
|
|
334
|
+
Object.assign(container.style, { display: 'none' });
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* When expanding with dropdown submenus, converts dynamically created menupopover elements
|
|
339
|
+
* back to div[data-trigger] containers.
|
|
340
|
+
* @internal
|
|
341
|
+
*/
|
|
342
|
+
convertFlyoutsToDropdowns() {
|
|
343
|
+
const dynamicPopovers = this.querySelectorAll(`${MENUPOPOVER_TAGNAME}[data-dynamic-popover]`);
|
|
344
|
+
dynamicPopovers.forEach(popover => {
|
|
345
|
+
const triggerId = popover.getAttribute('triggerid');
|
|
346
|
+
if (!triggerId)
|
|
347
|
+
return;
|
|
348
|
+
// Find the corresponding div[data-trigger]
|
|
349
|
+
const container = this.querySelector(`div[data-trigger="${CSS.escape(triggerId)}"]`);
|
|
350
|
+
if (!container)
|
|
351
|
+
return;
|
|
352
|
+
// Move children back from menupopover to div
|
|
353
|
+
while (popover.firstChild) {
|
|
354
|
+
container.appendChild(popover.firstChild);
|
|
355
|
+
}
|
|
356
|
+
// Remove the dynamic menupopover
|
|
357
|
+
popover.remove();
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Handles Escape key inside dropdown containers to close the dropdown
|
|
362
|
+
* and return focus to the parent trigger navmenuitem.
|
|
363
|
+
* @internal
|
|
364
|
+
*/
|
|
365
|
+
handleDropdownKeyDown(event) {
|
|
366
|
+
var _a, _b;
|
|
367
|
+
if (this.submenuType !== SUBMENU_TYPES.DROPDOWN || !this.expanded)
|
|
368
|
+
return;
|
|
369
|
+
const target = event.target;
|
|
370
|
+
const isNavMenuItem = target.tagName.toLowerCase() === NAVMENUITEM_TAGNAME;
|
|
371
|
+
if (!isNavMenuItem)
|
|
372
|
+
return;
|
|
373
|
+
const targetItem = target;
|
|
374
|
+
const action = this.getActionForKeyEvent(event);
|
|
375
|
+
if (action === ACTIONS.ESCAPE) {
|
|
376
|
+
const dropdownContainer = target.closest('div[data-trigger]');
|
|
377
|
+
if (!dropdownContainer)
|
|
378
|
+
return;
|
|
379
|
+
const triggerId = dropdownContainer.getAttribute('data-trigger');
|
|
380
|
+
if (!triggerId)
|
|
381
|
+
return;
|
|
382
|
+
const triggerItem = this.querySelector(`${NAVMENUITEM_TAGNAME}#${CSS.escape(triggerId)}`);
|
|
383
|
+
if (triggerItem) {
|
|
384
|
+
triggerItem.closeDropdown();
|
|
385
|
+
triggerItem.focus();
|
|
386
|
+
event.stopPropagation();
|
|
387
|
+
}
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const context = this.context.value;
|
|
391
|
+
const isInsideDropdown = targetItem.hasAttribute('in-dropdown-container');
|
|
392
|
+
const isOpen = targetItem.getAttribute('aria-expanded') === 'true';
|
|
393
|
+
const getDropdownContainer = (item) => { var _a, _b; return (_b = (_a = item.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector(`div[data-trigger="${item.id}"]`)) !== null && _b !== void 0 ? _b : null; };
|
|
394
|
+
const getFirstDropdownItem = (container) => container === null || container === void 0 ? void 0 : container.querySelector(`${NAVMENUITEM_TAGNAME}:not([disabled])`);
|
|
395
|
+
if (isInsideDropdown) {
|
|
396
|
+
const dropdownContainer = target.closest('div[data-trigger]');
|
|
397
|
+
if (!dropdownContainer)
|
|
398
|
+
return;
|
|
399
|
+
const children = Array.from(dropdownContainer.querySelectorAll(`${NAVMENUITEM_TAGNAME}:not([disabled])`));
|
|
400
|
+
const currentIndex = children.indexOf(targetItem);
|
|
401
|
+
if (currentIndex === -1)
|
|
402
|
+
return;
|
|
403
|
+
switch (action) {
|
|
404
|
+
case ACTIONS.DOWN: {
|
|
405
|
+
// Arrow Down: move focus to the next child navmenuitem in the dropdown container, if exists. If on the last child, move focus back to the first child.
|
|
406
|
+
const idx = currentIndex + 1 < children.length ? currentIndex + 1 : 0;
|
|
407
|
+
children[idx].focus();
|
|
408
|
+
event.preventDefault();
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
case ACTIONS.UP: {
|
|
412
|
+
// Arrow Up: move focus to the previous child navmenuitem in the dropdown container, if exists. If on the first child, move focus to the last child.
|
|
413
|
+
const idx = currentIndex > 0 ? currentIndex - 1 : children.length - 1;
|
|
414
|
+
children[idx].focus();
|
|
415
|
+
event.preventDefault();
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
case ACTIONS.RIGHT: {
|
|
419
|
+
// Arrow Right: move focus to the next parent-level navmenuitem in the main list (if exists). If this parent-level navmenuitem has a dropdown, then open the dropdown and move focus to the first child navmenuitem in the dropdown container.
|
|
420
|
+
const outerItems = this.navMenuItems.filter(item => !item.hasAttribute('in-dropdown-container'));
|
|
421
|
+
const triggerId = dropdownContainer.getAttribute('data-trigger');
|
|
422
|
+
const triggerIndex = outerItems.findIndex(item => item.id === triggerId);
|
|
423
|
+
if (triggerIndex !== -1 && triggerIndex + 1 < outerItems.length) {
|
|
424
|
+
const nextItem = outerItems[triggerIndex + 1];
|
|
425
|
+
nextItem.focus();
|
|
426
|
+
if (context.isDropDownParent(nextItem)) {
|
|
427
|
+
nextItem.openDropdown();
|
|
428
|
+
const nextDropdownContainer = getDropdownContainer(nextItem);
|
|
429
|
+
(_a = getFirstDropdownItem(nextDropdownContainer)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case ACTIONS.LEFT: {
|
|
435
|
+
// Arrow Left: move focus to the previous parent-level navmenuitem in the main list (if exists). If this parent-level navmenuitem has a dropdown, then open the dropdown and move focus to the first child navmenuitem in the dropdown container.
|
|
436
|
+
const outerItems = this.navMenuItems.filter(item => !item.hasAttribute('in-dropdown-container'));
|
|
437
|
+
const triggerId = dropdownContainer.getAttribute('data-trigger');
|
|
438
|
+
const triggerIndex = outerItems.findIndex(item => item.id === triggerId);
|
|
439
|
+
if (triggerIndex > 0) {
|
|
440
|
+
const previousItem = outerItems[triggerIndex - 1];
|
|
441
|
+
previousItem.focus();
|
|
442
|
+
if (context.isDropDownParent(previousItem)) {
|
|
443
|
+
previousItem.openDropdown();
|
|
444
|
+
const previousDropdownContainer = getDropdownContainer(previousItem);
|
|
445
|
+
(_b = getFirstDropdownItem(previousDropdownContainer)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
default:
|
|
451
|
+
event.preventDefault();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
// if the dropdown is closed and ArrowRight is pressed on the parent navmenuitem, open the dropdown and move focus to the first child navmenuitem in the dropdown container
|
|
455
|
+
if (action === ACTIONS.RIGHT) {
|
|
456
|
+
const dropdownContainer = getDropdownContainer(targetItem);
|
|
457
|
+
if (dropdownContainer) {
|
|
458
|
+
if (!isOpen) {
|
|
459
|
+
targetItem.openDropdown();
|
|
460
|
+
}
|
|
461
|
+
const firstChild = getFirstDropdownItem(dropdownContainer);
|
|
462
|
+
if (firstChild) {
|
|
463
|
+
firstChild.focus();
|
|
464
|
+
event.preventDefault();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
241
469
|
/**
|
|
242
470
|
* Sets the variant attribute for the sideNavigation component.
|
|
243
471
|
* If the provided variant is not included in the variant,
|
|
@@ -267,7 +495,7 @@ class SideNavigation extends Provider {
|
|
|
267
495
|
}
|
|
268
496
|
preventScrollOnSpace(event) {
|
|
269
497
|
// Prevent default space key behavior to avoid scrolling the page
|
|
270
|
-
if (event
|
|
498
|
+
if (this.getActionForKeyEvent(event) === ACTIONS.SPACE) {
|
|
271
499
|
event.preventDefault();
|
|
272
500
|
}
|
|
273
501
|
}
|
|
@@ -352,4 +580,8 @@ __decorate([
|
|
|
352
580
|
property({ type: Boolean, reflect: true, attribute: 'hide-fixed-section-divider' }),
|
|
353
581
|
__metadata("design:type", Boolean)
|
|
354
582
|
], SideNavigation.prototype, "hideFixedSectionDivider", void 0);
|
|
583
|
+
__decorate([
|
|
584
|
+
property({ type: String, reflect: true, attribute: 'submenu-type' }),
|
|
585
|
+
__metadata("design:type", String)
|
|
586
|
+
], SideNavigation.prototype, "submenuType", void 0);
|
|
355
587
|
export default SideNavigation;
|
|
@@ -6,7 +6,12 @@ declare const VARIANTS: {
|
|
|
6
6
|
FLEXIBLE_ON_HOVER: string;
|
|
7
7
|
HIDDEN: string;
|
|
8
8
|
};
|
|
9
|
+
declare const SUBMENU_TYPES: {
|
|
10
|
+
FLYOUT: string;
|
|
11
|
+
DROPDOWN: string;
|
|
12
|
+
};
|
|
9
13
|
declare const DEFAULTS: {
|
|
10
14
|
readonly VARIANT: string;
|
|
15
|
+
readonly SUBMENU_TYPE: string;
|
|
11
16
|
};
|
|
12
|
-
export { DEFAULTS, VARIANTS, TAG_NAME };
|
|
17
|
+
export { DEFAULTS, VARIANTS, SUBMENU_TYPES, TAG_NAME };
|
|
@@ -7,7 +7,12 @@ const VARIANTS = {
|
|
|
7
7
|
FLEXIBLE_ON_HOVER: 'flexible-on-hover',
|
|
8
8
|
HIDDEN: 'hidden',
|
|
9
9
|
};
|
|
10
|
+
const SUBMENU_TYPES = {
|
|
11
|
+
FLYOUT: 'flyout',
|
|
12
|
+
DROPDOWN: 'dropdown',
|
|
13
|
+
};
|
|
10
14
|
const DEFAULTS = {
|
|
11
15
|
VARIANT: VARIANTS.FLEXIBLE,
|
|
16
|
+
SUBMENU_TYPE: SUBMENU_TYPES.FLYOUT,
|
|
12
17
|
};
|
|
13
|
-
export { DEFAULTS, VARIANTS, TAG_NAME };
|
|
18
|
+
export { DEFAULTS, VARIANTS, SUBMENU_TYPES, TAG_NAME };
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import type NavMenuItem from '../navmenuitem/navmenuitem.component';
|
|
2
|
+
import type { SideNavigationSubmenuType } from './sidenavigation.types';
|
|
2
3
|
declare class SideNavigationContext {
|
|
3
4
|
variant?: string;
|
|
4
5
|
expanded?: boolean;
|
|
6
|
+
submenuType?: SideNavigationSubmenuType;
|
|
5
7
|
private currentActiveNavMenuItem?;
|
|
6
8
|
static context: {
|
|
7
9
|
__context__: SideNavigationContext;
|
|
8
10
|
};
|
|
9
|
-
constructor(defaultVariant?: string, defaultExpanded?: boolean);
|
|
11
|
+
constructor(defaultVariant?: string, defaultExpanded?: boolean, defaultSubmenuType?: SideNavigationSubmenuType);
|
|
12
|
+
get isDropdownSubmenuType(): boolean;
|
|
10
13
|
hasSiblingWithTriggerId(navMenuItem: NavMenuItem | undefined): boolean;
|
|
14
|
+
isDropDownParent(navMenuItem: NavMenuItem | undefined): boolean;
|
|
11
15
|
private getParentNavMenuItems;
|
|
16
|
+
private getTriggerElement;
|
|
12
17
|
setCurrentActiveNavMenuItem(navMenuItem: NavMenuItem | undefined): void;
|
|
13
18
|
}
|
|
14
19
|
export default SideNavigationContext;
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
import { createContext } from '@lit/context';
|
|
3
3
|
import { TAG_NAME as MENUPOPOVER_TAGNAME } from '../menupopover/menupopover.constants';
|
|
4
4
|
import { TAG_NAME as NAVMENUITEM_TAGNAME } from '../navmenuitem/navmenuitem.constants';
|
|
5
|
-
import { TAG_NAME } from './sidenavigation.constants';
|
|
5
|
+
import { SUBMENU_TYPES, TAG_NAME } from './sidenavigation.constants';
|
|
6
6
|
class SideNavigationContext {
|
|
7
|
-
constructor(defaultVariant, defaultExpanded) {
|
|
7
|
+
constructor(defaultVariant, defaultExpanded, defaultSubmenuType) {
|
|
8
8
|
this.variant = defaultVariant;
|
|
9
9
|
this.expanded = defaultExpanded;
|
|
10
|
+
this.submenuType = defaultSubmenuType;
|
|
11
|
+
}
|
|
12
|
+
get isDropdownSubmenuType() {
|
|
13
|
+
return this.submenuType === SUBMENU_TYPES.DROPDOWN;
|
|
10
14
|
}
|
|
11
15
|
hasSiblingWithTriggerId(navMenuItem) {
|
|
12
16
|
var _a, _b;
|
|
@@ -18,21 +22,33 @@ class SideNavigationContext {
|
|
|
18
22
|
sibling.tagName.toLowerCase() === MENUPOPOVER_TAGNAME &&
|
|
19
23
|
sibling.getAttribute('triggerid') === id);
|
|
20
24
|
}
|
|
25
|
+
isDropDownParent(navMenuItem) {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
const id = navMenuItem === null || navMenuItem === void 0 ? void 0 : navMenuItem.getAttribute('id');
|
|
28
|
+
if (!id)
|
|
29
|
+
return false;
|
|
30
|
+
const siblings = Array.from((_b = (_a = navMenuItem === null || navMenuItem === void 0 ? void 0 : navMenuItem.parentElement) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : []);
|
|
31
|
+
return siblings.some(sibling => sibling !== navMenuItem && sibling.matches(`div[data-trigger="${id}"]`));
|
|
32
|
+
}
|
|
21
33
|
getParentNavMenuItems(navMenuItem) {
|
|
34
|
+
var _a;
|
|
22
35
|
if (!navMenuItem)
|
|
23
36
|
return [];
|
|
24
37
|
const parents = [];
|
|
25
38
|
let current = navMenuItem;
|
|
26
39
|
while (current) {
|
|
27
|
-
|
|
40
|
+
let triggeringNavMenuItem = null;
|
|
41
|
+
// A navmenuitem is either inside a menupopover (flyout) or a div[data-trigger] (dropdown), never both.
|
|
28
42
|
const popover = current === null || current === void 0 ? void 0 : current.closest(MENUPOPOVER_TAGNAME);
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
if (popover) {
|
|
44
|
+
triggeringNavMenuItem = this.getTriggerElement(popover, 'triggerid');
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const dropdownContainer = (_a = current === null || current === void 0 ? void 0 : current.parentElement) === null || _a === void 0 ? void 0 : _a.closest('div[data-trigger]');
|
|
48
|
+
if (dropdownContainer) {
|
|
49
|
+
triggeringNavMenuItem = this.getTriggerElement(dropdownContainer, 'data-trigger');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
36
52
|
if (triggeringNavMenuItem && triggeringNavMenuItem.tagName.toLowerCase() === NAVMENUITEM_TAGNAME) {
|
|
37
53
|
parents.push(triggeringNavMenuItem);
|
|
38
54
|
current = triggeringNavMenuItem;
|
|
@@ -43,10 +59,19 @@ class SideNavigationContext {
|
|
|
43
59
|
}
|
|
44
60
|
return parents;
|
|
45
61
|
}
|
|
62
|
+
getTriggerElement(container, attribute) {
|
|
63
|
+
const triggerId = container.getAttribute(attribute);
|
|
64
|
+
if (!triggerId)
|
|
65
|
+
return null;
|
|
66
|
+
return document.getElementById(triggerId);
|
|
67
|
+
}
|
|
46
68
|
setCurrentActiveNavMenuItem(navMenuItem) {
|
|
47
69
|
var _a;
|
|
48
70
|
const isSameItem = ((_a = this.currentActiveNavMenuItem) === null || _a === void 0 ? void 0 : _a.navId) === (navMenuItem === null || navMenuItem === void 0 ? void 0 : navMenuItem.navId);
|
|
49
|
-
const shouldSkip = (navMenuItem === null || navMenuItem === void 0 ? void 0 : navMenuItem.cannotActivate) ||
|
|
71
|
+
const shouldSkip = (navMenuItem === null || navMenuItem === void 0 ? void 0 : navMenuItem.cannotActivate) ||
|
|
72
|
+
this.hasSiblingWithTriggerId(navMenuItem) ||
|
|
73
|
+
(this.isDropdownSubmenuType && this.expanded && this.isDropDownParent(navMenuItem)) ||
|
|
74
|
+
(navMenuItem === null || navMenuItem === void 0 ? void 0 : navMenuItem.softDisabled);
|
|
50
75
|
if (isSameItem || shouldSkip)
|
|
51
76
|
return;
|
|
52
77
|
// Clean up previous active item
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { TypedCustomEvent, ValueOf } from '../../utils/types';
|
|
2
2
|
import type SideNavigation from './sidenavigation.component';
|
|
3
|
-
import { VARIANTS } from './sidenavigation.constants';
|
|
3
|
+
import { SUBMENU_TYPES, VARIANTS } from './sidenavigation.constants';
|
|
4
4
|
type SideNavigationVariant = ValueOf<typeof VARIANTS>;
|
|
5
|
+
type SideNavigationSubmenuType = ValueOf<typeof SUBMENU_TYPES>;
|
|
5
6
|
type SideNavigationActiveChangeEvent = TypedCustomEvent<SideNavigation, {
|
|
6
7
|
navId: string;
|
|
7
8
|
active: boolean;
|
|
@@ -13,4 +14,4 @@ interface Events {
|
|
|
13
14
|
onActiveChangeEvent: SideNavigationActiveChangeEvent;
|
|
14
15
|
onToggleEvent: SideNavigationToggleEvent;
|
|
15
16
|
}
|
|
16
|
-
export type { SideNavigationVariant, Events };
|
|
17
|
+
export type { SideNavigationVariant, SideNavigationSubmenuType, Events };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Provider } from '../../models';
|
|
2
|
+
import SideNavigationContext from './sidenavigation.context';
|
|
3
|
+
/**
|
|
4
|
+
* The `SideNavigationBase` class serves as a foundational component for the side navigation feature. It extends the `Provider` class, which allows it to manage and provide the `SideNavigationContext` to its child components. Since `KeyToActionMixin` cannot be directly applied to a `Provider` with context, this base class is created as an intermediate layer. The `SideNavigation` component then extends this class and wraps it with `KeyToActionMixin` to gain mixin functionality while maintaining context management.
|
|
5
|
+
*/
|
|
6
|
+
declare class SideNavigationBase extends Provider<SideNavigationContext> {
|
|
7
|
+
}
|
|
8
|
+
export default SideNavigationBase;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Provider } from '../../models';
|
|
2
|
+
/**
|
|
3
|
+
* The `SideNavigationBase` class serves as a foundational component for the side navigation feature. It extends the `Provider` class, which allows it to manage and provide the `SideNavigationContext` to its child components. Since `KeyToActionMixin` cannot be directly applied to a `Provider` with context, this base class is created as an intermediate layer. The `SideNavigation` component then extends this class and wraps it with `KeyToActionMixin` to gain mixin functionality while maintaining context management.
|
|
4
|
+
*/
|
|
5
|
+
class SideNavigationBase extends Provider {
|
|
6
|
+
}
|
|
7
|
+
export default SideNavigationBase;
|