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
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 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"
=${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"
=${this._toggle}
placeholder=${ifDefined(this.placeholder)}
label=${ifDefined(this.label)}
=${this._stopPropagation}
=${this.handleMainInput}
=${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"
=${this.handleSearchInput}
=${this.handleSearchInputKeydown}
>
<igc-icon
slot=${this.caseSensitiveIcon && 'suffix'}
name="case_sensitive"
collection="default"
part=${partNameMap({
'case-icon': true,
active: this.filteringOptions.caseSensitive ?? false,
})}
=${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}
=${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)}
=${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