UNPKG

igniteui-webcomponents

Version:

Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.

453 lines (451 loc) 16.7 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var IgcSelectComponent_1; import { html } from 'lit'; import { property, query, state } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { addThemingController } from '../../theming/theming-controller.js'; import { addKeybindings, altKey, arrowDown, arrowLeft, arrowRight, arrowUp, endKey, enterKey, escapeKey, homeKey, spaceBar, tabKey, } from '../common/controllers/key-bindings.js'; import { addRootClickController } from '../common/controllers/root-click.js'; import { addRootScrollHandler } from '../common/controllers/root-scroll.js'; import { addSlotController, setSlots } from '../common/controllers/slot.js'; import { blazorAdditionalDependencies } from '../common/decorators/blazorAdditionalDependencies.js'; import { watch } from '../common/decorators/watch.js'; import { registerComponent } from '../common/definitions/register.js'; import { getActiveItems, getItems, getNextActiveItem, getPreviousActiveItem, IgcBaseComboBoxLikeComponent, setInitialSelectionState, } from '../common/mixins/combo-box.js'; import { EventEmitterMixin } from '../common/mixins/event-emitter.js'; import { FormAssociatedRequiredMixin } from '../common/mixins/forms/associated-required.js'; import { FormValueSelectTransformers } from '../common/mixins/forms/form-transformers.js'; import { createFormValueState } from '../common/mixins/forms/form-value.js'; import { partMap } from '../common/part-map.js'; import { addSafeEventListener, findElementFromEventPath, isString, } from '../common/util.js'; import IgcIconComponent from '../icon/icon.js'; import IgcInputComponent from '../input/input.js'; import IgcPopoverComponent from '../popover/popover.js'; import IgcValidationContainerComponent from '../validation-container/validation-container.js'; import IgcSelectGroupComponent from './select-group.js'; import IgcSelectHeaderComponent from './select-header.js'; import IgcSelectItemComponent from './select-item.js'; import { styles } from './themes/select.base.css.js'; import { styles as shared } from './themes/shared/select.common.css.js'; import { all } from './themes/themes.js'; import { selectValidators } from './validators.js'; const Slots = setSlots('prefix', 'suffix', 'header', 'footer', 'helper-text', 'toggle-icon', 'toggle-icon-expanded', 'value-missing', 'custom-error', 'invalid'); let IgcSelectComponent = IgcSelectComponent_1 = class IgcSelectComponent extends FormAssociatedRequiredMixin(EventEmitterMixin(IgcBaseComboBoxLikeComponent)) { static register() { registerComponent(IgcSelectComponent_1, IgcIconComponent, IgcInputComponent, IgcPopoverComponent, IgcSelectGroupComponent, IgcSelectHeaderComponent, IgcSelectItemComponent); } get __validators() { return selectValidators; } get _activeItems() { return Array.from(getActiveItems(this, IgcSelectItemComponent.tagName)); } set value(value) { this._updateValue(value); const item = this._getItem(this._formValue.value); item ? this._setSelectedItem(item) : this._clearSelectedItem(); } get value() { return this._formValue.value; } get items() { return Array.from(getItems(this, IgcSelectItemComponent.tagName)); } get groups() { return Array.from(getItems(this, IgcSelectGroupComponent.tagName)); } get selectedItem() { return this._selectedItem; } _scrollStrategyChange() { this._rootScrollController.update({ resetListeners: true }); } _openChange() { this._rootClickController.update(); this._rootScrollController.update(); } constructor() { super(); this._searchTerm = ''; this._lastKeyTime = 0; this._slots = addSlotController(this, { slots: Slots }); this._rootScrollController = addRootScrollHandler(this, { hideCallback: this._handleClosing, }); this._rootClickController = addRootClickController(this, { onHide: this._handleClosing, }); this._formValue = createFormValueState(this, { initialValue: undefined, transformers: FormValueSelectTransformers, }); this._selectedItem = null; this.outlined = false; this.distance = 0; this.placement = 'bottom-start'; this.scrollStrategy = 'scroll'; addThemingController(this, all); addKeybindings(this, { skip: () => this.disabled, bindingDefaults: { preventDefault: true, triggers: ['keydownRepeat'] }, }) .set([altKey, arrowDown], this._handleAltArrowDown) .set([altKey, arrowUp], this._handleAltArrowUp) .set(arrowDown, this._handleArrowDown) .set(arrowUp, this._handleArrowUp) .set(arrowLeft, this._handleArrowUp) .set(arrowRight, this._handleArrowDown) .set(tabKey, this._handleTab, { preventDefault: false }) .set(escapeKey, this._handleEscape) .set(homeKey, this._handleHome) .set(endKey, this._handleEnd) .set(spaceBar, this._handleSpace) .set(enterKey, this._handleEnter); addSafeEventListener(this, 'keydown', this._handleSearch); addSafeEventListener(this, 'focusin', this._handleFocusIn); addSafeEventListener(this, 'focusout', this._handleFocusOut); } async firstUpdated() { await this.updateComplete; const selected = setInitialSelectionState(this.items); if (this.value && !selected) { this._selectItem(this._getItem(this.value), false); } if (selected && selected.value !== this.value) { this.defaultValue = selected.value; this._selectItem(selected, false); } if (this.autofocus) { this.focus(); } this._validate(); } _handleSearch(event) { if (!/^.$/u.test(event.key)) { return; } event.preventDefault(); const now = Date.now(); if (now - this._lastKeyTime > 500) { this._searchTerm = ''; } this._lastKeyTime = now; this._searchTerm += event.key.toLocaleLowerCase(); const item = this._activeItems.find((item) => item.textContent?.trim().toLocaleLowerCase().startsWith(this._searchTerm)); if (item) { this.open ? this._activateItem(item) : this._selectItem(item); this._activeItem.focus(); } } _handleEnter() { this.open && this._activeItem ? this._selectItem(this._activeItem) : this.handleAnchorClick(); } _handleSpace() { if (!this.open) { this.handleAnchorClick(); } } _handleArrowDown() { const item = getNextActiveItem(this.items, this._activeItem); this.open ? this._navigateToActiveItem(item) : this._selectItem(item); } _handleArrowUp() { const item = getPreviousActiveItem(this.items, this._activeItem); this.open ? this._navigateToActiveItem(item) : this._selectItem(item); } _handleAltArrowDown() { if (!this.open) { this._show(true); this._focusItemOnOpen(); } } async _handleAltArrowUp() { if (this.open && (await this._hide(true))) { this._input.focus(); } } async _handleEscape() { if (await this._hide(true)) { this._input.focus(); } } _handleTab(event) { if (this.open) { event.preventDefault(); this._selectItem(this._activeItem ?? this._selectedItem); this._hide(true); } } _handleHome() { const item = this._activeItems.at(0); this.open ? this._navigateToActiveItem(item) : this._selectItem(item); } _handleEnd() { const item = this._activeItems.at(-1); this.open ? this._navigateToActiveItem(item) : this._selectItem(item); } _handleFocusIn() { this._setTouchedState(); } _handleFocusOut({ relatedTarget }) { if (this.contains(relatedTarget)) { return; } super._handleBlur(); } _handleClick(event) { const item = findElementFromEventPath(IgcSelectItemComponent.tagName, event); if (item && this._activeItems.includes(item)) { this._selectItem(item); } } _handleChange(item) { this._setTouchedState(); return this.emitEvent('igcChange', { detail: item }); } _handleClosing() { this._hide(true); } handleAnchorClick() { super.handleAnchorClick(); this._focusItemOnOpen(); } _restoreDefaultValue() { super._restoreDefaultValue(); this._formValue.setValueAndFormState(this._formValue.defaultValue); const item = this._getItem(this._formValue.value); item ? this._setSelectedItem(item) : this._clearSelectedItem(); } _activateItem(item) { if (this._activeItem) { this._activeItem.active = false; } this._activeItem = item; this._activeItem.active = true; } _setSelectedItem(item) { if (this._selectedItem) { this._selectedItem.selected = false; } this._selectedItem = item; this._selectedItem.selected = true; return this._selectedItem; } _selectItem(item, emit = true) { if (!item) { this._clearSelectedItem(); this._updateValue(); return null; } const shouldFocus = emit && this.open; const shouldHide = emit && !this.keepOpenOnSelect; if (this._selectedItem === item) { if (shouldFocus) this._input.focus(); return this._selectedItem; } const newItem = this._setSelectedItem(item); this._activateItem(newItem); this._updateValue(newItem.value); if (emit) this._handleChange(newItem); if (shouldFocus) this._input.focus(); if (shouldHide) this._hide(true); return this._selectedItem; } _navigateToActiveItem(item) { if (item) { this._activateItem(item); this._activeItem.focus({ preventScroll: true }); item.scrollIntoView({ behavior: 'auto', block: 'nearest' }); } } _updateValue(value) { this._formValue.setValueAndFormState(value); } _clearSelectedItem() { if (this._selectedItem) { this._selectedItem.selected = false; } this._selectedItem = null; } async _focusItemOnOpen() { await this.updateComplete; (this._selectedItem || this._activeItem)?.focus(); } _getItem(value) { return this.items.find((item) => item.value === value); } focus(options) { this._input.focus(options); } blur() { this._input.blur(); } reportValidity() { const valid = super.reportValidity(); if (!valid) this._input.focus(); return valid; } navigateTo(value) { const item = isString(value) ? this._getItem(value) : this.items[value]; if (item) { this._navigateToActiveItem(item); } return item ?? null; } select(value) { const item = isString(value) ? this._getItem(value) : this.items[value]; return item ? this._selectItem(item, false) : null; } clearSelection() { this._updateValue(); this._clearSelectedItem(); } _renderInputSlots() { const prefix = this._slots.hasAssignedElements('prefix') ? 'prefix' : ''; const suffix = this._slots.hasAssignedElements('suffix') ? 'suffix' : ''; return html ` <span slot=${prefix}> <slot name="prefix"></slot> </span> <span slot=${suffix}> <slot name="suffix"></slot> </span> `; } _renderToggleIcon() { const parts = { 'toggle-icon': true, filled: !!this.value }; const iconName = this.open ? 'input_collapse' : 'input_expand'; const iconHidden = this.open && this._slots.hasAssignedElements('toggle-icon-expanded'); return html ` <span slot="suffix" part=${partMap(parts)} aria-hidden="true"> <slot name="toggle-icon" ?hidden=${iconHidden}> <igc-icon name=${iconName} collection="default"></igc-icon> </slot> <slot name="toggle-icon-expanded" ?hidden=${!iconHidden}></slot> </span> `; } _renderHelperText() { return IgcValidationContainerComponent.create(this, { id: 'select-helper-text', slot: 'anchor', hasHelperText: true, }); } _renderInputAnchor() { const value = this.selectedItem?.textContent?.trim(); return html ` <igc-input id="input" slot="anchor" role="combobox" readonly aria-controls="dropdown" aria-describedby="select-helper-text" aria-expanded=${this.open} exportparts="container: input, input: native-input, label, prefix, suffix" tabIndex=${this.disabled ? -1 : 0} value=${ifDefined(value)} placeholder=${ifDefined(this.placeholder)} label=${ifDefined(this.label)} .disabled=${this.disabled} .required=${this.required} .invalid=${this.invalid} .outlined=${this.outlined} @click=${this.handleAnchorClick} > ${this._renderInputSlots()} ${this._renderToggleIcon()} </igc-input> ${this._renderHelperText()} `; } _renderDropdown() { return html ` <div part="base" .inert=${!this.open}> <div id="dropdown" role="listbox" part="list" aria-labelledby="input" @click=${this._handleClick} > <slot name="header"></slot> <slot></slot> <slot name="footer"></slot> </div> </div> `; } render() { return html ` <igc-popover ?open=${this.open} flip shift same-width .offset=${this.distance} .placement=${this.placement} > ${this._renderInputAnchor()} ${this._renderDropdown()} </igc-popover> `; } }; IgcSelectComponent.tagName = 'igc-select'; IgcSelectComponent.styles = [styles, shared]; __decorate([ state() ], IgcSelectComponent.prototype, "_selectedItem", void 0); __decorate([ state() ], IgcSelectComponent.prototype, "_activeItem", void 0); __decorate([ query(IgcInputComponent.tagName, true) ], IgcSelectComponent.prototype, "_input", void 0); __decorate([ property() ], IgcSelectComponent.prototype, "value", null); __decorate([ property({ reflect: true, type: Boolean }) ], IgcSelectComponent.prototype, "outlined", void 0); __decorate([ property({ type: Boolean }) ], IgcSelectComponent.prototype, "autofocus", void 0); __decorate([ property({ type: Number }) ], IgcSelectComponent.prototype, "distance", void 0); __decorate([ property() ], IgcSelectComponent.prototype, "label", void 0); __decorate([ property() ], IgcSelectComponent.prototype, "placeholder", void 0); __decorate([ property() ], IgcSelectComponent.prototype, "placement", void 0); __decorate([ property({ attribute: 'scroll-strategy' }) ], IgcSelectComponent.prototype, "scrollStrategy", void 0); __decorate([ watch('scrollStrategy', { waitUntilFirstUpdate: true }) ], IgcSelectComponent.prototype, "_scrollStrategyChange", null); __decorate([ watch('open', { waitUntilFirstUpdate: true }) ], IgcSelectComponent.prototype, "_openChange", null); IgcSelectComponent = IgcSelectComponent_1 = __decorate([ blazorAdditionalDependencies('IgcIconComponent, IgcInputComponent, IgcSelectGroupComponent, IgcSelectHeaderComponent, IgcSelectItemComponent') ], IgcSelectComponent); export default IgcSelectComponent; //# sourceMappingURL=select.js.map