UNPKG

ng-devui

Version:

DevUI components based on Angular

818 lines 239 kB
import { __decorate, __metadata } from "tslib"; import { CdkConnectedOverlay, CdkOverlayOrigin, ScrollStrategyOptions, } from '@angular/cdk/overlay'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, HostBinding, HostListener, Inject, Input, NgZone, Output, Renderer2, TemplateRef, ViewChild, forwardRef, } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { I18nService } from 'ng-devui/i18n'; import { AppendToBodyDirectionsConfig, DevConfigService, WithConfig, addClassToOrigin, fadeInOut, formWithDropDown, removeClassFromOrigin, } from 'ng-devui/utils'; import { WindowRef } from 'ng-devui/window-ref'; import { differenceBy, isEqual } from 'lodash-es'; import { BehaviorSubject, fromEvent, of } from 'rxjs'; import { debounceTime, filter, map, switchMap } from 'rxjs/operators'; import * as i0 from "@angular/core"; import * as i1 from "ng-devui/window-ref"; import * as i2 from "ng-devui/i18n"; import * as i3 from "ng-devui/utils"; import * as i4 from "@angular/cdk/overlay"; import * as i5 from "@angular/common"; import * as i6 from "@angular/forms"; import * as i7 from "@angular/cdk/scrolling"; import * as i8 from "ng-devui/checkbox"; import * as i9 from "ng-devui/loading"; import * as i10 from "ng-devui/tags"; import * as i11 from "./before-checkbox-change.pipe"; export class SelectComponent { get hasGlowStyle() { return this.showGlowStyle; } set isOpen(value) { this._isOpen = value; this.toggleChange.emit(value); this.setDocumentClickListener(); if (this.selectWrapper) { this.dropDownWidth = this.width ? this.width : this.selectWrapper.nativeElement.offsetWidth; } if (value) { addClassToOrigin(this.selectWrapper); setTimeout(() => { this.startAnimation = true; this.changeDetectorRef.detectChanges(); this.resetScrollTop(); }); } else { this.resetScrollTop(true); removeClassFromOrigin(this.selectWrapper); this.onTouch(); if (this.direction === 'auto') { this.clearText(); } } } get isOpen() { return this._isOpen; } get isClearIconShow() { const hasValue = this.multiple ? this.multiItems?.length : this.value; return this.allowClear && !this.disabled && hasValue; } get showMoreTags() { return this.multiItems.length > (this.extraConfig?.labelization?.maxTags || 40) && this.isSelectAll && this.virtualScroll; } get moreTagsNum() { return `+${this.multiItems.length - 1}`; } get showSelectAll() { return this.isSelectAll && this.multiple && this.availableOptions.length > 0; } constructor(doc, renderer, windowRef, changeDetectorRef, i18n, ngZone, el, devConfigService, scrollStrategyOption) { this.doc = doc; this.renderer = renderer; this.windowRef = windowRef; this.changeDetectorRef = changeDetectorRef; this.i18n = i18n; this.ngZone = ngZone; this.el = el; this.devConfigService = devConfigService; this.scrollStrategyOption = scrollStrategyOption; /** * 【必选】下拉选项资源,支持Array<string>, Array<{key: value}> */ this.options = []; /** * 【可选】是否支持过滤搜索 */ this.isSearch = false; /** * 【可选】是否支持聚焦自动展开下拉 */ this.toggleOnFocus = false; /** * 【可选】下拉菜单高度,建议使用px作为高度单位 */ this.scrollHight = '300px'; /** * 【可选】下拉高亮css */ this.highlightItemClass = 'active'; /** * 【可选】是否支持全选 */ this.isSelectAll = false; /** * 【可选】是否可以输入 */ this.readonly = true; /** * 【可选】是否appendToBody */ this.appendToBody = false; /** * 【可选】cdk模式overlay Positions的控制 */ this.appendToBodyDirections = ['rightDown', 'leftDown', 'rightUp', 'leftUp']; /** * 【可选】是否禁用下拉框 */ this.disabled = false; /** * 【可选】下拉默认显示文字 */ this.placeholder = ''; this.searchPlaceholder = ''; this.direction = 'down'; this.overview = 'border'; /** * 【可选】是否开启clear功能(是否开启只对单选生效) */ this.allowClear = false; /** * 【可选】启用数据懒加载,默认不启用 */ this.enableLazyLoad = false; /** * 【当传入资源(options)类型为Array<{key: value},必选】针对传入资源options的每项对应字段做禁用操作的key */ this.optionDisabledKey = ''; /** * 【当传入资源(options)类型为Array<{key: value},必选】针对传入资源options的每项对应字段禁止变更的key */ this.optionImmutableKey = ''; /** * 【可选】配置多选的时候是否维持原数组排序还是用户选择的顺序排序,默认是用户顺序 */ this.keepMultipleOrder = 'user-select'; /** * customViewTemplate的方向,支持下方和右方 */ this.customViewDirection = 'bottom'; this.autoScrollIntoActive = false; this.autoFocus = false; this.notAutoScroll = false; // 自动聚焦的时候,自动滚动到select位置 this.showItemTitle = false; this.showAnimation = true; this.styleType = 'default'; this.showGlowStyle = true; /** * select下拉toggle事件,值为true或false */ this.toggleChange = new EventEmitter(); this.loadMore = new EventEmitter(); /** * 输出函数,当选中某个选项项后,将会调用此函数,参数为当前选择项的值。如果需要获取所有选择状态的值,请参考(ngModelChange)方法 */ this.valueChange = new EventEmitter(); this._isOpen = false; this.allChecked = false; this.halfChecked = false; this.isMouseEvent = false; this.showLoading = false; this.startAnimation = false; this.limitMaxHeight = false; this.availableOptions = []; this.multiItems = []; this.filter = ''; this.activeIndex = -1; this.selectIndex = -1; this.menuPosition = 'bottom'; this.virtualScrollViewportSizeMightChange = false; this.virtualScrollItemSize = { sm: 30, normal: 36, lg: 50, space: 4, }; // 容器边距,虚拟滚动设置高度需考虑上下边距 this.CONTAINER_MARGINS = 12; this.ANIMATION_DELAY = 300; this.resetting = false; this.onChange = (_) => null; this.onTouch = () => null; this.getValue = (item, key) => { let result = item ?? ''; if (typeof item === 'object') { result = item[key] ?? ''; } return String(result); }; this.choose = (option, index, $event, operate) => { if ($event) { $event.preventDefault(); $event.stopPropagation(); } if (typeof option === 'object') { if (Object.keys(option).length === 0 || this.disabled) { this.isOpen = false; return; } } else { if (this.disabled) { this.isOpen = false; return; } } if (this.optionDisabledKey && option[this.optionDisabledKey]) { return; } if (this.optionImmutableKey && option[this.optionImmutableKey]) { return; } this.canChange(option, index, operate || 'select').then((change) => { if (!change) { return; } if (this.multiple) { const indexOfOption = this.multiItems.findIndex((item) => isEqual(item.option, option)); if (indexOfOption === -1) { this.multiItems.push({ id: index, option }); } else { this.multiItems.splice(indexOfOption, 1); } if (this.keepMultipleOrder === 'origin') { this.multiItems.sort((a, b) => a.id - b.id); } this.value = this.valueKey ? this.multiItems.map((item) => item.option[this.valueKey]) : this.multiItems.map((item) => item.option); this.setAllChecked(); } else { this.value = this.valueKey ? option[this.valueKey] : option; this.activeIndex = index; this.selectIndex = index; this.toggle(); } this.writeIntoInput(this.value); this.onChange(this.value); this.valueChange.emit(option); }); }; this.onDocumentClick = ($event) => { if (this.isOpen && !this.selectBoxElement.nativeElement.contains($event.target)) { this.isOpen = false; this.selectIndex = this.activeIndex ? this.activeIndex : -1; this.changeDetectorRef.detectChanges(); } }; this.getSelectedItem = () => { return this.extraConfig?.enableFocusFirstFilteredOption && this.multiple && this.availableOptions.length ? this.availableOptions[0] : this.availableOptions[this.selectIndex]; }; this.valueParser = (item) => this.getValue(item, this.filterKey); this.formatter = (item) => this.getValue(item, this.filterKey); this.scrollStrategy = this.scrollStrategyOption.reposition(); this.document = this.doc; } ngOnInit() { if (!this.searchFn) { this.searchFn = (term) => { return of((this.options || []) .map((option, index) => ({ option: option, id: index })) .filter((item) => this.formatter(item.option).toLowerCase().indexOf(term.toLowerCase()) !== -1)); }; } // 只有多选的情况isSelectAll为true才有意义 if (!this.multiple) { this.isSelectAll = false; } this.setI18nText(); this.registerFilterChange(); this.setPositions(); } ngAfterViewInit() { if (this.autoFocus && this.selectBoxElement) { setTimeout(() => { this.selectBoxElement.nativeElement.focus({ preventScroll: this.notAutoScroll, }); }); } // 当d-select添加limit-max-height样式类时为devui-dropdown-menu-wrap容器添加最大高度限制样式,避免当窗口高度小于指定下拉最大高度时,下拉内容超出cdk容器 if (this.el.nativeElement?.className.includes('limit-max-height')) { this.limitMaxHeight = true; } } ngOnDestroy() { if (this.sourceSubscription) { this.sourceSubscription.unsubscribe(); } if (this.filterSubscription) { this.filterSubscription.unsubscribe(); } if (this.i18nSubscription) { this.i18nSubscription.unsubscribe(); } this.document.removeEventListener('click', this.onDocumentClick); } ngOnChanges(changes) { const { searchFn, options, appendToBodyDirections, appendToBodyScrollStrategy, disabled } = changes; const globalScrollStrategy = this.devConfigService.getConfigForApi('appendToBodyScrollStrategy'); if (searchFn || options) { this.resetSource(); if (this.virtualScroll && this.virtualScrollViewport) { this.virtualScrollViewportSizeMightChange = true; this.virtualScrollViewport.checkViewportSize(); } } if (appendToBodyDirections) { this.setPositions(); } if (this.appendToBodyScrollStrategy && (appendToBodyScrollStrategy || globalScrollStrategy)) { const func = this.scrollStrategyOption[this.appendToBodyScrollStrategy]; this.scrollStrategy = func(); } if (disabled && this.isOpen) { this.toggle(); } } setPositions() { if (this.appendToBodyDirections && this.appendToBodyDirections.length > 0) { this.overlayPositions = this.appendToBodyDirections .map((position) => { if (typeof position === 'string') { return AppendToBodyDirectionsConfig[position]; } else { return position; } }) .filter((position) => position !== undefined); } else { this.overlayPositions = undefined; } } setI18nText() { this.i18nCommonText = this.i18n.getI18nText().common; this.i18nSubscription = this.i18n.langChange().subscribe((data) => { this.i18nCommonText = data.common; }); } getVirtualScrollHeight(len, size) { if (len > 0) { let height = (this.templateItemSize || this.virtualScrollItemSize[size || 'normal']) * len + this.virtualScrollItemSize.space * (len - 1); if (this.isSelectAll && this.multiple) { height += this.virtualScrollItemSize[size ? size : 'normal'] + this.virtualScrollItemSize.space; } const scrollHeight = parseInt(this.scrollHight, 10); this.scrollHeightNum = height > scrollHeight ? scrollHeight : height; return `${this.scrollHeightNum + this.CONTAINER_MARGINS * 2}px`; } } get realVirtualScrollItemSize() { const itemSize = (this.templateItemSize || this.virtualScrollItemSize[this.size || 'normal']) + this.virtualScrollItemSize.space; const num = Math.round(this.scrollHeightNum / itemSize) || 10; this.minBuffer = num * 1.5 * itemSize; this.maxBuffer = num * 2.5 * itemSize; return itemSize; } resetSource() { if (this.sourceSubscription && this.searchFn) { this.resetting = true; this.sourceSubscription.next(''); } } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouch = fn; } registerFilterChange() { this.sourceSubscription = new BehaviorSubject(''); this.sourceSubscription.pipe(switchMap((term) => this.searchFn(term))).subscribe((options) => { this.availableOptions = options; this.setAvailableOptions(); this.setAllChecked(); this.changeDetectorRef.markForCheck(); if (this.appendToBody) { setTimeout(() => { if (this.connectedOverlay && this.connectedOverlay.overlayRef) { this.connectedOverlay.overlayRef.updatePosition(); } }); } if (!this.multiple && (!this.value || (this.availableOptions && !this.availableOptions.find((option) => option.option === this.value)))) { this.selectIndex = this.filter && this.availableOptions && this.availableOptions.length > 0 ? 0 : -1; } }); this.sourceSubscription.subscribe((term) => { if (this.resetting && term === '') { this.writeValue(this.value); this.resetting = false; } }); this.searchInputValueChangeEvent(); } searchInputValueChangeEvent() { if (this.isSearch && this.isOpen && this.filterInputElement) { this.filterInputElement.nativeElement.focus(); if (!this.filterSubscription || this.appendToBody) { // 避免重复订阅 this.filterSubscription = fromEvent(this.filterInputElement.nativeElement, 'input') .pipe(map((e) => e.target.value), filter((term) => !this.disabled && this.searchFn && term.length >= 0), debounceTime(this.ANIMATION_DELAY)) .subscribe((term) => { this.selectIndex = -1; return this.sourceSubscription.next(term); }); } } } writeValue(value) { if (this.multiple) { this.value = value ?? []; if (this.showMoreTags) { this.value = this.value || []; this.value = Array.isArray(this.value) ? this.value : [this.value]; } else { this.getMultipleSelectedOption(); this.setAllChecked(); } } else { this.value = value ?? ''; this.getSingleSelectedOption(); if (this.autoScrollIntoActive) { this.scrollIntoActive(); } } this.writeIntoInput(this.value); this.changeDetectorRef.markForCheck(); } writeIntoInput(value) { let valueItem = value; if (this.valueKey) { valueItem = this.multiple ? this.multiItems.map((item) => item.option) : this.getOption(this.availableOptions, value, true); valueItem = valueItem ?? ''; } this._inputValue = this.multiple ? (valueItem || []).map((option) => this.valueParser(option)).join(', ') : this.valueParser(valueItem); this.setAvailableOptions(); if (this.showMoreTags) { this.multiItems = this.availableOptions.filter((item) => item.isChecked); this.setAllChecked(); } } setAvailableOptions() { const hasNoValue = this.value === undefined || this.value === ''; if (hasNoValue || !Array.isArray(this.availableOptions)) { return; } let _value = this.value; if (!this.multiple) { _value = [_value]; } this.availableOptions = this.availableOptions.map((item) => ({ isChecked: _value.findIndex((i) => isEqual(i, this.valueKey ? item.option[this.valueKey] : item.option)) > -1, id: item.id, option: item.option, })); } getMultipleSelectedOption() { this.value = this.value || []; this.value = Array.isArray(this.value) ? this.value : [this.value]; this.multiItems = this.valueKey ? this.value.map((value) => this.getOption(this.availableOptions, value)).filter((item) => item) : this.value.map((option) => ({ option: option, id: this.getOptionIndex(option) })); } getSingleSelectedOption() { const selectedItem = this.valueKey ? this.getOption(this.availableOptions, this.value) : this.availableOptions.find((item) => this.formatter(item.option) === this.formatter(this.value)); this.activeIndex = selectedItem ? selectedItem.id : -1; this.selectIndex = this.activeIndex >= 0 ? this.activeIndex : -1; } getOption(data, value, hasOption) { const hasValue = (value ?? undefined) !== undefined; const valueItem = this.getValue(value, this.valueKey); const result = hasValue ? data.find((item) => this.getValue(item.option, this.valueKey) === valueItem) || '' : ''; return hasOption && result ? result.option : result; } getOptionIndex(option) { return this.options?.length ? this.options.findIndex((item) => isEqual(item, option)) : this.availableOptions.findIndex((item) => isEqual(item.option, option)); } scrollIntoActive() { if (this.activeIndex >= 0) { setTimeout(() => { const items = this.dropdownUl?.nativeElement.querySelectorAll('.devui-dropdown-item') || []; if (items[this.activeIndex]) { items[this.activeIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }); } }, this.ANIMATION_DELAY); } } canChange(option, index, action) { let changeResult = Promise.resolve(true); if (this.beforeChange) { const result = this.beforeChange(index, option, action); if (typeof result !== 'undefined') { if (result.then) { changeResult = result; } else if (result.subscribe) { changeResult = result.toPromise(); } else { changeResult = Promise.resolve(result); } } } return changeResult; } updateCdkConnectedOverlayOrigin() { if (this.selectWrapper.nativeElement) { this.cdkConnectedOverlayOrigin = new CdkOverlayOrigin(formWithDropDown(this.selectWrapper) || this.selectWrapper.nativeElement); } } resetScrollTop(isClose = false) { const menuDom = this.selectMenuElement?.nativeElement.querySelector('ul.devui-select-list-unstyled.devui-scrollbar'); if (this.enableLazyLoad && menuDom) { if (isClose) { this.lastCloseScrollHeight = menuDom.scrollHeight ?? 0; } else if (menuDom.scrollHeight < this.lastCloseScrollHeight) { menuDom.scrollTop = 0; } } } autoToggle($event) { $event.preventDefault(); $event.stopPropagation(); if (this.toggleOnFocus && !this.disabled && !this.isOpen && !this.isMouseEvent) { this.toggle(); } } // mousedown mouseup解决focus与click冲突问题 setMouseEventTrue(event) { this.isMouseEvent = true; } setMouseEventFalse(event) { this.isMouseEvent = false; } toggle() { if (this.disabled) { if (this.isOpen) { this.isOpen = false; } return; } if (!this.isOpen) { this.filter = ''; this.resetSource(); if (!this.appendToBody) { let direction = ''; switch (this.direction) { case 'auto': direction = this.isBottomRectEnough() ? 'bottom' : 'top'; break; case 'down': direction = 'bottom'; break; case 'up': direction = 'top'; break; default: direction = 'bottom'; } this.popDirection = direction; } else { this.updateCdkConnectedOverlayOrigin(); } } else if (!this.showAnimation) { this.startAnimation = false; } this.isOpen = !this.isOpen; if (this.virtualScrollViewportSizeMightChange) { // 解决虚拟滚动更新options长度展开前无法获取正确高度影响 setTimeout(() => { if (this.virtualScrollViewportSizeMightChange && this.virtualScrollViewport) { this.virtualScrollViewportSizeMightChange = false; this.virtualScrollViewport.checkViewportSize(); } }, 0); } if (this.isSearch && this.isOpen) { // 条件外移减少setTimeout setTimeout(() => this.searchInputValueChangeEvent(), 100); } } isBottomRectEnough() { const selectMenuElement = this.selectMenuElement.nativeElement; const selectInputElement = this.selectInputElement || this.selectInputWithLabelElement || this.selectInputWithTemplateElement; const displayStyle = selectMenuElement.style.display || window.getComputedStyle(selectMenuElement).display; let tempStyle; if (displayStyle === 'none') { // 必要, 否则首次展开必有问题, 如果animationEnd之后设置为none也会有问题 tempStyle = { visibility: selectMenuElement.style.visibility, display: selectMenuElement.style.display, transform: selectMenuElement.style.transform, }; this.renderer.setStyle(selectMenuElement, 'visibility', 'hidden'); this.renderer.setStyle(selectMenuElement, 'display', 'block'); this.renderer.setStyle(selectMenuElement, 'transform', 'translate(0, -9999)'); } const elementHeight = selectMenuElement.offsetHeight; const bottomDistance = this.windowRef.innerHeight - selectInputElement.nativeElement.getBoundingClientRect().bottom; const isBottomEnough = bottomDistance >= elementHeight; if (displayStyle === 'none') { this.renderer.setStyle(selectMenuElement, 'visibility', tempStyle.visibility); this.renderer.setStyle(selectMenuElement, 'display', tempStyle.display); this.renderer.setStyle(selectMenuElement, 'transform', tempStyle.transform); } return isBottomEnough; } setDocumentClickListener() { this.ngZone.runOutsideAngular(() => { if (this.isOpen) { this.document.addEventListener('click', this.onDocumentClick); } else { this.document.removeEventListener('click', this.onDocumentClick); } }); } onEscKeyup($event) { if (this.isOpen) { $event.stopPropagation(); } this.isOpen = false; } handleKeyUpEvent($event) { if (this.isOpen) { $event.preventDefault(); $event.stopPropagation(); this.selectIndex = this.selectIndex === 0 || this.selectIndex === -1 ? this.availableOptions.length - 1 : this.selectIndex - 1; this.scrollToActive(); } } handleKeyDownEvent($event) { if (this.isOpen) { $event.preventDefault(); $event.stopPropagation(); this.selectIndex = this.selectIndex === this.availableOptions.length - 1 ? 0 : this.selectIndex + 1; this.scrollToActive(); } } scrollToActive() { const that = this; setTimeout(() => { try { const selectIndex = that.selectIndex + (that.isSelectAll ? 1 : 0); // 多了个全选会导致问题,index需要加1 const scrollPane = that.dropdownUl.nativeElement.children[selectIndex]; if (scrollPane.scrollIntoViewIfNeeded) { scrollPane.scrollIntoViewIfNeeded(false); } else { const containerInfo = that.dropdownUl.nativeElement.getBoundingClientRect(); const elementInfo = scrollPane.getBoundingClientRect(); if (elementInfo.bottom > containerInfo.bottom || elementInfo.top < containerInfo.top) { scrollPane.scrollIntoView(false); } } } catch (e) { } }); } handleKeyEnterEvent($event, isSearchInput = false) { if (this.isOpen) { $event.preventDefault(); $event.stopPropagation(); const item = this.getSelectedItem(); if (item) { this.choose(item.option, item.id, $event); } else if (!isSearchInput) { this.toggle(); } } else { this.toggle(); } } removeItem(item, $event) { this.choose(item.option, item.id, $event, 'remove'); } selectAll() { const mutableOption = this.optionImmutableKey ? this.availableOptions.filter((item) => !item.option[this.optionImmutableKey]) : this.availableOptions; const selectedImmutableOption = this.optionImmutableKey ? this.multiItems.filter((item) => item.option[this.optionImmutableKey]) : []; const hasNotSelected = differenceBy(mutableOption, this.multiItems, 'id'); if (hasNotSelected.length) { mutableOption.forEach((item) => { const indexOfOption = this.multiItems.findIndex((i) => isEqual(i.option, item.option)); if (indexOfOption === -1) { this.multiItems.push({ id: item.id, option: item.option }); } }); } else if (mutableOption.length === this.multiItems.length - selectedImmutableOption.length) { this.multiItems = [...selectedImmutableOption]; } else { this.multiItems = differenceBy(this.multiItems, mutableOption, 'id'); } this.value = this.valueKey ? this.multiItems.map((item) => item.option[this.valueKey]) : this.multiItems.map((item) => item.option); this.writeIntoInput(this.value); this.setAllChecked(); this.onChange(this.valueKey ? this.multiItems.map((item) => item.option[this.valueKey]) : this.value); this.valueChange.emit(this.multiItems); } trackByFn(index, item) { return index; } trackByOptionPointer(index, item) { return item.option; } loadMoreEvent(event) { this.showLoading = true; this.loadMore.emit({ instance: this, event: event }); } loadFinish() { this.showLoading = false; this.changeDetectorRef.markForCheck(); } loadStart() { this.showLoading = true; this.changeDetectorRef.markForCheck(); } onPositionChange(position) { this.menuPosition = position.connectionPair.originY; } animationEnd($event) { if (!this.isOpen && this.selectMenuElement && this.showAnimation) { const targetElement = this.selectMenuElement.nativeElement; this.startAnimation = false; setTimeout(() => { // 动画会覆盖导致display还是block, 所以要等动画覆盖完 this.renderer.setStyle(targetElement, 'display', 'none'); }); } } setAllChecked() { this.allChecked = false; this.halfChecked = false; if (!this.showSelectAll) { return; } if (!this.multiItems || this.multiItems.length === 0) { return; } // 从当前下拉选项中用 id 排除已选中选项 const result = differenceBy(this.availableOptions, this.multiItems, 'id'); if (result.length === 0) { this.allChecked = true; } else if (result.length === this.availableOptions.length) { this.allChecked = false; } else { this.halfChecked = true; } } forceSearchNext() { this.sourceSubscription.next(this.filter); } valueClear($event) { $event.stopPropagation(); if (this.multiple) { this.multiItems = []; this.value = []; this.setAllChecked(); } else { this.value = undefined; } this.resetStatus(); this.onChange(this.value); this.valueChange.emit(this.value); } resetStatus() { this.writeIntoInput(this.value); if (this.availableOptions && this.availableOptions[this.activeIndex]) { this.availableOptions[this.activeIndex].isChecked = false; } this.activeIndex = -1; this.selectIndex = -1; this.changeDetectorRef.markForCheck(); } clearText() { this.filter = ''; this.forceSearchNext(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, deps: [{ token: DOCUMENT }, { token: i0.Renderer2 }, { token: i1.WindowRef }, { token: i0.ChangeDetectorRef }, { token: i2.I18nService }, { token: i0.NgZone }, { token: i0.ElementRef }, { token: i3.DevConfigService }, { token: i4.ScrollStrategyOptions }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SelectComponent, selector: "d-select", inputs: { options: "options", isSearch: "isSearch", toggleOnFocus: "toggleOnFocus", scrollHight: "scrollHight", highlightItemClass: "highlightItemClass", filterKey: "filterKey", valueKey: "valueKey", multiple: "multiple", isSelectAll: "isSelectAll", readonly: "readonly", size: "size", appendToBody: "appendToBody", appendToBodyDirections: "appendToBodyDirections", appendToBodyScrollStrategy: "appendToBodyScrollStrategy", width: "width", templateItemSize: "templateItemSize", disabled: "disabled", placeholder: "placeholder", searchPlaceholder: "searchPlaceholder", searchFn: "searchFn", valueParser: "valueParser", formatter: "formatter", direction: "direction", overview: "overview", allowClear: "allowClear", color: "color", enableLazyLoad: "enableLazyLoad", virtualScroll: "virtualScroll", inputItemTemplate: "inputItemTemplate", extraConfig: "extraConfig", optionDisabledKey: "optionDisabledKey", optionImmutableKey: "optionImmutableKey", noResultItemTemplate: "noResultItemTemplate", keepMultipleOrder: "keepMultipleOrder", customViewTemplate: "customViewTemplate", customViewDirection: "customViewDirection", autoScrollIntoActive: "autoScrollIntoActive", autoFocus: "autoFocus", notAutoScroll: "notAutoScroll", loadingTemplateRef: "loadingTemplateRef", showItemTitle: "showItemTitle", showAnimation: "showAnimation", styleType: "styleType", showGlowStyle: "showGlowStyle", beforeChange: "beforeChange" }, outputs: { toggleChange: "toggleChange", loadMore: "loadMore", valueChange: "valueChange" }, host: { listeners: { "mousedown": "setMouseEventTrue($event)", "mouseup": "setMouseEventFalse($event)" }, properties: { "class.devui-glow-style": "this.hasGlowStyle" } }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SelectComponent), multi: true, }, ], queries: [{ propertyName: "itemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "selectWrapper", first: true, predicate: ["selectWrapper"], descendants: true, static: true }, { propertyName: "selectInputElement", first: true, predicate: ["selectInput"], descendants: true }, { propertyName: "selectMenuElement", first: true, predicate: ["selectMenu"], descendants: true }, { propertyName: "selectBoxElement", first: true, predicate: ["selectBox"], descendants: true, static: true }, { propertyName: "selectInputWithTemplateElement", first: true, predicate: ["selectInputWithTemplate"], descendants: true }, { propertyName: "selectInputWithLabelElement", first: true, predicate: ["selectInputWithLabel"], descendants: true }, { propertyName: "filterInputElement", first: true, predicate: ["filterInput"], descendants: true }, { propertyName: "dropdownUl", first: true, predicate: ["dropdownUl"], descendants: true }, { propertyName: "connectedOverlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "virtualScrollViewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }], exportAs: ["select"], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"devui-drop{{ popDirection === 'bottom' ? 'down' : 'up' }} \n {{ size ? 'devui-select-' + size : '' }} \n devui-dropdown-origin\"\n [ngClass]=\"{\n 'devui-select-underlined-border': overview === 'underlined',\n disabled: disabled,\n open: isOpen,\n 'devui-gray-style': overview !== 'underlined' && styleType === 'gray'\n }\"\n #selectWrapper\n>\n <div\n #selectBox\n class=\"devui-form-group devui-has-feedback\"\n (click)=\"toggle()\"\n (focus)=\"autoToggle($event)\"\n [tabindex]=\"0\"\n (keydown.esc)=\"onEscKeyup($event)\"\n (keydown.ArrowUp)=\"handleKeyUpEvent($event)\"\n (keydown.ArrowDown)=\"handleKeyDownEvent($event)\"\n (keydown.enter)=\"handleKeyEnterEvent($event)\"\n >\n <input\n #selectInput\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [ngClass]=\"[size ? 'devui-select-input-' + size : '']\"\n type=\"text\"\n class=\"devui-form-control devui-select-input devui-input\"\n [minLength]=\"0\"\n autocomplete=\"off\"\n [tabindex]=\"-1\"\n [readonly]=\"readonly\"\n [value]=\"_inputValue || ''\"\n *ngIf=\"\n !(extraConfig?.labelization?.enable && multiple) &&\n !(!extraConfig?.labelization?.enable && extraConfig?.selectedItemWithTemplate?.enable && (inputItemTemplate || itemTemplate))\n \"\n />\n <div\n #selectInputWithTemplate\n class=\"devui-select-list-wrapper devui-form-control {{ size ? 'devui-select-input-' + size : '' }}\"\n *ngIf=\"extraConfig?.selectedItemWithTemplate?.enable && (inputItemTemplate || itemTemplate) && !extraConfig?.labelization?.enable\"\n >\n <div class=\"devui-select-placeholder\" *ngIf=\"!_inputValue\">\n {{ placeholder }}\n </div>\n <div class=\"devui-select-selected-item-with-template\" *ngIf=\"_inputValue\">\n <ng-template\n [ngTemplateOutlet]=\"inputItemTemplate || itemTemplate\"\n [ngTemplateOutletContext]=\"{\n option: value,\n filterKey: filterKey,\n position: 'result',\n inputValue: _inputValue\n }\"\n >\n </ng-template>\n </div>\n </div>\n\n <div\n #selectInputWithLabel\n class=\"devui-select-list-wrapper devui-form-control {{ size ? 'devui-select-input-' + size : '' }}\"\n *ngIf=\"extraConfig?.labelization?.enable && multiple\"\n >\n <div class=\"devui-select-placeholder\" *ngIf=\"multiItems.length === 0\">\n {{ placeholder }}\n </div>\n <ul\n *ngIf=\"multiItems.length > 0\"\n class=\"devui-select-tag-list devui-scrollbar {{ extraConfig?.labelization?.overflow }}\"\n [style.maxHeight]=\"extraConfig?.labelization?.containerMaxHeight || extraConfig?.labelization?.containnerMaxHeight\"\n >\n <li\n class=\"devui-select-tag-item\"\n *ngFor=\"let item of showMoreTags ? [multiItems[0]] : multiItems; let index = index; trackBy: trackByOptionPointer\"\n [style.maxWidth]=\"extraConfig?.labelization?.labelMaxWidth\"\n [ngClass]=\"{\n disabled: optionDisabledKey && item.option[optionDisabledKey]\n }\"\n >\n <d-tag\n [deletable]=\"!(disabled || (optionDisabledKey && item.option[optionDisabledKey]))\"\n [maxWidth]=\"'100%'\"\n [tag]=\"valueParser(item.option)\"\n (tagDelete)=\"removeItem(item, $event?.event)\"\n ></d-tag>\n </li>\n <li *ngIf=\"showMoreTags\" class=\"devui-select-tag-item\">\n <d-tag [tag]=\"moreTagsNum\"></d-tag>\n </li>\n </ul>\n </div>\n\n <span class=\"devui-form-control-feedback devui-input-group-addon\" [ngClass]=\"{ 'devui-clear-icon-show': isClearIconShow }\">\n <span class=\"devui-select-clear-icon\" (mousedown)=\"$event.preventDefault()\" (click)=\"valueClear($event)\">\n <svg viewBox=\"0 0 16 16\">\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <path\n d=\"M8,1 C11.8659932,1 15,4.13400675 15,8 C15,11.8659932 11.8659932,15 8,15 C4.13400675,15 1,11.8659932 1,8 C1,4.13400675 4.13400675,1 8,1 Z M5.87867966,5.17157288 C5.68341751,4.97631073 5.36683502,4.97631073 5.17157288,5.17157288 C4.99800652,5.34513923 4.97872137,5.61456363 5.11371742,5.80943177 L5.17157288,5.87867966 L7.29218611,7.99929289 L5.17157288,10.1213203 C4.97631073,10.3165825 4.97631073,10.633165 5.17157288,10.8284271 C5.34513923,11.0019935 5.61456363,11.0212786 5.80943177,10.8862826 L5.87867966,10.8284271 L7.99929289,8.70639967 L10.1213203,10.8284271 C10.3165825,11.0236893 10.633165,11.0236893 10.8284271,10.8284271 C11.0019935,10.6548608 11.0212786,10.3854364 10.8862826,10.1905682 L10.8284271,10.1213203 L8.70710678,8 L10.8284271,5.87867966 C11.0236893,5.68341751 11.0236893,5.36683502 10.8284271,5.17157288 C10.6548608,4.99800652 10.3854364,4.97872137 10.1905682,5.11371742 L10.1213203,5.17157288 L8,7.29289322 L5.87867966,5.17157288 Z\"\n fill-rule=\"nonzero\"\n ></path>\n </g>\n </svg>\n </span>\n <span class=\"devui-select-chevron-icon\" [ngClass]=\"{ 'devui-select-chevron-icon-animation': showAnimation }\">\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <path\n d=\"M3.64644661,5.64644661 C3.82001296,5.47288026 4.08943736,5.45359511 4.2843055,5.58859116 L4.35355339,5.64644661 L8,9.293 L11.6464466,5.64644661 C11.820013,5.47288026 12.0894374,5.45359511 12.2843055,5.58859116 L12.3535534,5.64644661 C12.5271197,5.82001296 12.5464049,6.08943736 12.4114088,6.2843055 L12.3535534,6.35355339 L8.35355339,10.3535534 C8.17998704,10.5271197 7.91056264,10.5464049 7.7156945,10.4114088 L7.64644661,10.3535534 L3.64644661,6.35355339 C3.45118446,6.15829124 3.45118446,5.84170876 3.64644661,5.64644661 Z\"\n ></path>\n </g>\n </svg>\n </span>\n </span>\n </div>\n <ng-container *ngIf=\"appendToBody\">\n <ng-template\n cdk-connected-overlay\n [cdkConnectedOverlayOrigin]=\"cdkConnectedOverlayOrigin\"\n [cdkConnectedOverlayWidth]=\"dropDownWidth\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (positionChange)=\"onPositionChange($event)\"\n (backdropClick)=\"isOpen = false\"\n (detach)=\"isOpen && (isOpen = false)\"\n >\n <ng-template [ngTemplateOutlet]=\"selectDropdownTpl\"></ng-template>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!appendToBody\">\n <ng-template [ngTemplateOutlet]=\"selectDropdownTpl\"></ng-template>\n </ng-container>\n</div>\n\n<!--\u4E0B\u62C9\u6A21\u677F-->\n<ng-template #selectDropdownTpl>\n <div\n #selectMenu\n class=\"devui-dropdown-menu\"\n [ngClass]=\"{\n 'devui-dropdown-menu-cdk': appendToBody,\n 'devui-custom-right': customViewTemplate && customViewDirection === 'right',\n 'devui-custom-left': customViewTemplate && customViewDirection === 'left',\n 'devui-custom-top': customViewTemplate && customViewDirection === 'top',\n 'devui-dropdown-menu-multiple': multiple\n }\"\n [@fadeInOut]=\"isOpen && startAnimation ? (appendToBody ? menuPosition : popDirection) : 'void'\"\n (@fadeInOut.done)=\"animationEnd($event)\"\n [@.disabled]=\"!showAnimation\"\n >\n <ul\n dLoading\n [showLoading]=\"showLoading\"\n positionType=\"relative\"\n [message]=\"i18nCommonText?.loading\"\n [loadingTemplateRef]=\"loadingTemplateRef\"\n [backdrop]=\"true\"\n [class.limit-max-height]=\"limitMaxHeight\"\n class=\"devui-dropdown-menu-wrap {{ size ? 'devui-dropdown-menu-' + size : '' }}\"\n (click)=\"$event.stopPropagation(); $event.preventDefault()\"\n >\n <li [style.display]=\"isSearch ? 'block' : 'none'\">\n <div class=\"devui-form-group devui-has-feedback\" [style.margin]=\"'0'\">\n <input\n #filterInput\n [ngClass]=\"[size ? 'devui-select-input-' + size : '', filter ? 'devui-search-filter' : '']\"\n name=\"select\"\n type=\"text\"\n [(ngModel)]=\"filter\"\n autocomplete=\"off\"\n [placeholder]=\"searchPlaceholder\"\n class=\"devui-form-control devui-select-search devui-select-input\"\n (keydown.ArrowUp)=\"handleKeyUpEvent($event)\"\n (keydown.ArrowDown)=\"handleKeyDownEvent($event)\"\n (keydown.enter)=\"handleKeyEnterEvent($event, true)\"\n (keydown.esc)=\"onEscKeyup($event)\"\n />\n <span class=\"devui-form-control-feedback\">\n <svg\n width=\"14px\"\n height=\"14px\"\n viewBox=\"0 0 14 14\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon-search\"\n style=\"vertical-align: middle\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <g transform=\"translate(-1.000000, -1.000000)\">\n <path\n d=\"M7.30811922,1 C10.7919973,1 13.6162384,3.82424117 13.6162384,7.30811922 C13.6162384,8.72507634 13.149053,10.0329164 12.3603151,11.0860065 L14.7360564,13.4616238 C15.0879812,13.8135487 15.0879812,14.3841316 14.7360564,14.7360564 C14.3841316,15.0879812 13.8135487,15.0879812 13.4616238,14.7360564 L11.0860065,12.3603151 C10.0329164,13.149053 8.72507634,13.6162384 7.30811922,13.6162384 C3.82424117,13.6162384 1,10.7919973 1,7.30811922 C1,3.82424117 3.82424117,1 7.30811922,1 Z M7.31,2.51 C4.6590332,2.51 2.51,4.6590332 2.51,7.31 C2.51,9.9609668 4.6590332,12.11 7.31,12.11 C9.9609668,12.11 12.11,9.9609668 12.11,7.31 C12.11,4.6590332 9.9609668,2.51 7.31,2.51 Z\"\n fill-rule=\"nonzero\"\n ></path>\n </g>\n </g>\n </svg>\n </span>\n <span\n #clearIcon\n class=\"devui-form-control-feedback devui-search-clear\"\n [ngClass]=\"size ? 'devui-search-clear-' + size : ''\"\n (click)=\"clearText()\"\n *ngIf=\"filter\"\n >\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <path\n d=\"M8,1 C11.8659932,1 15,4.13400675 15,8 C15,11.8659932 11.8659932,15 8,15 C4.13400675,15 1,11.8659932 1,8 C1,4.13400675 4.13400675,1 8,1 Z M5.87867966,5.17157288 C5.68341751,4.97631073 5.36683502,4.97631073 5.17157288,5.17157288 C4.99800652,5.34513923 4.97872137,5.61456363 5.11371742,5.80943177 L5.17157288,5.87867966 L7.29218611,7.99929289 L5.17157288,10.1213203 C4.97631073,10.3165825 4.97631073,10.633165 5.17157288,10.8284271 C5.34513923,11.0019935 5.61456363,11.0212786 5.80943177,10.8862826 L5.87867966,10.8284271 L7.99929289,8.70639967 L10.1213203,10.8284271 C10.3165825,11.0236893 10.633165,11.0236893 10.8284271,10.8284271 C11.0019935,10.6548608 11.0212786,10.3854364 10.8862826,10.1905682 L10.8284271,10.1213203 L8.70710678,8 L10.8284271,5.87867966 C11.0236893,5.68341751 11.0236893,5.36683502 10.8284271,5.17157288 C10.6548608,4.99800652 10.3854364,4.97872137 10.1905682,5.11371742 L10.1213203,5.17157288 L8,7.29289322 L5.87867966,5.17157288 Z\"\n fill-rule=\"nonzero\"\n ></path>\n </g>\n </svg>\n </span>\n </div>\n </li>\n <ul\n *ngIf=\"!virtualScroll || enableLazyLoad; else scrollViewTpl\"\n class=\"devui-select-list-unstyled devui-scrollbar\"\n dLazyLoad\n [enableLazyLoad]=\"enableLazyLoad\"\n (loadMore)=\"loadMoreEvent($event)\"\n [ngStyle]=\"{ 'max-height': scrollHight, width: '100%' }\"\n #dropdownUl\n >\n <ng-template [ngTemplateOutlet]=\"selectAllTpl\"></ng-template>\n <ng-container *ngFor=\"let item of availableOptions; let index = index; trackBy: trackByFn\">\n <ng-template [ngTemplateOutlet]=\"dropdownItemTpl\" [ngTemplateOutletContext]=\"{ item: item, index: index }\"></ng-template>\n </ng-container>\n </ul>\n <div *ngIf=\"availableOptions?.length === 0 && !noResultItemTemplate && !this.showLoading\" class=\"devui-no-data-tip\">\n <span>{{ filter ? i18nCommonText?.noRecordsFound : i18nCommonText?.noData }}</span>\n </div>\n <div *ngIf=\"availableOptions?.length === 0 && noResultItemTemplate && !this.showLoading\" class=\"devui-no-data-tip\">\n <ng-template [ngTemplateOutlet]=\"noResultItemTemplate\" [ngTemplateOutletContext]=\"{ options: options }\"></ng-template>\n </div>\n </ul>\n <ng-container *ngIf=\"customViewTemplate\">\n <div class=\"devui-select-custom-area\">\n <ng-template [ngTemplateOutlet]=\"customViewTemplate\" [ngTemplateOutletContext]=\"{ $implicit: this, choose: choose }\"></ng-template>\n </div>\n </ng-container>\n </div>\n</ng-template>\n\n<!--\u865A\u62DF\u6EDA\u52A8\u6A21\u677F-->\n<ng-template #scrollViewTpl>\n <cdk-virtual-scroll-viewport\n *ngIf=\"availableOptions?.length > 0\"\n [ngStyle]=\"{\n height: getVirtualScrollHeight(availableOptions?.length, size),\n width: '100%',\n 'max-height': scrollHight,\n 'min-height': availableOptions?.length === 0 ? '46px' : showSelectAll ? '100px' : '60px'\n }\"\n [itemSize]=\"realVirtualScrollItemSize\"\n [minBufferPx]=\"minBuffer\"\n [maxBufferPx]=\"maxBuffer\"\n class=\"viewport-wrapper devui-select-list-unstyled devui-scrollbar\"\n #dropdownUl\n >\n <ng-template [ngTemplateOutlet]=\"selectAllTpl\"></ng-template>\n <ng-container *cdkVirtualFor=\"let item of availableOptions; let index = index; trackBy: trackByFn\">\n <ng-template [ngTemplateOutlet]=\"dropdownItemTpl\" [ngTemplateOutletContext]=\"{ item: item, index: index }\"></ng-template>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n</ng-template>\n\n<!--\u5168\u9009\u6A21\u677F-->\n<ng-template #selectAllTpl>\n <li class=\"devui-dropdown-item\" *ngIf=\"showSelectAll\" (clic