@vaadin/select 22.0.0-rc1 → 23.0.0-alpha2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/select",
3
- "version": "22.0.0-rc1",
3
+ "version": "23.0.0-alpha2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -34,24 +34,24 @@
34
34
  "dependencies": {
35
35
  "@polymer/iron-media-query": "^3.0.0",
36
36
  "@polymer/polymer": "^3.2.0",
37
- "@vaadin/button": "22.0.0-rc1",
38
- "@vaadin/component-base": "22.0.0-rc1",
39
- "@vaadin/field-base": "22.0.0-rc1",
40
- "@vaadin/input-container": "22.0.0-rc1",
41
- "@vaadin/item": "22.0.0-rc1",
42
- "@vaadin/list-box": "22.0.0-rc1",
43
- "@vaadin/vaadin-list-mixin": "22.0.0-rc1",
44
- "@vaadin/vaadin-lumo-styles": "22.0.0-rc1",
45
- "@vaadin/vaadin-material-styles": "22.0.0-rc1",
46
- "@vaadin/vaadin-overlay": "22.0.0-rc1",
47
- "@vaadin/vaadin-themable-mixin": "22.0.0-rc1"
37
+ "@vaadin/button": "23.0.0-alpha2",
38
+ "@vaadin/component-base": "23.0.0-alpha2",
39
+ "@vaadin/field-base": "23.0.0-alpha2",
40
+ "@vaadin/input-container": "23.0.0-alpha2",
41
+ "@vaadin/item": "23.0.0-alpha2",
42
+ "@vaadin/list-box": "23.0.0-alpha2",
43
+ "@vaadin/vaadin-list-mixin": "23.0.0-alpha2",
44
+ "@vaadin/vaadin-lumo-styles": "23.0.0-alpha2",
45
+ "@vaadin/vaadin-material-styles": "23.0.0-alpha2",
46
+ "@vaadin/vaadin-overlay": "23.0.0-alpha2",
47
+ "@vaadin/vaadin-themable-mixin": "23.0.0-alpha2"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@esm-bundle/chai": "^4.3.4",
51
- "@vaadin/polymer-legacy-adapter": "22.0.0-rc1",
51
+ "@vaadin/polymer-legacy-adapter": "23.0.0-alpha2",
52
52
  "@vaadin/testing-helpers": "^0.3.2",
53
53
  "lit": "^2.0.0",
54
54
  "sinon": "^9.2.0"
55
55
  },
56
- "gitHead": "7b6f44bcd2c0fd415028ace666feeb0fedb1d540"
56
+ "gitHead": "070f586dead02ca41b66717820c647f48bf1665f"
57
57
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { Item } from '@vaadin/item/src/vaadin-item.js';
7
+
8
+ /**
9
+ * An element used internally by `<vaadin-select>`. Not intended to be used separately.
10
+ *
11
+ * @extends Item
12
+ * @protected
13
+ */
14
+ class SelectItem extends Item {
15
+ static get is() {
16
+ return 'vaadin-select-item';
17
+ }
18
+ }
19
+
20
+ customElements.define(SelectItem.is, SelectItem);
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { ListBox } from '@vaadin/list-box/src/vaadin-list-box.js';
7
+
8
+ /**
9
+ * An element used internally by `<vaadin-select>`. Not intended to be used separately.
10
+ *
11
+ * @extends ListBox
12
+ * @protected
13
+ */
14
+ class SelectListBox extends ListBox {
15
+ static get is() {
16
+ return 'vaadin-select-list-box';
17
+ }
18
+ }
19
+
20
+ customElements.define(SelectListBox.is, SelectListBox);
@@ -14,14 +14,14 @@ registerStyles(
14
14
  min-width: 0;
15
15
  }
16
16
 
