@momentum-design/components 0.131.2 → 0.131.4
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 +223 -217
- package/dist/browser/index.js.map +3 -3
- package/dist/components/combobox/combobox.component.d.ts +26 -9
- package/dist/components/combobox/combobox.component.js +174 -75
- package/dist/components/combobox/combobox.events.d.ts +2 -2
- package/dist/components/timepicker/index.d.ts +1 -0
- package/dist/components/timepicker/index.js +1 -0
- package/dist/components/timepicker/timepicker.component.d.ts +1 -0
- package/dist/components/timepicker/timepicker.component.js +3 -3
- package/dist/components/timepicker/timepicker.styles.js +7 -1
- package/dist/custom-elements.json +7 -3
- package/dist/react/combobox/index.d.ts +4 -0
- package/dist/react/combobox/index.js +4 -0
- package/dist/react/timepicker/index.d.ts +1 -0
- package/dist/react/timepicker/index.js +1 -0
- package/dist/utils/keys.d.ts +1 -0
- package/dist/utils/keys.js +1 -0
- package/package.json +1 -1
|
@@ -21,6 +21,10 @@ declare const Combobox_base: import("../../utils/mixins/index.types").Constructo
|
|
|
21
21
|
*
|
|
22
22
|
* To set a default option, use the `selected` attribute on the `mdc-option` element.
|
|
23
23
|
*
|
|
24
|
+
* When the combobox `control-type` attribute is "controlled", then the value should be set by the parent only, and the combobox will emit `change` and `input` events
|
|
25
|
+
* with the selected option details when the user makes a selection or types in the input, but it won't update the selected value internally.
|
|
26
|
+
* The parent component is expected to listen to these events and update the `value` property of the combobox accordingly to reflect the changes in the UI.
|
|
27
|
+
*
|
|
24
28
|
* **Note:** Make sure to add `mdc-selectlistbox` as a child of `mdc-combobox` and wrap options/optgroup in it to ensure proper accessibility functionality. Read more about it in SelectListBox documentation.
|
|
25
29
|
*
|
|
26
30
|
* If you need to use `mdc-tooltip` with any options, make sure to place the tooltip component outside the `mdc-selectlistbox` element. Read more about it in Options documentation.
|
|
@@ -81,6 +85,8 @@ declare const Combobox_base: import("../../utils/mixins/index.types").Constructo
|
|
|
81
85
|
declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
82
86
|
/** @internal */
|
|
83
87
|
private itemsStore;
|
|
88
|
+
/** @internal */
|
|
89
|
+
private lastCommittedValue;
|
|
84
90
|
/**
|
|
85
91
|
* The placeholder text which will be shown on the text if provided.
|
|
86
92
|
* @default undefined
|
|
@@ -154,8 +160,6 @@ declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
|
154
160
|
/** @internal */
|
|
155
161
|
private filteredValue;
|
|
156
162
|
/** @internal */
|
|
157
|
-
private forceValueUpdate;
|
|
158
|
-
/** @internal */
|
|
159
163
|
private initialSelectedOption;
|
|
160
164
|
/** @internal */
|
|
161
165
|
get navItems(): Option[];
|
|
@@ -168,8 +172,6 @@ declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
|
168
172
|
/** @internal */
|
|
169
173
|
private closePopover;
|
|
170
174
|
/** @internal */
|
|
171
|
-
private toggleDropdown;
|
|
172
|
-
/** @internal */
|
|
173
175
|
private compareOptionWithValue;
|
|
174
176
|
/** @internal */
|
|
175
177
|
private getFirstSelectedOption;
|
|
@@ -179,6 +181,10 @@ declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
|
179
181
|
private handleUpdateError;
|
|
180
182
|
/** @internal */
|
|
181
183
|
private onStoreUpdate;
|
|
184
|
+
/** @internal */
|
|
185
|
+
private focusComboboxBase;
|
|
186
|
+
/** @internal */
|
|
187
|
+
private handleTriggerClick;
|
|
182
188
|
/**
|
|
183
189
|
* Update the selected value when an option is modified.
|
|
184
190
|
*
|
|
@@ -189,8 +195,8 @@ declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
|
189
195
|
* Sets the selected option of the combobox.
|
|
190
196
|
*
|
|
191
197
|
* @param option - the new option to be set
|
|
192
|
-
* @param updateFromValue - indicates if the update is triggered from the value attribute change
|
|
193
198
|
* @param emitEvents - indicates if the change and input events should be emitted
|
|
199
|
+
* @param updateFromValue - indicates if the update is triggered from the value attribute change
|
|
194
200
|
*
|
|
195
201
|
* @internal
|
|
196
202
|
*/
|
|
@@ -266,6 +272,7 @@ declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
|
266
272
|
/**
|
|
267
273
|
* Updates the visual focus state of a specific option in the dropdown list based on 'data-focused' attribute.
|
|
268
274
|
* It also updates the 'aria-selected' attribute for a11y purposes.
|
|
275
|
+
* If an option has a tooltip attached to it, this will open the tooltip when the option is focused and close it when the option is unfocused.
|
|
269
276
|
*
|
|
270
277
|
* @param option - The option element to update focus state for.
|
|
271
278
|
* @param value - The new focus state to set (true for focused, false for unfocused).
|
|
@@ -273,6 +280,20 @@ declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
|
273
280
|
* @internal
|
|
274
281
|
*/
|
|
275
282
|
private updateOptionAttributes;
|
|
283
|
+
/**
|
|
284
|
+
* Opens the tooltip associated with the given option, if it exists.
|
|
285
|
+
* @param option - option
|
|
286
|
+
*
|
|
287
|
+
* @internal
|
|
288
|
+
*/
|
|
289
|
+
private openTooltipIfExists;
|
|
290
|
+
/**
|
|
291
|
+
* Closes the tooltip associated with the given option, if it exists.
|
|
292
|
+
* @param option - option
|
|
293
|
+
*
|
|
294
|
+
* @internal
|
|
295
|
+
*/
|
|
296
|
+
private closeTooltipIfExists;
|
|
276
297
|
/**
|
|
277
298
|
* Handles the blur event of the combobox.
|
|
278
299
|
* This method is called when the combobox loses focus.
|
|
@@ -298,10 +319,6 @@ declare class Combobox extends Combobox_base implements AssociatedFormControl {
|
|
|
298
319
|
*/
|
|
299
320
|
private updateHiddenOptions;
|
|
300
321
|
/** @internal */
|
|
301
|
-
private hideOptionGroupAndDivider;
|
|
302
|
-
/** @internal */
|
|
303
|
-
private showOptionGroupAndDivider;
|
|
304
|
-
/** @internal */
|
|
305
322
|
private handleInputChange;
|
|
306
323
|
/** @internal */
|
|
307
324
|
private handleOptionsClick;
|
|
@@ -49,6 +49,10 @@ import styles from './combobox.styles';
|
|
|
49
49
|
*
|
|
50
50
|
* To set a default option, use the `selected` attribute on the `mdc-option` element.
|
|
51
51
|
*
|
|
52
|
+
* When the combobox `control-type` attribute is "controlled", then the value should be set by the parent only, and the combobox will emit `change` and `input` events
|
|
53
|
+
* with the selected option details when the user makes a selection or types in the input, but it won't update the selected value internally.
|
|
54
|
+
* The parent component is expected to listen to these events and update the `value` property of the combobox accordingly to reflect the changes in the UI.
|
|
55
|
+
*
|
|
52
56
|
* **Note:** Make sure to add `mdc-selectlistbox` as a child of `mdc-combobox` and wrap options/optgroup in it to ensure proper accessibility functionality. Read more about it in SelectListBox documentation.
|
|
53
57
|
*
|
|
54
58
|
* If you need to use `mdc-tooltip` with any options, make sure to place the tooltip component outside the `mdc-selectlistbox` element. Read more about it in Options documentation.
|
|
@@ -113,6 +117,8 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
113
117
|
}
|
|
114
118
|
constructor() {
|
|
115
119
|
super();
|
|
120
|
+
/** @internal */
|
|
121
|
+
this.lastCommittedValue = '';
|
|
116
122
|
/**
|
|
117
123
|
* The placement of the popover within Combobox.
|
|
118
124
|
* This defines the position of the popover relative to the combobox input field.
|
|
@@ -159,8 +165,6 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
159
165
|
/** @internal */
|
|
160
166
|
this.filteredValue = '';
|
|
161
167
|
/** @internal */
|
|
162
|
-
this.forceValueUpdate = false;
|
|
163
|
-
/** @internal */
|
|
164
168
|
this.initialSelectedOption = null;
|
|
165
169
|
/** @internal */
|
|
166
170
|
this.handleUpdateError = (error) => {
|
|
@@ -205,7 +209,11 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
205
209
|
*/
|
|
206
210
|
this.handleModifiedEvent = (event) => {
|
|
207
211
|
// When the combobox is controlled, we don't update/modify the selected value internally.
|
|
212
|
+
// Instead, we rely on the consumer to update the selected value based on the change event emitted.
|
|
208
213
|
if (this.controlType === 'controlled') {
|
|
214
|
+
// We still need to update the hidden options when an option is modified, because even in controlled mode,
|
|
215
|
+
// the modification of an option (e.g. changing the label) should be reflected in the UI by updating the hidden options based on the new label.
|
|
216
|
+
this.updateHiddenOptions();
|
|
209
217
|
return;
|
|
210
218
|
}
|
|
211
219
|
const firstSelectedOption = this.getFirstSelectedOption();
|
|
@@ -265,10 +273,6 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
265
273
|
this.isOpen = false;
|
|
266
274
|
}
|
|
267
275
|
/** @internal */
|
|
268
|
-
toggleDropdown() {
|
|
269
|
-
this.isOpen = !this.isOpen;
|
|
270
|
-
}
|
|
271
|
-
/** @internal */
|
|
272
276
|
compareOptionWithValue(option, value) {
|
|
273
277
|
const optionValue = option.getAttribute('label') || '';
|
|
274
278
|
return optionValue.toLowerCase().startsWith(value === null || value === void 0 ? void 0 : value.toLowerCase());
|
|
@@ -281,31 +285,69 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
281
285
|
getVisibleOptions(internalValue) {
|
|
282
286
|
return this.navItems.filter(option => this.compareOptionWithValue(option, internalValue));
|
|
283
287
|
}
|
|
288
|
+
/** @internal */
|
|
289
|
+
focusComboboxBase() {
|
|
290
|
+
if (this.disabled) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
this.updateComplete
|
|
294
|
+
.then(() => {
|
|
295
|
+
var _a;
|
|
296
|
+
// `visualCombobox` points to the slotted input with role="combobox".
|
|
297
|
+
// Clicking the surrounding `mdc-input` or the dropdown button can toggle the popover
|
|
298
|
+
// without moving DOM focus, so we explicitly focus the base input.
|
|
299
|
+
(_a = this.visualCombobox) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true });
|
|
300
|
+
})
|
|
301
|
+
.catch(this.handleUpdateError);
|
|
302
|
+
}
|
|
303
|
+
/** @internal */
|
|
304
|
+
handleTriggerClick() {
|
|
305
|
+
if (this.disabled) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// Toggle dropdown when clicking the trigger button
|
|
309
|
+
this.isOpen = !this.isOpen;
|
|
310
|
+
// Focus on the combobox after click
|
|
311
|
+
this.focusComboboxBase();
|
|
312
|
+
}
|
|
284
313
|
/**
|
|
285
314
|
* Sets the selected option of the combobox.
|
|
286
315
|
*
|
|
287
316
|
* @param option - the new option to be set
|
|
288
|
-
* @param updateFromValue - indicates if the update is triggered from the value attribute change
|
|
289
317
|
* @param emitEvents - indicates if the change and input events should be emitted
|
|
318
|
+
* @param updateFromValue - indicates if the update is triggered from the value attribute change
|
|
290
319
|
*
|
|
291
320
|
* @internal
|
|
292
321
|
*/
|
|
293
|
-
setSelectedValue(option, emitEvents = true) {
|
|
294
|
-
|
|
295
|
-
|
|
322
|
+
setSelectedValue(option, emitEvents = true, updateFromValue = false) {
|
|
323
|
+
const label = (option === null || option === void 0 ? void 0 : option.getAttribute('label')) || '';
|
|
324
|
+
const value = (option === null || option === void 0 ? void 0 : option.getAttribute('value')) || '';
|
|
325
|
+
// If the value and the label are the same as the current selected option,
|
|
326
|
+
// then do nothing to prevent unnecessary updates and event emissions.
|
|
327
|
+
if (this.value === value && this.filteredValue === label) {
|
|
296
328
|
return;
|
|
297
329
|
}
|
|
298
|
-
|
|
299
|
-
//
|
|
300
|
-
this.
|
|
301
|
-
|
|
302
|
-
|
|
330
|
+
// For controlled components, user interactions (not coming from a value prop change)
|
|
331
|
+
// should only emit events and let the parent drive the value.
|
|
332
|
+
if (this.controlType === 'controlled' && !updateFromValue) {
|
|
333
|
+
if (emitEvents) {
|
|
334
|
+
ComboboxEventManager.onInputCombobox(this, option);
|
|
335
|
+
ComboboxEventManager.onChangeCombobox(this, option);
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
this.filteredValue = label;
|
|
340
|
+
this.value = value;
|
|
341
|
+
// Update last committed values when a real selection is made
|
|
342
|
+
this.lastCommittedValue = value;
|
|
303
343
|
this.internals.setFormValue(this.value);
|
|
304
344
|
this.updateHiddenOptions();
|
|
305
|
-
|
|
345
|
+
if (option) {
|
|
346
|
+
this.updateSelectedOption(option);
|
|
347
|
+
}
|
|
306
348
|
this.setInputValidity();
|
|
307
349
|
this.resetHelpText();
|
|
308
|
-
if (emitEvents) {
|
|
350
|
+
if (emitEvents && !updateFromValue && option) {
|
|
309
351
|
ComboboxEventManager.onInputCombobox(this, option);
|
|
310
352
|
ComboboxEventManager.onChangeCombobox(this, option);
|
|
311
353
|
}
|
|
@@ -342,11 +384,10 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
342
384
|
*/
|
|
343
385
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
344
386
|
super.attributeChangedCallback(name, oldValue, newValue);
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
this.controlType !== 'controlled') {
|
|
387
|
+
// keep value-attribute based default selection working for both
|
|
388
|
+
// controlled and uncontrolled modes, while avoiding change/input events
|
|
389
|
+
// by delegating to setSelectedValue with updateFromValue=true.
|
|
390
|
+
if (name === 'value' && newValue !== '' && this.navItems.length) {
|
|
350
391
|
const firstSelectedOption = this.getFirstSelectedOption();
|
|
351
392
|
const valueBasedOption = this.navItems.find(option => option.value === newValue);
|
|
352
393
|
let optionToSelect = null;
|
|
@@ -364,7 +405,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
364
405
|
}
|
|
365
406
|
this.updateComplete
|
|
366
407
|
.then(() => {
|
|
367
|
-
this.setSelectedValue(optionToSelect);
|
|
408
|
+
this.setSelectedValue(optionToSelect, false, true);
|
|
368
409
|
})
|
|
369
410
|
.catch(this.handleUpdateError);
|
|
370
411
|
}
|
|
@@ -397,6 +438,8 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
397
438
|
else if (this.placeholder) {
|
|
398
439
|
this.setInputValidity();
|
|
399
440
|
}
|
|
441
|
+
// Initialize last committed values
|
|
442
|
+
this.lastCommittedValue = this.value;
|
|
400
443
|
this.navItems.forEach(option => {
|
|
401
444
|
option.setAttribute('tabindex', '-1');
|
|
402
445
|
});
|
|
@@ -407,10 +450,10 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
407
450
|
* @internal
|
|
408
451
|
*/
|
|
409
452
|
updateValueBasedSelection() {
|
|
410
|
-
this.forceValueUpdate = true;
|
|
411
453
|
const validOption = this.navItems.find(option => option.value === this.value);
|
|
412
454
|
if (validOption) {
|
|
413
|
-
|
|
455
|
+
// Sync UI from value prop without firing change/input events
|
|
456
|
+
this.setSelectedValue(validOption, false, true);
|
|
414
457
|
}
|
|
415
458
|
}
|
|
416
459
|
updated(changedProperties) {
|
|
@@ -470,7 +513,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
470
513
|
var _a;
|
|
471
514
|
const optionToResetTo = this.initialSelectedOption || null;
|
|
472
515
|
// Restore the selected option
|
|
473
|
-
this.setSelectedValue(optionToResetTo);
|
|
516
|
+
this.setSelectedValue(optionToResetTo, false, true);
|
|
474
517
|
// Reset the filtered text (typed value shown in input)
|
|
475
518
|
if (this.controlType !== 'controlled') {
|
|
476
519
|
this.filteredValue = (_a = optionToResetTo === null || optionToResetTo === void 0 ? void 0 : optionToResetTo.label) !== null && _a !== void 0 ? _a : '';
|
|
@@ -481,7 +524,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
481
524
|
/** @internal */
|
|
482
525
|
formStateRestoreCallback(state) {
|
|
483
526
|
const optionToRestoreTo = this.navItems.find(option => option.value === state || option.label === state);
|
|
484
|
-
this.setSelectedValue(optionToRestoreTo || null);
|
|
527
|
+
this.setSelectedValue(optionToRestoreTo || null, false, true);
|
|
485
528
|
}
|
|
486
529
|
/**
|
|
487
530
|
* When the native input is focused, visually highlight the dropdown options (the "visual combobox"),
|
|
@@ -518,6 +561,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
518
561
|
/**
|
|
519
562
|
* Updates the visual focus state of a specific option in the dropdown list based on 'data-focused' attribute.
|
|
520
563
|
* It also updates the 'aria-selected' attribute for a11y purposes.
|
|
564
|
+
* If an option has a tooltip attached to it, this will open the tooltip when the option is focused and close it when the option is unfocused.
|
|
521
565
|
*
|
|
522
566
|
* @param option - The option element to update focus state for.
|
|
523
567
|
* @param value - The new focus state to set (true for focused, false for unfocused).
|
|
@@ -529,12 +573,42 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
529
573
|
return;
|
|
530
574
|
if (value) {
|
|
531
575
|
option.setAttribute('data-focused', '');
|
|
576
|
+
this.openTooltipIfExists(option);
|
|
532
577
|
}
|
|
533
578
|
else {
|
|
534
579
|
option.removeAttribute('data-focused');
|
|
580
|
+
this.closeTooltipIfExists(option);
|
|
535
581
|
}
|
|
536
582
|
option.setAttribute('aria-selected', value.toString());
|
|
537
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Opens the tooltip associated with the given option, if it exists.
|
|
586
|
+
* @param option - option
|
|
587
|
+
*
|
|
588
|
+
* @internal
|
|
589
|
+
*/
|
|
590
|
+
openTooltipIfExists(option) {
|
|
591
|
+
const id = option.getAttribute('id');
|
|
592
|
+
if (!id)
|
|
593
|
+
return;
|
|
594
|
+
const tooltip = this.querySelector(`mdc-tooltip[triggerid="${id}"]`);
|
|
595
|
+
tooltip === null || tooltip === void 0 ? void 0 : tooltip.setAttribute('visible', '');
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Closes the tooltip associated with the given option, if it exists.
|
|
599
|
+
* @param option - option
|
|
600
|
+
*
|
|
601
|
+
* @internal
|
|
602
|
+
*/
|
|
603
|
+
closeTooltipIfExists(option) {
|
|
604
|
+
const id = option.getAttribute('id');
|
|
605
|
+
if (!id)
|
|
606
|
+
return;
|
|
607
|
+
const tooltip = this.querySelector(`mdc-tooltip[triggerid="${id}"][visible]`);
|
|
608
|
+
if (tooltip) {
|
|
609
|
+
tooltip.removeAttribute('visible');
|
|
610
|
+
}
|
|
611
|
+
}
|
|
538
612
|
/**
|
|
539
613
|
* Handles the blur event of the combobox.
|
|
540
614
|
* This method is called when the combobox loses focus.
|
|
@@ -553,15 +627,35 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
553
627
|
this.closePopover();
|
|
554
628
|
return;
|
|
555
629
|
}
|
|
556
|
-
if
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
630
|
+
// Check if the typed text exactly matches an option
|
|
631
|
+
const exactMatch = this.navItems.find(option => {
|
|
632
|
+
const optionLabel = option.getAttribute('label') || '';
|
|
633
|
+
return optionLabel.toLowerCase() === this.filteredValue.toLowerCase();
|
|
634
|
+
});
|
|
635
|
+
if (exactMatch) {
|
|
636
|
+
this.setSelectedValue(exactMatch);
|
|
637
|
+
this.closePopover();
|
|
638
|
+
return;
|
|
562
639
|
}
|
|
563
|
-
//
|
|
564
|
-
this.
|
|
640
|
+
// If user typed something but didn't select anything, check what to do
|
|
641
|
+
if (this.filteredValue !== '') {
|
|
642
|
+
// If the input doesn't match any option and we have a previously committed value, revert to it
|
|
643
|
+
if (this.lastCommittedValue) {
|
|
644
|
+
const newOption = this.navItems.find(option => option.value === this.lastCommittedValue);
|
|
645
|
+
this.setSelectedValue(newOption);
|
|
646
|
+
}
|
|
647
|
+
else if (this.invalidCustomValueText && !this.getFirstSelectedOption()) {
|
|
648
|
+
// Show invalid custom value message if configured
|
|
649
|
+
this.helpText = this.invalidCustomValueText;
|
|
650
|
+
this.helpTextType = VALIDATION.ERROR;
|
|
651
|
+
this.setInputValidity();
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
// When the input is cleared, reset the selected value to null (placeholder will be shown if present)
|
|
656
|
+
this.setSelectedValue(null);
|
|
657
|
+
}
|
|
658
|
+
this.closePopover();
|
|
565
659
|
}
|
|
566
660
|
/** @internal */
|
|
567
661
|
updateFocusAndScrollIntoView(options, oldIndex, newIndex) {
|
|
@@ -655,50 +749,54 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
655
749
|
* @internal
|
|
656
750
|
*/
|
|
657
751
|
updateHiddenOptions() {
|
|
752
|
+
const groupVisibleCounts = new Map();
|
|
753
|
+
const groups = new Set();
|
|
754
|
+
// First pass: update options and collect visibility info per optgroup
|
|
658
755
|
this.navItems.forEach(option => {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
756
|
+
var _a;
|
|
757
|
+
const matchesFilter = this.compareOptionWithValue(option, this.filteredValue);
|
|
758
|
+
if (matchesFilter) {
|
|
759
|
+
option.removeAttribute('data-hidden');
|
|
662
760
|
}
|
|
663
761
|
else {
|
|
664
|
-
option.
|
|
665
|
-
|
|
762
|
+
option.setAttribute('data-hidden', '');
|
|
763
|
+
}
|
|
764
|
+
const parent = option.parentElement;
|
|
765
|
+
if (parent && parent.matches(OPTIONGROUP_TAG_NAME)) {
|
|
766
|
+
const group = parent;
|
|
767
|
+
groups.add(group);
|
|
768
|
+
if (matchesFilter) {
|
|
769
|
+
groupVisibleCounts.set(group, ((_a = groupVisibleCounts.get(group)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
770
|
+
}
|
|
666
771
|
}
|
|
667
772
|
});
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
option.parentElement.nextElementSibling.setAttribute('data-hidden', '');
|
|
773
|
+
// Second pass: toggle optgroup + following divider based on aggregated counts
|
|
774
|
+
groups.forEach(group => {
|
|
775
|
+
var _a;
|
|
776
|
+
const hasVisibleChildren = ((_a = groupVisibleCounts.get(group)) !== null && _a !== void 0 ? _a : 0) > 0;
|
|
777
|
+
const divider = group.nextElementSibling;
|
|
778
|
+
if (hasVisibleChildren) {
|
|
779
|
+
group.removeAttribute('data-hidden');
|
|
780
|
+
if (divider && divider.matches(DIVIDER_TAG_NAME)) {
|
|
781
|
+
divider.removeAttribute('data-hidden');
|
|
678
782
|
}
|
|
679
783
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
var _a, _b, _c;
|
|
685
|
-
if ((_a = option.parentElement) === null || _a === void 0 ? void 0 : _a.matches(OPTIONGROUP_TAG_NAME)) {
|
|
686
|
-
const optionGroupChildren = (_b = Array.from(option.parentElement.children)) === null || _b === void 0 ? void 0 : _b.filter(option => !option.hasAttribute('data-hidden'));
|
|
687
|
-
if (optionGroupChildren.length > 0) {
|
|
688
|
-
option.parentElement.removeAttribute('data-hidden');
|
|
689
|
-
if ((_c = option.parentElement.nextElementSibling) === null || _c === void 0 ? void 0 : _c.matches(DIVIDER_TAG_NAME)) {
|
|
690
|
-
option.parentElement.nextElementSibling.removeAttribute('data-hidden');
|
|
784
|
+
else {
|
|
785
|
+
group.setAttribute('data-hidden', '');
|
|
786
|
+
if (divider && divider.matches(DIVIDER_TAG_NAME)) {
|
|
787
|
+
divider.setAttribute('data-hidden', '');
|
|
691
788
|
}
|
|
692
789
|
}
|
|
693
|
-
}
|
|
790
|
+
});
|
|
694
791
|
}
|
|
695
792
|
/** @internal */
|
|
696
793
|
handleInputChange(event) {
|
|
697
794
|
var _a;
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
795
|
+
event.preventDefault();
|
|
796
|
+
event.stopPropagation();
|
|
797
|
+
this.filteredValue = event.target.value;
|
|
798
|
+
// Don't reset the selected value immediately - preserve it until user makes a selection
|
|
799
|
+
// or loses focus and we determine what to do
|
|
702
800
|
this.resetFocusedOption();
|
|
703
801
|
this.updateHiddenOptions();
|
|
704
802
|
// remove the selected attribute on input change
|
|
@@ -706,17 +804,21 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
706
804
|
if (this.isOpen === false) {
|
|
707
805
|
this.openPopover();
|
|
708
806
|
}
|
|
807
|
+
// Dispatch custom event to notify about the input change with the current filtered value.
|
|
808
|
+
ComboboxEventManager.onInputCombobox(this, { value: this.filteredValue });
|
|
709
809
|
}
|
|
710
810
|
/** @internal */
|
|
711
811
|
handleOptionsClick(event) {
|
|
712
812
|
var _a;
|
|
813
|
+
event.preventDefault();
|
|
814
|
+
event.stopPropagation();
|
|
713
815
|
// ensure we get the actual option element even if the click target is a child node
|
|
714
816
|
const option = (_a = event.target.closest(OPTION_TAG_NAME)) !== null && _a !== void 0 ? _a : null;
|
|
715
817
|
if (option && !option.hasAttribute('disabled')) {
|
|
716
818
|
this.setSelectedValue(option);
|
|
717
819
|
this.closePopover();
|
|
718
820
|
// Focus on the combobox after click
|
|
719
|
-
this.
|
|
821
|
+
this.focusComboboxBase();
|
|
720
822
|
}
|
|
721
823
|
}
|
|
722
824
|
/** @internal */
|
|
@@ -772,6 +874,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
772
874
|
id="${this.id}"
|
|
773
875
|
slot="input"
|
|
774
876
|
?disabled="${this.disabled}"
|
|
877
|
+
tabindex="${this.disabled ? -1 : 0}"
|
|
775
878
|
.value="${live(this.filteredValue)}"
|
|
776
879
|
autocomplete="${AUTO_COMPLETE.OFF}"
|
|
777
880
|
part="input-text"
|
|
@@ -809,7 +912,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
809
912
|
<div part="combobox-base" id="${TRIGGER_ID}">
|
|
810
913
|
${this.renderNativeInput()}
|
|
811
914
|
<mdc-input
|
|
812
|
-
@click="${
|
|
915
|
+
@click="${this.handleTriggerClick}"
|
|
813
916
|
?disabled="${this.disabled}"
|
|
814
917
|
?readonly="${this.readonly}"
|
|
815
918
|
help-text-type="${this.helpTextType}"
|
|
@@ -817,7 +920,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
817
920
|
${this.renderBaseInput()}
|
|
818
921
|
</mdc-input>
|
|
819
922
|
<mdc-buttonsimple
|
|
820
|
-
@click="${
|
|
923
|
+
@click="${this.handleTriggerClick}"
|
|
821
924
|
part="combobox-button"
|
|
822
925
|
?disabled="${this.disabled}"
|
|
823
926
|
tabindex="-1"
|
|
@@ -838,7 +941,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
838
941
|
}}"
|
|
839
942
|
@closebyoutsideclick="${() => {
|
|
840
943
|
this.closePopover();
|
|
841
|
-
this.
|
|
944
|
+
this.focusComboboxBase();
|
|
842
945
|
}}"
|
|
843
946
|
backdrop
|
|
844
947
|
backdrop-append-to="${ifDefined(this.backdropAppendTo)}"
|
|
@@ -857,7 +960,7 @@ class Combobox extends KeyDownHandledMixin(KeyToActionMixin(CaptureDestroyEventF
|
|
|
857
960
|
z-index="${ifDefined(this.popoverZIndex)}"
|
|
858
961
|
>
|
|
859
962
|
${this.renderNoResultsText(options.length)}
|
|
860
|
-
<slot @
|
|
963
|
+
<slot @mousedown="${this.handleOptionsClick}"></slot>
|
|
861
964
|
</mdc-popover>
|
|
862
965
|
</div>
|
|
863
966
|
${this.renderHelperText()}
|
|
@@ -917,8 +1020,4 @@ __decorate([
|
|
|
917
1020
|
state(),
|
|
918
1021
|
__metadata("design:type", Object)
|
|
919
1022
|
], Combobox.prototype, "filteredValue", void 0);
|
|
920
|
-
__decorate([
|
|
921
|
-
state(),
|
|
922
|
-
__metadata("design:type", Object)
|
|
923
|
-
], Combobox.prototype, "forceValueUpdate", void 0);
|
|
924
1023
|
export default Combobox;
|
|
@@ -16,12 +16,12 @@ export declare class ComboboxEventManager {
|
|
|
16
16
|
* @param instance - The combobox instance.
|
|
17
17
|
* @param option - The value and label of the selected option.
|
|
18
18
|
*/
|
|
19
|
-
static onInputCombobox(instance: Combobox, option: Option): void;
|
|
19
|
+
static onInputCombobox(instance: Combobox, option: Option | null): void;
|
|
20
20
|
/**
|
|
21
21
|
* Dispatches a custom event for the Combobox when the selected option changes.
|
|
22
22
|
*
|
|
23
23
|
* @param instance - The combobox instance.
|
|
24
24
|
* @param option - The value and label of the selected option.
|
|
25
25
|
*/
|
|
26
|
-
static onChangeCombobox(instance: Combobox, option: Option): void;
|
|
26
|
+
static onChangeCombobox(instance: Combobox, option: Option | null): void;
|
|
27
27
|
}
|
|
@@ -43,6 +43,7 @@ declare const TimePicker_base: import("../../utils/mixins/index.types").Construc
|
|
|
43
43
|
* @cssproperty --mdc-timepicker-border-color - Border color of the timepicker input.
|
|
44
44
|
* @cssproperty --mdc-timepicker-text-color - Text color of the timepicker input.
|
|
45
45
|
* @cssproperty --mdc-timepicker-width - Width of the timepicker component.
|
|
46
|
+
* @cssproperty --mdc-timepicker-listbox-height - The max-height of the dropdown listbox. Default shows ~6 items.
|
|
46
47
|
*
|
|
47
48
|
* @csspart label - The label element.
|
|
48
49
|
* @csspart label-text - The container for the label and required indicator elements.
|
|
@@ -55,6 +55,7 @@ import styles from './timepicker.styles';
|
|
|
55
55
|
* @cssproperty --mdc-timepicker-border-color - Border color of the timepicker input.
|
|
56
56
|
* @cssproperty --mdc-timepicker-text-color - Text color of the timepicker input.
|
|
57
57
|
* @cssproperty --mdc-timepicker-width - Width of the timepicker component.
|
|
58
|
+
* @cssproperty --mdc-timepicker-listbox-height - The max-height of the dropdown listbox. Default shows ~6 items.
|
|
58
59
|
*
|
|
59
60
|
* @csspart label - The label element.
|
|
60
61
|
* @csspart label-text - The container for the label and required indicator elements.
|
|
@@ -881,15 +882,14 @@ class TimePicker extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)
|
|
|
881
882
|
}}"
|
|
882
883
|
exportparts="popover-content"
|
|
883
884
|
>
|
|
884
|
-
<
|
|
885
|
+
<mdc-selectlistbox
|
|
885
886
|
id="${LISTBOX_ID}"
|
|
886
887
|
part="listbox"
|
|
887
|
-
role="listbox"
|
|
888
888
|
aria-label="${this.localeTimeOptionsLabel}"
|
|
889
889
|
@keydown="${this.handleListboxKeydown}"
|
|
890
890
|
>
|
|
891
891
|
${this.renderTimeOptions()}
|
|
892
|
-
</
|
|
892
|
+
</mdc-selectlistbox>
|
|
893
893
|
</mdc-popover>
|
|
894
894
|
</div>
|
|
895
895
|
${this.helpText ? this.renderHelperText() : nothing}
|
|
@@ -7,6 +7,7 @@ const styles = css `
|
|
|
7
7
|
--mdc-timepicker-border-color: var(--mds-color-theme-outline-input-normal);
|
|
8
8
|
--mdc-timepicker-width: fit-content;
|
|
9
9
|
--mdc-timepicker-listbox-width: 100%;
|
|
10
|
+
--mdc-timepicker-listbox-height: 15rem;
|
|
10
11
|
|
|
11
12
|
display: flex;
|
|
12
13
|
flex-direction: column;
|
|
@@ -130,9 +131,14 @@ const styles = css `
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
/* Popover height, width & padding overrides */
|
|
134
|
+
:host mdc-popover {
|
|
135
|
+
--mdc-popover-max-width: var(--mdc-timepicker-listbox-width);
|
|
136
|
+
--mdc-popover-max-height: var(--mdc-timepicker-listbox-height);
|
|
137
|
+
min-width: max-content;
|
|
138
|
+
}
|
|
133
139
|
:host mdc-popover::part(popover-content) {
|
|
134
140
|
max-height: var(--mdc-popover-max-height);
|
|
135
|
-
min-width:
|
|
141
|
+
min-width: auto;
|
|
136
142
|
padding: 0.75rem 0.5rem;
|
|
137
143
|
}
|
|
138
144
|
|