@momentum-design/components 0.134.21 → 0.135.0
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 +582 -477
- 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/formfieldwrapper/formfieldwrapper.component.js +7 -3
- package/dist/components/formfieldwrapper/formfieldwrapper.constants.js +2 -7
- package/dist/components/formfieldwrapper/formfieldwrapper.styles.js +10 -0
- package/dist/components/formfieldwrapper/formfieldwrapper.types.d.ts +2 -2
- package/dist/components/formfieldwrapper/formfieldwrapper.utils.js +2 -11
- package/dist/components/formfieldwrapper/index.d.ts +1 -0
- package/dist/components/formfieldwrapper/index.js +1 -0
- 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/components/statusmessage/index.d.ts +9 -0
- package/dist/components/statusmessage/index.js +8 -0
- package/dist/components/statusmessage/statusmessage.component.d.ts +54 -0
- package/dist/components/statusmessage/statusmessage.component.js +80 -0
- package/dist/components/statusmessage/statusmessage.constants.d.ts +28 -0
- package/dist/components/statusmessage/statusmessage.constants.js +31 -0
- package/dist/components/statusmessage/statusmessage.styles.d.ts +2 -0
- package/dist/components/statusmessage/statusmessage.styles.js +66 -0
- package/dist/components/statusmessage/statusmessage.types.d.ts +5 -0
- package/dist/components/statusmessage/statusmessage.types.js +2 -0
- package/dist/custom-elements.json +283 -10
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/models/provider/provider.component.d.ts +2 -2
- package/dist/models/provider/provider.component.js +13 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/react/navmenuitem/index.d.ts +4 -2
- package/dist/react/navmenuitem/index.js +3 -1
- package/dist/react/statusmessage/index.d.ts +37 -0
- package/dist/react/statusmessage/index.js +32 -0
- package/package.json +1 -1
|
@@ -175,6 +175,11 @@ declare class DatePicker extends DatePicker_base implements AssociatedFormContro
|
|
|
175
175
|
protected willUpdate(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
176
176
|
private parseValueToInternal;
|
|
177
177
|
private internalToValue;
|
|
178
|
+
private getParsedMin;
|
|
179
|
+
private getParsedMax;
|
|
180
|
+
private clampValueToRange;
|
|
181
|
+
private getFieldRange;
|
|
182
|
+
private getNextSpinbuttonValue;
|
|
178
183
|
private syncFormValue;
|
|
179
184
|
private commitValue;
|
|
180
185
|
private notifyValueChange;
|
|
@@ -179,8 +179,8 @@ class DatePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
179
179
|
this.syncFormValue();
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
-
parseValueToInternal() {
|
|
183
|
-
const parts = parseISODate(
|
|
182
|
+
parseValueToInternal(value = this.value) {
|
|
183
|
+
const parts = parseISODate(value);
|
|
184
184
|
if (parts) {
|
|
185
185
|
this.internalMonth = parts.month;
|
|
186
186
|
this.internalDay = parts.day;
|
|
@@ -195,6 +195,86 @@ class DatePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
195
195
|
internalToValue() {
|
|
196
196
|
return buildISODate(this.internalMonth, this.internalDay, this.internalYear);
|
|
197
197
|
}
|
|
198
|
+
// AI-Assisted: keep input spinbuttons aligned with valid min/max date boundaries.
|
|
199
|
+
getParsedMin() {
|
|
200
|
+
var _a;
|
|
201
|
+
return this.min ? (_a = parseISO(this.min)) !== null && _a !== void 0 ? _a : undefined : undefined;
|
|
202
|
+
}
|
|
203
|
+
getParsedMax() {
|
|
204
|
+
var _a;
|
|
205
|
+
return this.max ? (_a = parseISO(this.max)) !== null && _a !== void 0 ? _a : undefined : undefined;
|
|
206
|
+
}
|
|
207
|
+
clampValueToRange(value) {
|
|
208
|
+
var _a, _b;
|
|
209
|
+
const dt = parseISO(value);
|
|
210
|
+
if (!dt)
|
|
211
|
+
return value;
|
|
212
|
+
const minDt = this.getParsedMin();
|
|
213
|
+
const maxDt = this.getParsedMax();
|
|
214
|
+
if (minDt && isBefore(dt, minDt))
|
|
215
|
+
return (_a = this.min) !== null && _a !== void 0 ? _a : value;
|
|
216
|
+
if (maxDt && isAfter(dt, maxDt))
|
|
217
|
+
return (_b = this.max) !== null && _b !== void 0 ? _b : value;
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
220
|
+
getFieldRange(field) {
|
|
221
|
+
const currentMonth = parseInt(this.internalMonth, 10) || undefined;
|
|
222
|
+
const currentYear = parseInt(this.internalYear, 10) || undefined;
|
|
223
|
+
const baseMin = getFieldMin(field);
|
|
224
|
+
const baseMax = getFieldMax(field, currentMonth, currentYear);
|
|
225
|
+
let min = baseMin;
|
|
226
|
+
let max = baseMax;
|
|
227
|
+
const minDt = this.getParsedMin();
|
|
228
|
+
const maxDt = this.getParsedMax();
|
|
229
|
+
if (field === 'year') {
|
|
230
|
+
if (minDt)
|
|
231
|
+
min = Math.max(min, minDt.getFullYear());
|
|
232
|
+
if (maxDt)
|
|
233
|
+
max = Math.min(max, maxDt.getFullYear());
|
|
234
|
+
}
|
|
235
|
+
else if (field === 'month' && currentYear) {
|
|
236
|
+
if (minDt && currentYear === minDt.getFullYear())
|
|
237
|
+
min = Math.max(min, minDt.getMonth() + 1);
|
|
238
|
+
if (maxDt && currentYear === maxDt.getFullYear())
|
|
239
|
+
max = Math.min(max, maxDt.getMonth() + 1);
|
|
240
|
+
}
|
|
241
|
+
else if (field === 'day' && currentMonth && currentYear) {
|
|
242
|
+
if (minDt &&
|
|
243
|
+
currentYear === minDt.getFullYear() &&
|
|
244
|
+
currentMonth === minDt.getMonth() + 1) {
|
|
245
|
+
min = Math.max(min, minDt.getDate());
|
|
246
|
+
}
|
|
247
|
+
if (maxDt &&
|
|
248
|
+
currentYear === maxDt.getFullYear() &&
|
|
249
|
+
currentMonth === maxDt.getMonth() + 1) {
|
|
250
|
+
max = Math.min(max, maxDt.getDate());
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (min > max) {
|
|
254
|
+
return { min: baseMin, max: baseMax, isMinConstrained: false, isMaxConstrained: false };
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
min,
|
|
258
|
+
max,
|
|
259
|
+
isMinConstrained: min !== baseMin,
|
|
260
|
+
isMaxConstrained: max !== baseMax,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
getNextSpinbuttonValue(currentValue, range, increment) {
|
|
264
|
+
if (increment) {
|
|
265
|
+
if (currentValue < range.min)
|
|
266
|
+
return range.min;
|
|
267
|
+
if (currentValue >= range.max)
|
|
268
|
+
return range.isMaxConstrained ? range.max : range.min;
|
|
269
|
+
return currentValue + 1;
|
|
270
|
+
}
|
|
271
|
+
if (currentValue > range.max)
|
|
272
|
+
return range.max;
|
|
273
|
+
if (currentValue <= range.min)
|
|
274
|
+
return range.isMinConstrained ? range.min : range.max;
|
|
275
|
+
return currentValue - 1;
|
|
276
|
+
}
|
|
277
|
+
// End AI-Assisted
|
|
198
278
|
syncFormValue() {
|
|
199
279
|
const val = this.value || this.internalToValue();
|
|
200
280
|
if (this.internals) {
|
|
@@ -211,17 +291,13 @@ class DatePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
211
291
|
}
|
|
212
292
|
commitValue() {
|
|
213
293
|
const newVal = this.internalToValue();
|
|
214
|
-
if (newVal
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (maxDt && isAfter(dt, maxDt))
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
this.value = newVal;
|
|
294
|
+
if (newVal) {
|
|
295
|
+
const clampedVal = this.clampValueToRange(newVal);
|
|
296
|
+
if (clampedVal !== newVal)
|
|
297
|
+
this.parseValueToInternal(clampedVal);
|
|
298
|
+
if (clampedVal === this.value)
|
|
299
|
+
return;
|
|
300
|
+
this.value = clampedVal;
|
|
225
301
|
this.syncFormValue();
|
|
226
302
|
this.notifyValueChange();
|
|
227
303
|
}
|
|
@@ -364,22 +440,19 @@ class DatePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
364
440
|
handleSpinbuttonKeydown(event, field) {
|
|
365
441
|
if (this.readonly)
|
|
366
442
|
return;
|
|
367
|
-
const
|
|
368
|
-
const currentYear = parseInt(this.internalYear, 10) || undefined;
|
|
369
|
-
const minVal = getFieldMin(field);
|
|
370
|
-
const maxVal = getFieldMax(field, currentMonth, currentYear);
|
|
443
|
+
const range = this.getFieldRange(field);
|
|
371
444
|
const currentVal = parseInt(this.getFieldValue(field), 10) || 0;
|
|
372
445
|
switch (event.key) {
|
|
373
446
|
case KEYS.ARROW_UP: {
|
|
374
447
|
event.preventDefault();
|
|
375
|
-
const newVal = currentVal
|
|
448
|
+
const newVal = this.getNextSpinbuttonValue(currentVal, range, true);
|
|
376
449
|
this.setFieldValue(field, newVal);
|
|
377
450
|
this.commitValue();
|
|
378
451
|
break;
|
|
379
452
|
}
|
|
380
453
|
case KEYS.ARROW_DOWN: {
|
|
381
454
|
event.preventDefault();
|
|
382
|
-
const newVal = currentVal
|
|
455
|
+
const newVal = this.getNextSpinbuttonValue(currentVal, range, false);
|
|
383
456
|
this.setFieldValue(field, newVal);
|
|
384
457
|
this.commitValue();
|
|
385
458
|
break;
|
|
@@ -399,7 +472,7 @@ class DatePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
399
472
|
default: {
|
|
400
473
|
if (/^\d$/.test(event.key)) {
|
|
401
474
|
event.preventDefault();
|
|
402
|
-
this.handleDigitInput(event.key, field,
|
|
475
|
+
this.handleDigitInput(event.key, field, range.min, range.max);
|
|
403
476
|
}
|
|
404
477
|
else {
|
|
405
478
|
event.preventDefault();
|
|
@@ -549,10 +622,7 @@ class DatePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
549
622
|
// -- Render methods --
|
|
550
623
|
renderSpinbutton(field) {
|
|
551
624
|
const value = this.getFieldValue(field);
|
|
552
|
-
const
|
|
553
|
-
const currentYear = parseInt(this.internalYear, 10) || undefined;
|
|
554
|
-
const minVal = getFieldMin(field);
|
|
555
|
-
const maxVal = getFieldMax(field, currentMonth, currentYear);
|
|
625
|
+
const range = this.getFieldRange(field);
|
|
556
626
|
const labelMap = {
|
|
557
627
|
month: this.localeMonthLabel,
|
|
558
628
|
day: this.localeDayLabel,
|
|
@@ -569,8 +639,8 @@ class DatePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
569
639
|
part="spinbutton spinbutton-${field}"
|
|
570
640
|
role="spinbutton"
|
|
571
641
|
aria-label="${labelMap[field]}"
|
|
572
|
-
aria-valuemin="${
|
|
573
|
-
aria-valuemax="${
|
|
642
|
+
aria-valuemin="${range.min}"
|
|
643
|
+
aria-valuemax="${range.max}"
|
|
574
644
|
aria-valuenow="${ifDefined(value ? parseInt(value, 10) : undefined)}"
|
|
575
645
|
aria-description="${this.localeSpinbuttonDescription}"
|
|
576
646
|
.value="${value}"
|
|
@@ -193,10 +193,14 @@ class FormfieldWrapper extends DisabledMixin(Component) {
|
|
|
193
193
|
if (!this.helpText) {
|
|
194
194
|
return nothing;
|
|
195
195
|
}
|
|
196
|
-
return html `<
|
|
197
|
-
|
|
196
|
+
return html `<mdc-statusmessage
|
|
197
|
+
part="help-text-container"
|
|
198
|
+
severity="${DEFAULTS.VALIDATION}"
|
|
199
|
+
exportparts="container: help-text-container, icon: helper-icon, text: help-text"
|
|
200
|
+
>
|
|
201
|
+
<slot name="help-icon" slot="icon">${this.renderHelpTextIcon()}</slot>
|
|
198
202
|
<slot name="help-text">${this.renderHelpText()}</slot>
|
|
199
|
-
</
|
|
203
|
+
</mdc-statusmessage>`;
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
FormfieldWrapper.styles = [...Component.styles, ...styles];
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import utils from '../../utils/tag-name';
|
|
2
2
|
import { POPOVER_PLACEMENT, STRATEGY } from '../popover/popover.constants';
|
|
3
3
|
import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
|
|
4
|
+
import { STATUSMESSAGE_SEVERITY } from '../statusmessage/statusmessage.constants';
|
|
4
5
|
const TAG_NAME = utils.constructTagName('formfieldwrapper');
|
|
5
|
-
const VALIDATION =
|
|
6
|
-
DEFAULT: 'default',
|
|
7
|
-
ERROR: 'error',
|
|
8
|
-
PRIORITY: 'priority',
|
|
9
|
-
SUCCESS: 'success',
|
|
10
|
-
WARNING: 'warning',
|
|
11
|
-
};
|
|
6
|
+
const VALIDATION = STATUSMESSAGE_SEVERITY;
|
|
12
7
|
const DEFAULTS = {
|
|
13
8
|
VALIDATION: VALIDATION.DEFAULT,
|
|
14
9
|
HELPER_TEXT_ID: 'helper-text-id',
|
|
@@ -59,6 +59,16 @@ const styles = css `
|
|
|
59
59
|
color: var(--mdc-help-text-color);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
mdc-statusmessage[part='help-text-container'] {
|
|
63
|
+
--mdc-statusmessage-color: var(--mdc-help-text-color);
|
|
64
|
+
--mdc-statusmessage-font-size: var(--mdc-help-text-font-size);
|
|
65
|
+
--mdc-statusmessage-font-weight: var(--mdc-help-text-font-weight);
|
|
66
|
+
--mdc-statusmessage-line-height: var(--mdc-help-text-line-height);
|
|
67
|
+
--mdc-statusmessage-gap: 0.5rem;
|
|
68
|
+
|
|
69
|
+
width: 100%;
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
:host::part(info-icon-btn) {
|
|
63
73
|
align-self: flex-start;
|
|
64
74
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ValueOf } from '../../utils/types';
|
|
2
|
-
import type {
|
|
2
|
+
import type { StatusMessageIcon } from '../statusmessage/statusmessage.types';
|
|
3
3
|
import { VALIDATION } from './formfieldwrapper.constants';
|
|
4
4
|
type ValidationType = ValueOf<typeof VALIDATION>;
|
|
5
|
-
type HelperIconsList =
|
|
5
|
+
type HelperIconsList = StatusMessageIcon;
|
|
6
6
|
export type { HelperIconsList, ValidationType };
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const getHelperIcon = (type) =>
|
|
3
|
-
const helperIconSizeMap = {
|
|
4
|
-
[VALIDATION.ERROR]: 'error-legacy-badge-filled',
|
|
5
|
-
[VALIDATION.WARNING]: 'warning-badge-filled',
|
|
6
|
-
[VALIDATION.SUCCESS]: 'check-circle-badge-filled',
|
|
7
|
-
[VALIDATION.PRIORITY]: 'priority-badge-filled',
|
|
8
|
-
[VALIDATION.DEFAULT]: '',
|
|
9
|
-
};
|
|
10
|
-
return helperIconSizeMap[type] || '';
|
|
11
|
-
};
|
|
1
|
+
import { STATUSMESSAGE_ICON_NAME_BY_SEVERITY } from '../statusmessage/statusmessage.constants';
|
|
2
|
+
const getHelperIcon = (type) => STATUSMESSAGE_ICON_NAME_BY_SEVERITY[type] || '';
|
|
12
3
|
export { getHelperIcon };
|
|
@@ -235,7 +235,8 @@ class MenuBar extends KeyDownHandledMixin(KeyToActionMixin(Component)) {
|
|
|
235
235
|
return !!element.closest(MENUPOPOVER_TAGNAME) && element.role === ROLE.MENUITEM;
|
|
236
236
|
}
|
|
237
237
|
async closeAllMenuPopovers() {
|
|
238
|
-
const
|
|
238
|
+
const allPopovers = this.getAllPopovers();
|
|
239
|
+
const popovers = this.depthManager.popUntil(item => allPopovers.includes(item));
|
|
239
240
|
await Promise.all(popovers.map(popover => popover.updateComplete));
|
|
240
241
|
}
|
|
241
242
|
async crossMenubarNavigationOnLeft(element) {
|
|
@@ -102,7 +102,7 @@ declare class MenuPopover extends Popover {
|
|
|
102
102
|
onOutsidePopoverClick: (event: MouseEvent) => void;
|
|
103
103
|
/**
|
|
104
104
|
* Toggles the visibility of the popover.
|
|
105
|
-
* This method checks if the trigger element has the `soft-disabled` attribute.
|
|
105
|
+
* This method checks if the trigger element has the `disabled` or `soft-disabled` attribute.
|
|
106
106
|
* If it does, the popover will not be toggled.
|
|
107
107
|
* If the popover is currently visible, it hides the popover; otherwise, it shows the popover.
|
|
108
108
|
* @returns - This method does not return anything.
|
|
@@ -78,14 +78,14 @@ class MenuPopover extends Popover {
|
|
|
78
78
|
};
|
|
79
79
|
/**
|
|
80
80
|
* Toggles the visibility of the popover.
|
|
81
|
-
* This method checks if the trigger element has the `soft-disabled` attribute.
|
|
81
|
+
* This method checks if the trigger element has the `disabled` or `soft-disabled` attribute.
|
|
82
82
|
* If it does, the popover will not be toggled.
|
|
83
83
|
* If the popover is currently visible, it hides the popover; otherwise, it shows the popover.
|
|
84
84
|
* @returns - This method does not return anything.
|
|
85
85
|
*/
|
|
86
86
|
this.togglePopoverVisible = (event) => {
|
|
87
|
-
var _a;
|
|
88
|
-
if ((_a = this.triggerElement) === null || _a === void 0 ? void 0 : _a.hasAttribute('soft-disabled'))
|
|
87
|
+
var _a, _b;
|
|
88
|
+
if (((_a = this.triggerElement) === null || _a === void 0 ? void 0 : _a.hasAttribute('disabled')) || ((_b = this.triggerElement) === null || _b === void 0 ? void 0 : _b.hasAttribute('soft-disabled')))
|
|
89
89
|
return;
|
|
90
90
|
// Handle mouse click in the parent menupopover to hide open sibling submenus
|
|
91
91
|
if (event.composedPath().find(el => el.tagName === this.tagName) === this) {
|
|
@@ -179,7 +179,7 @@ class MenuPopover extends Popover {
|
|
|
179
179
|
case ACTIONS.RIGHT: {
|
|
180
180
|
// If there is a submenu, open it.
|
|
181
181
|
const subMenu = this.getSubMenuPopoverOfTarget(target);
|
|
182
|
-
if (subMenu) {
|
|
182
|
+
if (subMenu && !target.hasAttribute('soft-disabled')) {
|
|
183
183
|
subMenu.show();
|
|
184
184
|
isKeyHandled = true;
|
|
185
185
|
}
|
|
@@ -5,7 +5,7 @@ import type { PopoverPlacement } from '../popover/popover.types';
|
|
|
5
5
|
import type { TooltipType } from '../tooltip/tooltip.types';
|
|
6
6
|
import type { BadgeType } from './navmenuitem.types';
|
|
7
7
|
/**
|
|
8
|
-
* `mdc-navmenuitem` is a
|
|
8
|
+
* `mdc-navmenuitem` is a `menuitem`-role component styled as a navigation tab, supporting a leading icon, optional badge, and dynamic text rendering. It can operate as a simple item, as a parent with a flyout submenu (`mdc-menupopover`), or as a parent with an inline dropdown submenu, and a tooltip can be attached for collapsed states or when a nested child is active.
|
|
9
9
|
*
|
|
10
10
|
* **When to use**
|
|
11
11
|
*
|
|
@@ -37,6 +37,7 @@ import type { BadgeType } from './navmenuitem.types';
|
|
|
37
37
|
* @cssproperty --mdc-navmenuitem-in-sidenav-collapsed-margin-left - Left margin of the navmenuitem, when collapsed.
|
|
38
38
|
* @cssproperty --mdc-navmenuitem-in-sidenav-collapsed-margin-right - Right margin of the navmenuitem, when collapsed.
|
|
39
39
|
* @cssproperty --mdc-navmenuitem-color - Text color of the navmenuitem in its normal state.
|
|
40
|
+
* @cssproperty --mdc-navmenuitem-notch-color - Notch color of the navmenuitem (if applicable) when active.
|
|
40
41
|
* @cssproperty --mdc-navmenuitem-disabled-color - Text color of the navmenuitem when disabled.
|
|
41
42
|
* @cssproperty --mdc-navmenuitem-rest-active-background-color - Background color of the active nav item in its rest state.
|
|
42
43
|
* @cssproperty --mdc-navmenuitem-hover-background-color - Background color of the navmenuitem when hovered.
|
|
@@ -50,7 +51,8 @@ import type { BadgeType } from './navmenuitem.types';
|
|
|
50
51
|
* @csspart badge - The badge of the navmenuitem.
|
|
51
52
|
* @csspart icon-container - The container of the icon.
|
|
52
53
|
* @csspart text-container - The container of the text.
|
|
53
|
-
* @csspart trailing-arrow - The trailing arrow of the navmenuitem.
|
|
54
|
+
* @csspart trailing-arrow - The trailing arrow of the navmenuitem when it has a flyout menu.
|
|
55
|
+
* @csspart trailing-arrow-dropdown - The trailing arrow of the navmenuitem when it has a dropdown menu.
|
|
54
56
|
*/
|
|
55
57
|
declare class NavMenuItem extends MenuItem {
|
|
56
58
|
/**
|
|
@@ -167,6 +169,11 @@ declare class NavMenuItem extends MenuItem {
|
|
|
167
169
|
* @internal
|
|
168
170
|
*/
|
|
169
171
|
hasActiveChild: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Tracks whether the dropdown submenu controlled by this navmenuitem is currently open.
|
|
174
|
+
* @internal
|
|
175
|
+
*/
|
|
176
|
+
private dropdownOpen;
|
|
170
177
|
/**
|
|
171
178
|
* @internal
|
|
172
179
|
*/
|
|
@@ -184,6 +191,11 @@ declare class NavMenuItem extends MenuItem {
|
|
|
184
191
|
* This method assumes nesting implies deeper levels of nav hierarchy.
|
|
185
192
|
*/
|
|
186
193
|
private isNested;
|
|
194
|
+
/**
|
|
195
|
+
* Check whether the navmenuitem is inside a dropdown container (div[data-trigger]).
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
private isInsideDropdownContainer;
|
|
187
199
|
/**
|
|
188
200
|
* Dispatch the activechange event.
|
|
189
201
|
* @internal
|
|
@@ -191,6 +203,24 @@ declare class NavMenuItem extends MenuItem {
|
|
|
191
203
|
*/
|
|
192
204
|
private emitNavMenuItemActiveChange;
|
|
193
205
|
private handleClickEvent;
|
|
206
|
+
/**
|
|
207
|
+
* Toggles the visibility of the dropdown container associated with this navmenuitem.
|
|
208
|
+
* @internal
|
|
209
|
+
*/
|
|
210
|
+
private toggleDropdown;
|
|
211
|
+
/**
|
|
212
|
+
* Opens the dropdown if it is closed.
|
|
213
|
+
*/
|
|
214
|
+
openDropdown(): void;
|
|
215
|
+
/**
|
|
216
|
+
* Closes the dropdown if it is open.
|
|
217
|
+
*/
|
|
218
|
+
closeDropdown(): void;
|
|
219
|
+
/**
|
|
220
|
+
* Returns the sibling div[data-trigger] element associated with this navmenuitem.
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
private getDropdownContainer;
|
|
194
224
|
private getFilledIconName;
|
|
195
225
|
private renderBadge;
|
|
196
226
|
render(): TemplateResult<1>;
|
|
@@ -13,12 +13,12 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
|
|
13
13
|
import { v4 } from 'uuid';
|
|
14
14
|
import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
|
|
15
15
|
import { TAG_NAME as MENUPOPOVER_TAGNAME } from '../menupopover/menupopover.constants';
|
|
16
|
+
import { TAG_NAME as TOOLTIP_TAG_NAME } from '../tooltip/tooltip.constants';
|
|
16
17
|
import MenuItem from '../menuitem/menuitem.component';
|
|
17
18
|
import providerUtils from '../../utils/provider';
|
|
18
19
|
import SideNavigation from '../sidenavigation/sidenavigation.component';
|
|
19
|
-
import { TAG_NAME as TOOLTIP_TAG_NAME } from '../tooltip/tooltip.constants';
|
|
20
20
|
import { getIconNameWithoutStyle } from '../button/button.utils';
|
|
21
|
-
import { ALLOWED_BADGE_TYPES, DEFAULTS, ICON_NAME } from './navmenuitem.constants';
|
|
21
|
+
import { ALLOWED_BADGE_TYPES, DEFAULTS, ICON_NAME, TAG_NAME as NAVMENUITEM_TAGNAME } from './navmenuitem.constants';
|
|
22
22
|
import styles from './navmenuitem.styles';
|
|
23
23
|
/**
|
|
24
24
|
* @tagname mdc-navmenuitem
|
|
@@ -41,6 +41,7 @@ import styles from './navmenuitem.styles';
|
|
|
41
41
|
* @cssproperty --mdc-navmenuitem-in-sidenav-collapsed-margin-left - Left margin of the navmenuitem, when collapsed.
|
|
42
42
|
* @cssproperty --mdc-navmenuitem-in-sidenav-collapsed-margin-right - Right margin of the navmenuitem, when collapsed.
|
|
43
43
|
* @cssproperty --mdc-navmenuitem-color - Text color of the navmenuitem in its normal state.
|
|
44
|
+
* @cssproperty --mdc-navmenuitem-notch-color - Notch color of the navmenuitem (if applicable) when active.
|
|
44
45
|
* @cssproperty --mdc-navmenuitem-disabled-color - Text color of the navmenuitem when disabled.
|
|
45
46
|
* @cssproperty --mdc-navmenuitem-rest-active-background-color - Background color of the active nav item in its rest state.
|
|
46
47
|
* @cssproperty --mdc-navmenuitem-hover-background-color - Background color of the navmenuitem when hovered.
|
|
@@ -54,7 +55,8 @@ import styles from './navmenuitem.styles';
|
|
|
54
55
|
* @csspart badge - The badge of the navmenuitem.
|
|
55
56
|
* @csspart icon-container - The container of the icon.
|
|
56
57
|
* @csspart text-container - The container of the text.
|
|
57
|
-
* @csspart trailing-arrow - The trailing arrow of the navmenuitem.
|
|
58
|
+
* @csspart trailing-arrow - The trailing arrow of the navmenuitem when it has a flyout menu.
|
|
59
|
+
* @csspart trailing-arrow-dropdown - The trailing arrow of the navmenuitem when it has a dropdown menu.
|
|
58
60
|
*/
|
|
59
61
|
class NavMenuItem extends MenuItem {
|
|
60
62
|
constructor() {
|
|
@@ -96,6 +98,11 @@ class NavMenuItem extends MenuItem {
|
|
|
96
98
|
* @internal
|
|
97
99
|
*/
|
|
98
100
|
this.hasActiveChild = false;
|
|
101
|
+
/**
|
|
102
|
+
* Tracks whether the dropdown submenu controlled by this navmenuitem is currently open.
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
this.dropdownOpen = false;
|
|
99
106
|
/**
|
|
100
107
|
* @internal
|
|
101
108
|
*/
|
|
@@ -122,6 +129,8 @@ class NavMenuItem extends MenuItem {
|
|
|
122
129
|
}
|
|
123
130
|
// Set in-menupopover attribute if nested
|
|
124
131
|
this.toggleAttribute('in-menupopover', this.isNested());
|
|
132
|
+
// Set in-dropdown-container attribute if inside a dropdown div
|
|
133
|
+
this.toggleAttribute('in-dropdown-container', this.isInsideDropdownContainer());
|
|
125
134
|
}
|
|
126
135
|
disconnectedCallback() {
|
|
127
136
|
super.disconnectedCallback();
|
|
@@ -139,7 +148,8 @@ class NavMenuItem extends MenuItem {
|
|
|
139
148
|
super.updated(changedProperties);
|
|
140
149
|
if (changedProperties.has('tooltipText') ||
|
|
141
150
|
changedProperties.has('showLabel') ||
|
|
142
|
-
changedProperties.has('hasActiveChild')
|
|
151
|
+
changedProperties.has('hasActiveChild') ||
|
|
152
|
+
changedProperties.has('dropdownOpen')) {
|
|
143
153
|
this.renderDynamicTooltip();
|
|
144
154
|
}
|
|
145
155
|
if (changedProperties.has('showLabel')) {
|
|
@@ -152,11 +162,36 @@ class NavMenuItem extends MenuItem {
|
|
|
152
162
|
if (!context)
|
|
153
163
|
return;
|
|
154
164
|
// Determine expansion state
|
|
155
|
-
this.showLabel = this.isNested() ? true : context.expanded;
|
|
165
|
+
this.showLabel = this.isNested() || this.isInsideDropdownContainer() ? true : context.expanded;
|
|
166
|
+
// When sidenavigation collapses, close any open dropdown
|
|
167
|
+
if (!context.expanded && this.dropdownOpen) {
|
|
168
|
+
this.closeDropdown();
|
|
169
|
+
}
|
|
170
|
+
// When dropdown submenu type is turned off, close any open dropdown
|
|
171
|
+
if (!context.isDropdownSubmenuType && this.dropdownOpen) {
|
|
172
|
+
this.closeDropdown();
|
|
173
|
+
}
|
|
174
|
+
// In dropdown mode, manage parent active styling based on expanded state:
|
|
175
|
+
// Expanded + dropdown open: only the child should appear active, not the parent.
|
|
176
|
+
// Expanded + dropdown closed: parent should appear active when it has an active child.
|
|
177
|
+
// Collapsed (icon-only): parent should appear active when it has an active child.
|
|
178
|
+
if (context.isDropdownSubmenuType && this.hasActiveChild && context.isDropDownParent(this)) {
|
|
179
|
+
if (context.expanded && this.dropdownOpen) {
|
|
180
|
+
this.removeAttribute('active');
|
|
181
|
+
this.active = false;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
this.setAttribute('active', '');
|
|
185
|
+
this.active = true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
156
188
|
}
|
|
157
189
|
renderDynamicTooltip() {
|
|
158
190
|
var _a;
|
|
159
191
|
this.removeTooltip();
|
|
192
|
+
if (this.disabled || this.dropdownOpen) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
160
195
|
if (this.hasActiveChild && !this.isActiveParentTooltipText) {
|
|
161
196
|
return;
|
|
162
197
|
}
|
|
@@ -206,11 +241,90 @@ class NavMenuItem extends MenuItem {
|
|
|
206
241
|
}
|
|
207
242
|
return false;
|
|
208
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Check whether the navmenuitem is inside a dropdown container (div[data-trigger]).
|
|
246
|
+
* @internal
|
|
247
|
+
*/
|
|
248
|
+
isInsideDropdownContainer() {
|
|
249
|
+
let parent = this.parentElement;
|
|
250
|
+
while (parent) {
|
|
251
|
+
if (parent.matches('div[data-trigger]')) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
parent = parent.parentElement;
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
209
258
|
handleClickEvent() {
|
|
210
|
-
|
|
259
|
+
var _a;
|
|
260
|
+
if (this.disabled || this.cannotActivate || this.softDisabled)
|
|
211
261
|
return;
|
|
262
|
+
const context = (_a = this.sideNavigationContext) === null || _a === void 0 ? void 0 : _a.value;
|
|
263
|
+
if ((context === null || context === void 0 ? void 0 : context.isDropdownSubmenuType) && (context === null || context === void 0 ? void 0 : context.expanded) && (context === null || context === void 0 ? void 0 : context.isDropDownParent(this))) {
|
|
264
|
+
this.toggleDropdown();
|
|
265
|
+
// move the focus to the first navmenuitem in the dropdown when opening the dropdown
|
|
266
|
+
if (this.dropdownOpen) {
|
|
267
|
+
const dropdownContainer = this.getDropdownContainer();
|
|
268
|
+
const firstNavMenuItem = dropdownContainer === null || dropdownContainer === void 0 ? void 0 : dropdownContainer.querySelector(NAVMENUITEM_TAGNAME);
|
|
269
|
+
firstNavMenuItem === null || firstNavMenuItem === void 0 ? void 0 : firstNavMenuItem.focus();
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
212
273
|
this.emitNavMenuItemActiveChange(this.active);
|
|
213
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Toggles the visibility of the dropdown container associated with this navmenuitem.
|
|
277
|
+
* @internal
|
|
278
|
+
*/
|
|
279
|
+
toggleDropdown() {
|
|
280
|
+
const dropdownContainer = this.getDropdownContainer();
|
|
281
|
+
if (!dropdownContainer)
|
|
282
|
+
return;
|
|
283
|
+
this.dropdownOpen = !this.dropdownOpen;
|
|
284
|
+
dropdownContainer.style.display = this.dropdownOpen ? 'flex' : 'none';
|
|
285
|
+
if (this.dropdownOpen) {
|
|
286
|
+
this.setAttribute('aria-expanded', 'true');
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
this.removeAttribute('aria-expanded');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Opens the dropdown if it is closed.
|
|
294
|
+
*/
|
|
295
|
+
openDropdown() {
|
|
296
|
+
if (this.dropdownOpen)
|
|
297
|
+
return;
|
|
298
|
+
const dropdownContainer = this.getDropdownContainer();
|
|
299
|
+
if (dropdownContainer) {
|
|
300
|
+
dropdownContainer.style.display = 'flex';
|
|
301
|
+
}
|
|
302
|
+
this.dropdownOpen = true;
|
|
303
|
+
this.setAttribute('aria-expanded', 'true');
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Closes the dropdown if it is open.
|
|
307
|
+
*/
|
|
308
|
+
closeDropdown() {
|
|
309
|
+
if (!this.dropdownOpen)
|
|
310
|
+
return;
|
|
311
|
+
const dropdownContainer = this.getDropdownContainer();
|
|
312
|
+
if (dropdownContainer) {
|
|
313
|
+
dropdownContainer.style.display = 'none';
|
|
314
|
+
}
|
|
315
|
+
this.dropdownOpen = false;
|
|
316
|
+
this.removeAttribute('aria-expanded');
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Returns the sibling div[data-trigger] element associated with this navmenuitem.
|
|
320
|
+
* @internal
|
|
321
|
+
*/
|
|
322
|
+
getDropdownContainer() {
|
|
323
|
+
var _a;
|
|
324
|
+
if (!this.id)
|
|
325
|
+
return null;
|
|
326
|
+
return (_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector(`div[data-trigger="${this.id}"]`);
|
|
327
|
+
}
|
|
214
328
|
getFilledIconName() {
|
|
215
329
|
if (!this.iconName) {
|
|
216
330
|
return undefined;
|
|
@@ -241,10 +355,20 @@ class NavMenuItem extends MenuItem {
|
|
|
241
355
|
render() {
|
|
242
356
|
var _a;
|
|
243
357
|
const context = (_a = this.sideNavigationContext) === null || _a === void 0 ? void 0 : _a.value;
|
|
358
|
+
const isDropdownMode = (context === null || context === void 0 ? void 0 : context.isDropdownSubmenuType) && (context === null || context === void 0 ? void 0 : context.expanded);
|
|
359
|
+
const isDropDownParent = context === null || context === void 0 ? void 0 : context.isDropDownParent(this);
|
|
360
|
+
const hasFlyoutSibling = context === null || context === void 0 ? void 0 : context.hasSiblingWithTriggerId(this);
|
|
244
361
|
return html `
|
|
245
362
|
<div part="icon-container">
|
|
246
|
-
|
|
247
|
-
|
|
363
|
+
${this.iconName
|
|
364
|
+
? html `<mdc-icon
|
|
365
|
+
name="${this.iconName}"
|
|
366
|
+
size="1.5"
|
|
367
|
+
length-unit="rem"
|
|
368
|
+
part="regular-icon"
|
|
369
|
+
></mdc-icon>`
|
|
370
|
+
: nothing}
|
|
371
|
+
${!this.cannotActivate && this.iconName
|
|
248
372
|
? html `<mdc-icon
|
|
249
373
|
name="${this.getFilledIconName()}"
|
|
250
374
|
size="1.5"
|
|
@@ -266,7 +390,16 @@ class NavMenuItem extends MenuItem {
|
|
|
266
390
|
${this.renderBadge(this.showLabel)}
|
|
267
391
|
`
|
|
268
392
|
: nothing}
|
|
269
|
-
${
|
|
393
|
+
${isDropdownMode && isDropDownParent
|
|
394
|
+
? html ` <mdc-icon
|
|
395
|
+
name=${ICON_NAME.DOWN_ARROW}
|
|
396
|
+
length-unit="rem"
|
|
397
|
+
part="trailing-arrow-dropdown"
|
|
398
|
+
class="${this.dropdownOpen ? 'arrow-rotated' : ''}"
|
|
399
|
+
>
|
|
400
|
+
</mdc-icon>`
|
|
401
|
+
: nothing}
|
|
402
|
+
${hasFlyoutSibling
|
|
270
403
|
? html ` <mdc-icon name=${ICON_NAME.RIGHT_ARROW} length-unit="rem" part="trailing-arrow"> </mdc-icon>`
|
|
271
404
|
: nothing}
|
|
272
405
|
`;
|
|
@@ -341,4 +474,8 @@ __decorate([
|
|
|
341
474
|
state(),
|
|
342
475
|
__metadata("design:type", Boolean)
|
|
343
476
|
], NavMenuItem.prototype, "hasActiveChild", void 0);
|
|
477
|
+
__decorate([
|
|
478
|
+
state(),
|
|
479
|
+
__metadata("design:type", Boolean)
|
|
480
|
+
], NavMenuItem.prototype, "dropdownOpen", void 0);
|
|
344
481
|
export default NavMenuItem;
|