@vaadin/select
Version:
vaadin-select
234 lines (220 loc) • 8.69 kB
JavaScript
/**
* @license
* Copyright (c) 2017 - 2025 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import '@vaadin/input-container/src/vaadin-input-container.js';
import './vaadin-select-item.js';
import './vaadin-select-list-box.js';
import './vaadin-select-overlay.js';
import './vaadin-select-value-button.js';
import { html, LitElement } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
import { screenReaderOnly } from '@vaadin/a11y-base/src/styles/sr-only-styles.js';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { inputFieldShared } from '@vaadin/field-base/src/styles/input-field-shared-styles.js';
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { selectStyles } from './styles/vaadin-select-base-styles.js';
import { SelectBaseMixin } from './vaadin-select-base-mixin.js';
/**
* `<vaadin-select>` is a Web Component for selecting values from a list of items.
*
* ### Items
*
* Use the `items` property to define possible options for the select:
*
* ```html
* <vaadin-select id="select"></vaadin-select>
* ```
* ```js
* const select = document.querySelector('#select');
* select.items = [
* { label: 'Most recent first', value: 'recent' },
* { component: 'hr' },
* { label: 'Rating: low to high', value: 'rating-asc', className: 'asc' },
* { label: 'Rating: high to low', value: 'rating-desc', className: 'desc' },
* { component: 'hr' },
* { label: 'Price: low to high', value: 'price-asc', disabled: true },
* { label: 'Price: high to low', value: 'price-desc', disabled: true }
* ];
* ```
*
* ### Rendering
*
* Alternatively, the content of the select can be populated by using the renderer callback function.
*
* The renderer function provides `root`, `select` arguments.
* Generate DOM content, append it to the `root` element and control the state
* of the host element by accessing `select`.
*
* ```js
* const select = document.querySelector('#select');
* select.renderer = function(root, select) {
* const listBox = document.createElement('vaadin-list-box');
* // append 3 <vaadin-item> elements
* ['Jose', 'Manolo', 'Pedro'].forEach(function(name) {
* const item = document.createElement('vaadin-item');
* item.textContent = name;
* item.setAttribute('label', name)
* listBox.appendChild(item);
* });
*
* // update the content
* root.appendChild(listBox);
* };
* ```
*
* Renderer is called on initialization of new select and on its opening.
* DOM generated during the renderer call can be reused
* in the next renderer call and will be provided with the `root` argument.
* On first call it will be empty.
*
* * Hint: By setting the `label` property of inner vaadin-items you will
* be able to change the visual representation of the selected value in the input part.
*
* ### Styling
*
* The following custom properties are available for styling:
*
* Custom property | Description | Default
* ---------------------------------|-----------------------------|--------
* `--vaadin-field-default-width` | Default width of the field | `12em`
* `--vaadin-select-overlay-width` | Width of the overlay |
*
* The following shadow DOM parts are available for styling:
*
* Part name | Description
* ---------------------|----------------
* `label` | The label element
* `input-field` | The element that wraps prefix, value and toggle button
* `field-button` | Set on the toggle button
* `error-message` | The error message element
* `helper-text` | The helper text element wrapper
* `required-indicator` | The `required` state indicator element
* `toggle-button` | The toggle button
* `backdrop` | Backdrop of the overlay
* `overlay` | The overlay container
* `content` | The overlay content
*
* The following state attributes are available for styling:
*
* Attribute | Description
* ---------------------|---------------------------------
* `disabled` | Set when the element is disabled
* `has-value` | Set when the element has a value
* `has-label` | Set when the element has a label
* `has-helper` | Set when the element has helper text or slot
* `has-error-message` | Set when the element has an error message
* `invalid` | Set when the element is invalid
* `focused` | Set when the element is focused
* `focus-ring` | Set when the element is keyboard focused
* `readonly` | Set when the element is readonly
* `opened` | Set when the overlay is opened
* `phone` | Set when the overlay is shown in phone mode
*
* ### Internal components
*
* In addition to `<vaadin-select>` itself, the following internal
* components are themable:
*
* - `<vaadin-select-value-button>` - has the same API as [`<vaadin-button>`](#/elements/vaadin-button).
* - `<vaadin-select-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
* - `<vaadin-select-item>` - has the same API as [`<vaadin-item>`](#/elements/vaadin-item).
*
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
*
* @fires {Event} change - Fired when the user commits a value change.
* @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
* @fires {CustomEvent} validated - Fired whenever the field is validated.
*
* @customElement
* @extends HTMLElement
* @mixes ElementMixin
* @mixes SelectBaseMixin
* @mixes ThemableMixin
*/
class Select extends SelectBaseMixin(ElementMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))) {
static get is() {
return 'vaadin-select';
}
static get styles() {
return [inputFieldShared, screenReaderOnly, selectStyles];
}
/** @protected */
render() {
return html`
<div class="vaadin-select-container">
<div part="label" @click="${this._onClick}">
<slot name="label"></slot>
<span part="required-indicator" aria-hidden="true" @click="${this.focus}"></span>
</div>
<vaadin-input-container
part="input-field"
.readonly="${this.readonly}"
.disabled="${this.disabled}"
.invalid="${this.invalid}"
theme="${ifDefined(this._theme)}"
@click="${this._onClick}"
>
<slot name="prefix" slot="prefix"></slot>
<slot name="value"></slot>
<div
part="field-button toggle-button"
slot="suffix"
aria-hidden="true"
@mousedown="${this._onToggleMouseDown}"
></div>
</vaadin-input-container>
<div part="helper-text">
<slot name="helper"></slot>
</div>
<div part="error-message">
<slot name="error-message"></slot>
</div>
</div>
<vaadin-select-overlay
id="overlay"
.owner="${this}"
.positionTarget="${this._inputContainer}"
.opened="${this.opened}"
.withBackdrop="${this._phone}"
.renderer="${this.renderer || this.__defaultRenderer}"
?phone="${this._phone}"
theme="${ifDefined(this._theme)}"
?no-vertical-overlap="${this.noVerticalOverlap}"
exportparts="backdrop, overlay, content"
@opened-changed="${this._onOpenedChanged}"
@vaadin-overlay-open="${this._onOverlayOpen}"
>
<slot name="overlay"></slot>
</vaadin-select-overlay>
<slot name="tooltip"></slot>
<div class="sr-only">
<slot name="sr-label"></slot>
</div>
`;
}
/** @private */
_onOpenedChanged(event) {
this.opened = event.detail.value;
}
/** @private */
_onOverlayOpen() {
if (this._menuElement) {
this._menuElement.focus({ focusVisible: isKeyboardActive() });
}
}
/**
* Fired when the user commits a value change.
*
* @event change
*/
}
defineCustomElement(Select);
export { Select };