@vaadin/multi-select-combo-box 24.8.4 → 25.0.0-alpha10
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/README.md +0 -23
- package/package.json +18 -19
- package/src/{vaadin-multi-select-combo-box-styles.d.ts → styles/vaadin-multi-select-combo-box-base-styles.d.ts} +1 -3
- package/src/styles/vaadin-multi-select-combo-box-base-styles.js +58 -0
- package/src/styles/vaadin-multi-select-combo-box-chip-base-styles.js +112 -0
- package/src/styles/vaadin-multi-select-combo-box-chip-core-styles.js +33 -0
- package/{theme/material/vaadin-multi-select-combo-box-chip-styles.d.ts → src/styles/vaadin-multi-select-combo-box-core-styles.d.ts} +3 -3
- package/src/{vaadin-multi-select-combo-box-styles.js → styles/vaadin-multi-select-combo-box-core-styles.js} +6 -29
- package/src/styles/vaadin-multi-select-combo-box-overlay-base-styles.js +19 -0
- package/src/styles/vaadin-multi-select-combo-box-overlay-core-styles.js +21 -0
- package/src/styles/vaadin-multi-select-combo-box-scroller-base-styles.js +8 -0
- package/src/styles/vaadin-multi-select-combo-box-scroller-core-styles.js +27 -0
- package/src/vaadin-multi-select-combo-box-chip.js +17 -11
- package/src/vaadin-multi-select-combo-box-container.js +27 -34
- package/src/vaadin-multi-select-combo-box-item.js +13 -12
- package/src/vaadin-multi-select-combo-box-mixin.d.ts +9 -82
- package/src/vaadin-multi-select-combo-box-mixin.js +380 -268
- package/src/vaadin-multi-select-combo-box-overlay.js +15 -25
- package/src/vaadin-multi-select-combo-box-scroller.js +10 -26
- package/src/vaadin-multi-select-combo-box.d.ts +14 -10
- package/src/vaadin-multi-select-combo-box.js +52 -69
- package/theme/lumo/vaadin-multi-select-combo-box-styles.js +4 -1
- package/web-types.json +207 -230
- package/web-types.lit.json +78 -78
- package/src/vaadin-lit-multi-select-combo-box-chip.js +0 -88
- package/src/vaadin-lit-multi-select-combo-box-container.js +0 -66
- package/src/vaadin-lit-multi-select-combo-box-internal.js +0 -56
- package/src/vaadin-lit-multi-select-combo-box-item.js +0 -68
- package/src/vaadin-lit-multi-select-combo-box-overlay.js +0 -64
- package/src/vaadin-lit-multi-select-combo-box-scroller.js +0 -96
- package/src/vaadin-lit-multi-select-combo-box.js +0 -146
- package/src/vaadin-multi-select-combo-box-internal-mixin.js +0 -449
- package/src/vaadin-multi-select-combo-box-internal.js +0 -51
- package/theme/lumo/vaadin-lit-multi-select-combo-box.d.ts +0 -3
- package/theme/lumo/vaadin-lit-multi-select-combo-box.js +0 -3
- package/theme/material/vaadin-lit-multi-select-combo-box.d.ts +0 -3
- package/theme/material/vaadin-lit-multi-select-combo-box.js +0 -3
- package/theme/material/vaadin-multi-select-combo-box-chip-styles.js +0 -102
- package/theme/material/vaadin-multi-select-combo-box-styles.d.ts +0 -9
- package/theme/material/vaadin-multi-select-combo-box-styles.js +0 -92
- package/theme/material/vaadin-multi-select-combo-box.d.ts +0 -8
- package/theme/material/vaadin-multi-select-combo-box.js +0 -8
- package/vaadin-lit-multi-select-combo-box.d.ts +0 -1
- package/vaadin-lit-multi-select-combo-box.js +0 -2
|
@@ -4,9 +4,11 @@
|
|
|
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
|
-
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
10
12
|
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
11
13
|
import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
12
14
|
import { InputController } from '@vaadin/field-base/src/input-controller.js';
|
|
@@ -14,11 +16,15 @@ import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-c
|
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* @polymerMixin
|
|
19
|
+
* @mixes ComboBoxDataProviderMixin
|
|
20
|
+
* @mixes ComboBoxItemsMixin
|
|
17
21
|
* @mixes InputControlMixin
|
|
18
22
|
* @mixes ResizeMixin
|
|
19
23
|
*/
|
|
20
24
|
export const MultiSelectComboBoxMixin = (superClass) =>
|
|
21
|
-
class MultiSelectComboBoxMixinClass extends
|
|
25
|
+
class MultiSelectComboBoxMixinClass extends ComboBoxDataProviderMixin(
|
|
26
|
+
ComboBoxItemsMixin(InputControlMixin(ResizeMixin(superClass))),
|
|
27
|
+
) {
|
|
22
28
|
static get properties() {
|
|
23
29
|
return {
|
|
24
30
|
/**
|
|
@@ -30,7 +36,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
30
36
|
type: Boolean,
|
|
31
37
|
value: false,
|
|
32
38
|
reflectToAttribute: true,
|
|
33
|
-
observer: '_autoExpandHorizontallyChanged',
|
|
34
39
|
sync: true,
|
|
35
40
|
},
|
|
36
41
|
|
|
@@ -44,37 +49,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
44
49
|
type: Boolean,
|
|
45
50
|
value: false,
|
|
46
51
|
reflectToAttribute: true,
|
|
47
|
-
observer: '_autoExpandVerticallyChanged',
|
|
48
|
-
sync: true,
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Set true to prevent the overlay from opening automatically.
|
|
53
|
-
* @attr {boolean} auto-open-disabled
|
|
54
|
-
*/
|
|
55
|
-
autoOpenDisabled: {
|
|
56
|
-
type: Boolean,
|
|
57
|
-
sync: true,
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Set to true to display the clear icon which clears the input.
|
|
62
|
-
* @attr {boolean} clear-button-visible
|
|
63
|
-
*/
|
|
64
|
-
clearButtonVisible: {
|
|
65
|
-
type: Boolean,
|
|
66
|
-
reflectToAttribute: true,
|
|
67
|
-
observer: '_clearButtonVisibleChanged',
|
|
68
|
-
value: false,
|
|
69
|
-
sync: true,
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* A full set of items to filter the visible options from.
|
|
74
|
-
* The items can be of either `String` or `Object` type.
|
|
75
|
-
*/
|
|
76
|
-
items: {
|
|
77
|
-
type: Array,
|
|
78
52
|
sync: true,
|
|
79
53
|
},
|
|
80
54
|
|
|
@@ -86,28 +60,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
86
60
|
*/
|
|
87
61
|
itemClassNameGenerator: {
|
|
88
62
|
type: Object,
|
|
89
|
-
observer: '__itemClassNameGeneratorChanged',
|
|
90
|
-
sync: true,
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* The item property used for a visual representation of the item.
|
|
95
|
-
* @attr {string} item-label-path
|
|
96
|
-
*/
|
|
97
|
-
itemLabelPath: {
|
|
98
|
-
type: String,
|
|
99
|
-
value: 'label',
|
|
100
|
-
sync: true,
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Path for the value of the item. If `items` is an array of objects,
|
|
105
|
-
* this property is used as a string value for the selected item.
|
|
106
|
-
* @attr {string} item-value-path
|
|
107
|
-
*/
|
|
108
|
-
itemValuePath: {
|
|
109
|
-
type: String,
|
|
110
|
-
value: 'value',
|
|
111
63
|
sync: true,
|
|
112
64
|
},
|
|
113
65
|
|
|
@@ -175,23 +127,12 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
175
127
|
sync: true,
|
|
176
128
|
},
|
|
177
129
|
|
|
178
|
-
/**
|
|
179
|
-
* A space-delimited list of CSS class names to set on the overlay element.
|
|
180
|
-
*
|
|
181
|
-
* @attr {string} overlay-class
|
|
182
|
-
*/
|
|
183
|
-
overlayClass: {
|
|
184
|
-
type: String,
|
|
185
|
-
sync: true,
|
|
186
|
-
},
|
|
187
|
-
|
|
188
130
|
/**
|
|
189
131
|
* When present, it specifies that the field is read-only.
|
|
190
132
|
*/
|
|
191
133
|
readonly: {
|
|
192
134
|
type: Boolean,
|
|
193
135
|
value: false,
|
|
194
|
-
observer: '_readonlyChanged',
|
|
195
136
|
reflectToAttribute: true,
|
|
196
137
|
sync: true,
|
|
197
138
|
},
|
|
@@ -207,53 +148,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
207
148
|
sync: true,
|
|
208
149
|
},
|
|
209
150
|
|
|
210
|
-
/**
|
|
211
|
-
* True if the dropdown is open, false otherwise.
|
|
212
|
-
*/
|
|
213
|
-
opened: {
|
|
214
|
-
type: Boolean,
|
|
215
|
-
notify: true,
|
|
216
|
-
value: false,
|
|
217
|
-
reflectToAttribute: true,
|
|
218
|
-
sync: true,
|
|
219
|
-
},
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Total number of items.
|
|
223
|
-
*/
|
|
224
|
-
size: {
|
|
225
|
-
type: Number,
|
|
226
|
-
sync: true,
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Number of items fetched at a time from the data provider.
|
|
231
|
-
* @attr {number} page-size
|
|
232
|
-
*/
|
|
233
|
-
pageSize: {
|
|
234
|
-
type: Number,
|
|
235
|
-
value: 50,
|
|
236
|
-
observer: '_pageSizeChanged',
|
|
237
|
-
sync: true,
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Function that provides items lazily. Receives two arguments:
|
|
242
|
-
*
|
|
243
|
-
* - `params` - Object with the following properties:
|
|
244
|
-
* - `params.page` Requested page index
|
|
245
|
-
* - `params.pageSize` Current page size
|
|
246
|
-
* - `params.filter` Currently applied filter
|
|
247
|
-
*
|
|
248
|
-
* - `callback(items, size)` - Callback function with arguments:
|
|
249
|
-
* - `items` Current page of items
|
|
250
|
-
* - `size` Total number of items.
|
|
251
|
-
*/
|
|
252
|
-
dataProvider: {
|
|
253
|
-
type: Object,
|
|
254
|
-
sync: true,
|
|
255
|
-
},
|
|
256
|
-
|
|
257
151
|
/**
|
|
258
152
|
* When true, the user can input a value that is not present in the items list.
|
|
259
153
|
* @attr {boolean} allow-custom-value
|
|
@@ -271,6 +165,7 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
271
165
|
placeholder: {
|
|
272
166
|
type: String,
|
|
273
167
|
observer: '_placeholderChanged',
|
|
168
|
+
reflectToAttribute: true,
|
|
274
169
|
sync: true,
|
|
275
170
|
},
|
|
276
171
|
|
|
@@ -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,10 +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);
|
|
433
330
|
|
|
434
|
-
|
|
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
|
+
}
|
|
435
360
|
}
|
|
436
361
|
|
|
437
362
|
/**
|
|
@@ -442,6 +367,17 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
442
367
|
return this.required && !this.readonly ? this._hasValue : true;
|
|
443
368
|
}
|
|
444
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
|
+
|
|
445
381
|
/**
|
|
446
382
|
* Clears the selected items.
|
|
447
383
|
*/
|
|
@@ -453,11 +389,15 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
453
389
|
|
|
454
390
|
/**
|
|
455
391
|
* Clears the cached pages and reloads data from data provider when needed.
|
|
392
|
+
* @override
|
|
456
393
|
*/
|
|
457
394
|
clearCache() {
|
|
458
|
-
|
|
459
|
-
|
|
395
|
+
// Do not clear the data provider cache when read-only.
|
|
396
|
+
if (this.readonly) {
|
|
397
|
+
return;
|
|
460
398
|
}
|
|
399
|
+
|
|
400
|
+
super.clearCache();
|
|
461
401
|
}
|
|
462
402
|
|
|
463
403
|
/**
|
|
@@ -467,34 +407,130 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
467
407
|
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
468
408
|
*/
|
|
469
409
|
requestContentUpdate() {
|
|
470
|
-
if (this
|
|
471
|
-
|
|
410
|
+
if (!this._scroller) {
|
|
411
|
+
return;
|
|
472
412
|
}
|
|
413
|
+
|
|
414
|
+
this._scroller.requestContentUpdate();
|
|
415
|
+
|
|
416
|
+
this._getItemElements().forEach((item) => {
|
|
417
|
+
item.requestContentUpdate();
|
|
418
|
+
});
|
|
473
419
|
}
|
|
474
420
|
|
|
475
421
|
/**
|
|
476
|
-
* Override method
|
|
422
|
+
* Override method from `ComboBoxBaseMixin` to implement clearing logic.
|
|
477
423
|
* @protected
|
|
478
424
|
* @override
|
|
479
425
|
*/
|
|
480
|
-
|
|
481
|
-
|
|
426
|
+
_onClearAction() {
|
|
427
|
+
this.clear();
|
|
428
|
+
}
|
|
482
429
|
|
|
483
|
-
|
|
484
|
-
|
|
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();
|
|
485
475
|
}
|
|
486
476
|
}
|
|
487
477
|
|
|
488
478
|
/**
|
|
489
|
-
* Override method inherited from `InputMixin` to forward the input to combo-box.
|
|
490
479
|
* @protected
|
|
491
480
|
* @override
|
|
492
481
|
*/
|
|
493
|
-
|
|
494
|
-
|
|
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
|
+
}
|
|
495
495
|
|
|
496
|
-
|
|
497
|
-
|
|
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
|
+
}
|
|
498
534
|
}
|
|
499
535
|
}
|
|
500
536
|
|
|
@@ -504,6 +540,10 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
504
540
|
* @protected
|
|
505
541
|
*/
|
|
506
542
|
_setFocused(focused) {
|
|
543
|
+
if (!focused) {
|
|
544
|
+
this._ignoreCommitValue = true;
|
|
545
|
+
}
|
|
546
|
+
|
|
507
547
|
super._setFocused(focused);
|
|
508
548
|
|
|
509
549
|
// Do not validate when focusout is caused by document
|
|
@@ -512,6 +552,10 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
512
552
|
this._focusedChipIndex = -1;
|
|
513
553
|
this._requestValidation();
|
|
514
554
|
}
|
|
555
|
+
|
|
556
|
+
if (!focused && this.readonly && !this._closeOnBlurIsPrevented) {
|
|
557
|
+
this.close();
|
|
558
|
+
}
|
|
515
559
|
}
|
|
516
560
|
|
|
517
561
|
/**
|
|
@@ -543,73 +587,6 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
543
587
|
super._delegateAttribute(name, value);
|
|
544
588
|
}
|
|
545
589
|
|
|
546
|
-
/** @private */
|
|
547
|
-
_autoExpandHorizontallyChanged(autoExpand, oldAutoExpand) {
|
|
548
|
-
if (autoExpand || oldAutoExpand) {
|
|
549
|
-
this.__updateChips();
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/** @private */
|
|
554
|
-
_autoExpandVerticallyChanged(autoExpand, oldAutoExpand) {
|
|
555
|
-
if (autoExpand || oldAutoExpand) {
|
|
556
|
-
this.__updateChips();
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Setting clear button visible reduces total space available
|
|
562
|
-
* for rendering chips, and making it hidden increases it.
|
|
563
|
-
* @private
|
|
564
|
-
*/
|
|
565
|
-
_clearButtonVisibleChanged(visible, oldVisible) {
|
|
566
|
-
if (visible || oldVisible) {
|
|
567
|
-
this.__updateChips();
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/**
|
|
572
|
-
* Implement two-way binding for the `filteredItems` property
|
|
573
|
-
* that can be set on the internal combo-box element.
|
|
574
|
-
*
|
|
575
|
-
* @param {CustomEvent} event
|
|
576
|
-
* @private
|
|
577
|
-
*/
|
|
578
|
-
_onFilteredItemsChanged(event) {
|
|
579
|
-
const { value } = event.detail;
|
|
580
|
-
if (Array.isArray(value) || value == null) {
|
|
581
|
-
this.filteredItems = value;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
/** @private */
|
|
586
|
-
_readonlyChanged(readonly, oldReadonly) {
|
|
587
|
-
if (readonly || oldReadonly) {
|
|
588
|
-
this.__updateChips();
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
if (this.dataProvider) {
|
|
592
|
-
this.clearCache();
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/** @private */
|
|
597
|
-
__itemClassNameGeneratorChanged(generator, oldGenerator) {
|
|
598
|
-
if (generator || oldGenerator) {
|
|
599
|
-
this.__updateChips();
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/** @private */
|
|
604
|
-
_pageSizeChanged(pageSize, oldPageSize) {
|
|
605
|
-
if (Math.floor(pageSize) !== pageSize || pageSize <= 0) {
|
|
606
|
-
this.pageSize = oldPageSize;
|
|
607
|
-
console.error('"pageSize" value must be an integer > 0');
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
this.$.comboBox.pageSize = this.pageSize;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
590
|
/** @private */
|
|
614
591
|
_placeholderChanged(placeholder) {
|
|
615
592
|
const tmpPlaceholder = this.__tmpA11yPlaceholder;
|
|
@@ -645,15 +622,90 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
645
622
|
|
|
646
623
|
// Update selected for dropdown items
|
|
647
624
|
this.requestContentUpdate();
|
|
625
|
+
}
|
|
648
626
|
|
|
649
|
-
|
|
650
|
-
|
|
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;
|
|
651
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);
|
|
652
686
|
}
|
|
653
687
|
|
|
654
688
|
/** @private */
|
|
655
|
-
|
|
656
|
-
|
|
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
|
+
}
|
|
657
709
|
}
|
|
658
710
|
|
|
659
711
|
/** @private */
|
|
@@ -684,13 +736,10 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
684
736
|
*/
|
|
685
737
|
__clearInternalValue(force = false) {
|
|
686
738
|
if (!this.keepFilter || force) {
|
|
687
|
-
// Clear both
|
|
739
|
+
// Clear both input value and filter.
|
|
688
740
|
this.filter = '';
|
|
689
|
-
this
|
|
741
|
+
this._inputElementValue = '';
|
|
690
742
|
} else {
|
|
691
|
-
// Only clear combo box value. This effectively resets _lastCommittedValue
|
|
692
|
-
// which allows toggling the same item multiple times via keyboard.
|
|
693
|
-
this.$.comboBox.clear();
|
|
694
743
|
// Restore input to the filter value. Needed when items are
|
|
695
744
|
// navigated with keyboard, which overrides the input value
|
|
696
745
|
// with the item label.
|
|
@@ -927,26 +976,20 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
927
976
|
}
|
|
928
977
|
}
|
|
929
978
|
|
|
930
|
-
/** @private */
|
|
931
|
-
_onClearButtonTouchend(event) {
|
|
932
|
-
// Cancel the following click and focus events
|
|
933
|
-
event.preventDefault();
|
|
934
|
-
// Prevent default combo box behavior which can otherwise unnecessarily
|
|
935
|
-
// clear the input and filter
|
|
936
|
-
event.stopPropagation();
|
|
937
|
-
|
|
938
|
-
this.clear();
|
|
939
|
-
}
|
|
940
|
-
|
|
941
979
|
/**
|
|
942
|
-
* Override method
|
|
980
|
+
* Override method from `ComboBoxBaseMixin` to deselect
|
|
981
|
+
* dropdown item by requesting content update on clear.
|
|
982
|
+
* @param {Event} event
|
|
943
983
|
* @protected
|
|
944
|
-
* @override
|
|
945
984
|
*/
|
|
946
985
|
_onClearButtonClick(event) {
|
|
947
986
|
event.stopPropagation();
|
|
948
987
|
|
|
949
|
-
|
|
988
|
+
super._onClearButtonClick(event);
|
|
989
|
+
|
|
990
|
+
if (this.opened) {
|
|
991
|
+
this.requestContentUpdate();
|
|
992
|
+
}
|
|
950
993
|
}
|
|
951
994
|
|
|
952
995
|
/**
|
|
@@ -971,10 +1014,86 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
971
1014
|
* @override
|
|
972
1015
|
*/
|
|
973
1016
|
_onEscape(event) {
|
|
974
|
-
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) {
|
|
975
1026
|
event.stopPropagation();
|
|
976
1027
|
this.selectedItems = [];
|
|
977
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
|
+
}
|
|
978
1097
|
}
|
|
979
1098
|
|
|
980
1099
|
/**
|
|
@@ -1104,36 +1223,29 @@ export const MultiSelectComboBoxMixin = (superClass) =>
|
|
|
1104
1223
|
}
|
|
1105
1224
|
}
|
|
1106
1225
|
|
|
1107
|
-
/**
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
/** @private */
|
|
1116
|
-
_onComboBoxItemSelected(event) {
|
|
1117
|
-
this.__selectItem(event.detail.item);
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
/** @private */
|
|
1121
|
-
_onCustomValueSet(event) {
|
|
1122
|
-
// Do not set combo-box value
|
|
1123
|
-
event.preventDefault();
|
|
1124
|
-
|
|
1125
|
-
// Stop the original event
|
|
1226
|
+
/**
|
|
1227
|
+
* @param {CustomEvent} event
|
|
1228
|
+
* @protected
|
|
1229
|
+
* @override
|
|
1230
|
+
*/
|
|
1231
|
+
_overlaySelectedItemChanged(event) {
|
|
1126
1232
|
event.stopPropagation();
|
|
1127
1233
|
|
|
1128
|
-
|
|
1234
|
+
// Do not un-select on click when readonly
|
|
1235
|
+
if (this.readonly) {
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1129
1238
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
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
|
+
}
|
|
1137
1249
|
}
|
|
1138
1250
|
|
|
1139
1251
|
/** @private */
|