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.
761 lines • 26.7 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 { ComboResourceStringsEN, } from 'igniteui-i18n-core';
import { html, nothing } from 'lit';
import { property, state } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { addThemingController } from '../../theming/theming-controller.js';
import { addRootClickController } from '../common/controllers/root-click.js';
import { addSlotController, setSlots } from '../common/controllers/slot.js';
import { blazorAdditionalDependencies } from '../common/decorators/blazorAdditionalDependencies.js';
import { blazorIndirectRender } from '../common/decorators/blazorIndirectRender.js';
import { shadowOptions } from '../common/decorators/shadow-options.js';
import { registerComponent } from '../common/definitions/register.js';
import { addI18nController } from '../common/i18n/i18n-controller.js';
import { IgcBaseComboBoxComponent } 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 { partMap } from '../common/part-map.js';
import { addSafeEventListener, asArray, bindIf, first, getElementFromPath, isDefined, isEmpty, stopPropagation, } 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 { DataState } from './controllers/data.js';
import { ComboNavigationController } from './controllers/navigation.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';
const SLOTS = setSlots('prefix', 'suffix', 'header', 'footer', 'empty', 'helper-text', 'toggle-icon', 'clear-icon', 'value-missing', 'custom-error', 'invalid');
let IgcComboComponent = class IgcComboComponent extends FormAssociatedRequiredMixin(EventEmitterMixin(IgcBaseComboBoxComponent)) {
static { IgcComboComponent_1 = this; }
static { this.tagName = 'igc-combo'; }
static { this.styles = [styles, shared]; }
static register() {
registerComponent(IgcComboComponent_1, IgcIconComponent, IgcComboListComponent, IgcComboItemComponent, IgcComboHeaderComponent, IgcInputComponent, IgcPopoverComponent, IgcValidationContainerComponent);
}
get __validators() {
return comboValidators;
}
set _activeIndex(index) {
this._navigation.active = index;
}
get _activeIndex() {
return this._navigation.active;
}
set _searchTerm(value) {
this._state.searchTerm = value;
}
get _searchTerm() {
return this._state.searchTerm;
}
get _mainAriaLabel() {
return isEmpty(this._selected)
? this.resourceStrings.combo_aria_label_no_options
: this.resourceStrings.combo_aria_label_options;
}
set data(value) {
if (this._data !== value) {
this._data = asArray(value);
}
}
get data() {
return this._data;
}
set singleSelect(value) {
this._singleSelect = Boolean(value);
this._syncSelectionFromValue();
if (this.hasUpdated) {
const pristine = this._pristine;
this._activeIndex = -1;
this._searchTerm = '';
this._formValue.setValueAndFormState(this.value);
this._pristine = pristine;
}
}
get singleSelect() {
return this._singleSelect;
}
set locale(value) {
this._i18nController.locale = value;
this._state.updateLocale(value);
}
get locale() {
return this._i18nController.locale;
}
set placeholderSearch(value) {
this._placeholderSearch = value;
}
get placeholderSearch() {
return (this._placeholderSearch ??
this.resourceStrings.combo_filter_search_placeholder ??
'Search');
}
set resourceStrings(value) {
this._i18nController.resourceStrings = value;
}
get resourceStrings() {
return this._i18nController.resourceStrings;
}
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 filteringOptions(value) {
this._filteringOptions = { ...this._filteringOptions, ...value };
}
get filteringOptions() {
return this._filteringOptions;
}
set disableFiltering(value) {
this._disableFiltering = value;
this._searchTerm = '';
}
get disableFiltering() {
return this._disableFiltering;
}
set value(items) {
this._formValue.setValueAndFormState(items);
this._syncSelectionFromValue();
if (this.hasUpdated) {
this._validate();
}
}
get value() {
return this._formValue.value;
}
get selection() {
return Array.from(this._selected);
}
constructor() {
super();
this._slots = addSlotController(this, { slots: SLOTS });
this._rootClickController = addRootClickController(this, {
onHide: this._handleClosing,
});
this._i18nController = addI18nController(this, {
defaultEN: ComboResourceStringsEN,
});
this._formValue = createFormValueState(this, {
initialValue: [],
transformers: {
setValue: asArray,
setDefaultValue: asArray,
setFormValue: (value) => {
if (isEmpty(value) || !this.name) {
return null;
}
if (this.singleSelect) {
return String(first(value));
}
const formData = new FormData();
for (const item of value) {
formData.append(this.name, String(item));
}
return formData;
},
},
});
this._inputRef = createRef();
this._searchRef = createRef();
this._listRef = createRef();
this._state = new DataState(this);
this._navigation = new ComboNavigationController(this, this._state, {
input: this._inputRef,
search: this._searchRef,
list: this._listRef,
interactions: {
show: () => this._show(true),
hide: () => this._hide(true),
toggleSelection: (index) => this._toggleSelection(index),
select: (index) => this._selectByIndex(index),
clearSelection: () => this._clearSelection(),
},
});
this._data = [];
this._disableFiltering = false;
this._singleSelect = false;
this._selected = new Set();
this._filteringOptions = {
filterKey: this.displayKey,
caseSensitive: false,
matchDiacritics: false,
};
this._displayValue = '';
this.outlined = false;
this.autofocusList = false;
this.groupSorting = 'asc';
this.caseSensitiveIcon = false;
this.disableClear = false;
this.itemTemplate = ({ item }) => html `${this.displayKey ? item[this.displayKey] : item}`;
this.groupHeaderTemplate = ({ item }) => html `${this.groupKey && item[this.groupKey]}`;
this._itemRenderer = (item, index) => {
if (!item) {
return nothing;
}
if (this.groupKey && item.header) {
return html `
<igc-combo-header part="group-header">
${this.groupHeaderTemplate({ item: item.value })}
</igc-combo-header>
`;
}
const position = index + 1;
const id = this.id ? `${this.id}-item-${position}` : `item-${position}`;
const active = this._activeIndex === index;
const selected = this._selected.has(this.data[item.dataIndex]);
if (active) {
this._activeDescendant = id;
}
return html `
<igc-combo-item
id=${id}
part=${partMap({ 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>
`;
};
addThemingController(this, all);
addSafeEventListener(this, 'blur', this._handleBlur);
addSafeEventListener(this, 'focusin', this._handleFocusIn);
}
willUpdate(props) {
if (props.has('open')) {
this._rootClickController.update();
}
if (props.has('groupKey') ||
props.has('groupSorting') ||
props.has('filteringOptions') ||
props.has('data')) {
this._state.invalidate();
}
if (props.has('data') && !isEmpty(this.data) && !isEmpty(this.value)) {
const pristine = this._pristine;
this._syncSelectionFromValue();
this._formValue.setValueAndFormState(this.value);
this._pristine = pristine;
}
}
updated(props) {
super.updated(props);
if (this._inputRef.value) {
this._inputRef.value._labelElements = this._internals.labels;
}
}
_restoreDefaultValue() {
super._restoreDefaultValue();
this._syncSelectionFromValue();
}
_setDefaultValue(current) {
try {
this.defaultValue = JSON.parse(current || '[]');
}
catch { }
}
async _show(emitEvent = false) {
const [canOpen, _] = await Promise.all([
super._show(emitEvent),
this.updateComplete,
]);
if (canOpen) {
if (!this.singleSelect) {
this._listRef.value?.focus();
}
if (!this.autofocusList) {
this._searchRef.value?.focus();
}
}
return canOpen;
}
async _hide(emitEvent = false) {
const [canClose, _] = await Promise.all([
super._hide(emitEvent),
this.updateComplete,
]);
if (canClose) {
this._activeIndex = -1;
}
return canClose;
}
_setSingleSelectionDisplayValue(value) {
if (this.singleSelect && this._inputRef.value) {
this._inputRef.value.value = value;
}
}
_resolveItems(items) {
if (this.valueKey) {
const keys = new Set(items);
return this.data.filter((item) => keys.has(item[this.valueKey]));
}
const dataSet = new Set(this.data);
return items.filter((item) => dataSet.has(item));
}
_resolveItemValue(record) {
return this.valueKey ? record[this.valueKey] : record;
}
_getValues(items, key) {
return Iterator.from(items)
.map((item) => (key ? item[key] : undefined) ?? item)
.toArray();
}
_emitSelectionChange(detail) {
return this.emitEvent('igcChange', { cancelable: true, detail });
}
_selectItems(items, emit = false) {
let collection = asArray(items);
if (this.singleSelect) {
this._selected.clear();
this._searchTerm = '';
}
if (isEmpty(collection)) {
if (!this.singleSelect) {
this._selected = new Set(this.data);
this.requestUpdate();
}
return;
}
if (this.singleSelect) {
collection = collection.slice(0, 1);
}
const resolved = this._resolveItems(collection);
if (emit &&
!this._emitSelectionChange({
newValue: this._getValues([...resolved, ...this._selected], this.valueKey),
items: resolved,
type: 'selection',
})) {
return;
}
for (const item of resolved) {
this._selected.add(item);
}
this.requestUpdate();
}
_deselectItems(items, emit = false) {
let collection = asArray(items);
if (isEmpty(collection)) {
if (emit &&
!this._emitSelectionChange({
newValue: [],
items: Array.from(this._selected),
type: 'deselection',
})) {
return;
}
this._selected.clear();
this.requestUpdate();
return;
}
if (this.singleSelect) {
collection = collection.slice(0, 1);
}
const resolved = this._resolveItems(collection);
const resolvedSet = new Set(resolved);
const remaining = Array.from(this._selected).filter((item) => !resolvedSet.has(item));
if (emit &&
!this._emitSelectionChange({
newValue: this._getValues(remaining, this.valueKey),
items: resolved,
type: 'deselection',
})) {
return;
}
for (const item of resolved) {
this._selected.delete(item);
}
this.requestUpdate();
}
_syncSelectionFromValue() {
this._selected.clear();
if (!isEmpty(this._formValue.value)) {
const values = this.singleSelect
? asArray(first(this._formValue.value))
: this._formValue.value;
if (this.singleSelect && values.length !== this._formValue.value.length) {
this._formValue.setValueAndFormState(values);
}
const items = Iterator.from(values)
.map((val) => this._findItemByValue(val, this.valueKey))
.filter(isDefined);
for (const item of items) {
this._selected.add(item);
}
}
this._displayValue = this._getValues(this._selected, this.displayKey).join(', ');
}
_findItemByValue(value, key) {
return this.data.find((item) => (key ? item[key] : item) === value);
}
_syncValueFromSelection(initial = false) {
const values = this._getValues(this._selected, this.valueKey);
this._displayValue = this._getValues(this._selected, this.displayKey).join(', ');
this._formValue.setValueAndFormState(values);
if (!initial) {
this._setSingleSelectionDisplayValue(this._displayValue);
this._validate();
this._listRef.value?.requestUpdate();
}
}
_toggleSelection(index) {
const record = this.data[this._state.dataState[index].dataIndex];
this._selected.has(record)
? this._deselectItems(this._resolveItemValue(record), true)
: this._selectItems(this._resolveItemValue(record), true);
this._activeIndex = index;
this._syncValueFromSelection();
}
_selectByIndex(index) {
this._selectItems(this._resolveItemValue(this.data[this._state.dataState[index].dataIndex]), true);
this._activeIndex = index;
this._syncValueFromSelection();
}
_clearSelection() {
if (this.singleSelect) {
this._searchTerm = '';
this._clearSingleSelection();
}
else {
this._deselectItems([], true);
}
this._syncValueFromSelection();
}
_clearSingleSelection() {
const [selection] = this._selected;
if (selection) {
this._deselectItems(this._resolveItemValue(selection), true);
this._formValue.setValueAndFormState([]);
}
}
async _handleMainInput({ detail, }) {
this._setTouchedState();
this._show(true);
this._searchTerm = detail;
await this.updateComplete;
this._activeIndex = this._state.dataState.findIndex((i) => !i.header);
this._clearSingleSelection();
}
_handleFocusIn() {
this._setTouchedState();
}
_handleBlur() {
if (isEmpty(this._selected)) {
this._searchTerm = '';
this._setSingleSelectionDisplayValue('');
}
super._handleBlur();
}
_handleSearchInput({ detail }) {
this._searchTerm = detail;
}
_handleClosing() {
this._hide(true);
}
async _itemClickHandler(event) {
const target = getElementFromPath(IgcComboItemComponent.tagName, event);
if (!target) {
return;
}
this._setTouchedState();
this._toggleSelection(target.index);
if (this.singleSelect) {
this._inputRef.value?.focus();
await this._hide(true);
}
else {
this._searchRef.value?.focus();
}
}
_handleClearIconClick(event) {
event.stopPropagation();
this._clearSelection();
this._activeIndex = -1;
}
_handleCaseSensitivity() {
this.filteringOptions = {
caseSensitive: !this.filteringOptions.caseSensitive,
};
}
focus(options) {
this._inputRef.value?.focus(options);
}
blur() {
this._inputRef.value?.blur();
}
select(items) {
this._selectItems(items);
this._syncValueFromSelection();
}
deselect(items) {
this._deselectItems(items);
this._syncValueFromSelection();
}
_renderToggleIcon() {
return html `
<span
slot="suffix"
part=${partMap({
'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.disableClear || isEmpty(this._selected)}
aria-label=${ifDefined(this.resourceStrings.combo_clearItems_placeholder)}
>
<slot name="clear-icon">
<igc-icon
name="input_clear"
collection="default"
aria-hidden="true"
></igc-icon>
</slot>
</span>
`;
}
_renderMainInput() {
const hasPrefix = this._slots.hasAssignedElements('prefix');
const hasSuffix = this._slots.hasAssignedElements('suffix');
return html `
<igc-input
${ref(this._inputRef)}
id="target"
slot="anchor"
role="combobox"
aria-controls="dropdown"
aria-owns="dropdown"
aria-expanded=${this.open}
aria-describedby="combo-helper-text"
aria-disabled=${this.disabled}
aria-label=${this._mainAriaLabel}
exportparts="container: input, input: native-input, label, prefix, suffix"
=${this._handleAnchorClick}
placeholder=${ifDefined(this.placeholder)}
label=${ifDefined(this.label)}
=${stopPropagation}
=${this._handleMainInput}
.value=${this._displayValue}
.disabled=${this.disabled}
.required=${this.required}
.invalid=${this.invalid}
.outlined=${this.outlined}
.autofocus=${this.autofocus}
?readonly=${!this.singleSelect}
>
<span slot=${bindIf(hasPrefix, 'prefix')}>
<slot name="prefix"></slot>
</span>
${this._renderClearIcon()}
<span slot=${bindIf(hasSuffix, 'suffix')}>
<slot name="suffix"></slot>
</span>
${this._renderToggleIcon()}
</igc-input>
`;
}
_renderSearchInput() {
return html `
<div
part="filter-input"
?hidden=${this.disableFiltering || this.singleSelect}
>
<igc-input
${ref(this._searchRef)}
.value=${this._searchTerm}
part="search-input"
placeholder=${this.placeholderSearch}
exportparts="input: search-input"
=${this._handleSearchInput}
>
<igc-icon
slot=${bindIf(this.caseSensitiveIcon, 'suffix')}
name="case_sensitive"
collection="default"
part=${partMap({
'case-icon': true,
active: this.filteringOptions.caseSensitive,
})}
=${this._handleCaseSensitivity}
></igc-icon>
</igc-input>
</div>
`;
}
_renderEmptyTemplate() {
return html `
<div part="empty" ?hidden=${!isEmpty(this._state.dataState)}>
<slot name="empty">${this.resourceStrings.combo_empty_message}</slot>
</div>
`;
}
_renderList() {
return html `
<div .inert=${!this.open} part="list-wrapper">
${this._renderSearchInput()}
<div part="header">
<slot name="header"></slot>
</div>
<igc-combo-list
${ref(this._listRef)}
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()}
`;
}
};
__decorate([
state()
], IgcComboComponent.prototype, "_activeIndex", null);
__decorate([
state()
], IgcComboComponent.prototype, "_searchTerm", null);
__decorate([
state()
], IgcComboComponent.prototype, "_activeDescendant", void 0);
__decorate([
state()
], IgcComboComponent.prototype, "_displayValue", 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, "locale", null);
__decorate([
property()
], IgcComboComponent.prototype, "label", void 0);
__decorate([
property()
], IgcComboComponent.prototype, "placeholder", void 0);
__decorate([
property({ attribute: 'placeholder-search' })
], IgcComboComponent.prototype, "placeholderSearch", null);
__decorate([
property({ attribute: false })
], IgcComboComponent.prototype, "resourceStrings", null);
__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", void 0);
__decorate([
property({ attribute: 'group-sorting' })
], IgcComboComponent.prototype, "groupSorting", void 0);
__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({ type: Boolean, attribute: 'disable-clear' })
], IgcComboComponent.prototype, "disableClear", void 0);
__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);
IgcComboComponent = IgcComboComponent_1 = __decorate([
blazorAdditionalDependencies('IgcIconComponent, IgcInputComponent'),
blazorIndirectRender,
shadowOptions({ delegatesFocus: true })
], IgcComboComponent);
export default IgcComboComponent;
//# sourceMappingURL=combo.js.map