@vaadin/multi-select-combo-box 25.0.0-alpha7 → 25.0.0-alpha9
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/package.json +14 -14
- package/src/styles/vaadin-multi-select-combo-box-base-styles.js +44 -45
- package/src/styles/vaadin-multi-select-combo-box-chip-base-styles.js +101 -103
- package/src/styles/vaadin-multi-select-combo-box-core-styles.js +4 -0
- package/src/styles/vaadin-multi-select-combo-box-overlay-base-styles.js +5 -7
- package/src/vaadin-multi-select-combo-box-chip.js +1 -1
- package/src/vaadin-multi-select-combo-box-item.js +1 -1
- package/src/vaadin-multi-select-combo-box-mixin.d.ts +9 -80
- package/src/vaadin-multi-select-combo-box-mixin.js +380 -266
- package/src/vaadin-multi-select-combo-box-overlay.js +1 -2
- package/src/vaadin-multi-select-combo-box.d.ts +13 -6
- package/src/vaadin-multi-select-combo-box.js +35 -88
- package/web-types.json +207 -230
- package/web-types.lit.json +78 -78
- package/src/vaadin-multi-select-combo-box-internal-mixin.js +0 -446
- package/src/vaadin-multi-select-combo-box-internal.js +0 -57
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { announce } from '@vaadin/a11y-base/src/announce.js';
|
|
7
|
+
import { ComboBoxDataProviderMixin } from '@vaadin/combo-box/src/vaadin-combo-box-data-provider-mixin.js';
|
|
8
|
+
import { ComboBoxItemsMixin } from '@vaadin/combo-box/src/vaadin-combo-box-items-mixin.js';
|
|
9
|
+
import { ComboBoxPlaceholder } from '@vaadin/combo-box/src/vaadin-combo-box-placeholder.js';
|
|
7
10
|
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
8
11
|
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
9
12
|
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
@@ -13,11 +16,15 @@ import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-c
|
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* @polymerMixin
|
|
19
|
+
* @mixes ComboBoxDataProviderMixin
|
|
20
|
+
* @mixes ComboBoxItemsMixin
|
|
16
21
|
* @mixes InputControlMixin
|
|
17
22
|
* @mixes ResizeMixin
|
|
18
23
|
*/
|
|
19
24
|
export const MultiSelectComboBoxMixin = (superClass) =>
|
|
20
|
-
class MultiSelectComboBoxMixinClass extends
|
|
25
|
+
class MultiSelectComboBoxMixinClass extends ComboBoxDataProviderMixin(
|
|
26
|
+
ComboBoxItemsMixin(InputControlMixin(ResizeMixin(superClass))),
|
|
27
|
+
) {
|
|
21
28
|
static get properties() {
|
|
22
29
|
return {
|
|
23
30
|
/**
|
|
@@ -29,7 +36,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
29
36
|
type: Boolean,
|
|
30
37
|
value: false,
|
|
31
38
|
reflectToAttribute: true,
|
|
32
|
-
observer: '_autoExpandHorizontallyChanged',
|
|
33
39
|
sync: true,
|
|
34
40
|
},
|
|
35
41
|
|
|
@@ -43,37 +49,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
43
49
|
type: Boolean,
|
|
44
50
|
value: false,
|
|
45
51
|
reflectToAttribute: true,
|
|
46
|
-
observer: '_autoExpandVerticallyChanged',
|
|
47
|
-
sync: true,
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Set true to prevent the overlay from opening automatically.
|
|
52
|
-
* @attr {boolean} auto-open-disabled
|
|
53
|
-
*/
|
|
54
|
-
autoOpenDisabled: {
|
|
55
|
-
type: Boolean,
|
|
56
|
-
sync: true,
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Set to true to display the clear icon which clears the input.
|
|
61
|
-
* @attr {boolean} clear-button-visible
|
|
62
|
-
*/
|
|
63
|
-
clearButtonVisible: {
|
|
64
|
-
type: Boolean,
|
|
65
|
-
reflectToAttribute: true,
|
|
66
|
-
observer: '_clearButtonVisibleChanged',
|
|
67
|
-
value: false,
|
|
68
|
-
sync: true,
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* A full set of items to filter the visible options from.
|
|
73
|
-
* The items can be of either `String` or `Object` type.
|
|
74
|
-
*/
|
|
75
|
-
items: {
|
|
76
|
-
type: Array,
|
|
77
52
|
sync: true,
|
|
78
53
|
},
|
|
79
54
|
|
|
@@ -85,28 +60,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
85
60
|
*/
|
|
86
61
|
itemClassNameGenerator: {
|
|
87
62
|
type: Object,
|
|
88
|
-
observer: '__itemClassNameGeneratorChanged',
|
|
89
|
-
sync: true,
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* The item property used for a visual representation of the item.
|
|
94
|
-
* @attr {string} item-label-path
|
|
95
|
-
*/
|
|
96
|
-
itemLabelPath: {
|
|
97
|
-
type: String,
|
|
98
|
-
value: 'label',
|
|
99
|
-
sync: true,
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Path for the value of the item. If `items` is an array of objects,
|
|
104
|
-
* this property is used as a string value for the selected item.
|
|
105
|
-
* @attr {string} item-value-path
|
|
106
|
-
*/
|
|
107
|
-
itemValuePath: {
|
|
108
|
-
type: String,
|
|
109
|
-
value: 'value',
|
|
110
63
|
sync: true,
|
|
111
64
|
},
|
|
112
65
|
|
|
@@ -174,23 +127,12 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
174
127
|
sync: true,
|
|
175
128
|
},
|
|
176
129
|
|
|
177
|
-
/**
|
|
178
|
-
* A space-delimited list of CSS class names to set on the overlay element.
|
|
179
|
-
*
|
|
180
|
-
* @attr {string} overlay-class
|
|
181
|
-
*/
|
|
182
|
-
overlayClass: {
|
|
183
|
-
type: String,
|
|
184
|
-
sync: true,
|
|
185
|
-
},
|
|
186
|
-
|
|
187
130
|
/**
|
|
188
131
|
* When present, it specifies that the field is read-only.
|
|
189
132
|
*/
|
|
190
133
|
readonly: {
|
|
191
134
|
type: Boolean,
|
|
192
135
|
value: false,
|
|
193
|
-
observer: '_readonlyChanged',
|
|
194
136
|
reflectToAttribute: true,
|
|
195
137
|
sync: true,
|
|
196
138
|
},
|
|
@@ -206,53 +148,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
206
148
|
sync: true,
|
|
207
149
|
},
|
|
208
150
|
|
|
209
|
-
/**
|
|
210
|
-
* True if the dropdown is open, false otherwise.
|
|
211
|
-
*/
|
|
212
|
-
opened: {
|
|
213
|
-
type: Boolean,
|
|
214
|
-
notify: true,
|
|
215
|
-
value: false,
|
|
216
|
-
reflectToAttribute: true,
|
|
217
|
-
sync: true,
|
|
218
|
-
},
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Total number of items.
|
|
222
|
-
*/
|
|
223
|
-
size: {
|
|
224
|
-
type: Number,
|
|
225
|
-
sync: true,
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Number of items fetched at a time from the data provider.
|
|
230
|
-
* @attr {number} page-size
|
|
231
|
-
*/
|
|
232
|
-
pageSize: {
|
|
233
|
-
type: Number,
|
|
234
|
-
value: 50,
|
|
235
|
-
observer: '_pageSizeChanged',
|
|
236
|
-
sync: true,
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Function that provides items lazily. Receives two arguments:
|
|
241
|
-
*
|
|
242
|
-
* - `params` - Object with the following properties:
|
|
243
|
-
* - `params.page` Requested page index
|
|
244
|
-
* - `params.pageSize` Current page size
|
|
245
|
-
* - `params.filter` Currently applied filter
|
|
246
|
-
*
|
|
247
|
-
* - `callback(items, size)` - Callback function with arguments:
|
|
248
|
-
* - `items` Current page of items
|
|
249
|
-
* - `size` Total number of items.
|
|
250
|
-
*/
|
|
251
|
-
dataProvider: {
|
|
252
|
-
type: Object,
|
|
253
|
-
sync: true,
|
|
254
|
-
},
|
|
255
|
-
|
|
256
151
|
/**
|
|
257
152
|
* When true, the user can input a value that is not present in the items list.
|
|
258
153
|
* @attr {boolean} allow-custom-value
|
|
@@ -290,26 +185,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
290
185
|
sync: true,
|
|
291
186
|
},
|
|
292
187
|
|
|
293
|
-
/**
|
|
294
|
-
* Filtering string the user has typed into the input field.
|
|
295
|
-
*/
|
|
296
|
-
filter: {
|
|
297
|
-
type: String,
|
|
298
|
-
value: '',
|
|
299
|
-
notify: true,
|
|
300
|
-
sync: true,
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* A subset of items, filtered based on the user input. Filtered items
|
|
305
|
-
* can be assigned directly to omit the internal filtering functionality.
|
|
306
|
-
* The items can be of either `String` or `Object` type.
|
|
307
|
-
*/
|
|
308
|
-
filteredItems: {
|
|
309
|
-
type: Array,
|
|
310
|
-
sync: true,
|
|
311
|
-
},
|
|
312
|
-
|
|
313
188
|
/**
|
|
314
189
|
* Set to true to group selected items at the top of the overlay.
|
|
315
190
|
* @attr {boolean} selected-items-on-top
|
|
@@ -348,6 +223,13 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
348
223
|
/** @private */
|
|
349
224
|
_topGroup: {
|
|
350
225
|
type: Array,
|
|
226
|
+
observer: '_topGroupChanged',
|
|
227
|
+
sync: true,
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/** @private */
|
|
231
|
+
_inputField: {
|
|
232
|
+
type: Object,
|
|
351
233
|
},
|
|
352
234
|
};
|
|
353
235
|
}
|
|
@@ -355,7 +237,9 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
355
237
|
static get observers() {
|
|
356
238
|
return [
|
|
357
239
|
'_selectedItemsChanged(selectedItems)',
|
|
240
|
+
'__openedOrItemsChanged(opened, _dropdownItems, loading, __keepOverlayOpened)',
|
|
358
241
|
'__updateOverflowChip(_overflow, _overflowItems, disabled, readonly)',
|
|
242
|
+
'__updateScroller(opened, _dropdownItems, _focusedIndex, _theme)',
|
|
359
243
|
'__updateTopGroup(selectedItemsOnTop, selectedItems, opened)',
|
|
360
244
|
];
|
|
361
245
|
}
|
|
@@ -399,6 +283,15 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
399
283
|
return this.selectedItems && this.selectedItems.length > 0;
|
|
400
284
|
}
|
|
401
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Tag name prefix used by scroller and items.
|
|
288
|
+
* @protected
|
|
289
|
+
* @return {string}
|
|
290
|
+
*/
|
|
291
|
+
get _tagNamePrefix() {
|
|
292
|
+
return 'vaadin-multi-select-combo-box';
|
|
293
|
+
}
|
|
294
|
+
|
|
402
295
|
/** @protected */
|
|
403
296
|
ready() {
|
|
404
297
|
super.ready();
|
|
@@ -419,6 +312,7 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
419
312
|
this._tooltipController.setAriaTarget(this.inputElement);
|
|
420
313
|
this._tooltipController.setShouldShow((target) => !target.opened);
|
|
421
314
|
|
|
315
|
+
this._toggleElement = this.$.toggleButton;
|
|
422
316
|
this._inputField = this.shadowRoot.querySelector('[part="input-field"]');
|
|
423
317
|
|
|
424
318
|
this._overflowController = new SlotController(this, 'overflow', 'vaadin-multi-select-combo-box-chip', {
|
|
@@ -428,8 +322,41 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
428
322
|
},
|
|
429
323
|
});
|
|
430
324
|
this.addController(this._overflowController);
|
|
325
|
+
}
|
|
431
326
|
|
|
432
|
-
|
|
327
|
+
/** @protected */
|
|
328
|
+
updated(props) {
|
|
329
|
+
super.updated(props);
|
|
330
|
+
|
|
331
|
+
['loading', 'itemIdPath', 'itemClassNameGenerator', 'renderer'].forEach((prop) => {
|
|
332
|
+
if (props.has(prop)) {
|
|
333
|
+
this._scroller[prop] = this[prop];
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
if (props.has('selectedItems') && this.opened) {
|
|
338
|
+
this.$.overlay._updateOverlayWidth();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const chipProps = [
|
|
342
|
+
'autoExpandHorizontally',
|
|
343
|
+
'autoExpandVertically',
|
|
344
|
+
'disabled',
|
|
345
|
+
'readonly',
|
|
346
|
+
'clearButtonVisible',
|
|
347
|
+
'itemClassNameGenerator',
|
|
348
|
+
];
|
|
349
|
+
if (chipProps.some((prop) => props.has(prop))) {
|
|
350
|
+
this.__updateChips();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (props.has('readonly')) {
|
|
354
|
+
this._setDropdownItems(this.filteredItems);
|
|
355
|
+
|
|
356
|
+
if (this.dataProvider) {
|
|
357
|
+
this.clearCache();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
433
360
|
}
|
|
434
361
|
|
|
435
362
|
/**
|
|
@@ -440,6 +367,17 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
440
367
|
return this.required && !this.readonly ? this._hasValue : true;
|
|
441
368
|
}
|
|
442
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Opens the dropdown list.
|
|
372
|
+
* @override
|
|
373
|
+
*/
|
|
374
|
+
open() {
|
|
375
|
+
// Allow opening dropdown when readonly.
|
|
376
|
+
if (!this.disabled && !(this.readonly && this.selectedItems.length === 0)) {
|
|
377
|
+
this.opened = true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
443
381
|
/**
|
|
444
382
|
* Clears the selected items.
|
|
445
383
|
*/
|
|
@@ -451,11 +389,15 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
451
389
|
|
|
452
390
|
/**
|
|
453
391
|
* Clears the cached pages and reloads data from data provider when needed.
|
|
392
|
+
* @override
|
|
454
393
|
*/
|
|
455
394
|
clearCache() {
|
|
456
|
-
|
|
457
|
-
|
|
395
|
+
// Do not clear the data provider cache when read-only.
|
|
396
|
+
if (this.readonly) {
|
|
397
|
+
return;
|
|
458
398
|
}
|
|
399
|
+
|
|
400
|
+
super.clearCache();
|
|
459
401
|
}
|
|
460
402
|
|
|
461
403
|
/**
|
|
@@ -465,34 +407,130 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
465
407
|
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
466
408
|
*/
|
|
467
409
|
requestContentUpdate() {
|
|
468
|
-
if (this
|
|
469
|
-
|
|
410
|
+
if (!this._scroller) {
|
|
411
|
+
return;
|
|
470
412
|
}
|
|
413
|
+
|
|
414
|
+
this._scroller.requestContentUpdate();
|
|
415
|
+
|
|
416
|
+
this._getItemElements().forEach((item) => {
|
|
417
|
+
item.requestContentUpdate();
|
|
418
|
+
});
|
|
471
419
|
}
|
|
472
420
|
|
|
473
421
|
/**
|
|
474
|
-
* Override method
|
|
422
|
+
* Override method from `ComboBoxBaseMixin` to implement clearing logic.
|
|
475
423
|
* @protected
|
|
476
424
|
* @override
|
|
477
425
|
*/
|
|
478
|
-
|
|
479
|
-
|
|
426
|
+
_onClearAction() {
|
|
427
|
+
this.clear();
|
|
428
|
+
}
|
|
480
429
|
|
|
481
|
-
|
|
482
|
-
|
|
430
|
+
/**
|
|
431
|
+
* Override method from `ComboBoxBaseMixin`
|
|
432
|
+
* to commit value on overlay closing.
|
|
433
|
+
* @protected
|
|
434
|
+
* @override
|
|
435
|
+
*/
|
|
436
|
+
_onClosed() {
|
|
437
|
+
// Do not commit selected item again on outside click
|
|
438
|
+
this._ignoreCommitValue = true;
|
|
439
|
+
|
|
440
|
+
if (!this.loading || this.allowCustomValue) {
|
|
441
|
+
this._commitValue();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** @private */
|
|
446
|
+
__updateScroller(opened, items, focusedIndex, theme) {
|
|
447
|
+
if (opened) {
|
|
448
|
+
this._scroller.style.maxHeight =
|
|
449
|
+
getComputedStyle(this).getPropertyValue(`--${this._tagNamePrefix}-overlay-max-height`) || '65vh';
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
this._scroller.setProperties({
|
|
453
|
+
items: opened ? items : [],
|
|
454
|
+
opened,
|
|
455
|
+
focusedIndex,
|
|
456
|
+
theme,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** @private */
|
|
461
|
+
__openedOrItemsChanged(opened, items, loading, keepOverlayOpened) {
|
|
462
|
+
// Close the overlay if there are no items to display.
|
|
463
|
+
// See https://github.com/vaadin/vaadin-combo-box/pull/964
|
|
464
|
+
this._overlayOpened = opened && (keepOverlayOpened || loading || !!(items && items.length));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* @protected
|
|
469
|
+
*/
|
|
470
|
+
_closeOrCommit() {
|
|
471
|
+
if (!this.opened) {
|
|
472
|
+
this._commitValue();
|
|
473
|
+
} else {
|
|
474
|
+
this.close();
|
|
483
475
|
}
|
|
484
476
|
}
|
|
485
477
|
|
|
486
478
|
/**
|
|
487
|
-
* Override method inherited from `InputMixin` to forward the input to combo-box.
|
|
488
479
|
* @protected
|
|
489
480
|
* @override
|
|
490
481
|
*/
|
|
491
|
-
|
|
492
|
-
|
|
482
|
+
_commitValue() {
|
|
483
|
+
// Store filter value for checking if user input is matching
|
|
484
|
+
// an item which is already selected, to not un-select it.
|
|
485
|
+
this._lastFilter = this.filter;
|
|
486
|
+
|
|
487
|
+
// Do not commit focused item on not blur / outside click
|
|
488
|
+
if (this._ignoreCommitValue) {
|
|
489
|
+
this._inputElementValue = '';
|
|
490
|
+
this._focusedIndex = -1;
|
|
491
|
+
this._ignoreCommitValue = false;
|
|
492
|
+
} else {
|
|
493
|
+
this.__commitUserInput();
|
|
494
|
+
}
|
|
493
495
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
+
// Clear filter unless keepFilter is set
|
|
497
|
+
if (!this.keepFilter || !this.opened) {
|
|
498
|
+
this.filter = '';
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/** @private */
|
|
503
|
+
__commitUserInput() {
|
|
504
|
+
if (this._focusedIndex > -1) {
|
|
505
|
+
const focusedItem = this._dropdownItems[this._focusedIndex];
|
|
506
|
+
this.__selectItem(focusedItem);
|
|
507
|
+
} else if (this._inputElementValue) {
|
|
508
|
+
// Detect if input value doesn't match an existing item
|
|
509
|
+
const items = [...this._dropdownItems];
|
|
510
|
+
const itemMatchingInputValue = items[this.__getItemIndexByLabel(items, this._inputElementValue)];
|
|
511
|
+
|
|
512
|
+
if (this.allowCustomValue && !itemMatchingInputValue) {
|
|
513
|
+
const customValue = this._inputElementValue;
|
|
514
|
+
|
|
515
|
+
// Store reference to the last custom value for checking it on focusout.
|
|
516
|
+
this._lastCustomValue = customValue;
|
|
517
|
+
|
|
518
|
+
this.__clearInternalValue(true);
|
|
519
|
+
|
|
520
|
+
this.dispatchEvent(
|
|
521
|
+
new CustomEvent('custom-value-set', {
|
|
522
|
+
detail: customValue,
|
|
523
|
+
composed: true,
|
|
524
|
+
bubbles: true,
|
|
525
|
+
}),
|
|
526
|
+
);
|
|
527
|
+
} else if (!this.allowCustomValue && !this.opened && itemMatchingInputValue) {
|
|
528
|
+
// An item matching by label was found, select it.
|
|
529
|
+
this.__selectItem(itemMatchingInputValue);
|
|
530
|
+
} else {
|
|
531
|
+
// Clear input value on Escape press while closed.
|
|
532
|
+
this._inputElementValue = '';
|
|
533
|
+
}
|
|
496
534
|
}
|
|
497
535
|
}
|
|
498
536
|
|
|
@@ -502,6 +540,10 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
502
540
|
* @protected
|
|
503
541
|
*/
|
|
504
542
|
_setFocused(focused) {
|
|
543
|
+
if (!focused) {
|
|
544
|
+
this._ignoreCommitValue = true;
|
|
545
|
+
}
|
|
546
|
+
|
|
505
547
|
super._setFocused(focused);
|
|
506
548
|
|
|
507
549
|
// Do not validate when focusout is caused by document
|
|
@@ -510,6 +552,10 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
510
552
|
this._focusedChipIndex = -1;
|
|
511
553
|
this._requestValidation();
|
|
512
554
|
}
|
|
555
|
+
|
|
556
|
+
if (!focused && this.readonly && !this._closeOnBlurIsPrevented) {
|
|
557
|
+
this.close();
|
|
558
|
+
}
|
|
513
559
|
}
|
|
514
560
|
|
|
515
561
|
/**
|
|
@@ -541,73 +587,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
541
587
|
super._delegateAttribute(name, value);
|
|
542
588
|
}
|
|
543
589
|
|
|
544
|
-
/** @private */
|
|
545
|
-
_autoExpandHorizontallyChanged(autoExpand, oldAutoExpand) {
|
|
546
|
-
if (autoExpand || oldAutoExpand) {
|
|
547
|
-
this.__updateChips();
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/** @private */
|
|
552
|
-
_autoExpandVerticallyChanged(autoExpand, oldAutoExpand) {
|
|
553
|
-
if (autoExpand || oldAutoExpand) {
|
|
554
|
-
this.__updateChips();
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* Setting clear button visible reduces total space available
|
|
560
|
-
* for rendering chips, and making it hidden increases it.
|
|
561
|
-
* @private
|
|
562
|
-
*/
|
|
563
|
-
_clearButtonVisibleChanged(visible, oldVisible) {
|
|
564
|
-
if (visible || oldVisible) {
|
|
565
|
-
this.__updateChips();
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Implement two-way binding for the `filteredItems` property
|
|
571
|
-
* that can be set on the internal combo-box element.
|
|
572
|
-
*
|
|
573
|
-
* @param {CustomEvent} event
|
|
574
|
-
* @private
|
|
575
|
-
*/
|
|
576
|
-
_onFilteredItemsChanged(event) {
|
|
577
|
-
const { value } = event.detail;
|
|
578
|
-
if (Array.isArray(value) || value == null) {
|
|
579
|
-
this.filteredItems = value;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/** @private */
|
|
584
|
-
_readonlyChanged(readonly, oldReadonly) {
|
|
585
|
-
if (readonly || oldReadonly) {
|
|
586
|
-
this.__updateChips();
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
if (this.dataProvider) {
|
|
590
|
-
this.clearCache();
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/** @private */
|
|
595
|
-
__itemClassNameGeneratorChanged(generator, oldGenerator) {
|
|
596
|
-
if (generator || oldGenerator) {
|
|
597
|
-
this.__updateChips();
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
/** @private */
|
|
602
|
-
_pageSizeChanged(pageSize, oldPageSize) {
|
|
603
|
-
if (Math.floor(pageSize) !== pageSize || pageSize <= 0) {
|
|
604
|
-
this.pageSize = oldPageSize;
|
|
605
|
-
console.error('"pageSize" value must be an integer > 0');
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
this.$.comboBox.pageSize = this.pageSize;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
590
|
/** @private */
|
|
612
591
|
_placeholderChanged(placeholder) {
|
|
613
592
|
const tmpPlaceholder = this.__tmpA11yPlaceholder;
|
|
@@ -643,15 +622,90 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
643
622
|
|
|
644
623
|
// Update selected for dropdown items
|
|
645
624
|
this.requestContentUpdate();
|
|
625
|
+
}
|
|
646
626
|
|
|
647
|
-
|
|
648
|
-
|
|
627
|
+
/** @private */
|
|
628
|
+
_topGroupChanged(topGroup) {
|
|
629
|
+
if (topGroup) {
|
|
630
|
+
this._setDropdownItems(this.filteredItems);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Override method from `ComboBoxBaseMixin` to handle valid value.
|
|
636
|
+
* @protected
|
|
637
|
+
* @override
|
|
638
|
+
*/
|
|
639
|
+
_hasValidInputValue() {
|
|
640
|
+
const hasInvalidOption = this._focusedIndex < 0 && this._inputElementValue !== '';
|
|
641
|
+
return this.allowCustomValue || !hasInvalidOption;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Override method inherited from the combo-box
|
|
646
|
+
* to not request data provider when read-only.
|
|
647
|
+
*
|
|
648
|
+
* @protected
|
|
649
|
+
* @override
|
|
650
|
+
*/
|
|
651
|
+
_shouldFetchData() {
|
|
652
|
+
if (this.readonly) {
|
|
653
|
+
return false;
|
|
649
654
|
}
|
|
655
|
+
|
|
656
|
+
return super._shouldFetchData();
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Override combo-box method to group selected
|
|
661
|
+
* items at the top of the overlay.
|
|
662
|
+
*
|
|
663
|
+
* @protected
|
|
664
|
+
* @override
|
|
665
|
+
*/
|
|
666
|
+
_setDropdownItems(items) {
|
|
667
|
+
if (this.readonly) {
|
|
668
|
+
this.__setDropdownItems(this.selectedItems);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (this.filter || !this.selectedItemsOnTop) {
|
|
673
|
+
this.__setDropdownItems(items);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if (items && items.length && this._topGroup && this._topGroup.length) {
|
|
678
|
+
// Filter out items included to the top group.
|
|
679
|
+
const filteredItems = items.filter((item) => this._findIndex(item, this._topGroup, this.itemIdPath) === -1);
|
|
680
|
+
|
|
681
|
+
this.__setDropdownItems(this._topGroup.concat(filteredItems));
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
this.__setDropdownItems(items);
|
|
650
686
|
}
|
|
651
687
|
|
|
652
688
|
/** @private */
|
|
653
|
-
|
|
654
|
-
|
|
689
|
+
__setDropdownItems(newItems) {
|
|
690
|
+
const oldItems = this._dropdownItems;
|
|
691
|
+
this._dropdownItems = newItems;
|
|
692
|
+
|
|
693
|
+
// Store the currently focused item if any. The focused index preserves
|
|
694
|
+
// in the case when more filtered items are loading but it is reset
|
|
695
|
+
// when the user types in a filter query.
|
|
696
|
+
const focusedItem = oldItems ? oldItems[this._focusedIndex] : null;
|
|
697
|
+
|
|
698
|
+
// Try to first set focus on the item that had been focused before `newItems` were updated
|
|
699
|
+
// if it is still present in the `newItems` array. Otherwise, set the focused index
|
|
700
|
+
// depending on the selected item or the filter query.
|
|
701
|
+
const focusedItemIndex = this.__getItemIndexByValue(newItems, this._getItemValue(focusedItem));
|
|
702
|
+
if (focusedItemIndex > -1) {
|
|
703
|
+
this._focusedIndex = focusedItemIndex;
|
|
704
|
+
} else {
|
|
705
|
+
// When the user filled in something that is different from the current value = filtering is enabled,
|
|
706
|
+
// set the focused index to the item that matches the filter query.
|
|
707
|
+
this._focusedIndex = this.__getItemIndexByLabel(newItems, this.filter);
|
|
708
|
+
}
|
|
655
709
|
}
|
|
656
710
|
|
|
657
711
|
/** @private */
|
|
@@ -682,13 +736,10 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
682
736
|
*/
|
|
683
737
|
__clearInternalValue(force = false) {
|
|
684
738
|
if (!this.keepFilter || force) {
|
|
685
|
-
// Clear both
|
|
739
|
+
// Clear both input value and filter.
|
|
686
740
|
this.filter = '';
|
|
687
|
-
this
|
|
741
|
+
this._inputElementValue = '';
|
|
688
742
|
} else {
|
|
689
|
-
// Only clear combo box value. This effectively resets _lastCommittedValue
|
|
690
|
-
// which allows toggling the same item multiple times via keyboard.
|
|
691
|
-
this.$.comboBox.clear();
|
|
692
743
|
// Restore input to the filter value. Needed when items are
|
|
693
744
|
// navigated with keyboard, which overrides the input value
|
|
694
745
|
// with the item label.
|
|
@@ -925,26 +976,20 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
925
976
|
}
|
|
926
977
|
}
|
|
927
978
|
|
|
928
|
-
/** @private */
|
|
929
|
-
_onClearButtonTouchend(event) {
|
|
930
|
-
// Cancel the following click and focus events
|
|
931
|
-
event.preventDefault();
|
|
932
|
-
// Prevent default combo box behavior which can otherwise unnecessarily
|
|
933
|
-
// clear the input and filter
|
|
934
|
-
event.stopPropagation();
|
|
935
|
-
|
|
936
|
-
this.clear();
|
|
937
|
-
}
|
|
938
|
-
|
|
939
979
|
/**
|
|
940
|
-
* Override method
|
|
980
|
+
* Override method from `ComboBoxBaseMixin` to deselect
|
|
981
|
+
* dropdown item by requesting content update on clear.
|
|
982
|
+
* @param {Event} event
|
|
941
983
|
* @protected
|
|
942
|
-
* @override
|
|
943
984
|
*/
|
|
944
985
|
_onClearButtonClick(event) {
|
|
945
986
|
event.stopPropagation();
|
|
946
987
|
|
|
947
|
-
|
|
988
|
+
super._onClearButtonClick(event);
|
|
989
|
+
|
|
990
|
+
if (this.opened) {
|
|
991
|
+
this.requestContentUpdate();
|
|
992
|
+
}
|
|
948
993
|
}
|
|
949
994
|
|
|
950
995
|
/**
|
|
@@ -969,10 +1014,86 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
969
1014
|
* @override
|
|
970
1015
|
*/
|
|
971
1016
|
_onEscape(event) {
|
|
972
|
-
if (this.
|
|
1017
|
+
if (this.readonly) {
|
|
1018
|
+
event.stopPropagation();
|
|
1019
|
+
if (this.opened) {
|
|
1020
|
+
this.close();
|
|
1021
|
+
}
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (this.clearButtonVisible && !this.opened && this.selectedItems && this.selectedItems.length) {
|
|
973
1026
|
event.stopPropagation();
|
|
974
1027
|
this.selectedItems = [];
|
|
975
1028
|
}
|
|
1029
|
+
|
|
1030
|
+
super._onEscape(event);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* Override method from `ComboBoxBaseMixin` to handle Escape pres.
|
|
1035
|
+
* @protected
|
|
1036
|
+
* @override
|
|
1037
|
+
*/
|
|
1038
|
+
_onEscapeCancel() {
|
|
1039
|
+
this._closeOrCommit();
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Override an event listener from `KeyboardMixin` to keep
|
|
1044
|
+
* overlay open when item is selected or unselected.
|
|
1045
|
+
* @param {!Event} event
|
|
1046
|
+
* @protected
|
|
1047
|
+
* @override
|
|
1048
|
+
*/
|
|
1049
|
+
_onEnter(event) {
|
|
1050
|
+
if (this.opened) {
|
|
1051
|
+
// Do not submit the surrounding form.
|
|
1052
|
+
event.preventDefault();
|
|
1053
|
+
// Do not trigger global listeners.
|
|
1054
|
+
event.stopPropagation();
|
|
1055
|
+
|
|
1056
|
+
if (this.readonly) {
|
|
1057
|
+
this.close();
|
|
1058
|
+
} else if (this._hasValidInputValue()) {
|
|
1059
|
+
// Keep selected item focused after committing on Enter.
|
|
1060
|
+
const focusedItem = this._dropdownItems[this._focusedIndex];
|
|
1061
|
+
this._commitValue();
|
|
1062
|
+
this._focusedIndex = this._dropdownItems.indexOf(focusedItem);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
super._onEnter(event);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Override method inherited from the combo-box
|
|
1073
|
+
* to not update focused item when readonly.
|
|
1074
|
+
* @protected
|
|
1075
|
+
* @override
|
|
1076
|
+
*/
|
|
1077
|
+
_onArrowDown() {
|
|
1078
|
+
if (!this.readonly) {
|
|
1079
|
+
super._onArrowDown();
|
|
1080
|
+
} else if (!this.opened) {
|
|
1081
|
+
this.open();
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Override method inherited from the combo-box
|
|
1087
|
+
* to not update focused item when readonly.
|
|
1088
|
+
* @protected
|
|
1089
|
+
* @override
|
|
1090
|
+
*/
|
|
1091
|
+
_onArrowUp() {
|
|
1092
|
+
if (!this.readonly) {
|
|
1093
|
+
super._onArrowUp();
|
|
1094
|
+
} else if (!this.opened) {
|
|
1095
|
+
this.open();
|
|
1096
|
+
}
|
|
976
1097
|
}
|
|
977
1098
|
|
|
978
1099
|
/**
|
|
@@ -1102,36 +1223,29 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
1102
1223
|
}
|
|
1103
1224
|
}
|
|
1104
1225
|
|
|
1105
|
-
/**
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
/** @private */
|
|
1114
|
-
_onComboBoxItemSelected(event) {
|
|
1115
|
-
this.__selectItem(event.detail.item);
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
/** @private */
|
|
1119
|
-
_onCustomValueSet(event) {
|
|
1120
|
-
// Do not set combo-box value
|
|
1121
|
-
event.preventDefault();
|
|
1122
|
-
|
|
1123
|
-
// Stop the original event
|
|
1226
|
+
/**
|
|
1227
|
+
* @param {CustomEvent} event
|
|
1228
|
+
* @protected
|
|
1229
|
+
* @override
|
|
1230
|
+
*/
|
|
1231
|
+
_overlaySelectedItemChanged(event) {
|
|
1124
1232
|
event.stopPropagation();
|
|
1125
1233
|
|
|
1126
|
-
|
|
1234
|
+
// Do not un-select on click when readonly
|
|
1235
|
+
if (this.readonly) {
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1127
1238
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1239
|
+
if (event.detail.item instanceof ComboBoxPlaceholder) {
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
if (this.opened) {
|
|
1244
|
+
// Store filter value for checking if user input is matching
|
|
1245
|
+
// an item which is already selected, to not un-select it.
|
|
1246
|
+
this._lastFilter = this._inputElementValue;
|
|
1247
|
+
this.__selectItem(event.detail.item);
|
|
1248
|
+
}
|
|
1135
1249
|
}
|
|
1136
1250
|
|
|
1137
1251
|
/** @private */
|