@vaadin/select 24.2.0-alpha1 → 24.2.0-alpha11
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 +24 -15
- package/src/lit/renderer-directives.d.ts +2 -2
- package/src/vaadin-select-base-mixin.d.ts +121 -0
- package/src/vaadin-select-base-mixin.js +640 -0
- package/src/vaadin-select-overlay.js +39 -20
- package/src/vaadin-select-value-button-styles.js +50 -0
- package/src/vaadin-select-value-button.js +4 -44
- package/src/vaadin-select.d.ts +10 -86
- package/src/vaadin-select.js +12 -577
- package/theme/lumo/vaadin-select-styles.js +2 -0
- package/theme/lumo/vaadin-select.js +0 -1
- package/theme/material/vaadin-select.js +0 -1
- package/web-types.json +74 -48
- package/web-types.lit.json +26 -12
package/src/vaadin-select.js
CHANGED
|
@@ -9,23 +9,13 @@ import './vaadin-select-list-box.js';
|
|
|
9
9
|
import './vaadin-select-overlay.js';
|
|
10
10
|
import './vaadin-select-value-button.js';
|
|
11
11
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
12
|
-
import { setAriaIDReference } from '@vaadin/a11y-base/src/aria-id-reference.js';
|
|
13
|
-
import { DelegateFocusMixin } from '@vaadin/a11y-base/src/delegate-focus-mixin.js';
|
|
14
|
-
import { KeyboardMixin } from '@vaadin/a11y-base/src/keyboard-mixin.js';
|
|
15
12
|
import { screenReaderOnly } from '@vaadin/a11y-base/src/styles/sr-only-styles.js';
|
|
16
|
-
import { DelegateStateMixin } from '@vaadin/component-base/src/delegate-state-mixin.js';
|
|
17
13
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
18
|
-
import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js';
|
|
19
|
-
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
|
|
20
14
|
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
21
|
-
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
22
|
-
import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
|
|
23
|
-
import { FieldMixin } from '@vaadin/field-base/src/field-mixin.js';
|
|
24
|
-
import { LabelController } from '@vaadin/field-base/src/label-controller.js';
|
|
25
15
|
import { fieldShared } from '@vaadin/field-base/src/styles/field-shared-styles.js';
|
|
26
16
|
import { inputFieldContainer } from '@vaadin/field-base/src/styles/input-field-container-styles.js';
|
|
27
17
|
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
28
|
-
import {
|
|
18
|
+
import { SelectBaseMixin } from './vaadin-select-base-mixin.js';
|
|
29
19
|
|
|
30
20
|
registerStyles('vaadin-select', [fieldShared, inputFieldContainer, screenReaderOnly], {
|
|
31
21
|
moduleId: 'vaadin-select-styles',
|
|
@@ -138,16 +128,10 @@ registerStyles('vaadin-select', [fieldShared, inputFieldContainer, screenReaderO
|
|
|
138
128
|
*
|
|
139
129
|
* @extends HTMLElement
|
|
140
130
|
* @mixes ElementMixin
|
|
131
|
+
* @mixes SelectBaseMixin
|
|
141
132
|
* @mixes ThemableMixin
|
|
142
|
-
* @mixes FieldMixin
|
|
143
|
-
* @mixes DelegateFocusMixin
|
|
144
|
-
* @mixes DelegateStateMixin
|
|
145
|
-
* @mixes KeyboardMixin
|
|
146
|
-
* @mixes OverlayClassMixin
|
|
147
133
|
*/
|
|
148
|
-
class Select extends
|
|
149
|
-
DelegateFocusMixin(DelegateStateMixin(KeyboardMixin(FieldMixin(ElementMixin(ThemableMixin(PolymerElement)))))),
|
|
150
|
-
) {
|
|
134
|
+
class Select extends SelectBaseMixin(ElementMixin(ThemableMixin(PolymerElement))) {
|
|
151
135
|
static get is() {
|
|
152
136
|
return 'vaadin-select';
|
|
153
137
|
}
|
|
@@ -155,6 +139,10 @@ class Select extends OverlayClassMixin(
|
|
|
155
139
|
static get template() {
|
|
156
140
|
return html`
|
|
157
141
|
<style>
|
|
142
|
+
:host {
|
|
143
|
+
position: relative;
|
|
144
|
+
}
|
|
145
|
+
|
|
158
146
|
::slotted([slot='value']) {
|
|
159
147
|
flex-grow: 1;
|
|
160
148
|
}
|
|
@@ -194,6 +182,7 @@ class Select extends OverlayClassMixin(
|
|
|
194
182
|
with-backdrop="[[_phone]]"
|
|
195
183
|
phone$="[[_phone]]"
|
|
196
184
|
theme$="[[_theme]]"
|
|
185
|
+
on-vaadin-overlay-open="_onOverlayOpen"
|
|
197
186
|
></vaadin-select-overlay>
|
|
198
187
|
|
|
199
188
|
<slot name="tooltip"></slot>
|
|
@@ -203,202 +192,15 @@ class Select extends OverlayClassMixin(
|
|
|
203
192
|
`;
|
|
204
193
|
}
|
|
205
194
|
|
|
206
|
-
static get properties() {
|
|
207
|
-
return {
|
|
208
|
-
/**
|
|
209
|
-
* An array containing items that will be rendered as the options of the select.
|
|
210
|
-
*
|
|
211
|
-
* #### Example
|
|
212
|
-
* ```js
|
|
213
|
-
* select.items = [
|
|
214
|
-
* { label: 'Most recent first', value: 'recent' },
|
|
215
|
-
* { component: 'hr' },
|
|
216
|
-
* { label: 'Rating: low to high', value: 'rating-asc' },
|
|
217
|
-
* { label: 'Rating: high to low', value: 'rating-desc' },
|
|
218
|
-
* { component: 'hr' },
|
|
219
|
-
* { label: 'Price: low to high', value: 'price-asc', disabled: true },
|
|
220
|
-
* { label: 'Price: high to low', value: 'price-desc', disabled: true }
|
|
221
|
-
* ];
|
|
222
|
-
* ```
|
|
223
|
-
*
|
|
224
|
-
* Note: each item is rendered by default as the internal `<vaadin-select-item>` that is an extension of `<vaadin-item>`.
|
|
225
|
-
* To render the item with a custom component, provide a tag name by the `component` property.
|
|
226
|
-
*
|
|
227
|
-
* @type {!Array<!SelectItem>}
|
|
228
|
-
*/
|
|
229
|
-
items: {
|
|
230
|
-
type: Array,
|
|
231
|
-
observer: '__itemsChanged',
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Set when the select is open
|
|
236
|
-
* @type {boolean}
|
|
237
|
-
*/
|
|
238
|
-
opened: {
|
|
239
|
-
type: Boolean,
|
|
240
|
-
value: false,
|
|
241
|
-
notify: true,
|
|
242
|
-
reflectToAttribute: true,
|
|
243
|
-
observer: '_openedChanged',
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Custom function for rendering the content of the `<vaadin-select>`.
|
|
248
|
-
* Receives two arguments:
|
|
249
|
-
*
|
|
250
|
-
* - `root` The `<vaadin-select-overlay>` internal container
|
|
251
|
-
* DOM element. Append your content to it.
|
|
252
|
-
* - `select` The reference to the `<vaadin-select>` element.
|
|
253
|
-
* @type {!SelectRenderer | undefined}
|
|
254
|
-
*/
|
|
255
|
-
renderer: Function,
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* The `value` property of the selected item, or an empty string
|
|
259
|
-
* if no item is selected.
|
|
260
|
-
* On change or initialization, the component finds the item which matches the
|
|
261
|
-
* value and displays it.
|
|
262
|
-
* If no value is provided to the component, it selects the first item without
|
|
263
|
-
* value or empty value.
|
|
264
|
-
* Hint: If you do not want to select any item by default, you can either set all
|
|
265
|
-
* the values of inner vaadin-items, or set the vaadin-select value to
|
|
266
|
-
* an inexistent value in the items list.
|
|
267
|
-
* @type {string}
|
|
268
|
-
*/
|
|
269
|
-
value: {
|
|
270
|
-
type: String,
|
|
271
|
-
value: '',
|
|
272
|
-
notify: true,
|
|
273
|
-
observer: '_valueChanged',
|
|
274
|
-
},
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* The name of this element.
|
|
278
|
-
*/
|
|
279
|
-
name: {
|
|
280
|
-
type: String,
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* A hint to the user of what can be entered in the control.
|
|
285
|
-
* The placeholder will be displayed in the case that there
|
|
286
|
-
* is no item selected, or the selected item has an empty
|
|
287
|
-
* string label, or the selected item has no label and it's
|
|
288
|
-
* DOM content is empty.
|
|
289
|
-
*/
|
|
290
|
-
placeholder: {
|
|
291
|
-
type: String,
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* When present, it specifies that the element is read-only.
|
|
296
|
-
* @type {boolean}
|
|
297
|
-
*/
|
|
298
|
-
readonly: {
|
|
299
|
-
type: Boolean,
|
|
300
|
-
value: false,
|
|
301
|
-
reflectToAttribute: true,
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
/** @private */
|
|
305
|
-
_phone: Boolean,
|
|
306
|
-
|
|
307
|
-
/** @private */
|
|
308
|
-
_phoneMediaQuery: {
|
|
309
|
-
value: '(max-width: 420px), (max-height: 420px)',
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
/** @private */
|
|
313
|
-
_inputContainer: Object,
|
|
314
|
-
|
|
315
|
-
/** @private */
|
|
316
|
-
_items: Object,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
static get delegateAttrs() {
|
|
321
|
-
return [...super.delegateAttrs, 'invalid'];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
195
|
static get observers() {
|
|
325
|
-
return [
|
|
326
|
-
'_updateAriaExpanded(opened, focusElement)',
|
|
327
|
-
'_updateSelectedItem(value, _items, placeholder)',
|
|
328
|
-
'_rendererChanged(renderer, _overlayElement)',
|
|
329
|
-
];
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
constructor() {
|
|
333
|
-
super();
|
|
334
|
-
|
|
335
|
-
this._itemId = `value-${this.localName}-${generateUniqueId()}`;
|
|
336
|
-
this._srLabelController = new LabelController(this);
|
|
337
|
-
this._srLabelController.slotName = 'sr-label';
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/** @protected */
|
|
341
|
-
disconnectedCallback() {
|
|
342
|
-
super.disconnectedCallback();
|
|
343
|
-
|
|
344
|
-
// Making sure the select is closed and removed from DOM after detaching the select.
|
|
345
|
-
this.opened = false;
|
|
196
|
+
return ['_rendererChanged(renderer, _overlayElement)'];
|
|
346
197
|
}
|
|
347
198
|
|
|
348
199
|
/** @protected */
|
|
349
200
|
ready() {
|
|
350
201
|
super.ready();
|
|
351
202
|
|
|
352
|
-
this._overlayElement = this.shadowRoot.querySelector('vaadin-select-overlay');
|
|
353
|
-
this._inputContainer = this.shadowRoot.querySelector('[part~="input-field"]');
|
|
354
|
-
|
|
355
|
-
this._valueButtonController = new ButtonController(this);
|
|
356
|
-
this.addController(this._valueButtonController);
|
|
357
|
-
this.addController(this._srLabelController);
|
|
358
|
-
this.addController(
|
|
359
|
-
new MediaQueryController(this._phoneMediaQuery, (matches) => {
|
|
360
|
-
this._phone = matches;
|
|
361
|
-
}),
|
|
362
|
-
);
|
|
363
|
-
|
|
364
203
|
processTemplates(this);
|
|
365
|
-
|
|
366
|
-
this._tooltipController = new TooltipController(this);
|
|
367
|
-
this._tooltipController.setPosition('top');
|
|
368
|
-
this.addController(this._tooltipController);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Requests an update for the content of the select.
|
|
373
|
-
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
374
|
-
*
|
|
375
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
376
|
-
*/
|
|
377
|
-
requestContentUpdate() {
|
|
378
|
-
if (!this._overlayElement) {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
this._overlayElement.requestContentUpdate();
|
|
383
|
-
|
|
384
|
-
if (this._menuElement && this._menuElement.items) {
|
|
385
|
-
this._updateSelectedItem(this.value, this._menuElement.items);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Override an observer from `FieldMixin`
|
|
391
|
-
* to validate when required is removed.
|
|
392
|
-
*
|
|
393
|
-
* @protected
|
|
394
|
-
* @override
|
|
395
|
-
*/
|
|
396
|
-
_requiredChanged(required) {
|
|
397
|
-
super._requiredChanged(required);
|
|
398
|
-
|
|
399
|
-
if (required === false) {
|
|
400
|
-
this.validate();
|
|
401
|
-
}
|
|
402
204
|
}
|
|
403
205
|
|
|
404
206
|
/**
|
|
@@ -411,385 +213,18 @@ class Select extends OverlayClassMixin(
|
|
|
411
213
|
return;
|
|
412
214
|
}
|
|
413
215
|
|
|
414
|
-
overlay.
|
|
216
|
+
overlay.renderer = renderer || this.__defaultRenderer;
|
|
415
217
|
|
|
416
218
|
this.requestContentUpdate();
|
|
417
219
|
}
|
|
418
220
|
|
|
419
|
-
/**
|
|
420
|
-
* @param {SelectItem[] | undefined | null} newItems
|
|
421
|
-
* @param {SelectItem[] | undefined | null} oldItems
|
|
422
|
-
* @private
|
|
423
|
-
*/
|
|
424
|
-
__itemsChanged(newItems, oldItems) {
|
|
425
|
-
if (newItems || oldItems) {
|
|
426
|
-
this.requestContentUpdate();
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* @param {HTMLElement} menuElement
|
|
432
|
-
* @protected
|
|
433
|
-
*/
|
|
434
|
-
_assignMenuElement(menuElement) {
|
|
435
|
-
if (menuElement && menuElement !== this.__lastMenuElement) {
|
|
436
|
-
this._menuElement = menuElement;
|
|
437
|
-
|
|
438
|
-
// Ensure items are initialized
|
|
439
|
-
this.__initMenuItems(menuElement);
|
|
440
|
-
|
|
441
|
-
menuElement.addEventListener('items-changed', () => {
|
|
442
|
-
this.__initMenuItems(menuElement);
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
menuElement.addEventListener('selected-changed', () => this.__updateValueButton());
|
|
446
|
-
// Use capture phase to make it possible for `<vaadin-grid-pro-edit-select>`
|
|
447
|
-
// to override and handle the keydown event before the value change happens.
|
|
448
|
-
menuElement.addEventListener('keydown', (e) => this._onKeyDownInside(e), true);
|
|
449
|
-
menuElement.addEventListener(
|
|
450
|
-
'click',
|
|
451
|
-
() => {
|
|
452
|
-
this.__userInteraction = true;
|
|
453
|
-
this.opened = false;
|
|
454
|
-
},
|
|
455
|
-
true,
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
// Store the menu element reference
|
|
459
|
-
this.__lastMenuElement = menuElement;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/** @private */
|
|
464
|
-
__initMenuItems(menuElement) {
|
|
465
|
-
if (menuElement.items) {
|
|
466
|
-
this._items = menuElement.items;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
221
|
/** @private */
|
|
471
|
-
|
|
472
|
-
this.
|
|
473
|
-
|
|
474
|
-
// Validate only if `value` changes after initialization.
|
|
475
|
-
if (oldValue !== undefined) {
|
|
476
|
-
this.validate();
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Opens the overlay if the field is not read-only.
|
|
482
|
-
*
|
|
483
|
-
* @private
|
|
484
|
-
*/
|
|
485
|
-
_onClick(event) {
|
|
486
|
-
// Prevent parent components such as `vaadin-grid`
|
|
487
|
-
// from handling the click event after it bubbles.
|
|
488
|
-
event.preventDefault();
|
|
489
|
-
|
|
490
|
-
this.opened = !this.readonly;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/** @private */
|
|
494
|
-
_onToggleMouseDown(event) {
|
|
495
|
-
// Prevent mousedown event to avoid blur and preserve focused state
|
|
496
|
-
// while opening, and to restore focus-ring attribute on closing.
|
|
497
|
-
event.preventDefault();
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* @param {!KeyboardEvent} e
|
|
502
|
-
* @protected
|
|
503
|
-
* @override
|
|
504
|
-
*/
|
|
505
|
-
_onKeyDown(e) {
|
|
506
|
-
if (e.target === this.focusElement && !this.readonly && !this.opened) {
|
|
507
|
-
if (/^(Enter|SpaceBar|\s|ArrowDown|Down|ArrowUp|Up)$/u.test(e.key)) {
|
|
508
|
-
e.preventDefault();
|
|
509
|
-
this.opened = true;
|
|
510
|
-
} else if (/[\p{L}\p{Nd}]/u.test(e.key) && e.key.length === 1) {
|
|
511
|
-
const selected = this._menuElement.selected;
|
|
512
|
-
const currentIdx = selected !== undefined ? selected : -1;
|
|
513
|
-
const newIdx = this._menuElement._searchKey(currentIdx, e.key);
|
|
514
|
-
if (newIdx >= 0) {
|
|
515
|
-
this.__userInteraction = true;
|
|
516
|
-
|
|
517
|
-
// Announce the value selected with the first letter shortcut
|
|
518
|
-
this._updateAriaLive(true);
|
|
519
|
-
this._menuElement.selected = newIdx;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* @param {!KeyboardEvent} e
|
|
527
|
-
* @protected
|
|
528
|
-
*/
|
|
529
|
-
_onKeyDownInside(e) {
|
|
530
|
-
if (/^(Tab)$/u.test(e.key)) {
|
|
531
|
-
this.opened = false;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/** @private */
|
|
536
|
-
_openedChanged(opened, wasOpened) {
|
|
537
|
-
if (opened) {
|
|
538
|
-
// Avoid multiple announcements when a value gets selected from the dropdown
|
|
539
|
-
this._updateAriaLive(false);
|
|
540
|
-
|
|
541
|
-
if (!this._overlayElement || !this._menuElement || !this.focusElement || this.disabled || this.readonly) {
|
|
542
|
-
this.opened = false;
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
this._overlayElement.style.setProperty(
|
|
547
|
-
'--vaadin-select-text-field-width',
|
|
548
|
-
`${this._inputContainer.offsetWidth}px`,
|
|
549
|
-
);
|
|
550
|
-
|
|
551
|
-
// Preserve focus-ring to restore it later
|
|
552
|
-
const hasFocusRing = this.hasAttribute('focus-ring');
|
|
553
|
-
this._openedWithFocusRing = hasFocusRing;
|
|
554
|
-
|
|
555
|
-
// Opened select should not keep focus-ring
|
|
556
|
-
if (hasFocusRing) {
|
|
557
|
-
this.removeAttribute('focus-ring');
|
|
558
|
-
}
|
|
559
|
-
|
|
222
|
+
_onOverlayOpen() {
|
|
223
|
+
if (this._menuElement) {
|
|
560
224
|
this._menuElement.focus();
|
|
561
|
-
} else if (wasOpened) {
|
|
562
|
-
this.focus();
|
|
563
|
-
if (this._openedWithFocusRing) {
|
|
564
|
-
this.setAttribute('focus-ring', '');
|
|
565
|
-
}
|
|
566
|
-
this.validate();
|
|
567
225
|
}
|
|
568
226
|
}
|
|
569
227
|
|
|
570
|
-
/** @private */
|
|
571
|
-
_updateAriaExpanded(opened, focusElement) {
|
|
572
|
-
if (focusElement) {
|
|
573
|
-
focusElement.setAttribute('aria-expanded', opened ? 'true' : 'false');
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/** @private */
|
|
578
|
-
_updateAriaLive(ariaLive) {
|
|
579
|
-
if (this.focusElement) {
|
|
580
|
-
if (ariaLive) {
|
|
581
|
-
this.focusElement.setAttribute('aria-live', 'polite');
|
|
582
|
-
} else {
|
|
583
|
-
this.focusElement.removeAttribute('aria-live');
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/** @private */
|
|
589
|
-
__attachSelectedItem(selected) {
|
|
590
|
-
let labelItem;
|
|
591
|
-
|
|
592
|
-
const label = selected.getAttribute('label');
|
|
593
|
-
if (label) {
|
|
594
|
-
labelItem = this.__createItemElement({ label });
|
|
595
|
-
} else {
|
|
596
|
-
labelItem = selected.cloneNode(true);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// Store reference to the original item
|
|
600
|
-
labelItem._sourceItem = selected;
|
|
601
|
-
|
|
602
|
-
this.__appendValueItemElement(labelItem, this.focusElement);
|
|
603
|
-
|
|
604
|
-
// Ensure the item gets proper styles
|
|
605
|
-
labelItem.selected = true;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* @param {!SelectItem} item
|
|
610
|
-
* @private
|
|
611
|
-
*/
|
|
612
|
-
__createItemElement(item) {
|
|
613
|
-
const itemElement = document.createElement(item.component || 'vaadin-select-item');
|
|
614
|
-
if (item.label) {
|
|
615
|
-
itemElement.textContent = item.label;
|
|
616
|
-
}
|
|
617
|
-
if (item.value) {
|
|
618
|
-
itemElement.value = item.value;
|
|
619
|
-
}
|
|
620
|
-
if (item.disabled) {
|
|
621
|
-
itemElement.disabled = item.disabled;
|
|
622
|
-
}
|
|
623
|
-
return itemElement;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* @param {!HTMLElement} itemElement
|
|
628
|
-
* @param {!HTMLElement} parent
|
|
629
|
-
* @private
|
|
630
|
-
*/
|
|
631
|
-
__appendValueItemElement(itemElement, parent) {
|
|
632
|
-
parent.appendChild(itemElement);
|
|
633
|
-
itemElement.removeAttribute('tabindex');
|
|
634
|
-
itemElement.removeAttribute('aria-selected');
|
|
635
|
-
itemElement.removeAttribute('role');
|
|
636
|
-
itemElement.setAttribute('id', this._itemId);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* @param {string} accessibleName
|
|
641
|
-
* @protected
|
|
642
|
-
*/
|
|
643
|
-
_accessibleNameChanged(accessibleName) {
|
|
644
|
-
this._srLabelController.setLabel(accessibleName);
|
|
645
|
-
this._setCustomAriaLabelledBy(accessibleName ? this._srLabelController.defaultId : null);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* @param {string} accessibleNameRef
|
|
650
|
-
* @protected
|
|
651
|
-
*/
|
|
652
|
-
_accessibleNameRefChanged(accessibleNameRef) {
|
|
653
|
-
this._setCustomAriaLabelledBy(accessibleNameRef);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
/**
|
|
657
|
-
* @param {string} ariaLabelledby
|
|
658
|
-
* @private
|
|
659
|
-
*/
|
|
660
|
-
_setCustomAriaLabelledBy(ariaLabelledby) {
|
|
661
|
-
const labelId = this._getLabelIdWithItemId(ariaLabelledby);
|
|
662
|
-
this._fieldAriaController.setLabelId(labelId, true);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/**
|
|
666
|
-
* @param {string | null} labelId
|
|
667
|
-
* @returns string | null
|
|
668
|
-
* @private
|
|
669
|
-
*/
|
|
670
|
-
_getLabelIdWithItemId(labelId) {
|
|
671
|
-
const selected = this._items ? this._items[this._menuElement.selected] : false;
|
|
672
|
-
const itemId = selected || this.placeholder ? this._itemId : '';
|
|
673
|
-
|
|
674
|
-
return labelId ? `${labelId} ${itemId}`.trim() : null;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
/** @private */
|
|
678
|
-
__updateValueButton() {
|
|
679
|
-
const valueButton = this.focusElement;
|
|
680
|
-
|
|
681
|
-
if (!valueButton) {
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
valueButton.innerHTML = '';
|
|
686
|
-
|
|
687
|
-
const selected = this._items[this._menuElement.selected];
|
|
688
|
-
|
|
689
|
-
valueButton.removeAttribute('placeholder');
|
|
690
|
-
|
|
691
|
-
if (!selected) {
|
|
692
|
-
if (this.placeholder) {
|
|
693
|
-
const item = this.__createItemElement({ label: this.placeholder });
|
|
694
|
-
this.__appendValueItemElement(item, valueButton);
|
|
695
|
-
valueButton.setAttribute('placeholder', '');
|
|
696
|
-
}
|
|
697
|
-
} else {
|
|
698
|
-
this.__attachSelectedItem(selected);
|
|
699
|
-
|
|
700
|
-
if (!this._valueChanging) {
|
|
701
|
-
this._selectedChanging = true;
|
|
702
|
-
this.value = selected.value || '';
|
|
703
|
-
if (this.__userInteraction) {
|
|
704
|
-
this.opened = false;
|
|
705
|
-
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
|
706
|
-
this.__userInteraction = false;
|
|
707
|
-
}
|
|
708
|
-
delete this._selectedChanging;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
const labelledIdReferenceConfig = selected || this.placeholder ? { newId: this._itemId } : { oldId: this._itemId };
|
|
713
|
-
|
|
714
|
-
setAriaIDReference(valueButton, 'aria-labelledby', labelledIdReferenceConfig);
|
|
715
|
-
if (this.accessibleName || this.accessibleNameRef) {
|
|
716
|
-
this._setCustomAriaLabelledBy(this.accessibleNameRef || this._srLabelController.defaultId);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
/** @private */
|
|
721
|
-
_updateSelectedItem(value, items) {
|
|
722
|
-
if (items) {
|
|
723
|
-
const valueAsString = value == null ? value : value.toString();
|
|
724
|
-
this._menuElement.selected = items.reduce((prev, item, idx) => {
|
|
725
|
-
return prev === undefined && item.value === valueAsString ? idx : prev;
|
|
726
|
-
}, undefined);
|
|
727
|
-
if (!this._selectedChanging) {
|
|
728
|
-
this._valueChanging = true;
|
|
729
|
-
this.__updateValueButton();
|
|
730
|
-
delete this._valueChanging;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* Override method inherited from `FocusMixin` to not remove focused
|
|
737
|
-
* state when select is opened and focus moves to list-box.
|
|
738
|
-
* @return {boolean}
|
|
739
|
-
* @protected
|
|
740
|
-
* @override
|
|
741
|
-
*/
|
|
742
|
-
_shouldRemoveFocus() {
|
|
743
|
-
return !this.opened;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/**
|
|
747
|
-
* Override method inherited from `FocusMixin` to validate on blur.
|
|
748
|
-
* @param {boolean} focused
|
|
749
|
-
* @protected
|
|
750
|
-
* @override
|
|
751
|
-
*/
|
|
752
|
-
_setFocused(focused) {
|
|
753
|
-
super._setFocused(focused);
|
|
754
|
-
|
|
755
|
-
if (!focused) {
|
|
756
|
-
this.validate();
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Returns true if the current value satisfies all constraints (if any)
|
|
762
|
-
*
|
|
763
|
-
* @return {boolean}
|
|
764
|
-
*/
|
|
765
|
-
checkValidity() {
|
|
766
|
-
return !this.required || this.readonly || !!this.value;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
/**
|
|
770
|
-
* Renders items when they are provided by the `items` property and clears the content otherwise.
|
|
771
|
-
* @param {!HTMLElement} root
|
|
772
|
-
* @param {!Select} _select
|
|
773
|
-
* @private
|
|
774
|
-
*/
|
|
775
|
-
__defaultRenderer(root, _select) {
|
|
776
|
-
if (!this.items || this.items.length === 0) {
|
|
777
|
-
root.textContent = '';
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
let listBox = root.firstElementChild;
|
|
782
|
-
if (!listBox) {
|
|
783
|
-
listBox = document.createElement('vaadin-select-list-box');
|
|
784
|
-
root.appendChild(listBox);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
listBox.textContent = '';
|
|
788
|
-
this.items.forEach((item) => {
|
|
789
|
-
listBox.appendChild(this.__createItemElement(item));
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
|
|
793
228
|
/**
|
|
794
229
|
* Fired when the user commits a value change.
|
|
795
230
|
*
|
|
@@ -98,10 +98,12 @@ const selectOverlay = css`
|
|
|
98
98
|
|
|
99
99
|
/* Small viewport adjustment */
|
|
100
100
|
:host([phone]) {
|
|
101
|
+
/* stylelint-disable declaration-block-no-redundant-longhand-properties */
|
|
101
102
|
top: 0 !important;
|
|
102
103
|
right: 0 !important;
|
|
103
104
|
bottom: var(--vaadin-overlay-viewport-bottom, 0) !important;
|
|
104
105
|
left: 0 !important;
|
|
106
|
+
/* stylelint-enable declaration-block-no-redundant-longhand-properties */
|
|
105
107
|
align-items: stretch;
|
|
106
108
|
justify-content: flex-end;
|
|
107
109
|
}
|
|
@@ -4,6 +4,5 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import '@vaadin/input-container/theme/lumo/vaadin-input-container.js';
|
|
7
|
-
import '@vaadin/overlay/theme/lumo/vaadin-overlay.js';
|
|
8
7
|
import './vaadin-select-styles.js';
|
|
9
8
|
import '../../src/vaadin-select.js';
|
|
@@ -4,6 +4,5 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import '@vaadin/input-container/theme/material/vaadin-input-container.js';
|
|
7
|
-
import '@vaadin/overlay/theme/material/vaadin-overlay.js';
|
|
8
7
|
import './vaadin-select-styles.js';
|
|
9
8
|
import '../../src/vaadin-select.js';
|