@vaadin/multi-select-combo-box 24.6.0-alpha9 → 24.6.0-beta1
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 +16 -15
- package/src/vaadin-lit-multi-select-combo-box-chip.js +79 -0
- package/src/vaadin-lit-multi-select-combo-box-container.js +66 -0
- package/src/vaadin-lit-multi-select-combo-box-internal.js +56 -0
- package/src/vaadin-lit-multi-select-combo-box-item.js +50 -0
- package/src/vaadin-lit-multi-select-combo-box-overlay.js +64 -0
- package/src/vaadin-lit-multi-select-combo-box-scroller.js +96 -0
- package/src/vaadin-lit-multi-select-combo-box.d.ts +1 -0
- package/src/vaadin-lit-multi-select-combo-box.js +146 -0
- package/src/vaadin-multi-select-combo-box-chip.js +6 -27
- package/src/vaadin-multi-select-combo-box-internal-mixin.js +425 -0
- package/src/vaadin-multi-select-combo-box-internal.js +3 -399
- package/src/vaadin-multi-select-combo-box-mixin.d.ts +253 -0
- package/src/vaadin-multi-select-combo-box-mixin.js +1150 -0
- package/src/vaadin-multi-select-combo-box-styles.d.ts +10 -0
- package/src/vaadin-multi-select-combo-box-styles.js +73 -0
- package/src/vaadin-multi-select-combo-box.d.ts +5 -213
- package/src/vaadin-multi-select-combo-box.js +5 -1139
- package/theme/lumo/vaadin-lit-multi-select-combo-box.d.ts +3 -0
- package/theme/lumo/vaadin-lit-multi-select-combo-box.js +3 -0
- package/theme/material/vaadin-lit-multi-select-combo-box.d.ts +3 -0
- package/theme/material/vaadin-lit-multi-select-combo-box.js +3 -0
- package/vaadin-lit-multi-select-combo-box.d.ts +1 -0
- package/vaadin-lit-multi-select-combo-box.js +2 -0
- package/web-types.json +5 -5
- package/web-types.lit.json +8 -8
|
@@ -7,58 +7,12 @@ import './vaadin-multi-select-combo-box-chip.js';
|
|
|
7
7
|
import './vaadin-multi-select-combo-box-container.js';
|
|
8
8
|
import './vaadin-multi-select-combo-box-internal.js';
|
|
9
9
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
10
|
-
import { announce } from '@vaadin/a11y-base/src/announce.js';
|
|
11
10
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
12
11
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
13
|
-
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
14
|
-
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
15
|
-
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
16
|
-
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
17
|
-
import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
18
|
-
import { InputController } from '@vaadin/field-base/src/input-controller.js';
|
|
19
|
-
import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-controller.js';
|
|
20
12
|
import { inputFieldShared } from '@vaadin/field-base/src/styles/input-field-shared-styles.js';
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
:host {
|
|
25
|
-
--input-min-width: var(--vaadin-multi-select-combo-box-input-min-width, 4em);
|
|
26
|
-
--_chip-min-width: var(--vaadin-multi-select-combo-box-chip-min-width, 50px);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
#chips {
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
::slotted(input) {
|
|
35
|
-
box-sizing: border-box;
|
|
36
|
-
flex: 1 0 var(--input-min-width);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
::slotted([slot='chip']),
|
|
40
|
-
::slotted([slot='overflow']) {
|
|
41
|
-
flex: 0 1 auto;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
::slotted([slot='chip']) {
|
|
45
|
-
overflow: hidden;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
:host(:is([readonly], [disabled])) ::slotted(input) {
|
|
49
|
-
flex-grow: 0;
|
|
50
|
-
flex-basis: 0;
|
|
51
|
-
padding: 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
:host([auto-expand-vertically]) #chips {
|
|
55
|
-
display: contents;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
:host([auto-expand-horizontally]) [class$='container'] {
|
|
59
|
-
width: auto;
|
|
60
|
-
}
|
|
61
|
-
`;
|
|
13
|
+
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
14
|
+
import { MultiSelectComboBoxMixin } from './vaadin-multi-select-combo-box-mixin.js';
|
|
15
|
+
import { multiSelectComboBox } from './vaadin-multi-select-combo-box-styles.js';
|
|
62
16
|
|
|
63
17
|
registerStyles('vaadin-multi-select-combo-box', [inputFieldShared, multiSelectComboBox], {
|
|
64
18
|
moduleId: 'vaadin-multi-select-combo-box-styles',
|
|
@@ -145,10 +99,9 @@ registerStyles('vaadin-multi-select-combo-box', [inputFieldShared, multiSelectCo
|
|
|
145
99
|
* @extends HTMLElement
|
|
146
100
|
* @mixes ElementMixin
|
|
147
101
|
* @mixes ThemableMixin
|
|
148
|
-
* @mixes
|
|
149
|
-
* @mixes ResizeMixin
|
|
102
|
+
* @mixes MultiSelectComboBoxMixin
|
|
150
103
|
*/
|
|
151
|
-
class MultiSelectComboBox extends
|
|
104
|
+
class MultiSelectComboBox extends MultiSelectComboBoxMixin(ThemableMixin(ElementMixin(PolymerElement))) {
|
|
152
105
|
static get is() {
|
|
153
106
|
return 'vaadin-multi-select-combo-box';
|
|
154
107
|
}
|
|
@@ -227,1093 +180,6 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
|
|
|
227
180
|
<slot name="tooltip"></slot>
|
|
228
181
|
`;
|
|
229
182
|
}
|
|
230
|
-
|
|
231
|
-
static get properties() {
|
|
232
|
-
return {
|
|
233
|
-
/**
|
|
234
|
-
* Set to true to auto expand horizontally, causing input field to
|
|
235
|
-
* grow until max width is reached.
|
|
236
|
-
* @attr {boolean} auto-expand-horizontally
|
|
237
|
-
*/
|
|
238
|
-
autoExpandHorizontally: {
|
|
239
|
-
type: Boolean,
|
|
240
|
-
value: false,
|
|
241
|
-
reflectToAttribute: true,
|
|
242
|
-
observer: '_autoExpandHorizontallyChanged',
|
|
243
|
-
},
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Set to true to not collapse selected items chips into the overflow
|
|
247
|
-
* chip and instead always expand vertically, causing input field to
|
|
248
|
-
* wrap into multiple lines when width is limited.
|
|
249
|
-
* @attr {boolean} auto-expand-vertically
|
|
250
|
-
*/
|
|
251
|
-
autoExpandVertically: {
|
|
252
|
-
type: Boolean,
|
|
253
|
-
value: false,
|
|
254
|
-
reflectToAttribute: true,
|
|
255
|
-
observer: '_autoExpandVerticallyChanged',
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Set true to prevent the overlay from opening automatically.
|
|
260
|
-
* @attr {boolean} auto-open-disabled
|
|
261
|
-
*/
|
|
262
|
-
autoOpenDisabled: Boolean,
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Set to true to display the clear icon which clears the input.
|
|
266
|
-
* @attr {boolean} clear-button-visible
|
|
267
|
-
*/
|
|
268
|
-
clearButtonVisible: {
|
|
269
|
-
type: Boolean,
|
|
270
|
-
reflectToAttribute: true,
|
|
271
|
-
observer: '_clearButtonVisibleChanged',
|
|
272
|
-
value: false,
|
|
273
|
-
},
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* A full set of items to filter the visible options from.
|
|
277
|
-
* The items can be of either `String` or `Object` type.
|
|
278
|
-
*/
|
|
279
|
-
items: {
|
|
280
|
-
type: Array,
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* A function used to generate CSS class names for dropdown
|
|
285
|
-
* items and selected chips based on the item. The return
|
|
286
|
-
* value should be the generated class name as a string, or
|
|
287
|
-
* multiple class names separated by whitespace characters.
|
|
288
|
-
*/
|
|
289
|
-
itemClassNameGenerator: {
|
|
290
|
-
type: Object,
|
|
291
|
-
observer: '__itemClassNameGeneratorChanged',
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* The item property used for a visual representation of the item.
|
|
296
|
-
* @attr {string} item-label-path
|
|
297
|
-
*/
|
|
298
|
-
itemLabelPath: {
|
|
299
|
-
type: String,
|
|
300
|
-
value: 'label',
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Path for the value of the item. If `items` is an array of objects,
|
|
305
|
-
* this property is used as a string value for the selected item.
|
|
306
|
-
* @attr {string} item-value-path
|
|
307
|
-
*/
|
|
308
|
-
itemValuePath: {
|
|
309
|
-
type: String,
|
|
310
|
-
value: 'value',
|
|
311
|
-
},
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Path for the id of the item, used to detect whether the item is selected.
|
|
315
|
-
* @attr {string} item-id-path
|
|
316
|
-
*/
|
|
317
|
-
itemIdPath: {
|
|
318
|
-
type: String,
|
|
319
|
-
},
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* The object used to localize this component.
|
|
323
|
-
* To change the default localization, replace the entire
|
|
324
|
-
* _i18n_ object or just the property you want to modify.
|
|
325
|
-
*
|
|
326
|
-
* The object has the following JSON structure and default values:
|
|
327
|
-
* ```
|
|
328
|
-
* {
|
|
329
|
-
* // Screen reader announcement on clear button click.
|
|
330
|
-
* cleared: 'Selection cleared',
|
|
331
|
-
* // Screen reader announcement when a chip is focused.
|
|
332
|
-
* focused: ' focused. Press Backspace to remove',
|
|
333
|
-
* // Screen reader announcement when item is selected.
|
|
334
|
-
* selected: 'added to selection',
|
|
335
|
-
* // Screen reader announcement when item is deselected.
|
|
336
|
-
* deselected: 'removed from selection',
|
|
337
|
-
* // Screen reader announcement of the selected items count.
|
|
338
|
-
* // {count} is replaced with the actual count of items.
|
|
339
|
-
* total: '{count} items selected',
|
|
340
|
-
* }
|
|
341
|
-
* ```
|
|
342
|
-
* @type {!MultiSelectComboBoxI18n}
|
|
343
|
-
* @default {English/US}
|
|
344
|
-
*/
|
|
345
|
-
i18n: {
|
|
346
|
-
type: Object,
|
|
347
|
-
value: () => {
|
|
348
|
-
return {
|
|
349
|
-
cleared: 'Selection cleared',
|
|
350
|
-
focused: 'focused. Press Backspace to remove',
|
|
351
|
-
selected: 'added to selection',
|
|
352
|
-
deselected: 'removed from selection',
|
|
353
|
-
total: '{count} items selected',
|
|
354
|
-
};
|
|
355
|
-
},
|
|
356
|
-
},
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* When true, filter string isn't cleared after selecting an item.
|
|
360
|
-
*/
|
|
361
|
-
keepFilter: {
|
|
362
|
-
type: Boolean,
|
|
363
|
-
value: false,
|
|
364
|
-
},
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* True when loading items from the data provider, false otherwise.
|
|
368
|
-
*/
|
|
369
|
-
loading: {
|
|
370
|
-
type: Boolean,
|
|
371
|
-
value: false,
|
|
372
|
-
reflectToAttribute: true,
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* A space-delimited list of CSS class names to set on the overlay element.
|
|
377
|
-
*
|
|
378
|
-
* @attr {string} overlay-class
|
|
379
|
-
*/
|
|
380
|
-
overlayClass: {
|
|
381
|
-
type: String,
|
|
382
|
-
},
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* When present, it specifies that the field is read-only.
|
|
386
|
-
*/
|
|
387
|
-
readonly: {
|
|
388
|
-
type: Boolean,
|
|
389
|
-
value: false,
|
|
390
|
-
observer: '_readonlyChanged',
|
|
391
|
-
reflectToAttribute: true,
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* The list of selected items.
|
|
396
|
-
* Note: modifying the selected items creates a new array each time.
|
|
397
|
-
*/
|
|
398
|
-
selectedItems: {
|
|
399
|
-
type: Array,
|
|
400
|
-
value: () => [],
|
|
401
|
-
notify: true,
|
|
402
|
-
},
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* True if the dropdown is open, false otherwise.
|
|
406
|
-
*/
|
|
407
|
-
opened: {
|
|
408
|
-
type: Boolean,
|
|
409
|
-
notify: true,
|
|
410
|
-
value: false,
|
|
411
|
-
reflectToAttribute: true,
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Total number of items.
|
|
416
|
-
*/
|
|
417
|
-
size: {
|
|
418
|
-
type: Number,
|
|
419
|
-
},
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Number of items fetched at a time from the data provider.
|
|
423
|
-
* @attr {number} page-size
|
|
424
|
-
*/
|
|
425
|
-
pageSize: {
|
|
426
|
-
type: Number,
|
|
427
|
-
value: 50,
|
|
428
|
-
observer: '_pageSizeChanged',
|
|
429
|
-
},
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Function that provides items lazily. Receives two arguments:
|
|
433
|
-
*
|
|
434
|
-
* - `params` - Object with the following properties:
|
|
435
|
-
* - `params.page` Requested page index
|
|
436
|
-
* - `params.pageSize` Current page size
|
|
437
|
-
* - `params.filter` Currently applied filter
|
|
438
|
-
*
|
|
439
|
-
* - `callback(items, size)` - Callback function with arguments:
|
|
440
|
-
* - `items` Current page of items
|
|
441
|
-
* - `size` Total number of items.
|
|
442
|
-
*/
|
|
443
|
-
dataProvider: {
|
|
444
|
-
type: Object,
|
|
445
|
-
},
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* When true, the user can input a value that is not present in the items list.
|
|
449
|
-
* @attr {boolean} allow-custom-value
|
|
450
|
-
*/
|
|
451
|
-
allowCustomValue: {
|
|
452
|
-
type: Boolean,
|
|
453
|
-
value: false,
|
|
454
|
-
},
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* A hint to the user of what can be entered in the control.
|
|
458
|
-
* The placeholder will be only displayed in the case when
|
|
459
|
-
* there is no item selected.
|
|
460
|
-
*/
|
|
461
|
-
placeholder: {
|
|
462
|
-
type: String,
|
|
463
|
-
observer: '_placeholderChanged',
|
|
464
|
-
},
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Custom function for rendering the content of every item.
|
|
468
|
-
* Receives three arguments:
|
|
469
|
-
*
|
|
470
|
-
* - `root` The `<vaadin-multi-select-combo-box-item>` internal container DOM element.
|
|
471
|
-
* - `comboBox` The reference to the `<vaadin-multi-select-combo-box>` element.
|
|
472
|
-
* - `model` The object with the properties related with the rendered
|
|
473
|
-
* item, contains:
|
|
474
|
-
* - `model.index` The index of the rendered item.
|
|
475
|
-
* - `model.item` The item.
|
|
476
|
-
*/
|
|
477
|
-
renderer: Function,
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Filtering string the user has typed into the input field.
|
|
481
|
-
*/
|
|
482
|
-
filter: {
|
|
483
|
-
type: String,
|
|
484
|
-
value: '',
|
|
485
|
-
notify: true,
|
|
486
|
-
},
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* A subset of items, filtered based on the user input. Filtered items
|
|
490
|
-
* can be assigned directly to omit the internal filtering functionality.
|
|
491
|
-
* The items can be of either `String` or `Object` type.
|
|
492
|
-
*/
|
|
493
|
-
filteredItems: Array,
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Set to true to group selected items at the top of the overlay.
|
|
497
|
-
* @attr {boolean} selected-items-on-top
|
|
498
|
-
*/
|
|
499
|
-
selectedItemsOnTop: {
|
|
500
|
-
type: Boolean,
|
|
501
|
-
value: false,
|
|
502
|
-
},
|
|
503
|
-
|
|
504
|
-
/** @private */
|
|
505
|
-
value: {
|
|
506
|
-
type: String,
|
|
507
|
-
},
|
|
508
|
-
|
|
509
|
-
/** @private */
|
|
510
|
-
_overflowItems: {
|
|
511
|
-
type: Array,
|
|
512
|
-
value: () => [],
|
|
513
|
-
},
|
|
514
|
-
|
|
515
|
-
/** @private */
|
|
516
|
-
_focusedChipIndex: {
|
|
517
|
-
type: Number,
|
|
518
|
-
value: -1,
|
|
519
|
-
observer: '_focusedChipIndexChanged',
|
|
520
|
-
},
|
|
521
|
-
|
|
522
|
-
/** @private */
|
|
523
|
-
_lastFilter: {
|
|
524
|
-
type: String,
|
|
525
|
-
},
|
|
526
|
-
|
|
527
|
-
/** @private */
|
|
528
|
-
_topGroup: {
|
|
529
|
-
type: Array,
|
|
530
|
-
},
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
static get observers() {
|
|
535
|
-
return [
|
|
536
|
-
'_selectedItemsChanged(selectedItems, selectedItems.*)',
|
|
537
|
-
'__updateOverflowChip(_overflow, _overflowItems, disabled, readonly)',
|
|
538
|
-
'__updateTopGroup(selectedItemsOnTop, selectedItems, opened)',
|
|
539
|
-
];
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/** @protected */
|
|
543
|
-
get slotStyles() {
|
|
544
|
-
const tag = this.localName;
|
|
545
|
-
return [
|
|
546
|
-
...super.slotStyles,
|
|
547
|
-
`
|
|
548
|
-
${tag}[has-value] input::placeholder {
|
|
549
|
-
color: transparent !important;
|
|
550
|
-
forced-color-adjust: none;
|
|
551
|
-
}
|
|
552
|
-
`,
|
|
553
|
-
];
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/**
|
|
557
|
-
* Used by `InputControlMixin` as a reference to the clear button element.
|
|
558
|
-
* @protected
|
|
559
|
-
* @return {!HTMLElement}
|
|
560
|
-
*/
|
|
561
|
-
get clearElement() {
|
|
562
|
-
return this.$.clearButton;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/** @protected */
|
|
566
|
-
get _chips() {
|
|
567
|
-
return [...this.querySelectorAll('[slot="chip"]')];
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* Override a getter from `InputMixin` to compute
|
|
572
|
-
* the presence of value based on `selectedItems`.
|
|
573
|
-
*
|
|
574
|
-
* @protected
|
|
575
|
-
* @override
|
|
576
|
-
*/
|
|
577
|
-
get _hasValue() {
|
|
578
|
-
return this.selectedItems && this.selectedItems.length > 0;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/** @protected */
|
|
582
|
-
ready() {
|
|
583
|
-
super.ready();
|
|
584
|
-
|
|
585
|
-
this.addController(
|
|
586
|
-
new InputController(this, (input) => {
|
|
587
|
-
this._setInputElement(input);
|
|
588
|
-
this._setFocusElement(input);
|
|
589
|
-
this.stateTarget = input;
|
|
590
|
-
this.ariaTarget = input;
|
|
591
|
-
}),
|
|
592
|
-
);
|
|
593
|
-
this.addController(new LabelledInputController(this.inputElement, this._labelController));
|
|
594
|
-
|
|
595
|
-
this._tooltipController = new TooltipController(this);
|
|
596
|
-
this.addController(this._tooltipController);
|
|
597
|
-
this._tooltipController.setPosition('top');
|
|
598
|
-
this._tooltipController.setAriaTarget(this.inputElement);
|
|
599
|
-
this._tooltipController.setShouldShow((target) => !target.opened);
|
|
600
|
-
|
|
601
|
-
this._inputField = this.shadowRoot.querySelector('[part="input-field"]');
|
|
602
|
-
|
|
603
|
-
this._overflowController = new SlotController(this, 'overflow', 'vaadin-multi-select-combo-box-chip', {
|
|
604
|
-
initializer: (chip) => {
|
|
605
|
-
chip.addEventListener('mousedown', (e) => this._preventBlur(e));
|
|
606
|
-
this._overflow = chip;
|
|
607
|
-
},
|
|
608
|
-
});
|
|
609
|
-
this.addController(this._overflowController);
|
|
610
|
-
|
|
611
|
-
this.__updateChips();
|
|
612
|
-
|
|
613
|
-
processTemplates(this);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Returns true if the current input value satisfies all constraints (if any).
|
|
618
|
-
* @return {boolean}
|
|
619
|
-
*/
|
|
620
|
-
checkValidity() {
|
|
621
|
-
return this.required && !this.readonly ? this._hasValue : true;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
/**
|
|
625
|
-
* Clears the selected items.
|
|
626
|
-
*/
|
|
627
|
-
clear() {
|
|
628
|
-
this.__updateSelection([]);
|
|
629
|
-
|
|
630
|
-
announce(this.i18n.cleared);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
* Clears the cached pages and reloads data from data provider when needed.
|
|
635
|
-
*/
|
|
636
|
-
clearCache() {
|
|
637
|
-
if (this.$ && this.$.comboBox) {
|
|
638
|
-
this.$.comboBox.clearCache();
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* Requests an update for the content of items.
|
|
644
|
-
* While performing the update, it invokes the renderer (passed in the `renderer` property) once an item.
|
|
645
|
-
*
|
|
646
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
647
|
-
*/
|
|
648
|
-
requestContentUpdate() {
|
|
649
|
-
if (this.$ && this.$.comboBox) {
|
|
650
|
-
this.$.comboBox.requestContentUpdate();
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Override method inherited from `DisabledMixin` to forward disabled to chips.
|
|
656
|
-
* @protected
|
|
657
|
-
* @override
|
|
658
|
-
*/
|
|
659
|
-
_disabledChanged(disabled, oldDisabled) {
|
|
660
|
-
super._disabledChanged(disabled, oldDisabled);
|
|
661
|
-
|
|
662
|
-
if (disabled || oldDisabled) {
|
|
663
|
-
this.__updateChips();
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Override method inherited from `InputMixin` to forward the input to combo-box.
|
|
669
|
-
* @protected
|
|
670
|
-
* @override
|
|
671
|
-
*/
|
|
672
|
-
_inputElementChanged(input) {
|
|
673
|
-
super._inputElementChanged(input);
|
|
674
|
-
|
|
675
|
-
if (input) {
|
|
676
|
-
this.$.comboBox._setInputElement(input);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
/**
|
|
681
|
-
* Override method inherited from `FocusMixin` to validate on blur.
|
|
682
|
-
* @param {boolean} focused
|
|
683
|
-
* @protected
|
|
684
|
-
*/
|
|
685
|
-
_setFocused(focused) {
|
|
686
|
-
super._setFocused(focused);
|
|
687
|
-
|
|
688
|
-
// Do not validate when focusout is caused by document
|
|
689
|
-
// losing focus, which happens on browser tab switch.
|
|
690
|
-
if (!focused && document.hasFocus()) {
|
|
691
|
-
this._focusedChipIndex = -1;
|
|
692
|
-
this._requestValidation();
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* Implement callback from `ResizeMixin` to update chips.
|
|
698
|
-
* @protected
|
|
699
|
-
* @override
|
|
700
|
-
*/
|
|
701
|
-
_onResize() {
|
|
702
|
-
this.__updateChips();
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Override method from `DelegateStateMixin` to set required state
|
|
707
|
-
* using `aria-required` attribute instead of `required`, in order
|
|
708
|
-
* to prevent screen readers from announcing "invalid entry".
|
|
709
|
-
* @protected
|
|
710
|
-
* @override
|
|
711
|
-
*/
|
|
712
|
-
_delegateAttribute(name, value) {
|
|
713
|
-
if (!this.stateTarget) {
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (name === 'required') {
|
|
718
|
-
this._delegateAttribute('aria-required', value ? 'true' : false);
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
super._delegateAttribute(name, value);
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
/** @private */
|
|
726
|
-
_autoExpandHorizontallyChanged(autoExpand, oldAutoExpand) {
|
|
727
|
-
if (autoExpand || oldAutoExpand) {
|
|
728
|
-
this.__updateChips();
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
/** @private */
|
|
733
|
-
_autoExpandVerticallyChanged(autoExpand, oldAutoExpand) {
|
|
734
|
-
if (autoExpand || oldAutoExpand) {
|
|
735
|
-
this.__updateChips();
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
* Setting clear button visible reduces total space available
|
|
741
|
-
* for rendering chips, and making it hidden increases it.
|
|
742
|
-
* @private
|
|
743
|
-
*/
|
|
744
|
-
_clearButtonVisibleChanged(visible, oldVisible) {
|
|
745
|
-
if (visible || oldVisible) {
|
|
746
|
-
this.__updateChips();
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
/**
|
|
751
|
-
* Implement two-way binding for the `filteredItems` property
|
|
752
|
-
* that can be set on the internal combo-box element.
|
|
753
|
-
*
|
|
754
|
-
* @param {CustomEvent} event
|
|
755
|
-
* @private
|
|
756
|
-
*/
|
|
757
|
-
_onFilteredItemsChanged(event) {
|
|
758
|
-
const { value } = event.detail;
|
|
759
|
-
if (Array.isArray(value) || value == null) {
|
|
760
|
-
this.filteredItems = value;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
/** @private */
|
|
765
|
-
_readonlyChanged(readonly, oldReadonly) {
|
|
766
|
-
if (readonly || oldReadonly) {
|
|
767
|
-
this.__updateChips();
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
if (this.dataProvider) {
|
|
771
|
-
this.clearCache();
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
/** @private */
|
|
776
|
-
__itemClassNameGeneratorChanged(generator, oldGenerator) {
|
|
777
|
-
if (generator || oldGenerator) {
|
|
778
|
-
this.__updateChips();
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
/** @private */
|
|
783
|
-
_pageSizeChanged(pageSize, oldPageSize) {
|
|
784
|
-
if (Math.floor(pageSize) !== pageSize || pageSize <= 0) {
|
|
785
|
-
this.pageSize = oldPageSize;
|
|
786
|
-
console.error('"pageSize" value must be an integer > 0');
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
this.$.comboBox.pageSize = this.pageSize;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
/** @private */
|
|
793
|
-
_placeholderChanged(placeholder) {
|
|
794
|
-
const tmpPlaceholder = this.__tmpA11yPlaceholder;
|
|
795
|
-
// Do not store temporary placeholder
|
|
796
|
-
if (tmpPlaceholder !== placeholder) {
|
|
797
|
-
this.__savedPlaceholder = placeholder;
|
|
798
|
-
|
|
799
|
-
if (tmpPlaceholder) {
|
|
800
|
-
this.placeholder = tmpPlaceholder;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/** @private */
|
|
806
|
-
_selectedItemsChanged(selectedItems) {
|
|
807
|
-
this._toggleHasValue(this._hasValue);
|
|
808
|
-
|
|
809
|
-
// Use placeholder for announcing items
|
|
810
|
-
if (this._hasValue) {
|
|
811
|
-
const tmpPlaceholder = this._mergeItemLabels(selectedItems);
|
|
812
|
-
if (this.__tmpA11yPlaceholder === undefined) {
|
|
813
|
-
this.__savedPlaceholder = this.placeholder;
|
|
814
|
-
}
|
|
815
|
-
this.__tmpA11yPlaceholder = tmpPlaceholder;
|
|
816
|
-
this.placeholder = tmpPlaceholder;
|
|
817
|
-
} else if (this.__tmpA11yPlaceholder !== undefined) {
|
|
818
|
-
delete this.__tmpA11yPlaceholder;
|
|
819
|
-
this.placeholder = this.__savedPlaceholder;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// Re-render chips
|
|
823
|
-
this.__updateChips();
|
|
824
|
-
|
|
825
|
-
// Update selected for dropdown items
|
|
826
|
-
this.requestContentUpdate();
|
|
827
|
-
|
|
828
|
-
if (this.opened) {
|
|
829
|
-
this.$.comboBox.$.overlay._updateOverlayWidth();
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/** @private */
|
|
834
|
-
_getItemLabel(item) {
|
|
835
|
-
return this.$.comboBox._getItemLabel(item);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
/** @private */
|
|
839
|
-
_mergeItemLabels(items) {
|
|
840
|
-
return items.map((item) => this._getItemLabel(item)).join(', ');
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
/** @private */
|
|
844
|
-
_findIndex(item, selectedItems, itemIdPath) {
|
|
845
|
-
if (itemIdPath && item) {
|
|
846
|
-
for (let index = 0; index < selectedItems.length; index++) {
|
|
847
|
-
if (selectedItems[index] && selectedItems[index][itemIdPath] === item[itemIdPath]) {
|
|
848
|
-
return index;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
return -1;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
return selectedItems.indexOf(item);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
/**
|
|
858
|
-
* Clear the internal combo box value and filter. Filter will not be cleared
|
|
859
|
-
* when the `keepFilter` option is enabled. Using `force` can enforce clearing
|
|
860
|
-
* the filter.
|
|
861
|
-
* @param {boolean} force overrides the keepFilter option
|
|
862
|
-
* @private
|
|
863
|
-
*/
|
|
864
|
-
__clearInternalValue(force = false) {
|
|
865
|
-
if (!this.keepFilter || force) {
|
|
866
|
-
// Clear both combo box value and filter.
|
|
867
|
-
this.filter = '';
|
|
868
|
-
this.$.comboBox.clear();
|
|
869
|
-
} else {
|
|
870
|
-
// Only clear combo box value. This effectively resets _lastCommittedValue
|
|
871
|
-
// which allows toggling the same item multiple times via keyboard.
|
|
872
|
-
this.$.comboBox.clear();
|
|
873
|
-
// Restore input to the filter value. Needed when items are
|
|
874
|
-
// navigated with keyboard, which overrides the input value
|
|
875
|
-
// with the item label.
|
|
876
|
-
this._inputElementValue = this.filter;
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
/** @private */
|
|
881
|
-
__announceItem(itemLabel, isSelected, itemCount) {
|
|
882
|
-
const state = isSelected ? 'selected' : 'deselected';
|
|
883
|
-
const total = this.i18n.total.replace('{count}', itemCount || 0);
|
|
884
|
-
announce(`${itemLabel} ${this.i18n[state]} ${total}`);
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
/** @private */
|
|
888
|
-
__removeItem(item) {
|
|
889
|
-
const itemsCopy = [...this.selectedItems];
|
|
890
|
-
itemsCopy.splice(itemsCopy.indexOf(item), 1);
|
|
891
|
-
this.__updateSelection(itemsCopy);
|
|
892
|
-
const itemLabel = this._getItemLabel(item);
|
|
893
|
-
this.__announceItem(itemLabel, false, itemsCopy.length);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
/** @private */
|
|
897
|
-
__selectItem(item) {
|
|
898
|
-
const itemsCopy = [...this.selectedItems];
|
|
899
|
-
|
|
900
|
-
const index = this._findIndex(item, itemsCopy, this.itemIdPath);
|
|
901
|
-
const itemLabel = this._getItemLabel(item);
|
|
902
|
-
|
|
903
|
-
let isSelected = false;
|
|
904
|
-
|
|
905
|
-
if (index !== -1) {
|
|
906
|
-
const lastFilter = this._lastFilter;
|
|
907
|
-
// Do not unselect when manually typing and committing an already selected item.
|
|
908
|
-
if (lastFilter && lastFilter.toLowerCase() === itemLabel.toLowerCase()) {
|
|
909
|
-
this.__clearInternalValue();
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
itemsCopy.splice(index, 1);
|
|
914
|
-
} else {
|
|
915
|
-
itemsCopy.push(item);
|
|
916
|
-
isSelected = true;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
this.__updateSelection(itemsCopy);
|
|
920
|
-
|
|
921
|
-
// Suppress `value-changed` event.
|
|
922
|
-
this.__clearInternalValue();
|
|
923
|
-
|
|
924
|
-
this.__announceItem(itemLabel, isSelected, itemsCopy.length);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
/** @private */
|
|
928
|
-
__updateSelection(selectedItems) {
|
|
929
|
-
this.selectedItems = selectedItems;
|
|
930
|
-
|
|
931
|
-
this._requestValidation();
|
|
932
|
-
|
|
933
|
-
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
/** @private */
|
|
937
|
-
__updateTopGroup(selectedItemsOnTop, selectedItems, opened) {
|
|
938
|
-
if (!selectedItemsOnTop) {
|
|
939
|
-
this._topGroup = [];
|
|
940
|
-
} else if (!opened) {
|
|
941
|
-
this._topGroup = [...selectedItems];
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
/** @private */
|
|
946
|
-
__createChip(item) {
|
|
947
|
-
const chip = document.createElement('vaadin-multi-select-combo-box-chip');
|
|
948
|
-
chip.setAttribute('slot', 'chip');
|
|
949
|
-
|
|
950
|
-
chip.item = item;
|
|
951
|
-
chip.disabled = this.disabled;
|
|
952
|
-
chip.readonly = this.readonly;
|
|
953
|
-
|
|
954
|
-
const label = this._getItemLabel(item);
|
|
955
|
-
chip.label = label;
|
|
956
|
-
chip.setAttribute('title', label);
|
|
957
|
-
|
|
958
|
-
if (typeof this.itemClassNameGenerator === 'function') {
|
|
959
|
-
chip.className = this.itemClassNameGenerator(item);
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
chip.addEventListener('item-removed', (e) => this._onItemRemoved(e));
|
|
963
|
-
chip.addEventListener('mousedown', (e) => this._preventBlur(e));
|
|
964
|
-
|
|
965
|
-
return chip;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
/** @private */
|
|
969
|
-
__getOverflowWidth() {
|
|
970
|
-
const chip = this._overflow;
|
|
971
|
-
|
|
972
|
-
chip.style.visibility = 'hidden';
|
|
973
|
-
chip.removeAttribute('hidden');
|
|
974
|
-
|
|
975
|
-
const count = chip.getAttribute('count');
|
|
976
|
-
|
|
977
|
-
// Detect max possible width of the overflow chip
|
|
978
|
-
// by measuring it with widest number (2 digits)
|
|
979
|
-
chip.setAttribute('count', '99');
|
|
980
|
-
const overflowStyle = getComputedStyle(chip);
|
|
981
|
-
const overflowWidth = chip.clientWidth + parseInt(overflowStyle.marginInlineStart);
|
|
982
|
-
|
|
983
|
-
chip.setAttribute('count', count);
|
|
984
|
-
chip.setAttribute('hidden', '');
|
|
985
|
-
chip.style.visibility = '';
|
|
986
|
-
|
|
987
|
-
return overflowWidth;
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
/** @private */
|
|
991
|
-
__updateChips() {
|
|
992
|
-
if (!this._inputField || !this.inputElement) {
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
// Clear all chips except the overflow
|
|
997
|
-
this._chips.forEach((chip) => {
|
|
998
|
-
chip.remove();
|
|
999
|
-
});
|
|
1000
|
-
|
|
1001
|
-
const items = [...this.selectedItems];
|
|
1002
|
-
|
|
1003
|
-
// Detect available remaining width for chips
|
|
1004
|
-
const totalWidth = this._inputField.$.wrapper.clientWidth;
|
|
1005
|
-
const inputWidth = parseInt(getComputedStyle(this.inputElement).flexBasis);
|
|
1006
|
-
|
|
1007
|
-
let remainingWidth = totalWidth - inputWidth;
|
|
1008
|
-
|
|
1009
|
-
if (items.length > 1) {
|
|
1010
|
-
remainingWidth -= this.__getOverflowWidth();
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
const chipMinWidth = parseInt(getComputedStyle(this).getPropertyValue('--_chip-min-width'));
|
|
1014
|
-
|
|
1015
|
-
if (this.autoExpandHorizontally) {
|
|
1016
|
-
const chips = [];
|
|
1017
|
-
|
|
1018
|
-
// First, add all chips to make the field fully expand
|
|
1019
|
-
for (let i = items.length - 1, refNode = null; i >= 0; i--) {
|
|
1020
|
-
const chip = this.__createChip(items[i]);
|
|
1021
|
-
this.insertBefore(chip, refNode);
|
|
1022
|
-
refNode = chip;
|
|
1023
|
-
chips.unshift(chip);
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
const overflowItems = [];
|
|
1027
|
-
const availableWidth = this._inputField.$.wrapper.clientWidth - this.$.chips.clientWidth;
|
|
1028
|
-
|
|
1029
|
-
// When auto expanding vertically, no need to measure width
|
|
1030
|
-
if (!this.autoExpandVertically && availableWidth < inputWidth) {
|
|
1031
|
-
// Always show at least last item as a chip
|
|
1032
|
-
while (chips.length > 1) {
|
|
1033
|
-
const lastChip = chips.pop();
|
|
1034
|
-
lastChip.remove();
|
|
1035
|
-
overflowItems.unshift(items.pop());
|
|
1036
|
-
|
|
1037
|
-
// Remove chips until there is enough width for the input element to fit
|
|
1038
|
-
const neededWidth = overflowItems.length > 0 ? inputWidth + this.__getOverflowWidth() : inputWidth;
|
|
1039
|
-
if (this._inputField.$.wrapper.clientWidth - this.$.chips.clientWidth >= neededWidth) {
|
|
1040
|
-
break;
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
if (chips.length === 1) {
|
|
1045
|
-
chips[0].style.maxWidth = `${Math.max(chipMinWidth, remainingWidth)}px`;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
this._overflowItems = overflowItems;
|
|
1050
|
-
return;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// Add chips until remaining width is exceeded
|
|
1054
|
-
for (let i = items.length - 1, refNode = null; i >= 0; i--) {
|
|
1055
|
-
const chip = this.__createChip(items[i]);
|
|
1056
|
-
this.insertBefore(chip, refNode);
|
|
1057
|
-
|
|
1058
|
-
// When auto expanding vertically, no need to measure remaining width
|
|
1059
|
-
if (!this.autoExpandVertically && this.$.chips.clientWidth > remainingWidth) {
|
|
1060
|
-
// Always show at least last selected item as a chip
|
|
1061
|
-
if (refNode === null) {
|
|
1062
|
-
chip.style.maxWidth = `${Math.max(chipMinWidth, remainingWidth)}px`;
|
|
1063
|
-
} else {
|
|
1064
|
-
chip.remove();
|
|
1065
|
-
break;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
items.pop();
|
|
1070
|
-
refNode = chip;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
this._overflowItems = items;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
/** @private */
|
|
1077
|
-
__updateOverflowChip(overflow, items, disabled, readonly) {
|
|
1078
|
-
if (overflow) {
|
|
1079
|
-
const count = items.length;
|
|
1080
|
-
|
|
1081
|
-
overflow.label = `${count}`;
|
|
1082
|
-
overflow.setAttribute('count', `${count}`);
|
|
1083
|
-
overflow.setAttribute('title', this._mergeItemLabels(items));
|
|
1084
|
-
overflow.toggleAttribute('hidden', count === 0);
|
|
1085
|
-
|
|
1086
|
-
overflow.disabled = disabled;
|
|
1087
|
-
overflow.readonly = readonly;
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
/** @private */
|
|
1092
|
-
_onClearButtonTouchend(event) {
|
|
1093
|
-
// Cancel the following click and focus events
|
|
1094
|
-
event.preventDefault();
|
|
1095
|
-
// Prevent default combo box behavior which can otherwise unnecessarily
|
|
1096
|
-
// clear the input and filter
|
|
1097
|
-
event.stopPropagation();
|
|
1098
|
-
|
|
1099
|
-
this.clear();
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
/**
|
|
1103
|
-
* Override method inherited from `InputControlMixin` and clear items.
|
|
1104
|
-
* @protected
|
|
1105
|
-
* @override
|
|
1106
|
-
*/
|
|
1107
|
-
_onClearButtonClick(event) {
|
|
1108
|
-
event.stopPropagation();
|
|
1109
|
-
|
|
1110
|
-
this.clear();
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
/**
|
|
1114
|
-
* Override an event listener from `InputControlMixin` to
|
|
1115
|
-
* stop the change event re-targeted from the input.
|
|
1116
|
-
*
|
|
1117
|
-
* @param {!Event} event
|
|
1118
|
-
* @protected
|
|
1119
|
-
* @override
|
|
1120
|
-
*/
|
|
1121
|
-
_onChange(event) {
|
|
1122
|
-
event.stopPropagation();
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
/**
|
|
1126
|
-
* Override an event listener from `KeyboardMixin`.
|
|
1127
|
-
* Do not call `super` in order to override clear
|
|
1128
|
-
* button logic defined in `InputControlMixin`.
|
|
1129
|
-
*
|
|
1130
|
-
* @param {!KeyboardEvent} event
|
|
1131
|
-
* @protected
|
|
1132
|
-
* @override
|
|
1133
|
-
*/
|
|
1134
|
-
_onEscape(event) {
|
|
1135
|
-
if (this.clearButtonVisible && this.selectedItems && this.selectedItems.length) {
|
|
1136
|
-
event.stopPropagation();
|
|
1137
|
-
this.selectedItems = [];
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
/**
|
|
1142
|
-
* Override an event listener from `KeyboardMixin`.
|
|
1143
|
-
* @param {KeyboardEvent} event
|
|
1144
|
-
* @protected
|
|
1145
|
-
* @override
|
|
1146
|
-
*/
|
|
1147
|
-
_onKeyDown(event) {
|
|
1148
|
-
super._onKeyDown(event);
|
|
1149
|
-
|
|
1150
|
-
const chips = this._chips;
|
|
1151
|
-
|
|
1152
|
-
if (!this.readonly && chips.length > 0) {
|
|
1153
|
-
switch (event.key) {
|
|
1154
|
-
case 'Backspace':
|
|
1155
|
-
this._onBackSpace(chips);
|
|
1156
|
-
break;
|
|
1157
|
-
case 'ArrowLeft':
|
|
1158
|
-
this._onArrowLeft(chips, event);
|
|
1159
|
-
break;
|
|
1160
|
-
case 'ArrowRight':
|
|
1161
|
-
this._onArrowRight(chips, event);
|
|
1162
|
-
break;
|
|
1163
|
-
default:
|
|
1164
|
-
this._focusedChipIndex = -1;
|
|
1165
|
-
break;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
/** @private */
|
|
1171
|
-
_onArrowLeft(chips, event) {
|
|
1172
|
-
if (this.inputElement.selectionStart !== 0) {
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
const idx = this._focusedChipIndex;
|
|
1177
|
-
if (idx !== -1) {
|
|
1178
|
-
event.preventDefault();
|
|
1179
|
-
}
|
|
1180
|
-
let newIdx;
|
|
1181
|
-
|
|
1182
|
-
if (!this.__isRTL) {
|
|
1183
|
-
if (idx === -1) {
|
|
1184
|
-
// Focus last chip
|
|
1185
|
-
newIdx = chips.length - 1;
|
|
1186
|
-
} else if (idx > 0) {
|
|
1187
|
-
// Focus prev chip
|
|
1188
|
-
newIdx = idx - 1;
|
|
1189
|
-
}
|
|
1190
|
-
} else if (idx === chips.length - 1) {
|
|
1191
|
-
// Blur last chip
|
|
1192
|
-
newIdx = -1;
|
|
1193
|
-
} else if (idx > -1) {
|
|
1194
|
-
// Focus next chip
|
|
1195
|
-
newIdx = idx + 1;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
if (newIdx !== undefined) {
|
|
1199
|
-
this._focusedChipIndex = newIdx;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
/** @private */
|
|
1204
|
-
_onArrowRight(chips, event) {
|
|
1205
|
-
if (this.inputElement.selectionStart !== 0) {
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
const idx = this._focusedChipIndex;
|
|
1210
|
-
if (idx !== -1) {
|
|
1211
|
-
event.preventDefault();
|
|
1212
|
-
}
|
|
1213
|
-
let newIdx;
|
|
1214
|
-
|
|
1215
|
-
if (this.__isRTL) {
|
|
1216
|
-
if (idx === -1) {
|
|
1217
|
-
// Focus last chip
|
|
1218
|
-
newIdx = chips.length - 1;
|
|
1219
|
-
} else if (idx > 0) {
|
|
1220
|
-
// Focus prev chip
|
|
1221
|
-
newIdx = idx - 1;
|
|
1222
|
-
}
|
|
1223
|
-
} else if (idx === chips.length - 1) {
|
|
1224
|
-
// Blur last chip
|
|
1225
|
-
newIdx = -1;
|
|
1226
|
-
} else if (idx > -1) {
|
|
1227
|
-
// Focus next chip
|
|
1228
|
-
newIdx = idx + 1;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
if (newIdx !== undefined) {
|
|
1232
|
-
this._focusedChipIndex = newIdx;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
/** @private */
|
|
1237
|
-
_onBackSpace(chips) {
|
|
1238
|
-
if (this.inputElement.selectionStart !== 0) {
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
const idx = this._focusedChipIndex;
|
|
1243
|
-
if (idx === -1) {
|
|
1244
|
-
this._focusedChipIndex = chips.length - 1;
|
|
1245
|
-
} else {
|
|
1246
|
-
this.__removeItem(chips[idx].item);
|
|
1247
|
-
this._focusedChipIndex = -1;
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/** @private */
|
|
1252
|
-
_focusedChipIndexChanged(focusedIndex, oldFocusedIndex) {
|
|
1253
|
-
if (focusedIndex > -1 || oldFocusedIndex > -1) {
|
|
1254
|
-
const chips = this._chips;
|
|
1255
|
-
chips.forEach((chip, index) => {
|
|
1256
|
-
chip.toggleAttribute('focused', index === focusedIndex);
|
|
1257
|
-
});
|
|
1258
|
-
|
|
1259
|
-
// Announce focused chip
|
|
1260
|
-
if (focusedIndex > -1) {
|
|
1261
|
-
const item = chips[focusedIndex].item;
|
|
1262
|
-
const itemLabel = this._getItemLabel(item);
|
|
1263
|
-
announce(`${itemLabel} ${this.i18n.focused}`);
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
/** @private */
|
|
1269
|
-
_onComboBoxChange() {
|
|
1270
|
-
const item = this.$.comboBox.selectedItem;
|
|
1271
|
-
if (item) {
|
|
1272
|
-
this.__selectItem(item);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
/** @private */
|
|
1277
|
-
_onComboBoxItemSelected(event) {
|
|
1278
|
-
this.__selectItem(event.detail.item);
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
/** @private */
|
|
1282
|
-
_onCustomValueSet(event) {
|
|
1283
|
-
// Do not set combo-box value
|
|
1284
|
-
event.preventDefault();
|
|
1285
|
-
|
|
1286
|
-
// Stop the original event
|
|
1287
|
-
event.stopPropagation();
|
|
1288
|
-
|
|
1289
|
-
this.__clearInternalValue(true);
|
|
1290
|
-
|
|
1291
|
-
this.dispatchEvent(
|
|
1292
|
-
new CustomEvent('custom-value-set', {
|
|
1293
|
-
detail: event.detail,
|
|
1294
|
-
composed: true,
|
|
1295
|
-
bubbles: true,
|
|
1296
|
-
}),
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
/** @private */
|
|
1301
|
-
_onItemRemoved(event) {
|
|
1302
|
-
this.__removeItem(event.detail.item);
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
/** @private */
|
|
1306
|
-
_preventBlur(event) {
|
|
1307
|
-
// Prevent mousedown event to keep the input focused
|
|
1308
|
-
// and keep the overlay opened when clicking a chip.
|
|
1309
|
-
event.preventDefault();
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
/**
|
|
1313
|
-
* Fired when the user sets a custom value.
|
|
1314
|
-
* @event custom-value-set
|
|
1315
|
-
* @param {string} detail the custom value
|
|
1316
|
-
*/
|
|
1317
183
|
}
|
|
1318
184
|
|
|
1319
185
|
defineCustomElement(MultiSelectComboBox);
|