UNPKG

ng-devui

Version:

DevUI components based on Angular

1,209 lines (1,205 loc) 152 kB
import { __decorate, __metadata } from 'tslib'; import * as i3 from '@angular/common'; import { DOCUMENT, CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { Directive, Input, EventEmitter, Component, Inject, HostBinding, Output, ViewChild, ViewChildren, ContentChildren, NgModule } from '@angular/core'; import * as i2 from 'ng-devui/i18n'; import * as i13 from 'ng-devui/tree'; import { TreeModule } from 'ng-devui/tree'; import { DefaultIcons } from 'ng-devui/tree-select'; import * as i1 from 'ng-devui/utils'; import { DefaultDateConverter, WithConfig, PopperModule } from 'ng-devui/utils'; import { intersectionBy, merge, cloneDeep, mergeWith, isEqual } from 'lodash-es'; import { Subject, fromEvent } from 'rxjs'; import { takeUntil, tap, debounceTime } from 'rxjs/operators'; import * as i4 from 'ng-devui/form'; import { FormModule } from 'ng-devui/form'; import * as i5 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import * as i6 from 'ng-devui/dropdown'; import { DropDownModule } from 'ng-devui/dropdown'; import * as i7 from 'ng-devui/text-input'; import { TextInputModule } from 'ng-devui/text-input'; import * as i8 from 'ng-devui/search'; import { SearchModule } from 'ng-devui/search'; import * as i9 from 'ng-devui/tags'; import { TagsModule } from 'ng-devui/tags'; import * as i10 from 'ng-devui/button'; import { ButtonModule } from 'ng-devui/button'; import * as i11 from 'ng-devui/checkbox'; import { CheckBoxModule } from 'ng-devui/checkbox'; import * as i12 from 'ng-devui/input-number'; import { InputNumberModule } from 'ng-devui/input-number'; import * as i14 from 'ng-devui/datepicker-pro'; import { DatepickerProModule } from 'ng-devui/datepicker-pro'; import { LoadingModule } from 'ng-devui/loading'; const COLORS = [ '#f2f5fc', '#e9edfa', '#beccfa', '#96adfa', '#7693f5', '#5e7ce0', '#526ecc', '#465eb8', '#3c51a6', '#344899', '#2a3c85', '#ebf6ff', '#d1ebff', '#b8e0ff', '#9ed5ff', '#85caff', '#6cbfff', '#4ea6e6', '#3590cc', '#207ab3', '#0f6999', '#035880', '#edfff9', '#cffcee', '#acf2dc', '#8be8cb', '#6ddebb', '#50d4ab', '#3ac295', '#27b080', '#169e6c', '#088c58', '#007a45', '#f0ffe6', '#e5ffd4', '#d8fcc0', '#c5f2a7', '#b3e890', '#a6dd82', '#92cc68', '#7eba50', '#6ca83b', '#5e9629', '#518519', '#fffbf0', '#fff1c2', '#ffe794', '#ffdc66', '#ffd138', '#fac20a', '#e3aa00', '#cc9600', '#b58200', '#9e6f00', '#875c00', '#fff3e8', '#ffe1c7', '#ffd0a6', '#ffbf85', '#ffad63', '#fa9841', '#e37d29', '#cc6414', '#b54e04', '#9e3f00', '#873400', '#ffeeed', '#ffd5d4', '#ffbcba', '#ffa4a1', '#ff8b87', '#f66f6a', '#de504e', '#c73636', '#b02121', '#991111', '#820404', '#ffedf3', '#ffd4e3', '#ffbad2', '#ffa1c2', '#fc86b0', '#f3689a', '#db4d83', '#c4356e', '#ad215b', '#96114d', '#800440', '#f5f0ff', '#e7d9ff', '#d8c2ff', '#caabff', '#bc94ff', '#a97af8', '#8a5ce0', '#6f42c9', '#572db3', '#3f1a9c', '#2a0c85', ]; class ContentTemplateDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContentTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: ContentTemplateDirective, selector: "ng-template[field], ng-template[dField]", inputs: { field: "field", dField: "dField" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ContentTemplateDirective, decorators: [{ type: Directive, args: [{ /* eslint-disable */ selector: 'ng-template[field], ng-template[dField]', }] }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { field: [{ type: Input }], dField: [{ type: Input }] } }); class DefaultTemplateDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: DefaultTemplateDirective, selector: "ng-template[type], ng-template[dType]", inputs: { type: "type", dType: "dType" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultTemplateDirective, decorators: [{ type: Directive, args: [{ /* eslint-disable */ selector: 'ng-template[type], ng-template[dType]', }] }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { type: [{ type: Input }], dType: [{ type: Input }] } }); class CategorySearchComponent { static { this.ID_SEED = 0; } get hasGlowStyle() { return this.showGlowStyle; } get showFilterNameClear() { return typeof this.filterName === 'string' && this.filterName.length > 0; } get showExtendedConfig() { return this.operationConfig.show ?? Boolean(this.selectedTags.length || this.searchKey); } constructor(doc, devConfigService, i18n, el) { this.doc = doc; this.devConfigService = devConfigService; this.i18n = i18n; this.el = el; this.category = []; this.defaultSearchField = []; this.selectedTags = []; /** * @deprecated */ this.allowClear = true; /** * @deprecated */ this.allowSave = true; /** * @deprecated */ this.allowShowMore = false; this.disabled = false; this.toggleScrollToTail = false; this.searchKey = ''; this.inputReadOnly = false; this.inputAutofocus = true; this.dropdownBoundary = false; this.showSearchCategory = true; // 配置是否显示搜索相关下拉选项 this.categoryInGroup = false; // 是否按组别显示分类下拉列表 this.textConfig = { keywordName: '', createFilter: '', filterTitle: '', labelConnector: '|', noCategoriesAvailable: '', }; this.filterNameRules = []; this.styleType = 'default'; this.showGlowStyle = true; this.searchEvent = new EventEmitter(); this.selectedTagsChange = new EventEmitter(); this.createFilterEvent = new EventEmitter(); this.clearAllEvent = new EventEmitter(); this.searchKeyChange = new EventEmitter(); this.treeSearchKey = ''; this.filterName = ''; this.searchKeyCache = ''; this.enterSearch = false; this.isShowSavePanel = false; this.isSearchCategory = false; this.isHover = false; this.isFocus = false; this.treeNoRecord = false; this.showNoDataTips = false; this.icons = DefaultIcons; this.destroy$ = new Subject(); this.scrollToTailFlag = true; // 是否在更新标签内容后滚动至输入框的开关 this.DROPDOWN_ANIMATION_TIMEOUT = 200; // 下拉动画延迟 this.DELAY = 300; // 防抖延迟 this.templates = {}; // 所有类型默认模板 this.customTemplates = {}; // 按field标记的自定义模板集合 this.treeFactories = {}; // 按field存储treeSelect类型的treefactory数据 this.joinLabelTypes = ['checkbox', 'label']; this.valueIsArrayTypes = ['dateRange', 'numberRange', 'treeSelect']; this.operationConfig = { clear: { show: true }, save: { show: true }, more: { show: false }, }; this.categoryOrder = []; this.categoryDictionary = {}; this.document = this.doc; this.dateConverter = new DefaultDateConverter(); this.id = CategorySearchComponent.ID_SEED++; this.showSearchConfig = { keyword: true, field: true, category: true, noCategoriesAvailableTip: true }; this.i18n .langChange() .pipe(takeUntil(this.destroy$)) .subscribe((data) => this.setI18nAndFilterText(data)); } ngOnChanges(changes) { const { allowClear, allowSave, allowShowMore, defaultSearchField, disabled, category, extendedConfig, selectedTags, searchKey, showSearchCategory, textConfig, } = changes; const hasExtendChanged = [allowClear, allowSave, allowShowMore, extendedConfig].some((change) => change); if (searchKey) { // searchKey输入变化更新缓存,优先处理searchKey避免初始化时再添加关键字 this.searchKeyCache = this.searchKey; this.setSearchKeyTag(false); } if (selectedTags?.previousValue?.length) { const result = intersectionBy([selectedTags.previousValue], selectedTags.currentValue, 'field'); result.forEach((selectedTag) => { const item = this.category.find((categoryItem) => categoryItem.field === selectedTag.field); if (item?.value.value) { this.resetValue(item); } }); } if (defaultSearchField || category || selectedTags) { this.init(); } if (disabled) { this.setDisabled(); } if (showSearchCategory) { this.setSearchShow(); } if (textConfig) { this.setI18nAndFilterText(this.i18n.getI18nText()); } if (hasExtendChanged) { this.setExtendedConfig(); } } ngAfterViewInit() { // 获取所有默认模板,规避脏检查添加延时 setTimeout(() => this.defaultTemplates.forEach((item) => { this.templates[item.type || item.dType] = item.template; })); if (this.scrollBarContainer && this.inputEle) { // 初始化如果有滚动条直接位移至输入框 this.scrollToTail(true); // 监听滚动事件,在跳转至指定标签后触发其下拉列表 fromEvent(this.scrollBarContainer.nativeElement, 'scroll') .pipe(takeUntil(this.destroy$), tap(() => { // 指定标签在可视范围则展开下拉,不在可视范围,即触发滚动后清除定时的展开下拉动作 if (this.scrollTimeout) { clearTimeout(this.scrollTimeout); this.scrollTimeout = undefined; } // TODO: 无法区分手动和自动滚动,待处理滚动时关闭已打开的下拉 }), // 300毫秒内不再触发滚动事件则展开下拉列表 debounceTime(this.DELAY)) .subscribe((event) => this.openCurrentScrollTagMenu(event)); } } ngAfterContentInit() { this.setCustomTemplate(this.contentTemplates); this.contentTemplates.changes.subscribe((data) => this.setCustomTemplate(data)); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } setCustomTemplate(data) { if (data?.length && this.category) { this.customTemplates = {}; data.forEach((item) => { this.customTemplates[item.field || item.dField] = item.template; }); this.category.forEach((tag) => { tag.customTemplate = this.customTemplates[tag.field]; }); this.selectedTags.forEach((tag) => { tag.customTemplate = this.customTemplates[tag.field]; }); } } setDisabled() { if (this.disabled) { this.searchKey = ''; this.operationConfig.show = false; } else { this.operationConfig.show = undefined; this.setExtendedConfig(); } } setI18nAndFilterText(data) { this.i18nCommonText = data.common; this.i18nCategorySearchText = data.categorySearch; this.textConfig.createFilter = this.textConfig.createFilter || this.i18nCategorySearchText?.saveFilter; this.textConfig.filterTitle = this.textConfig.filterTitle || this.i18nCategorySearchText?.filterTitle; this.showSearchConfig.keywordDescription = this.showSearchCategory.keywordDescription || this.i18nCategorySearchText.getSearchMessage; this.showSearchConfig.fieldDescription = this.showSearchCategory.fieldDescription || this.i18nCategorySearchText.getFindingMessage; this.showSearchConfig.categoryDescription = this.showSearchCategory.categoryDescription || this.i18nCategorySearchText.selectFilterCondition; // 关键字分类内文本不能随语言对象变化,需重新赋值 const keyword = this.selectedTags.find((item) => item.field === 'devuiCategorySearchKeyword'); if (keyword) { keyword.label = this.textConfig.keywordName || this.i18nCategorySearchText.keyword; keyword.title = `${keyword.label}:${keyword.value?.label}`; } } setSearchShow() { const customConfig = typeof this.showSearchCategory === 'boolean' ? { keyword: this.showSearchCategory, field: this.showSearchCategory, category: this.showSearchCategory, noCategoriesAvailableTip: this.showSearchCategory, } : this.showSearchCategory; this.showSearchConfig = { ...this.showSearchConfig, ...customConfig }; } setSearchKeyTag(isSearch = true) { const result = this.searchKey || this.searchKeyCache; if (this.showSearchConfig.keyword) { const existingSearchKeyTag = this.selectedTags.find((tag) => tag.field === 'devuiCategorySearchKeyword'); if (existingSearchKeyTag && !isSearch && this.searchKey === '') { this.removeTag(existingSearchKeyTag); } else if (this.searchKey && this.searchKey !== existingSearchKeyTag?.value.value) { this.createSearchKeyTag(isSearch); } } this.searchKey = ''; if (isSearch) { setTimeout(() => { this.enterSearch = false; }, this.DELAY); } return result; } createSearchKeyTag(isSearch) { const label = this.textConfig.keywordName || this.i18nCategorySearchText.keyword; const searchKeyTag = { options: [], field: 'devuiCategorySearchKeyword', label: label, type: 'keyword', title: `${label}:${this.searchKey}`, value: { label: this.searchKey, value: this.searchKey, cache: this.searchKey, }, }; this.updateSelectedTags(searchKeyTag, isSearch); this.searchKeyCache = this.searchKey; this.searchKey = ''; } setExtendedConfig() { const oldConfig = { clear: { show: this.allowClear }, save: { show: this.allowSave }, more: { show: this.allowShowMore }, }; merge(oldConfig, this.extendedConfig); merge(this.operationConfig, oldConfig); } init() { this.setValue(this.category); this.setValue(this.selectedTags, true); this.initCategoryDisplay(true); if (this.defaultSearchField?.length && this.category?.length) { this.searchField = this.category.filter((item) => this.defaultSearchField.includes(item.field) && !this.valueIsArrayTypes.includes(item.type)); } // 初始化时判断已选中分类中最后一项是否赋值,未赋值则识别为正在处理的分类,优先显示赋值下拉列表 if (this.selectedTags.length) { const [lastItem] = this.selectedTags.slice(-1); const isNull = lastItem.value[lastItem.filterKey || 'label'] === undefined; this.currentSelectTag = isNull && (lastItem.value.value === undefined || (Array.isArray(lastItem.value.value) && lastItem.value.value.length === 0)) ? lastItem : undefined; } if (this.searchKeyCache) { this.searchKey = this.searchKeyCache; this.setSearchKeyTag(false); } } // 初始化tag的value属性:{filterKey | label, value, data} initCategoryItem(item) { const preValue = this.valueIsArrayTypes.includes(item.type) ? { value: [] } : { value: undefined }; preValue[item.filterKey || 'label'] = undefined; if (item.value) { for (const prop in preValue) { if (item.value[prop] === undefined) { item.value[prop] = preValue[prop]; } } } else { item.value = preValue; } item.value.cache = (item.value.value && typeof item.value.value === 'object' && cloneDeep(item.value.value)) || item.value.value; item.customTemplate = this.customTemplates[item.field || item.dField] || item.customTemplate; return item; } mergeCheck(objValue, srcValue, key) { if (key === 'options' && objValue !== srcValue) { return srcValue; } } setValue(data, isSelectedTags = false) { if (Array.isArray(data) && data.length) { data.forEach((item) => { if (!isSelectedTags || !this.category) { item = this.initCategoryItem(item); return; } let result = ''; const originItem = this.category.find((categoryItem) => categoryItem.field === item.field); mergeWith(item, originItem, this.mergeCheck); if (item.value.value) { if (item.type === 'treeSelect') { this.getTreeValue(item, false); return; } item.value.cache = cloneDeep(item.value.value); if (this.joinLabelTypes.includes(item.type)) { result = this.getItemValue(item.value.value, item.filterKey || 'label'); this.isSelectAll(item); } } item.title = this.setTitle(item, item.type, result); if (item.type === 'label' && item.options[0] && !item.options[0].$label) { this.mergeToLabel(item); } }); } } setTitle(tag, type, result) { return this.joinLabelTypes.includes(type) ? `${tag.label}: ${result || ''}` : `${tag.label}: ${result || (tag.value && tag.value[tag.filterKey || 'label']) || ''}`; } initCategoryDisplay(isInit = false) { const selectedTagsField = this.selectedTags.map((item) => item.field); if (isInit) { this.category = this.category || []; this.categoryOrder = []; this.categoryDictionary = {}; this.initGroupAndDictionary(); this.initCategoryOrder(); } this.categoryDisplay = this.categoryOrder.map((item) => { item.isSelected = selectedTagsField.includes(item.field); this.handleGroupLength(item, item.isSelected, isInit); return item; }); this.showNoDataTips = this.showSearchConfig.noCategoriesAvailableTip ? this.categoryDisplay.every((item) => item.isSelected) : false; } initGroupAndDictionary() { this.category.forEach((item) => { if (this.categoryInGroup && item.group) { if (this.categoryDictionary[item.group]) { this.categoryDictionary[item.group].groupLength++; } else { this.categoryDictionary[item.group] = { groupName: item.group, groupLength: 1, children: [] }; } this.categoryDictionary[item.group].children.push(item); } this.categoryDictionary[item.field] = item; }); } initCategoryOrder() { const keys = this.groupOrderConfig || Object.keys(this.categoryDictionary); keys.forEach((key) => { const item = this.categoryDictionary[key]; if (item) { if (this.categoryInGroup) { if (item.groupName) { this.categoryOrder.push(item, ...item.children); } else if (!item.group) { this.categoryOrder.push(item); } } else { this.categoryOrder.push(item); } } }); } handleGroupLength(tag, isSelected, isInit = false) { if (this.categoryInGroup && tag.group) { const group = this.categoryDictionary[tag.group]; const len = group.groupLength; const resInit = isInit ? len : len + 1; group.groupLength = isSelected ? len - 1 : resInit; group.isSelected = group.groupLength === 0; } } insertCategoryToGroup(groupName, groupObj, item, isInsert) { if (!groupName) { return; } groupObj[groupName] = groupObj[groupName] || []; if (isInsert) { groupObj[groupName].push(item); } } searchCategory(item, value) { if (this.valueIsArrayTypes.includes(item.type)) { return; } this.updateFieldValue(item, value || this.searchKey); this.updateSelectedTags(item); this.searchKey = ''; this.enterSearch = false; this.finishChoose(); } searchInputValue(event) { if (event) { event.preventDefault(); event.stopPropagation(); } // 当有分类正在选择时输入关键字不处理 if (!this.currentSelectTag) { this.searchEvent.emit({ selectedTags: this.getSelectedTagsExceptKeyword(), searchKey: this.setSearchKeyTag(), }); } this.isFocus = true; } chooseCategory(item, inputDropdown) { // 点选分组名称不处理 if (item.groupLength !== undefined) { return; } setTimeout(() => { this.currentSelectTag = item; if (this.currentSelectTag.type === 'label') { this.currentSelectTag = this.mergeToLabel(this.currentSelectTag); } this.currentSelectTag.title = this.setTitle(this.currentSelectTag, this.currentSelectTag.type, ''); inputDropdown.isOpen = true; }, this.DROPDOWN_ANIMATION_TIMEOUT); this.updateSelectedTags(item, false); } getSelectedTagsExceptKeyword() { return this.showSearchConfig.keyword ? this.selectedTags.filter((item) => item.field !== 'devuiCategorySearchKeyword') : this.selectedTags; } updateSelectedTags(tag, valueChanged = true) { this.canChange(tag, 'add').then((val) => { if (!val) { return; } const index = this.selectedTags.map((item) => item.field).indexOf(tag.field); if (index > -1) { if (!tag.value.value) { // 通过输入选择分类时避免空值覆盖已选值 merge(tag, this.selectedTags[index]); } this.selectedTags[index] = tag; } else { this.selectedTags.push(tag); } if (valueChanged) { // 只在新增标签时位移滚动条 if (this.scrollToTailFlag) { setTimeout(() => this.scrollToTail()); } this.selectedTagsChange.emit({ selectedTags: this.getSelectedTagsExceptKeyword(), currentChangeTag: tag, operation: 'add', }); this.isSearchCategory = false; } else { this.resolveCategoryDisplay(tag, 'delete'); } }); } // 判断滚动条是否存在,如果存在自动滚动到末尾的输入框 scrollToTail(isInit) { const dom = this.scrollBarContainer.nativeElement; if (this.toggleScrollToTail && dom.scrollWidth > dom.clientWidth) { if (isInit) { dom.scrollLeft = dom.scrollWidth - dom.clientWidth; } else { this.inputEle.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }); } } else if (!isInit && this.inputAutofocus) { // 初始化不聚焦,避免展开下拉 this.inputEle.nativeElement.focus(); } } // 滚动至指定的tag scrollToTag(index, event) { const tags = this.scrollBarContainer?.nativeElement.querySelectorAll('ul.devui-category-search-line>li'); if (tags[index]) { this.currentScrollTagIndex = index; tags[index].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }); this.scrollTimeout = setTimeout(() => this.openCurrentScrollTagMenu(event), this.DELAY); } } openCurrentScrollTagMenu(event) { if (this.currentScrollTagIndex !== undefined) { const dropdownArr = this.selectedDropdownList.toArray(); this.openMenu(dropdownArr[this.currentScrollTagIndex], event); this.currentScrollTagIndex = undefined; } } updateFieldValue(field, value) { const result = {}; const filterKey = field.filterKey || 'label'; const colorKey = field.colorKey || 'color'; result[filterKey] = value; if (field.type === 'radio') { field.value.value = { [filterKey]: value }; } if (field.type === 'textInput') { field.value.value = value; } if (field.type === 'label') { if (field.options[0] && !field.options[0].$label) { this.mergeToLabel(field); } result[colorKey] = COLORS[Math.floor(COLORS.length * Math.random())]; result.$label = `${value}_${result[colorKey]}`; } if (this.joinLabelTypes.includes(field.type)) { field.value.value = [result]; } field.value[filterKey] = value; field.value.cache = cloneDeep(field.value.value); field.title = this.setTitle(field, field.type, value); } removeTag(tag, event) { this.canChange(tag, 'delete').then((val) => { if (!val) { if (this.beforeTagChange && event) { event.stopPropagation(); } return; } tag = this.resetValue(tag); this.selectedTags = this.selectedTags.filter((item) => item.field !== tag.field); const result = this.getSelectedTagsExceptKeyword(); if (tag.type === 'keyword') { this.searchKey = this.searchKey === this.searchKeyCache ? '' : this.searchKey; this.searchKeyCache = ''; this.enterSearch = this.searchKey !== ''; this.searchEvent.emit({ selectedTags: result, searchKey: this.searchKey }); } else { if (tag.type === 'treeSelect') { this.treeFactories[tag.field] = undefined; } this.resolveCategoryDisplay(tag, 'add'); this.selectedTagsChange.emit({ selectedTags: result, currentChangeTag: tag, operation: 'delete' }); } this.currentSelectTag = undefined; }); } checkInputSearching() { this.isFocus = true; if (this.searchKey === '') { this.enterSearch = false; } else { this.enterSearch = true; } } // 解决dropdownMenu展开时焦点会自动到toggle上的问题 focusInput() { setTimeout(() => { if (this.inputAutofocus) { this.inputEle.nativeElement.focus(); } }); } // 失焦时关闭当前下拉列表,延时用以防止关闭掉点击分类展开的内容列表 blurInput(event) { this.blurTimer = setTimeout(() => { const className = event.relatedTarget && event.relatedTarget.className; const outsideHost = !className || (typeof className === 'string' && className.indexOf(`devui-category-dropdown-menu-${this.id}`) === -1); if (!this.currentSelectTag && this.currentOpenDropdown && outsideHost) { this.currentOpenDropdown.isOpen = false; } }, this.DELAY); } openMenu(inputDropdown, event) { const code = event.code; if (inputDropdown.isOpen || (event.type === 'keyup' && ['Enter', 'Backspace', 'Escape'].includes(code))) { return; } setTimeout(() => { if (!inputDropdown.isOpen) { inputDropdown.toggle(); } }); } closeMenu(inputDropdown) { if (inputDropdown.isOpen) { inputDropdown.toggle(); } } backspaceEvent(inputDropdown) { if (this.searchKey) { return; } if (this.currentSelectTag) { this.currentSelectTag = undefined; this.closeMenu(inputDropdown); return; } if (this.selectedTags.length) { const tag = this.selectedTags[this.selectedTags.length - 1]; this.removeTag(tag); } this.closeMenu(inputDropdown); } canChange(tag, operation) { let changeResult = Promise.resolve(true); if (this.beforeTagChange) { const result = this.beforeTagChange(tag, this.searchKey, operation); if (typeof result !== 'undefined') { if (result.then) { changeResult = result; } else if (result.subscribe) { changeResult = result.toPromise(); } else { changeResult = Promise.resolve(result); } } } return changeResult; } finishChoose() { this.currentSelectTag = undefined; if (this.inputAutofocus) { this.inputEle.nativeElement.focus(); } } clearFilter(event) { if (this.selectedTags.length) { this.selectedTags = this.selectedTags.filter((item) => { if (item.deletable === false) { return item; } this.resetValue(item); return undefined; }); } if (this.searchKey || this.searchKeyCache) { this.searchKey = ''; this.searchKeyCache = ''; setTimeout(() => { this.enterSearch = false; }, this.DELAY); } if (this.currentSelectTag) { this.currentSelectTag = undefined; } this.selectedTagsChange.emit({ selectedTags: this.selectedTags, currentChangeTag: undefined, operation: 'clear' }); this.clearAllEvent.emit(event); this.initCategoryDisplay(); } resolveCategoryDisplay(tag, type) { if (tag.field === 'devuiCategorySearchKeyword' || !this.categoryDictionary[tag.field]) { return; } this.handleGroupLength(tag, type === 'delete'); this.categoryDictionary[tag.field].isSelected = type === 'delete'; } createFilterFn() { this.createFilterEvent.emit({ name: this.filterName, selectedTags: this.getSelectedTagsExceptKeyword(), keyword: this.searchKey }); setTimeout(() => { this.filterName = ''; }, this.DELAY); } createFilterInputAutoFocus(dropdown, inputElm, filterNameForm) { if (dropdown.isOpen) { filterNameForm.form.reset(); setTimeout(() => inputElm.focus()); } } searchKeyChangeEvent(event) { this.searchKey = event; this.enterSearch = !!event; this.currentSearchCategory = event ? this.category.filter((item) => item.label.toLowerCase().includes(event.toLowerCase())) : []; this.searchKeyChange.emit(event); } checkType(tag) { return tag && tag.type === 'radio' ? 'all' : 'blank'; } resetValue(tag) { tag.value = this.valueIsArrayTypes.includes(tag.type) ? { value: [] } : { value: undefined }; tag.value[tag.filterKey || 'label'] = undefined; return tag; } afterDropdownClosed() { setTimeout(() => { this.currentSelectTag = undefined; }, this.DROPDOWN_ANIMATION_TIMEOUT + 100); } /* 各类型模板调用方法 */ // radio 单选 处理选中项方法 chooseItem(tag, chooseItem) { this.afterDropdownClosed(); const key = tag.filterKey || 'label'; tag.value = { value: chooseItem, cache: cloneDeep(chooseItem) }; tag.value[key] = chooseItem[key]; tag.title = this.setTitle(tag, 'radio'); this.updateSelectedTags(tag); } // date 改变输入值相应变化 dateChange(datepicker, tag) { const index = datepicker.currentActiveInput === 'start' ? 0 : 1; const dateFormat = tag.showTime ? 'y/MM/dd HH:mm:ss' : 'y/MM/dd'; const targetDate = datepicker.dateValue[index]; const inputDate = datepicker.datepickerConvert.parse(targetDate); const valueFormat = inputDate && !isNaN(inputDate.getTime()) && datepicker.datepickerConvert.format(inputDate, dateFormat); if (targetDate === valueFormat) { tag.value.value[index] = inputDate; if (tag.showTime) { datepicker.pickerSrv.updateTimeChange.next({ activeInput: datepicker.currentActiveInput, hour: inputDate.getHours(), min: inputDate.getMinutes(), seconds: inputDate.getSeconds(), }); } } } confirmDate(datepicker, tag, dropdown) { if (!tag.value.value) { return; } if (datepicker.currentActiveInput === 'start') { datepicker.focusChange('end'); } else if (!tag.value.value[0]) { datepicker.focusChange('start'); } else if (tag.value.value.length > 1) { this.closeMenu(dropdown); this.afterDropdownClosed(); const start = tag.value.value[0]; const end = tag.value.value[1]; tag.value.cache = cloneDeep(tag.value.value); tag.value[tag.filterKey || 'label'] = tag.showTime ? `${this.dateConverter.formatDateTime(start)} - ${this.dateConverter.formatDateTime(end)}` : `${this.dateConverter.format(start)} - ${this.dateConverter.format(end)}`; tag.title = this.setTitle(tag, 'dateRange'); this.updateSelectedTags(tag); } } // checkbox | label 多选 处理选中项方法 chooseItems(tag) { this.afterDropdownClosed(); const key = tag.filterKey || 'label'; const result = this.getItemValue(tag.value.value, key); if (result) { tag.title = this.setTitle(tag, 'checkbox', result); tag.value[key] = result; tag.value.cache = cloneDeep(tag.value.value); this.updateSelectedTags(tag); } else { this.removeTag(tag); } } // checkbox | label 将选中项对应filterKey的值合并的方法 getItemValue(value, key) { if (value && Array.isArray(value)) { const result = value.map((item) => item[key]); return result.join(' | '); } } checkDropdownBoundary(dropdown, dropdownMenu) { if (this.dropdownBoundary && dropdownMenu) { const tagRect = dropdown.el.nativeElement.getBoundingClientRect(); const hostRect = this.el.nativeElement.getBoundingClientRect(); if (tagRect.left < hostRect.left) { dropdownMenu.overlay.offsetX = hostRect.left - tagRect.left; dropdownMenu.overlay.overlayRef.updatePosition(); } else if (tagRect.left + tagRect.width > hostRect.left + hostRect.width) { dropdownMenu.overlay.offsetX = 0; dropdownMenu.overlay.overlayRef.updatePosition(); } } } // checkbox | label 当下拉菜单展开重置多选的选中状态 resetContent(dropdown, tag, dropdownMenu) { const tagItem = tag || this.currentSelectTag || { type: '', options: [] }; if (this.blurTimer) { clearTimeout(this.blurTimer); } if (this.toggleEvent) { this.toggleEvent(dropdown, tag, this.currentSelectTag); } if (dropdown.isOpen) { this.checkDropdownBoundary(dropdown, dropdownMenu); this.currentOpenDropdown = dropdown; } else { const item = tagItem; this.clearCurrentSelectTagFromSearch(); this.currentOpenDropdown = undefined; this.showNoDataTips = false; delete item.allChecked; delete item.halfChecked; return; } if (tag && tag !== this.currentSelectTag) { this.scrollToTailFlag = false; if (!isEqual(tag.value.value, tag.value.cache)) { tag.value.value = cloneDeep(tag.value.cache); } this.handleAccordingType(tag, dropdown, false); } else if (this.currentSelectTag) { this.scrollToTailFlag = true; // checkbox 值不是必须数组,但重置时需空数组才能触发,undefined 不行 this.currentSelectTag.value.value = ['checkbox', ...this.valueIsArrayTypes].includes(this.currentSelectTag.type) ? [] : undefined; this.handleAccordingType(this.currentSelectTag, dropdown, true); } this.showNoDataTips = this.showSearchConfig.noCategoriesAvailableTip ? this.categoryDisplay.every((item) => item.isSelected) : false; } cacheTreeData(tag) { if (tag.type === 'treeSelect') { // 下拉关闭时存储当前分类树的treeFactory this.treeFactories[tag.field] = this.treeInstance.treeFactory; } } showCurrentSearchCategory(tag, inputDropdown) { this.isSearchCategory = true; this.searchKey = ''; this.closeMenu(inputDropdown); this.chooseCategory(tag, inputDropdown); setTimeout(() => this.checkInputSearching(), this.DELAY); } clearCurrentSelectTagFromSearch() { if (this.currentSelectTag && this.isSearchCategory) { this.isSearchCategory = false; setTimeout(() => this.finishChoose(), this.DELAY); } } handleAccordingType(tag, dropdown, isCurrentSelectTag) { switch (tag.type) { case 'keyword': this.searchKey = this.searchKeyCache; this.searchKeyChangeEvent(this.searchKey); this.inputEle.nativeElement.focus(); dropdown.isOpen = false; break; case 'treeSelect': // 新选分类时使用原始tree数据,已选分类时使用空数据,待渲染后用存储数据覆盖 if (!isCurrentSelectTag) { setTimeout(() => { // 下拉展开将存储的数据合并覆盖当前树实例,刷新当前树显示。 merge(this.treeInstance.treeFactory, this.treeFactories[tag.field]); this.treeSearch(''); }); } setTimeout(() => { const dom = tag.searchable && dropdown.menuEl.nativeElement.querySelector('.devui-search-container .devui-search>input'); if (dom) { dom.focus(); } }, this.DROPDOWN_ANIMATION_TIMEOUT); break; case 'textInput': setTimeout(() => { const inputDom = this.document.querySelector('.devui-category-search-type-text-input'); if (inputDom) { inputDom.focus(); } }, this.DROPDOWN_ANIMATION_TIMEOUT); break; case 'checkbox': case 'label': if (tag.showSelectAll) { this.isSelectAll(tag, false); } break; default: } } // label 合并名称和颜色字段赋给tag,待[tag]支持传入对象后可移除 mergeToLabel(obj) { if (obj?.options && Array.isArray(obj.options)) { obj.options.forEach((item) => { item.$label = `${item[obj.filterKey || 'label']}_${item[obj.colorKey || 'color']}`; }); } return obj; } // label 拆分名称和颜色用于下拉选项显示 splitLabel(key, value) { // 初始化选中类别生成标签时,value为label的对象,不需要对值进行操作 if (typeof value !== 'string') { return; } const res = value && value.split('_'); const obj = res && { label: res[0], color: res[1] }; return obj && obj[key]; } selectAll(tag) { tag.halfChecked = false; if (tag.allChecked) { tag.value.value = cloneDeep(tag.options); } else { tag.value.value = []; } } isSelectAll(tag, isValue = true) { const value = isValue ? tag.value.value : tag.value.cache; tag.allChecked = isEqual(value, tag.options); tag.halfChecked = !tag.allChecked && !!value.length; } // textInput 文本输入框 处理选中项方法 getTextInputValue(tag) { this.afterDropdownClosed(); tag.value[tag.filterKey || 'label'] = tag.value.cache = tag.value.value; tag.title = this.setTitle(tag, 'textInput'); this.updateSelectedTags(tag); } // numberRange 数字范围 处理选中项方法 getNumberRangeValue(tag, dropdown) { const startNum = tag.value.value[0] || 0; const endNum = tag.value.value[1] || 0; if (tag.validateFunc) { if (!tag.validateFunc(startNum, endNum, tag)) { return; } if (dropdown) { this.closeMenu(dropdown); } } this.afterDropdownClosed(); tag.value.value = [startNum, endNum]; tag.value.cache = [startNum, endNum]; tag.value[tag.filterKey || 'label'] = `${startNum} - ${endNum}`; tag.title = this.setTitle(tag, 'numberRange'); this.updateSelectedTags(tag); } // treeSelect 树 处理选中项方法 getTreeValue(tag, isConfirm = true) { if (isConfirm) { this.afterDropdownClosed(); } const result = []; const selectedIds = []; const data = tag.value.value; data.forEach((item) => { result.push(item[tag.filterKey || tag.treeNodeTitleKey || 'title']); selectedIds.push(item[tag.treeNodeIdKey || 'id']); }); if (result.length) { tag.value.cache = cloneDeep(tag.value.value); tag.value[tag.filterKey || 'label'] = result.join(','); tag.title = this.setTitle(tag, 'treeSelect'); if (isConfirm) { this.cacheTreeData(tag); this.updateSelectedTags(tag); } else { tag.options = this.initTreeChecked(tag, tag.options, selectedIds); } } else { this.removeTag(tag); } } initTreeChecked(tag, tree, selectedIds) { if (Array.isArray(tree)) { return tree.map((item) => this.initTreeChecked(tag, item, selectedIds)); } else if (tree) { const idKey = tag.treeNodeIdKey || 'id'; const childKey = tag.treeNodeChildrenKey || 'children'; if (Array.isArray(tree[childKey]) && ['upward', 'both', undefined].includes(tag.checkableRelation)) { let checkedChild = 0; tree[childKey] = this.initTreeChecked(tag, tree[childKey], selectedIds); tree[childKey].forEach((child) => child.isChecked && checkedChild++); tree.halfChecked = checkedChild > 0 && checkedChild < tree[childKey].length; tree.isChecked = checkedChild > 0 && !tree.halfChecked; return tree; } delete tree.halfChecked; tree.isChecked = selectedIds.includes(tree[idKey]); return tree; } } onOperableNodeChecked(selectedNodes, tag) { const selectedValueExtractor = (nodes) => { return tag.leafOnly ? nodes.filter((node) => !node.data.isParent).map((node) => node.data.originItem) : nodes.map((node) => node.data.originItem); }; if (tag.multiple) { tag.value.value = selectedValueExtractor(selectedNodes); } } onOperableNodeSelected(selectedNode, tag) { if (tag.multiple || (tag.leafOnly && selectedNode.data.isParent)) { return; } if (selectedNode.data.isActive) { tag.value.value = [selectedNode.data.originItem]; this.getTreeValue(tag); } else { selectedNode.data.isActive = true; } } treeSearch(value, searchFn) { this.treeSearchKey = value; if (this.treeInstance) { const searchRes = searchFn ? searchFn(this.treeSearchKey, this.treeInstance) : this.treeInstance.treeFactory.searchTree(this.treeSearchKey, true); if (typeof searchRes === 'boolean') { this.treeNoRecord = searchRes; } else if (Array.isArray(searchRes)) { this.treeNoRecord = searchRes.every((res) => !res); } this.treeInstance.treeFactory.getFlattenNodes(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CategorySearchComponent, deps: [{ token: DOCUMENT }, { token: i1.DevConfigService }, { token: i2.I18nService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CategorySearchComponent, selector: "d-category-search", inputs: { category: "category", defaultSearchField: "defaultSearchField", selectedTags: "selectedTags", allowClear: "allowClear", allowSave: "allowSave", allowShowMore: "allowShowMore", disabled: "disabled", extendedConfig: "extendedConfig", toggleScrollToTail: "toggleScrollToTail", searchKey: "searchKey", placeholderText: "placeholderText", inputReadOnly: "inputReadOnly", inputAutofocus: "inputAutofocus", dropdownBoundary: "dropdownBoundary", showSearchCategory: "showSearchCategory", categoryInGroup: "categoryInGroup", groupOrderConfig: "groupOrderConfig", customGroupNameTemplate: "customGroupNameTemplate", customCategoryNameTemplate: "customCategoryNameTemplate", tagMaxWidth: "tagMaxWidth", textConfig: "textConfig", filterNameRules: "filterNameRules", beforeTagChange: "beforeTagChange", toggleEvent: "toggleEvent", styleType: "styleType", showGlowStyle: "showGlowStyle" }, outputs: { searchEvent: "searchEvent", selectedTagsChange: "selectedTagsChange", createFilterEvent: "createFilterEvent", clearAllEvent: "clearAllEvent", searchKeyChange: "searchKeyChange" }, host: { properties: { "class.devui-glow-style": "this.hasGlowStyle" } }, queries: [{ propertyName: "contentTemplates", predicate: ContentTemplateDirective }], viewQueries: [{ propertyName: "inputEle", first: true, predicate: ["InputEle"], descendants: true, static: true }, { propertyName: "scrollBarContainer", first: true, predicate: ["ScrollBarContainer"], descendants: true, static: true }, { propertyName: "primeContainer", first: true, predicate: ["PrimeContainer"], descendants: true, static: true }, { propertyName: "treeInstance", first: true, predicate: ["OperableTree"], descendants: true }, { propertyName: "selectedDropdownList", predicate: ["selectedDropdown"], descendants: true }, { propertyName: "defaultTemplates", predicate: DefaultTemplateDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\r\n #PrimeContainer\r\n class=\"devui-category-search-container devui-category-search-id-{{ id }}\"\r\n [ngClass]=\"{\r\n 'container-hover': isHover && !isFocus,\r\n 'container-active': inputDropdown.isOpen,\r\n 'devui-gray-style': styleType === 'gray',\r\n disabled: disabled\r\n }\"\r\n (mouseenter)=\"isHover = true\"\r\n (mouseleave)=\"isHover = false\"\r\n>\r\n <div class=\"devui-category-search-icon\" (click)=\"searchInputValue()\">\r\n <svg\r\n class=\"svg-icon-search\"\r\n width=\"16px\"\r\n height=\"16px\"\r\n viewBox=\"0 0 16 16\"\r\n version=\"1.1\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\r\n >\r\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\r\n <path\r\n d=\"M7.71295742,1.53283795 C11.0266659,1.53283795 13.7129574,4.21912945 13.7129574,7.53283795 C13.7129574,9.0690006 13.1356609,10.4703284 12.1861834,11.5317059 L14.5333041,13.8784875 C14.7285663,14.0737497 14.7285663,14.3903321 14.5333041,14.5855943 C14.3597378,14.7591606 14.0903134,14.7784458 13.8954453,14.6434497 L13.8261974,14.5855943 L11.4604434,12.2188804 C10.4336319,13.0411023 9.13072017,13.5328379 7.71295742,13.5328379 C4.39924893,13.5328379 1.71295742,10.8465464 1.71295742,7.53283795 C1.71295742,4.21912945 4.39924893,1.53283795 7.71295742,1.53283795 Z M7.71295742,2.53283795 C4.95153368,2.53283795 2.71295742,4.7714142 2.71295742,7.53283795 C2.71295742,10.2942617 4.95153368,12.5328379 7.71295742,12.5328379 C10.4743812,12.5328379 12.7129574,10.2942617 12.712957