UNPKG

@progress/kendo-angular-dropdowns

Version:

A wide variety of native Angular dropdown components including AutoComplete, ComboBox, DropDownList, DropDownTree, MultiColumnComboBox, MultiSelect, and MultiSelectTree

1,310 lines (1,300 loc) 758 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import * as i0 from '@angular/core'; import { EventEmitter, Component, Input, HostBinding, Output, Directive, Injectable, HostListener, ViewChildren, ViewChild, forwardRef, isDevMode, ViewContainerRef, ContentChild, ContentChildren, ChangeDetectionStrategy, NgModule } from '@angular/core'; import * as i10 from '@progress/kendo-angular-common'; import { isDocumentAvailable, isObjectPresent, removeHTMLAttributes, parseAttributes, isSafari, Keys, setHTMLAttributes, EventsOutsideAngularDirective, isChanged, TemplateContextDirective, ResizeSensorComponent, closest as closest$1, isControlRequired, hasObservers, KendoInput, SuffixTemplateDirective, PrefixTemplateDirective, SeparatorComponent, MultiTabStop, anyChanged, guid as guid$1, ToggleButtonTabStopDirective, ResizeBatchService, KENDO_ADORNMENTS, KENDO_TOGGLEBUTTONTABSTOP } from '@progress/kendo-angular-common'; export { PrefixTemplateDirective, SeparatorComponent, SuffixTemplateDirective, ToggleButtonTabStopDirective } from '@progress/kendo-angular-common'; import * as i7 from '@progress/kendo-angular-utils'; import { AdaptiveService } from '@progress/kendo-angular-utils'; import * as i8 from '@angular/forms'; import { NgControl, NG_VALUE_ACCESSOR, ReactiveFormsModule, FormsModule } from '@angular/forms'; import { validatePackage } from '@progress/kendo-licensing'; import { getter as getter$1, touchEnabled, pointers } from '@progress/kendo-common'; import * as i1 from '@progress/kendo-angular-l10n'; import { ComponentMessages, LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n'; import { Subscription, merge, fromEvent, Subject, of, interval } from 'rxjs'; import * as i2 from '@progress/kendo-angular-popup'; import { PopupService } from '@progress/kendo-angular-popup'; import { map, switchMap, take, auditTime, tap, filter, partition, throttleTime, catchError, skipWhile, concatMap, takeUntil, debounceTime } from 'rxjs/operators'; import { NgIf, NgStyle, NgFor, NgClass, NgTemplateOutlet } from '@angular/common'; import { xIcon, caretAltDownIcon, searchIcon, xCircleIcon, plusIcon } from '@progress/kendo-svg-icons'; import { IconComponent, IconWrapperComponent, IconsService } from '@progress/kendo-angular-icons'; import { ActionSheetComponent, ActionSheetTemplateDirective } from '@progress/kendo-angular-navigation'; import { TextBoxComponent, TextBoxPrefixTemplateDirective } from '@progress/kendo-angular-inputs'; import { ButtonComponent } from '@progress/kendo-angular-buttons'; import * as i1$1 from '@progress/kendo-angular-treeview'; import { DataBoundComponent, ExpandableComponent, TreeViewComponent, SelectDirective, FlatDataBindingDirective, HierarchyBindingDirective, ExpandDirective } from '@progress/kendo-angular-treeview'; import { DialogContainerService, DialogService, WindowService, WindowContainerService } from '@progress/kendo-angular-dialog'; /** * @hidden */ const packageMetadata = { name: '@progress/kendo-angular-dropdowns', productName: 'Kendo UI for Angular', productCode: 'KENDOUIANGULAR', productCodes: ['KENDOUIANGULAR'], publishDate: 1745303969, version: '18.5.2', licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/' }; /* eslint-disable no-bitwise */ /** * @hidden */ const isPresent = (value) => value !== null && value !== undefined; /** * @hidden */ const isNumber = (value) => !isNaN(value); /** * @hidden */ const guid = () => { let id = ""; let i; let random; for (i = 0; i < 32; i++) { random = Math.random() * 16 | 0; if (i === 8 || i === 12 || i === 16 || i === 20) { id += "-"; } id += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16); } return id; }; /** * @hidden */ const combineStr = (begin, end) => { return begin.concat(end.substr(end.toLowerCase().indexOf(begin.toLowerCase()) + begin.length)); }; /** * @hidden */ const isWindowAvailable = () => typeof window !== 'undefined'; /** * @hidden */ const isArray = (value) => Array.isArray(value); /** * @hidden */ const isObject = (value) => isPresent(value) && typeof value === 'object'; /** * @hidden */ const isEmptyString = (value) => typeof value === 'string' && value.length === 0; /** * @hidden */ const resolveValuesInArray = (values, data = [], valueField) => values .map(value => { return data.find(item => getter(item, valueField) === value); }) .filter(value => value !== undefined); /** * @hidden */ const validateComplexValues = (values, valueField) => isArray(values) && values.filter(item => { return isObject(item) && getter(item, valueField) !== undefined; }); /** * @hidden */ const resolveAllValues = (value, data, valueField) => { const customValues = validateComplexValues(value, valueField) || []; const resolvedValues = resolveValuesInArray(value, data, valueField) || []; return resolvedValues.concat(customValues); }; /** * @hidden */ const isObjectArray = (values) => { return isArray(values) && values.every(item => isObject(item)); }; /** * @hidden */ const selectedIndices = (values, data, valueField) => { const extractedValues = data.map(item => { return isPresent(item) && isPresent(getter(item, valueField)) ? getter(item, valueField) : item; }); return values.reduce((arr, item) => { const value = isPresent(item) && isPresent(getter(item, valueField)) ? getter(item, valueField) : item; const index = extractedValues.indexOf(value); if (index !== -1) { arr.push(index); } return arr; }, []); }; /** * @hidden */ const getter = (dataItem, field) => { if (!isPresent(dataItem)) { return null; } if (!isPresent(field) || !isObject(dataItem)) { return dataItem; } // creates a field accessor supporting nested fields processing const valueFrom = getter$1(field); return valueFrom(dataItem); }; /** * @hidden */ const resolveValue = (args) => { let dataItem; if (isPresent(args.value)) { const data = [args.defaultItem, ...args.data]; dataItem = data.find(element => getter(element, args.valueField) === args.value); return { dataItem: dataItem, focused: args.data.indexOf(dataItem), selected: args.data.indexOf(dataItem) }; } else if (args.index) { dataItem = args.data[args.index]; return { dataItem: args.data[args.index], focused: args.index, selected: args.index }; } return { dataItem: args.defaultItem, focused: -1, selected: -1 }; }; /** * @hidden */ const sameCharsOnly = (word, character) => { for (let idx = 0; idx < word.length; idx++) { if (word.charAt(idx) !== character) { return false; } } return true; }; /** * @hidden */ const shuffleData = (data, splitIndex, defaultItem) => { let result = data; if (defaultItem) { result = [defaultItem].concat(result); } return result.slice(splitIndex).concat(result.slice(0, splitIndex)); }; /** * @hidden */ const matchText = (text, word, ignoreCase) => { if (!isPresent(text)) { return false; } let temp = String(text); if (ignoreCase) { temp = temp.toLowerCase(); } return temp.indexOf(word) === 0; }; /** * @hidden */ const elementFromPoint = (x, y) => { if (!isDocumentAvailable()) { return; } return document.elementFromPoint(x, y); }; /** * @hidden * * Checks whether the passed object has all of the listed properties. */ const hasProps = (obj, props) => { if (!isPresent(obj)) { return false; } return props.every(prop => obj.hasOwnProperty(prop)); }; /** * @hidden * * Checks whether an element is untouched by looking for the ng-untouched css class */ const isUntouched = (element) => element.className.includes('ng-untouched'); /** * @hidden */ const noop = (_) => { }; /** * IE element `matches` polyfill. * https://developer.mozilla.org/en-US/docs/Web/API/Element/matches */ const matches = (element, selector) => { const matcher = element.matches || element.msMatchesSelector || element.webkitMatchesSelector; if (!matcher) { return false; } return matcher.call(element, selector); }; /** * @hidden * * IE element `closest` polyfill. * https://developer.mozilla.org/en-US/docs/Web/API/Element/closest */ const closest = (element, selector) => { let parent = element; while (parent !== null && parent.nodeType === 1) { if (matches(parent, selector)) { return parent; } parent = parent.parentElement || parent.parentNode; } return null; }; /** * @hidden * * Parses a provided value to its type 'number' representation. * If the parsed value (via Number(value)) is NaN, the provided default value is returned. * Uses 0 as default value if a second param is not provided. */ const parseNumber = (num, defaultValue = 0) => { const normalizedValue = Number(num); return isNaN(normalizedValue) ? defaultValue : normalizedValue; }; /** * @hidden * * Checks whether the passed target element is inside the provided host or popupRef. */ const inDropDown = (host, target, popupRef) => { return host.nativeElement.contains(target) || (popupRef && popupRef.popupElement.contains(target)); }; /** * @hidden * * Calculates the hierarchical level of an item, based on the provided index. * The result level is zero-based (starts from 0). */ const getHierarchicalItemLevel = (index) => { return (index || '').split('_').length - 1; }; /** * @hidden * * Retrieves all descendant nodes' lookups which are currently registered in the provided lookup item as a flat array. */ const fetchDescendentNodes = (lookup, filterExpression) => { if (!isPresent(lookup) || lookup.children.length === 0) { return []; } let descendants = lookup.children; if (isPresent(filterExpression)) { descendants = descendants.filter(descendent => filterExpression(descendent.item)); } descendants.forEach(child => descendants = descendants.concat(fetchDescendentNodes(child, filterExpression))); return descendants; }; /** * @hidden * * Retrieves the correct value based on the item's level and the provided value field/s. * Used in the MultiSelectTree component. */ const valueFrom = ({ dataItem, index, level }, valueField) => { const fields = Array.isArray(valueField) ? valueField : [valueField]; // either use the explicitly provided value level, or infer it from the item index const valueLevel = isPresent(level) ? level : getHierarchicalItemLevel(index); // fall-back to the last available one, if the current node is in a deeper level const normalizedLevel = Math.min(valueLevel, fields.length - 1); const field = fields[normalizedLevel]; return getter$1(field)(dataItem); }; /** * @hidden * Returns the size class based on the component and size input. */ const getSizeClass = (component, size) => { const SIZE_CLASSES = { 'small': `k-${component}-sm`, 'medium': `k-${component}-md`, 'large': `k-${component}-lg` }; return SIZE_CLASSES[size]; }; /** * @hidden * Returns the rounded class based on the rounded input. */ const getRoundedClass = (rounded) => { const ROUNDED_CLASSES = { 'small': 'k-rounded-sm', 'medium': 'k-rounded-md', 'large': 'k-rounded-lg', 'full': 'k-rounded-full' }; return ROUNDED_CLASSES[rounded]; }; /** * @hidden * Return the fillMode class based on the component and fillMode input. */ const getFillModeClass = (component, fillMode) => { const FILLMODE_CLASSES = { 'solid': `k-${component}-solid`, 'flat': `k-${component}-flat`, 'outline': `k-${component}-outline` }; return FILLMODE_CLASSES[fillMode]; }; /** * @hidden */ const filterAndMap = (arr, predicate, mapper) => arr.reduce((acc, curr) => predicate(curr) ? [...acc, mapper(curr)] : acc, []); /** * @hidden * * Checks if input is Japanese IME */ const isJapanese = (input) => { const japaneseRegex = /[\u3000-\u303F]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\uFF00-\uFFEF]|[\u4E00-\u9FAF]|[\u2605-\u2606]|[\u2190-\u2195]|\u203B/g; return japaneseRegex.test(input); }; /** * @hidden */ const isLetter = (text) => { const isLetter = /[a-zA-Z]/; return isLetter.test(text) && text?.length === 1; }; /** * @hidden */ const getTextField = (field, level) => { if (isArray(field)) { return field[level]; } return field; }; /** * @hidden */ const getSearchableItems = (treeViewId, element) => { const nodeSeletor = `kendo-treeview[id='${treeViewId}'] li.k-treeview-item`; const liElements = Array.from(element.querySelectorAll(nodeSeletor)); return liElements.map((liElement) => { return { text: liElement.innerText, index: liElement.getAttribute('data-treeindex') }; }); }; /** * @hidden */ const isTruthy = (value) => !!value; /** * @hidden */ const updateActionSheetAdaptiveAppearance = (actionSheet, windowSize, renderer) => { const element = actionSheet['element'].nativeElement.querySelector('.k-actionsheet'); const animationContainer = actionSheet['element'].nativeElement.querySelector('.k-child-animation-container'); if (windowSize === 'medium') { renderer.removeClass(element, 'k-actionsheet-fullscreen'); renderer.addClass(element, 'k-actionsheet-bottom'); renderer.addClass(element, 'k-adaptive-actionsheet'); renderer.removeStyle(animationContainer, 'top'); renderer.removeStyle(animationContainer, 'height'); renderer.setStyle(animationContainer, 'bottom', '0px'); } else if (windowSize === 'small') { renderer.removeClass(element, 'k-actionsheet-bottom'); renderer.addClass(element, 'k-actionsheet-fullscreen'); renderer.addClass(element, 'k-adaptive-actionsheet'); renderer.setStyle(animationContainer, 'bottom', '0px'); renderer.setStyle(animationContainer, 'height', '100%'); } }; /** * @hidden */ const setListBoxAriaLabelledBy = (optionsList, element, renderer) => { const listBox = optionsList.wrapper.nativeElement.querySelector('kendo-list ul'); const ariaLabel = element.nativeElement.getAttribute('aria-labelledby') || element.nativeElement.getAttribute('data-kendo-label-id'); if (ariaLabel) { renderer.setAttribute(listBox, 'aria-labelledby', ariaLabel); } }; /** * @hidden */ const setActionSheetTitle = (element, actionSheetTitle) => { const ariaLabel = element.nativeElement.getAttribute('aria-labelledby') || element.nativeElement.getAttribute('data-kendo-label-id'); if (!actionSheetTitle && ariaLabel) { return document.getElementById(ariaLabel).innerText; } return actionSheetTitle; }; /** * @hidden */ const animationDuration = 300; /** * @hidden */ class SearchBarComponent { localization; injector; input; ngZone; direction; tagListId; set readonly(readonly) { this._readonly = readonly; if (this._readonly) { this.renderer.setAttribute(this.input.nativeElement, 'readonly', ''); } else { this.renderer.removeAttribute(this.input.nativeElement, 'readonly'); } } get readonly() { return this._readonly; } set disabled(disabled) { this._disabled = disabled; if (this._disabled) { this.renderer.setAttribute(this.input.nativeElement, 'disabled', ''); } else { this.renderer.removeAttribute(this.input.nativeElement, 'disabled'); } } get disabled() { return this._disabled; } set isRequired(isRequired) { this._isRequired = isRequired; if (this._isRequired) { this.renderer.setAttribute(this.input.nativeElement, 'required', ''); } else { this.renderer.removeAttribute(this.input.nativeElement, 'required'); } } get isRequired() { return this._isRequired; } set isSuggestable(isSuggestable) { this._isSuggestable = isSuggestable; this.setAriaAutocomplete(); } get isSuggestable() { return this._isSuggestable; } set isFilterable(isFilterable) { this._isFilterable = isFilterable; this.setAriaAutocomplete(); } get isFilterable() { return this._isFilterable; } get userInput() { return this._userInput; } set userInput(userInput) { this._userInput = userInput || ""; } /** * @hidden */ get formControl() { const ngControl = this.injector.get(NgControl, null); return ngControl?.control || null; } suggestedText; /** * @hidden */ set inputAttributes(attributes) { if (isObjectPresent(this.parsedAttributes)) { removeHTMLAttributes(this.parsedAttributes, this.renderer, this.input.nativeElement); } this._inputAttributes = attributes; this.parsedAttributes = this.inputAttributes ? parseAttributes(this.inputAttributes, this.defaultAttributes) : this.inputAttributes; this.setInputAttributes(); } get inputAttributes() { return this._inputAttributes; } id; activeDescendant; tabIndex; isLoading; ariaControls; ariaExpanded = null; get attrAriaInvalid() { return this.formControl?.invalid ? true : null; } set placeholder(text) { this._placeholder = text || ''; this.setInputSize(); } get placeholder() { return this._placeholder; } role = 'combobox'; get dir() { return this.direction; } valueChange = new EventEmitter(); onBlur = new EventEmitter(); onFocus = new EventEmitter(); onClick = new EventEmitter(); onNavigate = new EventEmitter(); get value() { return this.input.nativeElement.value; } _isRequired; _readonly; _disabled; _userInput = ""; _previousValue = ""; _placeholder = ""; _isSuggestable = false; _isFilterable = false; renderer; subs = new Subscription(); _inputAttributes; parsedAttributes = {}; get defaultAttributes() { return { id: this.id, disabled: this.disabled ? '' : null, readonly: this.readonly ? '' : null, placeholder: this.placeholder, tabIndex: this.tabIndex, tabindex: this.tabIndex, dir: this.direction, required: this.isRequired ? '' : null, 'aria-haspopup': 'listbox', 'aria-expanded': this.ariaExpanded, 'aria-controls': this.ariaControls, 'aria-activedescendant': this.activeDescendant, 'aria-busy': this.isLoading, 'aria-invalid': this.formControl?.invalid }; } get mutableAttributes() { return { autocomplete: 'off', role: this.role, 'aria-describedby': this.tagListId }; } constructor(localization, renderer, injector, input, ngZone) { this.localization = localization; this.injector = injector; this.input = input; this.ngZone = ngZone; this.direction = localization.rtl ? 'rtl' : 'ltr'; this.renderer = renderer; this.renderer.addClass(this.input.nativeElement, 'k-input-inner'); this.renderer.setAttribute(this.input.nativeElement, 'aria-haspopup', 'listbox'); this.renderer.setAttribute(this.input.nativeElement, 'autocomplete', 'off'); } ngOnInit() { this.subs.add(this.localization .changes.subscribe(({ rtl }) => this.direction = rtl ? 'rtl' : 'ltr')); } ngOnChanges(changes) { if (!isDocumentAvailable()) { return; } let previousUserInput; if (this.input && (changes.userInput || changes.suggestedText)) { if (changes.userInput && changes.userInput.previousValue) { if (this._previousValue === changes.userInput.previousValue) { previousUserInput = this._previousValue; } else { previousUserInput = changes.userInput.currentValue || ""; } } else { previousUserInput = this._previousValue; } const caretStart = this.input.nativeElement.selectionStart; const caretAtEnd = previousUserInput.length === caretStart; this.writeInputValue(this.suggestedText ? combineStr(this.userInput, this.suggestedText) : this.userInput); if (this.suggestedText) { this.setInputSelection(this.userInput.length, this.suggestedText.length); } else if (isSafari(navigator.userAgent) && !caretAtEnd) { this.setInputSelection(caretStart, this.userInput.length); } else if (caretAtEnd) { this.setInputSelection(this.userInput.length, this.userInput.length); } else { this.setInputSelection(caretStart, caretStart); } this._previousValue = this.userInput; } } ngAfterViewInit() { this.subs.add(this.input.nativeElement.addEventListener('input', (event) => this.handleInput(event))); this.subs.add(this.input.nativeElement.addEventListener('focus', (event) => this.handleFocus(event))); this.subs.add(this.input.nativeElement.addEventListener('blur', (event) => this.handleBlur(event))); this.subs.add(this.input.nativeElement.addEventListener('keydown', (event) => this.handleKeydown(event))); } ngOnDestroy() { this.subs.unsubscribe(); } writeInputValue(text) { if (isDocumentAvailable()) { this.renderer.setProperty(this.input.nativeElement, 'value', text); } } setInputSelection(start, end) { if (isDocumentAvailable() && this.input.nativeElement === document.activeElement) { try { this.input.nativeElement.setSelectionRange(start, end); } catch (e) { //Make sure that the element is in the DOM before you invoke its methods } } } setAriaAutocomplete() { if (this.isFilterable) { this.renderer.setAttribute(this.input.nativeElement, 'aria-autocomplete', 'list'); } if (this.isSuggestable) { this.renderer.setAttribute(this.input.nativeElement, 'aria-autocomplete', 'inline'); } if (this.isFilterable && this.isSuggestable) { this.renderer.setAttribute(this.input.nativeElement, 'aria-autocomplete', 'both'); } if (!this.isFilterable && !this.isSuggestable) { this.renderer.removeAttribute(this.input.nativeElement, 'aria-autocomplete'); } } handleInput(event) { const target = event.target; const isBrowserSafari = isSafari(navigator.userAgent); const value = isBrowserSafari && isJapanese(target.value) ? event.data : target.value; if (value !== this.userInput) { this._previousValue = value; this.valueChange.emit(value); } } handleFocus(event) { this.onFocus.emit(event); } handleBlur(event) { this.onBlur.emit(event); } handleKeydown(event) { const keyCode = event.keyCode; const keys = [Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowLeft, Keys.ArrowRight, Keys.Enter, Keys.Escape, Keys.Delete, Keys.Backspace, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp]; if (keys.indexOf(keyCode) > -1) { this.onNavigate.emit(event); } } focus() { if (isDocumentAvailable()) { this.input.nativeElement.focus(); } } blur() { if (isDocumentAvailable()) { this.input.nativeElement.blur(); } } setInputSize() { const lengthOf = x => x ? x.length : 0; const input = this.input.nativeElement; const placeholderLength = lengthOf(this.placeholder); const textLength = lengthOf(this.value); const size = Math.max(placeholderLength, textLength, 1); this.renderer.setAttribute(input, 'size', size.toString()); } setInputAttributes() { const attributesToRender = Object.assign({}, this.mutableAttributes, this.parsedAttributes); setHTMLAttributes(attributesToRender, this.renderer, this.input.nativeElement, this.ngZone); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SearchBarComponent, deps: [{ token: i1.LocalizationService }, { token: i0.Renderer2 }, { token: i0.Injector }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: SearchBarComponent, isStandalone: true, selector: "input[kendoSearchbar]", inputs: { tagListId: "tagListId", readonly: "readonly", disabled: "disabled", isRequired: "isRequired", isSuggestable: "isSuggestable", isFilterable: "isFilterable", userInput: "userInput", suggestedText: "suggestedText", inputAttributes: "inputAttributes", id: "id", activeDescendant: "activeDescendant", tabIndex: "tabIndex", isLoading: "isLoading", ariaControls: "ariaControls", ariaExpanded: "ariaExpanded", placeholder: "placeholder" }, outputs: { valueChange: "valueChange", onBlur: "onBlur", onFocus: "onFocus", onClick: "onClick", onNavigate: "onNavigate" }, host: { properties: { "attr.id": "this.id", "attr.aria-activedescendant": "this.activeDescendant", "attr.tabindex": "this.tabIndex", "attr.aria-busy": "this.isLoading", "attr.aria-controls": "this.ariaControls", "attr.aria-expanded": "this.ariaExpanded", "attr.aria-invalid": "this.attrAriaInvalid", "attr.placeholder": "this.placeholder", "attr.role": "this.role", "attr.dir": "this.dir" } }, usesOnChanges: true, ngImport: i0, template: ``, isInline: true }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SearchBarComponent, decorators: [{ type: Component, args: [{ selector: 'input[kendoSearchbar]', template: ``, standalone: true, imports: [EventsOutsideAngularDirective] }] }], ctorParameters: function () { return [{ type: i1.LocalizationService }, { type: i0.Renderer2 }, { type: i0.Injector }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { tagListId: [{ type: Input }], readonly: [{ type: Input }], disabled: [{ type: Input }], isRequired: [{ type: Input }], isSuggestable: [{ type: Input }], isFilterable: [{ type: Input }], userInput: [{ type: Input }], suggestedText: [{ type: Input }], inputAttributes: [{ type: Input }], id: [{ type: HostBinding, args: ['attr.id'] }, { type: Input }], activeDescendant: [{ type: HostBinding, args: ['attr.aria-activedescendant'] }, { type: Input }], tabIndex: [{ type: HostBinding, args: ['attr.tabindex'] }, { type: Input }], isLoading: [{ type: HostBinding, args: ['attr.aria-busy'] }, { type: Input }], ariaControls: [{ type: HostBinding, args: ['attr.aria-controls'] }, { type: Input }], ariaExpanded: [{ type: HostBinding, args: ['attr.aria-expanded'] }, { type: Input }], attrAriaInvalid: [{ type: HostBinding, args: ['attr.aria-invalid'] }], placeholder: [{ type: HostBinding, args: ['attr.placeholder'] }, { type: Input }], role: [{ type: HostBinding, args: ['attr.role'] }], dir: [{ type: HostBinding, args: ['attr.dir'] }], valueChange: [{ type: Output }], onBlur: [{ type: Output }], onFocus: [{ type: Output }], onClick: [{ type: Output }], onNavigate: [{ type: Output }] } }); /** * Renders the list item content. To define the item template, nest an `<ng-template>` tag * with the `kendo<ComponentName>ItemTemplate` directive inside the component tag. The template context is * set to the current component. To get a reference to the current data item, use the `let-dataItem` directive. * * - [Using `ItemTemplate` with the AutoComplete]({% slug templates_autocomplete %}#toc-item-template) * - [Using `ItemTemplate` with the ComboBox]({% slug templates_combobox %}#toc-item-template) * - [Using `ItemTemplate` with the DropDownList]({% slug templates_ddl %}#toc-item-template) * - [Using `ItemTemplate` with the MultiSelect]({% slug templates_multiselect %}#toc-item-template) * * @example * ```ts * _@Component({ * selector: 'my-app', * template: ` * <kendo-combobox [data]="listItems"> * <ng-template kendoComboBoxItemTemplate let-dataItem> * <span>{{dataItem}} option</span> * </ng-template> * </kendo-combobox> * ` * }) * class AppComponent { * public listItems: Array<string> = ["Item 1", "Item 2", "Item 3", "Item 4"]; * } * ``` */ class ItemTemplateDirective { templateRef; constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ItemTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ItemTemplateDirective, isStandalone: true, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ItemTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * Renders the header content of the list. To define the header template, nest an `<ng-template>` tag * with the `kendo<ComponentName>HeaderTemplate` directive inside the component tag. * * - [Using `HeaderTemplate` with the AutoComplete]({% slug templates_autocomplete %}#toc-header-template) * - [Using `HeaderTemplate` with the ComboBox]({% slug templates_combobox %}#toc-header-template) * - [Using `HeaderTemplate` with the MultiColumnComboBox]({% slug templates_multicolumncombobox %}#toc-header-template) * - [Using `HeaderTemplate` with the DropDownList]({% slug templates_ddl %}#toc-header-template) * - [Using `HeaderTemplate` with the DropDownTree]({% slug templates_ddt %}#toc-header-template) * - [Using `HeaderTemplate` with the MultiSelect]({% slug templates_multiselect %}#toc-header-template) * * @example * ```ts * _@Component({ * selector: 'my-app', * template: ` * <kendo-combobox [data]="listItems"> * <ng-template kendoComboBoxHeaderTemplate> * <h4>Header template</h4> * </ng-template> * </kendo-combobox> * ` * }) * class AppComponent { * public listItems: Array<string> = ["Item 1", "Item 2", "Item 3", "Item 4"]; * } * ``` */ class HeaderTemplateDirective { templateRef; constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeaderTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: HeaderTemplateDirective, isStandalone: true, selector: "[kendoDropDownListHeaderTemplate],[kendoComboBoxHeaderTemplate],[kendoDropDownTreeHeaderTemplate],[kendoMultiColumnComboBoxHeaderTemplate],[kendoAutoCompleteHeaderTemplate],[kendoMultiSelectHeaderTemplate],[kendoMultiSelectTreeHeaderTemplate]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeaderTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoDropDownListHeaderTemplate],[kendoComboBoxHeaderTemplate],[kendoDropDownTreeHeaderTemplate],[kendoMultiColumnComboBoxHeaderTemplate],[kendoAutoCompleteHeaderTemplate],[kendoMultiSelectHeaderTemplate],[kendoMultiSelectTreeHeaderTemplate]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * Renders the footer content of the list. To define the footer template, nest an `<ng-template>` tag * with the `kendo<ComponentName>FooterTemplate` directive inside the component tag. * * - [Using `FooterTemplate` with the AutoComplete]({% slug templates_autocomplete %}#toc-footer-template) * - [Using `FooterTemplate` with the ComboBox]({% slug templates_combobox %}#toc-footer-template) * - [Using `FooterTemplate` with the MultiColumnComboBox]({% slug templates_multicolumncombobox %}#toc-footer-template) * - [Using `FooterTemplate` with the DropDownList]({% slug templates_ddl %}#toc-footer-template) * - [Using `FooterTemplate` with the DropDownTree]({% slug templates_ddt %}#toc-footer-template) * - [Using `FooterTemplate` with the MultiSelect]({% slug templates_multiselect %}#toc-footer-template) * * @example * ```ts * _@Component({ * selector: 'my-app', * template: ` * <kendo-combobox [data]="listItems"> * <ng-template kendoComboBoxFooterTemplate> * <h4>Footer template</h4> * </ng-template> * </kendo-combobox> * ` * }) * class AppComponent { * public listItems: Array<string> = ["Item 1", "Item 2", "Item 3", "Item 4"]; * } * ``` */ class FooterTemplateDirective { templateRef; constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FooterTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: FooterTemplateDirective, isStandalone: true, selector: "[kendoDropDownListFooterTemplate],[kendoComboBoxFooterTemplate],[kendoDropDownTreeFooterTemplate],[kendoMultiColumnComboBoxFooterTemplate],[kendoAutoCompleteFooterTemplate],[kendoMultiSelectFooterTemplate],[kendoMultiSelectTreeFooterTemplate]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FooterTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoDropDownListFooterTemplate],[kendoComboBoxFooterTemplate],[kendoDropDownTreeFooterTemplate],[kendoMultiColumnComboBoxFooterTemplate],[kendoAutoCompleteFooterTemplate],[kendoMultiSelectFooterTemplate],[kendoMultiSelectTreeFooterTemplate]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * Renders the group header content. To define the group template, nest an `<ng-template>` tag * with the `kendo<ComponentName>GroupTemplate` directive inside the component tag. The template context is * set to the current component. To get a reference to the current data item, use the `let-groupName` directive. * * - [Using `GroupTemplate` with the AutoComplete]({% slug templates_autocomplete %}#toc-group-header-template) * - [Using `GroupTemplate` with the ComboBox]({% slug templates_combobox %}#toc-group-header-template) * - [Using `GroupTemplate` with the MultiColumnComboBox]({% slug templates_multicolumncombobox %}#toc-group-header-template) * - [Using `GroupTemplate` with the DropDownList]({% slug templates_ddl %}#toc-group-header-template) * - [Using `GroupTemplate` with the MultiSelect]({% slug templates_multiselect %}#toc-group-header-template) * * @example * ```ts * import { groupBy } from '@progress/kendo-data-query'; * _@Component({ * selector: 'my-app', * template: ` * <kendo-combobox [data]="groupedData" textField="name" valueField="name"> * <ng-template kendoComboBoxGroupTemplate let-groupName> * <span>Food type: {{groupName}} option</span> * </ng-template> * </kendo-combobox> * ` * }) * class AppComponent { * public data = [ * { name: "Pork", category: "Food", subcategory: "Meat" }, * { name: "Pepper", category: "Food", subcategory: "Vegetables" }, * { name: "Beef", category: "Food", subcategory: "Meat" } * ]; * public groupedData = groupBy(this.data, [{field: "subcategory"}]); * } * ``` */ class GroupTemplateDirective { templateRef; constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: GroupTemplateDirective, isStandalone: true, selector: "[kendoDropDownListGroupTemplate],[kendoComboBoxGroupTemplate],[kendoMultiColumnComboBoxGroupTemplate],[kendoAutoCompleteGroupTemplate],[kendoMultiSelectGroupTemplate]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoDropDownListGroupTemplate],[kendoComboBoxGroupTemplate],[kendoMultiColumnComboBoxGroupTemplate],[kendoAutoCompleteGroupTemplate],[kendoMultiSelectGroupTemplate]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * Renders the fixed group header content. To define the fixed group template, nest an `<ng-template>` tag * with the `kendo<ComponentName>FixedGroupTemplate` directive inside the component tag. The template context is * set to the current component. To get a reference to the current data item, use the `let-groupName` directive. * * - [Using `FixedGroupTemplate` with the AutoComplete]({% slug templates_autocomplete %}#toc-fixed-group-header-template) * - [Using `FixedGroupTemplate` with the ComboBox]({% slug templates_combobox %}#toc-fixed-group-header-template) * - [Using `FixedGroupTemplate` with the MultiColumnComboBox]({% slug templates_multicolumncombobox %}#toc-fixed-group-header-template) * - [Using `FixedGroupTemplate` with the DropDownList]({% slug templates_ddl %}#toc-fixed-group-header-template) * - [Using `FixedGroupTemplate` with the MultiSelect]({% slug templates_multiselect %}#toc-fixed-group-header-template) * * @example * ```ts * import { groupBy } from '@progress/kendo-data-query'; * _@Component({ * selector: 'my-app', * template: ` * <kendo-combobox [data]="groupedData" textField="name" valueField="name"> * <ng-template kendoComboBoxFixedGroupTemplate let-groupName> * <span>Food type: {{groupName}} option</span> * </ng-template> * </kendo-combobox> * ` * }) * class AppComponent { * public data = [ * { name: "Pork", category: "Food", subcategory: "Meat" }, * { name: "Pepper", category: "Food", subcategory: "Vegetables" }, * { name: "Beef", category: "Food", subcategory: "Meat" } * ]; * public groupedData = groupBy(this.data, [{field: "subcategory"}]); * } * ``` */ class FixedGroupTemplateDirective { templateRef; constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FixedGroupTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: FixedGroupTemplateDirective, isStandalone: true, selector: "[kendoDropDownListFixedGroupTemplate],[kendoComboBoxFixedGroupTemplate],[kendoMultiColumnComboBoxFixedGroupTemplate],[kendoAutoCompleteFixedGroupTemplate],[kendoMultiSelectFixedGroupTemplate]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FixedGroupTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoDropDownListFixedGroupTemplate],[kendoComboBoxFixedGroupTemplate],[kendoMultiColumnComboBoxFixedGroupTemplate],[kendoAutoCompleteFixedGroupTemplate],[kendoMultiSelectFixedGroupTemplate]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * @hidden */ class DataService { grouped = false; groupIndices = []; view; _data; _flatData; set data(data) { this._data = data; this.grouped = this.isGrouped(data); if (this.grouped) { this.groupIndices = this.getGroupIndices(data); this._flatData = this.flatten(data); } } get data() { if (this.grouped) { return this._flatData; } return this._data; } /** * @hidden * Used to get the actual items count, i.e. excluding the header items in case of grouping. */ get itemsCount() { if (!isPresent(this.data) || this.data.length === 0) { return 0; } const items = this.grouped ? this._flatData.filter(item => !item.header) : this.data; return items.length; } /** * @hidden * Used to determine if the component received grouped data. */ isGrouped(data) { // GroupResult { aggregates: AggregateResult, field: string, items: object[], value: any } // https://www.telerik.com/kendo-angular-ui/components/dataquery/api/GroupResult/ return (isPresent(data) && data.length !== 0) && isPresent(data[0]) && hasProps(data[0], ['aggregates', 'field', 'items', 'value']); } /** * @hidden * Used to calculate the last item index of each group. */ getGroupIndices(data) { const groupIndices = []; for (let i = 0; i <= data.length - 1; i++) { groupIndices[i] = (groupIndices[i - 1] || 0) + data[i].items.length; } return groupIndices; } /** * @hidden * Used to get a flat array containing all items matching certain criteria. */ filter(predicate) { let result = []; if (this.isGrouped(this.data)) { for (let i = 0; i <= this.groupIndices.length - 1; i++) { const matches = this.data[i].items.filter(predicate); if (matches) { result = result.concat(matches); } } } else { result = this.data.filter(predicate); } return result; } /** * @hidden * Used to get the index of a given data item. */ indexOf(item, startFrom = 0) { let predicate = (element) => { return element === item; }; if (this.grouped) { predicate = (element) => { return element.value === item; }; } return this.findIndex(predicate, startFrom); } /** * @hidden * Used to get the index of a data item based on an expression. */ findIndex(predicate, startFrom = 0) { let index = -1; if (this.grouped) { const data = this._flatData.filter(item => !item.header && item.offsetIndex >= startFrom); index = data.findIndex(predicate); index = data[index] ? data[index].offsetIndex : -1; } else { const data = this.data.slice(startFrom); const itemIndex = data.findIndex(predicate); index = itemIndex !== -1 ? itemIndex + startFrom : -1; } return index; } /** * @hidden * Used to get the closest group header prior to an item index. */ closestGroup(index) { for (let i = index; i >= 0; i--) { if (this._flatData[i].header) { return this._flatData[i]; } } } /** * @hidden * Used to get the first item matching the criteria. */ find(predicate) { const index = this.findIndex(predicate); return this.itemAt(index); } /** * @hidden * Used to get the true index in a flattened data array. */ flatIndex(index) { if (this.itemsCount === 0) { return -1; } if (this.grouped) { const match = this._flatData.find((item) => !item.header && item.offsetIndex === index); if (match) { return match.index; } } else { return index; } return -1; } /** * @hidden * Used to get the item at the provided index. */ itemAt(index) { let dataItem; if (this.itemsCount === 0) { return dataItem; } if (this.grouped) { const match = this._flatData.find((item) => !item.header && item.offsetIndex === index); if (match) { dataItem = match.value; } } else { dataItem = this.data[index]; } return dataItem; } /** * @hidden * Used to get the group at the provided index. */ groupAt(index) { if (this.itemsCount === 0 || !this.isGrouped) { return; } return this._flatData.find((item) => item.header && item.index === index); } /** * @hidden * Used to get all group items indices. */ groupItemsIndices() { if (this.isGrouped) { return filterAndMap(this.data, item => item.header, mappedItem => mappedItem.index); } return []; } /** * @hidden * Used to get the field by which the data is grouped. */ groupField() { if (this.itemsCount === 0 || !this.isGrouped) { return null; } return this._data[0].field; } /** * @hidden * Used to get the group to which a dataItem belongs. */ itemGroup(item) { if (!item || this.itemsCount === 0 || !this.isGrouped) { return; } const fieldName = this.groupField(); if (fieldName) { return getter(item, fieldName); } } flatten(data, group = undefined, offset = 0, groupIndex = 0) { let flat = []; if (isPresent(group)) { flat.push({ header: true, groupIndex: groupIndex, index: groupIndex + offset, offsetIndex: groupIndex, value: group }); } for (let i = 0; i < data.length; i++) { let result = []; if (data[i].items) { result = this.flatten(data[i].items, data[i].value, offset, i); offset = offset + data[i].items.length; } else { result.push({ header: false, groupIndex: groupIndex, index: groupIndex + offset + i + 1, offsetIndex: offset + i, value: data[i] }); } flat = flat.concat(result); } return flat; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataService }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataService, decorators: [{ type: Injectable }] }); /** * @hidden */ class DisabledItemsService { dataService; defaultItem; itemDisabled = null; constructor(dataSe