ng-devui
Version:
DevUI components based on Angular
1,209 lines (1,205 loc) • 152 kB
JavaScript
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