17
- ::slotted(vaadin-item) {
17
+ ::slotted(:not([slot])) {
18
18
  padding-left: 0;
19
19
  padding-right: 0;
20
20
  flex: auto;
21
21
  }
22
22
 
23
23
  /* placeholder styles */
24
- ::slotted(:not([selected])) {
24
+ ::slotted(:not([slot]):not([selected])) {
25
25
  line-height: normal;
26
26
  }
27
27
 
@@ -9,6 +9,19 @@ import { DelegateFocusMixin } from '@vaadin/field-base/src/delegate-focus-mixin.
9
9
  import { FieldMixin } from '@vaadin/field-base/src/field-mixin.js';
10
10
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
11
11
 
12
+ export interface SelectItem {
13
+ label?: string;
14
+ component?: string;
15
+ disabled?: boolean;
16
+ }
17
+
18
+ /**
19
+ * Fired when the user commits a value change.
20
+ */
21
+ export type SelectChangeEvent = Event & {
22
+ target: Select;
23
+ };
24
+
12
25
  /**
13
26
  * Function for rendering the content of the `<vaadin-select>`.
14
27
  * Receives two arguments:
@@ -42,22 +55,41 @@ export interface SelectCustomEventMap {
42
55
  'value-changed': SelectValueChangedEvent;
43
56
  }
44
57
 
45
- export interface SelectEventMap extends HTMLElementEventMap, SelectCustomEventMap {}
58
+ export interface SelectEventMap extends HTMLElementEventMap, SelectCustomEventMap {
59
+ change: SelectChangeEvent;
60
+ }
46
61
 
47
62
  /**
48
63
  * `<vaadin-select>` is a Web Component for selecting values from a list of items.
49
64
  *
65
+ * ### Items
66
+ *
67
+ * Use the `items` property to define possible options for the select:
68
+ *
69
+ * ```html
70
+ * <vaadin-select id="select"></vaadin-select>
71
+ * ```
72
+ * ```js
73
+ * const select = document.querySelector('#select');
74
+ * select.items = [
75
+ * { label: 'Most recent first', value: 'recent' },
76
+ * { component: 'hr' },
77
+ * { label: 'Rating: low to high', value: 'rating-asc' },
78
+ * { label: 'Rating: high to low', value: 'rating-desc' },
79
+ * { component: 'hr' },
80
+ * { label: 'Price: low to high', value: 'price-asc', disabled: true },
81
+ * { label: 'Price: high to low', value: 'price-desc', disabled: true }
82
+ * ];
83
+ * ```
84
+ *
50
85
  * ### Rendering
51
86
  *
52
- * The content of the select can be populated by using the renderer callback function.
87
+ * Alternatively, the content of the select can be populated by using the renderer callback function.
53
88
  *
54
89
  * The renderer function provides `root`, `select` arguments.
55
90
  * Generate DOM content, append it to the `root` element and control the state
56
91
  * of the host element by accessing `select`.
57
92
  *
58
- * ```html
59
- * <vaadin-select id="select"></vaadin-select>
60
- * ```
61
93
  * ```js
62
94
  * const select = document.querySelector('#select');
63
95
  * select.renderer = function(root, select) {
@@ -132,6 +164,29 @@ export interface SelectEventMap extends HTMLElementEventMap, SelectCustomEventMa
132
164
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
133
165
  */
134
166
  declare class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(ThemableMixin(HTMLElement))))) {
167
+ /**
168
+ * An array containing items that will be rendered as the options of the select.
169
+ *
170
+ * #### Example
171
+ * ```js
172
+ * select.items = [
173
+ * { label: 'Most recent first', value: 'recent' },
174
+ * { component: 'hr' },
175
+ * { label: 'Rating: low to high', value: 'rating-asc' },
176
+ * { label: 'Rating: high to low', value: 'rating-desc' },
177
+ * { component: 'hr' },
178
+ * { label: 'Price: low to high', value: 'price-asc', disabled: true },
179
+ * { label: 'Price: high to low', value: 'price-desc', disabled: true }
180
+ * ];
181
+ * ```
182
+ *
183
+ * Note: each item is rendered by default as the internal `<vaadin-select-item>` that is an extension of `<vaadin-item>`.
184
+ * To render the item with a custom component, provide a tag name by the `component` property.
185
+ *
186
+ * @type {!Array<!SelectItem>}
187
+ */
188
+ items: SelectItem[] | null | undefined;
189
+
135
190
  /**
136
191
  * Set when the select is open
137
192
  */
@@ -5,6 +5,8 @@
5
5
  */
6
6
  import '@polymer/iron-media-query/iron-media-query.js';
7
7
  import '@vaadin/input-container/src/vaadin-input-container.js';
8
+ import './vaadin-select-item.js';
9
+ import './vaadin-select-list-box.js';
8
10
  import './vaadin-select-overlay.js';
9
11
  import './vaadin-select-value-button.js';
10
12
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
@@ -22,17 +24,34 @@ registerStyles('vaadin-select', [fieldShared, inputFieldContainer], { moduleId:
22
24
  /**
23
25
  * `<vaadin-select>` is a Web Component for selecting values from a list of items.
24
26
  *
27
+ * ### Items
28
+ *
29
+ * Use the `items` property to define possible options for the select:
30
+ *
31
+ * ```html
32
+ * <vaadin-select id="select"></vaadin-select>
33
+ * ```
34
+ * ```js
35
+ * const select = document.querySelector('#select');
36
+ * select.items = [
37
+ * { label: 'Most recent first', value: 'recent' },
38
+ * { component: 'hr' },
39
+ * { label: 'Rating: low to high', value: 'rating-asc' },
40
+ * { label: 'Rating: high to low', value: 'rating-desc' },
41
+ * { component: 'hr' },
42
+ * { label: 'Price: low to high', value: 'price-asc', disabled: true },
43
+ * { label: 'Price: high to low', value: 'price-desc', disabled: true }
44
+ * ];
45
+ * ```
46
+ *
25
47
  * ### Rendering
26
48
  *
27
- * The content of the select can be populated by using the renderer callback function.
49
+ * Alternatively, the content of the select can be populated by using the renderer callback function.
28
50
  *
29
51
  * The renderer function provides `root`, `select` arguments.
30
52
  * Generate DOM content, append it to the `root` element and control the state
31
53
  * of the host element by accessing `select`.
32
54
  *
33
- * ```html
34
- * <vaadin-select id="select"></vaadin-select>
35
- * ```
36
55
  * ```js
37
56
  * const select = document.querySelector('#select');
38
57
  * select.renderer = function(root, select) {
@@ -169,6 +188,32 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
169
188
 
170
189
  static get properties() {
171
190
  return {
191
+ /**
192
+ * An array containing items that will be rendered as the options of the select.
193
+ *
194
+ * #### Example
195
+ * ```js
196
+ * select.items = [
197
+ * { label: 'Most recent first', value: 'recent' },
198
+ * { component: 'hr' },
199
+ * { label: 'Rating: low to high', value: 'rating-asc' },
200
+ * { label: 'Rating: high to low', value: 'rating-desc' },
201
+ * { component: 'hr' },
202
+ * { label: 'Price: low to high', value: 'price-asc', disabled: true },
203
+ * { label: 'Price: high to low', value: 'price-desc', disabled: true }
204
+ * ];
205
+ * ```
206
+ *
207
+ * Note: each item is rendered by default as the internal `<vaadin-select-item>` that is an extension of `<vaadin-item>`.
208
+ * To render the item with a custom component, provide a tag name by the `component` property.
209
+ *
210
+ * @type {!Array<!SelectItem>}
211
+ */
212
+ items: {
213
+ type: Array,
214
+ observer: '__itemsChanged'
215
+ },
216
+
172
217
  /**
173
218
  * Set when the select is open
174
219
  * @type {boolean}
@@ -357,13 +402,17 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
357
402
  }
358
403
  }
359
404
 
360
- /** @private */
405
+ /**
406
+ * @param {SelectRenderer | undefined | null} renderer
407
+ * @param {SelectOverlay | undefined} overlay
408
+ * @private
409
+ */
361
410
  _rendererChanged(renderer, overlay) {
362
411
  if (!overlay) {
363
412
  return;
364
413
  }
365
414
 
366
- overlay.setProperties({ owner: this, renderer });
415
+ overlay.setProperties({ owner: this, renderer: renderer || this.__defaultRenderer });
367
416
 
368
417
  this.requestContentUpdate();
369
418
 
@@ -372,6 +421,17 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
372
421
  }
373
422
  }
374
423
 
424
+ /**
425
+ * @param {SelectItem[] | undefined | null} newItems
426
+ * @param {SelectItem[] | undefined | null} oldItems
427
+ * @private
428
+ */
429
+ __itemsChanged(newItems, oldItems) {
430
+ if (newItems || oldItems) {
431
+ this.requestContentUpdate();
432
+ }
433
+ }
434
+
375
435
  /** @private */
376
436
  _assignMenuElement() {
377
437
  const menuElement = this.__getMenuElement();
@@ -489,13 +549,9 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
489
549
 
490
550
  this._menuElement.focus();
491
551
  } else if (wasOpened) {
492
- if (this._phone) {
493
- this._setFocused(false);
494
- } else {
495
- this.focus();
496
- if (this._openedWithFocusRing) {
497
- this.setAttribute('focus-ring', '');
498
- }
552
+ this.focus();
553
+ if (this._openedWithFocusRing) {
554
+ this.setAttribute('focus-ring', '');
499
555
  }
500
556
  this.validate();
501
557
  }
@@ -532,7 +588,7 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
532
588
 
533
589
  const label = selected.getAttribute('label');
534
590
  if (label) {
535
- labelItem = this.__createItem(label);
591
+ labelItem = this.__createItemElement({ label });
536
592
  } else {
537
593
  labelItem = selected.cloneNode(true);
538
594
  }
@@ -540,26 +596,40 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
540
596
  // store reference to the original item
541
597
  labelItem._sourceItem = selected;
542
598
 
543
- this.__appendItem(labelItem);
599
+ this.__appendValueItemElement(labelItem);
544
600
 
545
601
  // ensure the item gets proper styles
546
602
  labelItem.selected = true;
547
603
  }
548
604
 
549
- /** @private */
550
- __createItem(text) {
551
- const item = document.createElement('vaadin-item');
552
- item.textContent = text;
553
- return item;
605
+ /**
606
+ * @param {!SelectItem} item
607
+ * @private
608
+ */
609
+ __createItemElement(item) {
610
+ const itemElement = document.createElement(item.component || 'vaadin-select-item');
611
+ if (item.label) {
612
+ itemElement.textContent = item.label;
613
+ }
614
+ if (item.value) {
615
+ itemElement.value = item.value;
616
+ }
617
+ if (item.disabled) {
618
+ itemElement.disabled = item.disabled;
619
+ }
620
+ return itemElement;
554
621
  }
555
622
 
556
- /** @private */
557
- __appendItem(item) {
558
- item.removeAttribute('tabindex');
559
- item.removeAttribute('role');
560
- item.setAttribute('id', this._fieldId);
623
+ /**
624
+ * @param {!HTMLElement} itemElement
625
+ * @private
626
+ */
627
+ __appendValueItemElement(itemElement) {
628
+ itemElement.removeAttribute('tabindex');
629
+ itemElement.removeAttribute('role');
630
+ itemElement.setAttribute('id', this._fieldId);
561
631
 
562
- this._valueButton.appendChild(item);
632
+ this._valueButton.appendChild(itemElement);
563
633
  }
564
634
 
565
635
  /** @private */
@@ -576,8 +646,8 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
576
646
 
577
647
  if (!selected) {
578
648
  if (this.placeholder) {
579
- const item = this.__createItem(this.placeholder);
580
- this.__appendItem(item);
649
+ const item = this.__createItemElement({ label: this.placeholder });
650
+ this.__appendValueItemElement(item);
581
651
  this._valueButton.setAttribute('placeholder', '');
582
652
  }
583
653
  } else {
@@ -644,6 +714,30 @@ class Select extends DelegateFocusMixin(FieldMixin(SlotMixin(ElementMixin(Themab
644
714
  return !(this.invalid = !(this.disabled || !this.required || this.value));
645
715
  }
646
716
 
717
+ /**
718
+ * Renders items when they are provided by the `items` property and clears the content otherwise.
719
+ * @param {!HTMLElement} root
720
+ * @param {!Select} _select
721
+ * @private
722
+ */
723
+ __defaultRenderer(root, _select) {
724
+ if (!this.items || this.items.length === 0) {
725
+ root.textContent = '';
726
+ return;
727
+ }
728
+
729
+ let listBox = root.firstElementChild;
730
+ if (!listBox) {
731
+ listBox = document.createElement('vaadin-select-list-box');
732
+ root.appendChild(listBox);
733
+ }
734
+
735
+ listBox.textContent = '';
736
+ this.items.forEach((item) => {
737
+ listBox.appendChild(this.__createItemElement(item));
738
+ });
739
+ }
740
+
647
741
  /**
648
742
  * Fired when the user commits a value change.
649
743
  *
@@ -67,7 +67,7 @@ registerStyles(
67
67
  box-shadow: none;
68
68
  }
69
69
 
70
- ::slotted(vaadin-item:hover) {
70
+ ::slotted(:not([slot]):hover) {
71
71
  background-color: transparent;
72
72
  }
73
73
  `,
@@ -54,12 +54,12 @@ registerStyles(
54
54
  display: none;
55
55
  }
56
56
 
57
- ::slotted(vaadin-item) {
57
+ ::slotted(:not([slot])) {
58
58
  font: inherit;
59
59
  padding: 4px 0;
60
60
  }
61
61
 
62
- ::slotted(vaadin-item:hover) {
62
+ ::slotted(:not([slot]):hover) {
63
63
  background-color: transparent;
64
64
  }
65
65
  `,