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.
457 lines (455 loc) • 16.4 kB
JavaScript
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, queryAssignedElements, state, } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { themes } from '../../theming/theming-decorator.js';
import { addKeybindings, altKey, arrowDown, arrowLeft, arrowRight, arrowUp, endKey, enterKey, escapeKey, homeKey, spaceBar, tabKey, } from '../common/controllers/key-bindings.js';
import { addRootScrollHandler } from '../common/controllers/root-scroll.js';
import { blazorAdditionalDependencies } from '../common/decorators/blazorAdditionalDependencies.js';
import { watch } from '../common/decorators/watch.js';
import { registerComponent } from '../common/definitions/register.js';
import { IgcBaseComboBoxLikeComponent, getActiveItems, getItems, getNextActiveItem, getPreviousActiveItem, 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 { createFormValueState, } from '../common/mixins/forms/form-value.js';
import { findElementFromEventPath, isEmpty, isString, partNameMap, } 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';
let IgcSelectComponent = IgcSelectComponent_1 = class IgcSelectComponent extends FormAssociatedRequiredMixin(EventEmitterMixin(IgcBaseComboBoxLikeComponent)) {
static register() {
registerComponent(IgcSelectComponent_1, IgcIconComponent, IgcInputComponent, IgcPopoverComponent, IgcSelectGroupComponent, IgcSelectHeaderComponent, IgcSelectItemComponent);
}
get _activeItems() {
return Array.from(getActiveItems(this, IgcSelectItemComponent.tagName));
}
get __validators() {
return selectValidators;
}
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;
}
scrollStrategyChanged() {
this._rootScrollController.update({ resetListeners: true });
}
openChange() {
this._rootClickController.update();
this._rootScrollController.update();
}
constructor() {
super();
this._searchTerm = '';
this._lastKeyTime = 0;
this._rootScrollController = addRootScrollHandler(this, {
hideCallback: this.handleClosing,
});
this._selectedItem = null;
this.outlined = false;
this.distance = 0;
this.placement = 'bottom-start';
this.scrollStrategy = 'scroll';
this._formValue = createFormValueState(this, {
initialValue: undefined,
transformers: {
setValue: (value) => value || undefined,
setDefaultValue: (value) => value || undefined,
},
});
this._rootClickController.update({ hideCallback: this.handleClosing });
addKeybindings(this, {
skip: () => this.disabled,
bindingDefaults: { preventDefault: true, triggers: ['keydownRepeat'] },
})
.set([altKey, arrowDown], this.altArrowDown)
.set([altKey, arrowUp], this.altArrowUp)
.set(arrowDown, this.onArrowDown)
.set(arrowUp, this.onArrowUp)
.set(arrowLeft, this.onArrowUp)
.set(arrowRight, this.onArrowDown)
.set(tabKey, this.onTabKey, { preventDefault: false })
.set(escapeKey, this.onEscapeKey)
.set(homeKey, this.onHomeKey)
.set(endKey, this.onEndKey)
.set(spaceBar, this.onSpaceBarKey)
.set(enterKey, this.onEnterKey);
this.addEventListener('keydown', this.handleSearch);
this.addEventListener('focusin', this.handleFocusIn);
this.addEventListener('focusout', this.handleFocusOut);
}
createRenderRoot() {
const root = super.createRenderRoot();
root.addEventListener('slotchange', () => this.requestUpdate());
return root;
}
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._updateValidity();
}
handleFocusIn({ relatedTarget }) {
this._dirty = true;
if (this.contains(relatedTarget) || this.open) {
return;
}
}
handleFocusOut({ relatedTarget }) {
if (this.contains(relatedTarget)) {
return;
}
this.checkValidity();
}
handleClick(event) {
const item = findElementFromEventPath(IgcSelectItemComponent.tagName, event);
if (item && this._activeItems.includes(item)) {
this._selectItem(item);
}
}
handleChange(item) {
return this.emitEvent('igcChange', { detail: item });
}
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();
}
}
handleAnchorClick() {
super.handleAnchorClick();
this.focusItemOnOpen();
}
onEnterKey() {
this.open && this._activeItem
? this._selectItem(this._activeItem)
: this.handleAnchorClick();
}
onSpaceBarKey() {
if (!this.open) {
this.handleAnchorClick();
}
}
onArrowDown() {
const item = getNextActiveItem(this.items, this._activeItem);
this.open ? this._navigateToActiveItem(item) : this._selectItem(item);
}
onArrowUp() {
const item = getPreviousActiveItem(this.items, this._activeItem);
this.open ? this._navigateToActiveItem(item) : this._selectItem(item);
}
altArrowDown() {
if (!this.open) {
this._show(true);
this.focusItemOnOpen();
}
}
async altArrowUp() {
if (this.open && (await this._hide(true))) {
this._input.focus();
}
}
async onEscapeKey() {
if (await this._hide(true)) {
this._input.focus();
}
}
onTabKey(event) {
if (this.open) {
event.preventDefault();
this._selectItem(this._activeItem ?? this._selectedItem);
this._hide(true);
}
}
onHomeKey() {
const item = this._activeItems.at(0);
this.open ? this._navigateToActiveItem(item) : this._selectItem(item);
}
onEndKey() {
const item = this._activeItems.at(-1);
this.open ? this._navigateToActiveItem(item) : this._selectItem(item);
}
handleClosing() {
this._hide(true);
}
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);
this._validate();
}
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 prefixName = isEmpty(this._inputPrefix) ? '' : 'prefix';
const suffixName = isEmpty(this._inputSuffix) ? '' : 'suffix';
return html `
<span slot=${prefixName}>
<slot name="prefix"></slot>
</span>
<span slot=${suffixName}>
<slot name="suffix"></slot>
</span>
`;
}
renderToggleIcon() {
const parts = partNameMap({ 'toggle-icon': true, filled: this.value });
const iconHidden = this.open && !isEmpty(this._expandedIconSlot);
const iconExpandedHidden = !iconHidden;
return html `
<span slot="suffix" part=${parts} aria-hidden="true">
<slot name="toggle-icon" ?hidden=${iconHidden}>
<igc-icon
name=${this.open ? 'input_collapse' : 'input_expand'}
collection="default"
></igc-icon>
</slot>
<slot name="toggle-icon-expanded" ?hidden=${iconExpandedHidden}></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 ? 'true' : 'false'}
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}
=${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"
=${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([
queryAssignedElements({ slot: 'suffix' })
], IgcSelectComponent.prototype, "_inputSuffix", void 0);
__decorate([
queryAssignedElements({ slot: 'prefix' })
], IgcSelectComponent.prototype, "_inputPrefix", void 0);
__decorate([
queryAssignedElements({ slot: 'toggle-icon-expanded' })
], IgcSelectComponent.prototype, "_expandedIconSlot", 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, "scrollStrategyChanged", null);
__decorate([
watch('open', { waitUntilFirstUpdate: true })
], IgcSelectComponent.prototype, "openChange", null);
IgcSelectComponent = IgcSelectComponent_1 = __decorate([
themes(all),
blazorAdditionalDependencies('IgcIconComponent, IgcInputComponent, IgcSelectGroupComponent, IgcSelectHeaderComponent, IgcSelectItemComponent')
], IgcSelectComponent);
export default IgcSelectComponent;
//# sourceMappingURL=select.js.map