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.

761 lines 26.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 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" @click=${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" @click=${this._handleAnchorClick} placeholder=${ifDefined(this.placeholder)} label=${ifDefined(this.label)} @igcChange=${stopPropagation} @igcInput=${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" @igcInput=${this._handleSearchInput} > <igc-icon slot=${bindIf(this.caseSensitiveIcon, 'suffix')} name="case_sensitive" collection="default" part=${partMap({ 'case-icon': true, active: this.filteringOptions.caseSensitive, })} @click=${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)} @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()} `; } }; __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