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.

655 lines 21.8 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 IgcComboComponent_1; import { LitElement, html, nothing } from 'lit'; import { property, query, queryAssignedElements, state, } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { live } from 'lit/directives/live.js'; import { themes } from '../../theming/theming-decorator.js'; import { addRootClickHandler } from '../common/controllers/root-click.js'; import { blazorAdditionalDependencies } from '../common/decorators/blazorAdditionalDependencies.js'; import { blazorIndirectRender } from '../common/decorators/blazorIndirectRender.js'; import { watch } from '../common/decorators/watch.js'; import { registerComponent } from '../common/definitions/register.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 { asArray, findElementFromEventPath, first, isEmpty, 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 IgcComboHeaderComponent from './combo-header.js'; import IgcComboItemComponent from './combo-item.js'; import IgcComboListComponent from './combo-list.js'; import { DataController } from './controllers/data.js'; import { NavigationController } from './controllers/navigation.js'; import { SelectionController } from './controllers/selection.js'; import { styles } from './themes/combo.base.css.js'; import { styles as shared } from './themes/shared/combo.common.css.js'; import { all } from './themes/themes.js'; import { comboValidators } from './validators.js'; let IgcComboComponent = IgcComboComponent_1 = class IgcComboComponent extends FormAssociatedRequiredMixin(EventEmitterMixin(LitElement)) { static register() { registerComponent(IgcComboComponent_1, IgcIconComponent, IgcComboListComponent, IgcComboItemComponent, IgcComboHeaderComponent, IgcInputComponent, IgcPopoverComponent, IgcValidationContainerComponent); } get __validators() { return comboValidators; } set data(value) { if (this._data === value) { return; } this._data = asArray(value); const pristine = this._pristine; this.value = asArray(this.value); this._pristine = pristine; this._state.runPipeline(); } get data() { return this._data; } set singleSelect(value) { this._singleSelect = value; this._selection.clear(); if (this.hasUpdated) { this.updateValue(); this.resetSearchTerm(); this._navigation.active = -1; } } get singleSelect() { return this._singleSelect; } set valueKey(value) { this._valueKey = value; this._displayKey = this._displayKey ?? this._valueKey; } get valueKey() { return this._valueKey; } set displayKey(value) { this._displayKey = value; if (!this.filteringOptions.filterKey) { this.filteringOptions = { filterKey: this.displayKey }; } } get displayKey() { return this._displayKey ?? this._valueKey; } set groupKey(value) { this._groupKey = value; this._state.runPipeline(); } get groupKey() { return this._groupKey; } set groupSorting(value) { this._groupSorting = value; this._state.runPipeline(); } get groupSorting() { return this._groupSorting; } set filteringOptions(value) { this._filteringOptions = { ...this._filteringOptions, ...value }; this._state.runPipeline(); } get filteringOptions() { return this._filteringOptions; } set disableFiltering(value) { this._disableFiltering = value; this.resetSearchTerm(); } get disableFiltering() { return this._disableFiltering; } set value(items) { this._formValue.value = items; if (this.hasUpdated) { this._updateSelection(); this.updateValue(); } } get value() { return this._formValue.value; } _updateSelection() { this._selection.deselect(); if (!isEmpty(this.value)) { this._selection.select(this.value); } } toggleDirectiveChange() { this._rootClickController.update(); } constructor() { super(); this._data = []; this._disableFiltering = false; this._singleSelect = false; this._groupSorting = 'asc'; this._filteringOptions = { filterKey: this.displayKey, caseSensitive: false, matchDiacritics: false, }; this._displayValue = ''; this._state = new DataController(this); this._selection = new SelectionController(this, this._state); this._navigation = new NavigationController(this, this._state); this.outlined = false; this.autofocusList = false; this.placeholderSearch = 'Search'; this.open = false; this.caseSensitiveIcon = false; this.itemTemplate = ({ item }) => html `${this.displayKey ? item[this.displayKey] : item}`; this.groupHeaderTemplate = ({ item }) => html `${this.groupKey && item[this.groupKey]}`; this._rootClickController = addRootClickHandler(this, { hideCallback: async () => { if (!this.handleClosing()) return; this.open = false; await this.updateComplete; this.emitEvent('igcClosed'); }, }); this.itemRenderer = (item, index) => { if (!item) { return html `${nothing}`; } if (this.groupKey && item.header) { return html ` <igc-combo-header part="group-header"> ${this.groupHeaderTemplate({ item: item.value })} </igc-combo-header> `; } const { id, position } = this._getActiveDescendantId(index); const active = this._navigation.active === index; const selected = this._selection.has(this.data.at(item.dataIndex)); if (active) { this._activeDescendant = id; } return html ` <igc-combo-item id=${id} part=${partNameMap({ item: true, selected, active })} aria-setsize=${this._state.dataState.length} aria-posinset=${position} exportparts="checkbox, checkbox-indicator, checked" .index=${index} ?active=${active} ?selected=${selected} ?hide-checkbox=${this.singleSelect} > ${this.itemTemplate({ item: item.value })} </igc-combo-item> `; }; this._formValue = createFormValueState(this, { initialValue: [], transformers: { setValue: asArray, setDefaultValue: asArray, }, }); this.addEventListener('blur', this._handleBlur); this.addEventListener('keydown', this._navigation.navigateHost.bind(this._navigation)); } async firstUpdated() { await this.updateComplete; this._updateSelection(); this.updateValue(this.hasUpdated); this._pristine = true; this._state.runPipeline(); } _restoreDefaultValue() { this._formValue.value = this._formValue.defaultValue; this._updateSelection(); this.updateValue(true); this._updateValidity(); } _setDefaultValue(current) { this.defaultValue = JSON.parse(current ?? '[]'); } _setFormValue() { if (isEmpty(this.value)) { super._setFormValue(null); return; } if (this.singleSelect) { super._setFormValue(`${first(this.value)}`); return; } if (this.name) { const value = new FormData(); for (const item of this.value) { value.append(this.name, `${item}`); } super._setFormValue(value); } } resetSearchTerm() { this._state.searchTerm = ''; } updateValue(initial = false) { if (isEmpty(this.data)) { return; } this._formValue.value = this._selection.getSelectedValuesByKey(this.valueKey); this._displayValue = this._selection .getSelectedValuesByKey(this.displayKey) .join(', '); this._setFormValue(); if (!initial) { this._validate(); this._list.requestUpdate(); } } focus(options) { this._input.focus(options); } blur() { this._input.blur(); } get selection() { return this._selection.asArray; } select(items) { this._selection.select(items); this.updateValue(); } deselect(items) { this._selection.deselect(items); this.updateValue(); } async handleMainInput({ detail }) { this._show(); this._state.searchTerm = detail; await this.updateComplete; const matchIndex = this._state.dataState.findIndex((i) => !i.header); this._navigation.active = detail ? matchIndex : -1; this._list.requestUpdate(); this.clearSingleSelection(); } _handleBlur() { if (this._selection.isEmpty) { this._displayValue = ''; this.resetSearchTerm(); } this.checkValidity(); } handleSearchInput({ detail }) { this._state.searchTerm = detail; } handleOpening() { return this.emitEvent('igcOpening', { cancelable: true }); } handleClosing() { return this.emitEvent('igcClosing', { cancelable: true }); } async _show(emitEvent = true) { if (this.open || (emitEvent && !this.handleOpening())) { return false; } this.open = true; await this.updateComplete; if (emitEvent) { this.emitEvent('igcOpened'); } if (!this.singleSelect) { this._list.focus(); } if (!this.autofocusList) { this._searchInput.focus(); } return true; } async show() { return this._show(false); } async _hide(emitEvent = true) { if (!this.open || (emitEvent && !this.handleClosing())) { return false; } this.open = false; await this.updateComplete; if (emitEvent) { this.emitEvent('igcClosed'); } this._navigation.active = -1; return true; } async hide() { return this._hide(false); } _toggle(emit = true) { return this.open ? this._hide(emit) : this._show(emit); } async toggle() { return this._toggle(false); } _getActiveDescendantId(index) { const position = index + 1; const id = this.id ? `${this.id}-item-${position}` : `item-${position}`; return { id, position }; } listKeydownHandler(event) { const target = findElementFromEventPath(IgcComboListComponent.tagName, event); if (target) { this._navigation.navigateList(event, target); } } itemClickHandler(event) { const target = findElementFromEventPath(IgcComboItemComponent.tagName, event); if (!target) { return; } this.toggleSelect(target.index); if (this.singleSelect) { this._input.focus(); this._hide(); } else { this._searchInput.focus(); } } toggleSelect(index) { const { dataIndex } = this._state.dataState.at(index); this._selection.changeSelection(dataIndex); this._navigation.active = index; this.updateValue(); } selectByIndex(index) { const { dataIndex } = this._state.dataState.at(index); this._selection.selectByIndex(dataIndex); this._navigation.active = index; this.updateValue(); } clearSingleSelection() { const _selection = this._selection.asArray; const selection = first(_selection); if (selection) { const item = this.valueKey ? selection[this.valueKey] : selection; this._selection.deselect(item, !isEmpty(_selection)); this._formValue.value = []; } } handleClearIconClick(e) { e.stopPropagation(); if (this.singleSelect) { this.resetSearchTerm(); this.clearSingleSelection(); } else { this._selection.deselect([], true); } this.updateValue(); this._navigation.active = -1; } handleMainInputKeydown(e) { this._navigation.navigateMainInput(e, this._list); } handleSearchInputKeydown(e) { this._navigation.navigateSearchInput(e, this._list); } toggleCaseSensitivity() { this.filteringOptions = { caseSensitive: !this.filteringOptions.caseSensitive, }; } _stopPropagation(e) { e.stopPropagation(); } renderToggleIcon() { return html ` <span slot="suffix" part=${partNameMap({ 'toggle-icon': true, filled: !isEmpty(this.value), })} > <slot name="toggle-icon"> <igc-icon name=${this.open ? 'input_collapse' : 'input_expand'} collection="default" aria-hidden="true" ></igc-icon> </slot> </span> `; } renderClearIcon() { return html ` <span slot="suffix" part="clear-icon" @click=${this.handleClearIconClick} ?hidden=${this._selection.isEmpty} > <slot name="clear-icon"> <igc-icon name="input_clear" collection="default" aria-hidden="true" ></igc-icon> </slot> </span> `; } renderMainInput() { return html ` <igc-input id="target" slot="anchor" role="combobox" aria-controls="dropdown" aria-owns="dropdown" aria-expanded=${this.open ? 'true' : 'false'} aria-describedby="combo-helper-text" aria-disabled=${this.disabled} exportparts="container: input, input: native-input, label, prefix, suffix" @click=${this._toggle} placeholder=${ifDefined(this.placeholder)} label=${ifDefined(this.label)} @igcChange=${this._stopPropagation} @igcInput=${this.handleMainInput} @keydown=${this.handleMainInputKeydown} .value=${this._displayValue} .disabled=${this.disabled} .required=${this.required} .invalid=${live(this.invalid)} .outlined=${this.outlined} .autofocus=${this.autofocus} ?readonly=${!this.singleSelect} > <span slot=${!isEmpty(this.inputPrefix) && 'prefix'}> <slot name="prefix"></slot> </span> ${this.renderClearIcon()} <span slot=${!isEmpty(this.inputSuffix) && 'suffix'}> <slot name="suffix"></slot> </span> ${this.renderToggleIcon()} </igc-input> `; } renderSearchInput() { return html ` <div part="filter-input" ?hidden=${this.disableFiltering || this.singleSelect} > <igc-input .value=${this._state.searchTerm} part="search-input" placeholder=${this.placeholderSearch} exportparts="input: search-input" @igcInput=${this.handleSearchInput} @keydown=${this.handleSearchInputKeydown} > <igc-icon slot=${this.caseSensitiveIcon && 'suffix'} name="case_sensitive" collection="default" part=${partNameMap({ 'case-icon': true, active: this.filteringOptions.caseSensitive ?? false, })} @click=${this.toggleCaseSensitivity} ></igc-icon> </igc-input> </div> `; } renderEmptyTemplate() { return html ` <div part="empty" ?hidden=${!isEmpty(this._state.dataState)}> <slot name="empty">The list is empty</slot> </div> `; } renderList() { return html ` <div .inert=${!this.open} @keydown=${this.listKeydownHandler} part="list-wrapper" > ${this.renderSearchInput()} <div part="header"> <slot name="header"></slot> </div> <igc-combo-list aria-multiselectable=${!this.singleSelect} id="dropdown" part="list" role="listbox" tabindex="0" aria-labelledby="target" aria-activedescendant=${ifDefined(this._activeDescendant)} .items=${this._state.dataState} .renderItem=${this.itemRenderer} ?hidden=${isEmpty(this._state.dataState)} @click=${this.itemClickHandler} > </igc-combo-list> ${this.renderEmptyTemplate()} <div part="footer"> <slot name="footer"></slot> </div> </div> `; } renderHelperText() { return IgcValidationContainerComponent.create(this, { id: 'combo-helper-text', hasHelperText: true, }); } render() { return html ` <igc-popover ?open=${this.open} flip shift same-width> ${this.renderMainInput()} ${this.renderList()} </igc-popover> ${this.renderHelperText()} `; } }; IgcComboComponent.tagName = 'igc-combo'; IgcComboComponent.styles = [styles, shared]; __decorate([ state() ], IgcComboComponent.prototype, "_activeDescendant", void 0); __decorate([ state() ], IgcComboComponent.prototype, "_displayValue", void 0); __decorate([ queryAssignedElements({ slot: 'suffix' }) ], IgcComboComponent.prototype, "inputSuffix", void 0); __decorate([ queryAssignedElements({ slot: 'prefix' }) ], IgcComboComponent.prototype, "inputPrefix", void 0); __decorate([ query('[part="search-input"]') ], IgcComboComponent.prototype, "_searchInput", void 0); __decorate([ query('#target', true) ], IgcComboComponent.prototype, "_input", void 0); __decorate([ query(IgcComboListComponent.tagName, true) ], IgcComboComponent.prototype, "_list", void 0); __decorate([ property({ attribute: false }) ], IgcComboComponent.prototype, "data", null); __decorate([ property({ type: Boolean, reflect: true }) ], IgcComboComponent.prototype, "outlined", void 0); __decorate([ property({ type: Boolean, reflect: true, attribute: 'single-select' }) ], IgcComboComponent.prototype, "singleSelect", null); __decorate([ property({ type: Boolean }) ], IgcComboComponent.prototype, "autofocus", void 0); __decorate([ property({ type: Boolean, attribute: 'autofocus-list' }) ], IgcComboComponent.prototype, "autofocusList", void 0); __decorate([ property() ], IgcComboComponent.prototype, "label", void 0); __decorate([ property() ], IgcComboComponent.prototype, "placeholder", void 0); __decorate([ property({ attribute: 'placeholder-search' }) ], IgcComboComponent.prototype, "placeholderSearch", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], IgcComboComponent.prototype, "open", void 0); __decorate([ property({ attribute: 'value-key' }) ], IgcComboComponent.prototype, "valueKey", null); __decorate([ property({ attribute: 'display-key' }) ], IgcComboComponent.prototype, "displayKey", null); __decorate([ property({ attribute: 'group-key' }) ], IgcComboComponent.prototype, "groupKey", null); __decorate([ property({ attribute: 'group-sorting' }) ], IgcComboComponent.prototype, "groupSorting", null); __decorate([ property({ type: Object, attribute: 'filtering-options' }) ], IgcComboComponent.prototype, "filteringOptions", null); __decorate([ property({ type: Boolean, attribute: 'case-sensitive-icon' }) ], IgcComboComponent.prototype, "caseSensitiveIcon", void 0); __decorate([ property({ type: Boolean, attribute: 'disable-filtering' }) ], IgcComboComponent.prototype, "disableFiltering", null); __decorate([ property({ attribute: false }) ], IgcComboComponent.prototype, "itemTemplate", void 0); __decorate([ property({ attribute: false }) ], IgcComboComponent.prototype, "groupHeaderTemplate", void 0); __decorate([ property({ type: Array }) ], IgcComboComponent.prototype, "value", null); __decorate([ watch('open') ], IgcComboComponent.prototype, "toggleDirectiveChange", null); IgcComboComponent = IgcComboComponent_1 = __decorate([ themes(all), blazorAdditionalDependencies('IgcIconComponent, IgcInputComponent'), blazorIndirectRender ], IgcComboComponent); export default IgcComboComponent; //# sourceMappingURL=combo.js.map