UNPKG

ng-devui

Version:

DevUI components based on Angular

797 lines (794 loc) 103 kB
import * as i0 from '@angular/core'; import { Injectable, Component, Input, HostListener, EventEmitter, forwardRef, ViewChild, Output, HostBinding, NgModule } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil, debounceTime, distinctUntilChanged, filter } from 'rxjs/operators'; import { cloneDeep } from 'lodash-es'; import * as i4 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i5 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import * as i4$1 from 'ng-devui/checkbox'; import { CheckBoxModule } from 'ng-devui/checkbox'; import { __decorate, __metadata } from 'tslib'; import * as i6 from 'ng-devui/dropdown'; import { DropDownAppendToBodyComponent, DropDownModule } from 'ng-devui/dropdown'; import * as i2 from 'ng-devui/i18n'; import * as i3 from 'ng-devui/utils'; import { WithConfig } from 'ng-devui/utils'; import * as i7 from 'ng-devui/tags'; import { TagsModule } from 'ng-devui/tags'; import { SelectModule } from 'ng-devui/select'; class CascaderService { constructor() { this._currentValue = []; this.multipleValue = []; this.columnList = []; this.searchResultList = []; this.canSelectParent = false; this.isMultiple = false; this.isLazyLoad = false; this.lazyloadCache = {}; this.checkboxRelation = { upward: true, downward: true }; this.closeMianDropdown = new Subject(); this.currentValueChange = new Subject(); this.resetStatus = new Subject(); this.openDrawer = new Subject(); this.updateShowText = new Subject(); this.updateTagList = new Subject(); } set currentValue(value) { this._currentValue = value.filter((item) => item !== undefined); this.currentValueChange.next(this._currentValue); } get currentValue() { return this._currentValue; } set currentMultipleValue(value) { this.resetNodeStatus(); this.multipleValue = value; } get currentMultipleValue() { this.multipleValue = []; this.getMultipleValue([], this.options); return this.multipleValue; } initOptions(options) { this.columnList = []; this.options = cloneDeep(options); // 标记根节点 this.options.forEach((t) => { t.isRoot = true; }); this.columnList.push(this.options); } openColumn(option, colIndex, islazyLoad, reload = false) { this.clearTargetActive(this.columnList[colIndex].find((t) => t.active)); option.active = true; this.columnList.splice(colIndex + 1); if (option.children && option.children.length) { this.columnList.push(option.children); this.openDrawer.next(); } else if (islazyLoad) { option._loading = true; const fn = this.loadChildrenFn(option); if (fn.then) { fn.then((res) => { this.columnList.splice(colIndex + 1); // 防止多个同时懒加载,造成多余的添加 option.children = res || []; option._loading = false; this.columnList.push(res || []); this.openDrawer.next(); if (this.isMultiple) { this.updateOptionCheckedStatus(option.value, option.checked, true, true, !reload); } if (reload) { if (!this.isMultiple) { this.updateOptionByValue(); } this.updateShowText.next(); } }); } else { fn.subscribe((res) => { this.columnList.splice(colIndex + 1); // 防止多个同时懒加载,造成多余的添加 option.children = res || []; option._loading = false; this.columnList.push(res || []); this.openDrawer.next(); if (this.isMultiple) { this.updateOptionCheckedStatus(option.value, option.checked, true, true, !reload); } if (reload) { if (!this.isMultiple) { this.updateOptionByValue(); } this.updateShowText.next(); } }); } } } clearTargetActive(option) { if (!option) { return; } option.active = false; if (option.children) { this.clearTargetActive(option.children.find((t) => t.active)); } } setCurrentValue() { this.currentValue = this.columnList.map((listItem) => listItem.find((optionItem) => optionItem.active)?.value); } updateOptionByValue() { this.resetNodeStatus(); this.columnList = [this.options]; for (let index = 0; index < this.currentValue.length; index++) { const target = this.columnList[index]?.find((listItem) => listItem.value === this.currentValue[index]); if (target) { target.active = true; if (target.children && target.children.length) { // 有子菜单展开子菜单 this.columnList.push(target.children); } else if (this.isLazyLoad) { // 懒加载没有子菜单的情况下,非叶子节点执行展开,叶子节点执行选中 this.openColumn(target, index, !target.isLeaf, !target.isLeaf); break; } } else { break; } } } lazyloadMultipleChild(target, index) { if (!this.lazyloadCache[target.value]) { this.lazyloadCache[target.value] = true; this.openColumn(target, index, true, true); } } resetNodeStatus(option = this.options) { option.forEach((item) => { item.active = false; item.checked = false; item.halfChecked = false; if (item.children) { this.resetNodeStatus(item.children); } }); } // 在多选模式下,更新节点树的checked状态 updateOptionCheckedStatus(targetValue, checked, upward = true, downward = true, isEmit = true) { let targetNode = this.options.find((t) => t.value === targetValue); // 当主下拉列表包含了目标,即目标无父节点 if (targetNode) { targetNode.checked = checked; targetNode.halfChecked = false; if (targetNode.children && downward) { this.updateChildrenChecked(targetNode, checked, isEmit); } } else { // 当存在父节点时,需要检查同级节点来确定父节点状态 const parentNode = this.getParentNode(targetValue); targetNode = parentNode.children.find((t) => t.value === targetValue); targetNode.checked = checked; targetNode.halfChecked = false; if (upward) { this.updateParentChecked(parentNode, isEmit); } if (targetNode.children && downward) { this.updateChildrenChecked(targetNode, checked, isEmit); } } } // 子节点按父节点状态更新 updateChildrenChecked(node, checked, isEmit) { let hasDisable = false; node.children.forEach((child) => { if (!child.disabled) { child.checked = checked; child.halfChecked = false; if (child.children && child.children.length) { if (this.canSelectParent) { this.updateTagList.next({ isAdd: checked, option: child, isEmit, }); } this.updateChildrenChecked(child, checked, isEmit); } else { this.updateTagList.next({ isAdd: checked, option: child, isEmit, }); } } else { hasDisable = true; } }); if (hasDisable && !this.canSelectParent) { this.updateParentChecked(node, isEmit); } } // 父节点按所有子节点状态更新 updateParentChecked(node, isEmit) { const checkedChild = node.children.find((t) => t.checked); const halfcheckedChild = node.children.find((t) => t.halfChecked); const uncheckedChild = node.children.find((t) => !t.halfChecked && !t.checked); if (halfcheckedChild || (checkedChild && uncheckedChild)) { node.checked = false; node.halfChecked = true; } else if (!checkedChild && !halfcheckedChild) { node.checked = false; node.halfChecked = false; } else { node.checked = true; node.halfChecked = false; } if (this.canSelectParent) { this.updateTagList.next({ isAdd: node.checked, option: node, isEmit, }); } // 如果此节点非根节点,则继续找它的父节点进行更新 if (!node.isRoot) { this.updateParentChecked(this.getParentNode(node.value), isEmit); } } // 获取父节点 getParentNode(childValue) { const queue = [...this.options]; let cur; while (queue.length) { cur = queue.shift(); if (cur.children && cur.children.find((t) => t.value === childValue)) { break; } else if (cur.children) { queue.push(...cur.children); } } return cur; } getMultipleValue(value, option) { const isNoRelation = !this.checkboxRelation.downward || !this.checkboxRelation.upward; option.forEach((item) => { const _value = [...value]; _value.push(item.value); if (item.children && item.children.length && (item.checked || item.halfChecked)) { this.getMultipleValue(_value, item.children); if (isNoRelation) { this.multipleValue.push(_value); } } else if (item.checked) { this.multipleValue.push(_value); } else if (isNoRelation && item.children?.length) { this.getMultipleValue(_value, item.children); } }); } closeAllDropdown() { this.closeMianDropdown.next(); } // 搜索功能 searchByString(str, currentlabel, currentValue = [], list = this.options) { list.forEach((item) => { const label = currentlabel ? currentlabel + ' / ' + item.label : item.label; const valueList = [...currentValue, item.value]; if (item.children && item.children.length) { this.searchByString(str, label, valueList, item.children); } else { if (!item.disabled && label.toLowerCase().indexOf(str.toLowerCase()) !== -1) { this.searchResultList.push({ label, valueList, checked: item.checked, }); } } }); } ngOnDestroy() { this.closeMianDropdown.complete(); this.currentValueChange.complete(); this.updateTagList.complete(); this.resetStatus.complete(); this.openDrawer.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CascaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CascaderService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CascaderService, decorators: [{ type: Injectable }] }); class CascaderLiComponent { onClick(event) { event.stopPropagation(); } onMouseEnter(event) { if (this.trigger === 'hover') { if (this.option.disabled) { return; } this.cascaderSrv.openColumn(this.option, this.colIndex, false); } } constructor(cascaderSrv, cdr) { this.cascaderSrv = cascaderSrv; this.cdr = cdr; this.width = 200; this.trigger = 'hover'; this.multiple = false; this.canSelectParent = false; this.checkboxRelation = { upward: true, downward: true }; this.unsubscribe$ = new Subject(); } ngOnInit() { if (this.isLazyLoad) { this.isLeaf = !!this.option.isLeaf; } else { // 当不是懒加载时可以通过children是否为空来判断是否为叶子节点 this.isLeaf = this.option.isLeaf || !(this.option.children && this.option.children.length); } this.initObserable(); } initObserable() { this.cascaderSrv.resetStatus.pipe(takeUntil(this.unsubscribe$)).subscribe(res => { this.selected = false; this.halfCheck = false; this.active = false; }); } clickLeaf() { if (!this.option.disabled && !this.multiple) { this.cascaderSrv.openColumn(this.option, this.colIndex, false); this.option.active = true; this.cascaderSrv.setCurrentValue(); this.cascaderSrv.closeAllDropdown(); } } clickItem() { if (this.option.disabled) { return; } this.cascaderSrv.openColumn(this.option, this.colIndex, this.isLazyLoad); if (this.canSelectParent && !this.multiple) { this.cascaderSrv.setCurrentValue(); } } clickCheckbox(event) { if (this.option.disabled) { return; } event.stopPropagation(); // 如果是半选状态,更新为false,其他状态则更新为与checked相反 const status = !!this.option.halfChecked; this.cascaderSrv.updateOptionCheckedStatus(this.option.value, this.option.halfChecked ? false : !this.option.checked, this.checkboxRelation.upward, this.checkboxRelation.downward); if (status) { this.option.halfChecked = false; } if (this.isLeaf || this.canSelectParent) { this.updateValue(this.option.checked); } } // 阻止checkbox本身的点击事件变化 avoidCheckboxChange() { return false; } updateValue(checked) { this.cascaderSrv.updateTagList.next({ isAdd: checked, option: this.option, isEmit: true }); } ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CascaderLiComponent, deps: [{ token: CascaderService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CascaderLiComponent, selector: "d-cascader-li", inputs: { width: "width", trigger: "trigger", option: "option", multiple: "multiple", canSelectParent: "canSelectParent", colIndex: "colIndex", dropDownItemTemplate: "dropDownItemTemplate", isLazyLoad: "isLazyLoad", checkboxRelation: "checkboxRelation" }, host: { listeners: { "click": "onClick($event)", "mouseenter": "onMouseEnter($event)" } }, ngImport: i0, template: "<li *ngIf=\"!isLeaf\" role=\"menuitem\" [ngClass]=\"{ disabled: option.disabled, 'devui-leaf-active': option.active }\" (click)=\"clickItem()\">\n <div class=\"devui-dropdown-item\">\n <d-checkbox\n *ngIf=\"multiple\"\n class=\"devui-dropdown-item-checkbox\"\n [disabled]=\"option.disabled\"\n [isShowTitle]=\"false\"\n [halfchecked]=\"option.halfChecked\"\n (click)=\"clickCheckbox($event)\"\n [ngModel]=\"option.checked\"\n [beforeChange]=\"avoidCheckboxChange\"\n ></d-checkbox>\n <i *ngIf=\"option.icon\" class=\"icon {{ option.icon }}\"></i>\n <span class=\"dropdown-item-label\">\n <ng-template\n [ngTemplateOutlet]=\"dropDownItemTemplate || defaultDropdownItemTemplate\"\n [ngTemplateOutletContext]=\"{\n label: option.label,\n option: option\n }\"\n >\n </ng-template>\n </span>\n <svg\n *ngIf=\"!option._loading\"\n style=\"float: right\"\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n class=\"icon devui-cascader-icon-right\"\n >\n <g id=\"icon-icon-chevron-right-2\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <g\n id=\"ipd_right\"\n transform=\"translate(8.000000, 8.000000) scale(-1, 1) rotate(-270.000000) translate(-8.000000, -8.000000) translate(3.000000, 5.500000)\"\n fill=\"#71757F\"\n fill-rule=\"nonzero\"\n >\n <path\n d=\"M-0.353553391,-0.353553391 C-0.179987039,-0.527119742 0.0894373624,-0.546404893 0.284305503,-0.411408841 L0.353553391,-0.353553391 L4.999,4.293 L9.64644661,-0.353553391 C9.82001296,-0.527119742 10.0894374,-0.546404893 10.2843055,-0.411408841 L10.3535534,-0.353553391 C10.5271197,-0.179987039 10.5464049,0.0894373624 10.4114088,0.284305503 L10.3535534,0.353553391 L5.35355339,5.35355339 C5.17998704,5.52711974 4.91056264,5.54640489 4.7156945,5.41140884 L4.64644661,5.35355339 L-0.353553391,0.353553391 C-0.548815536,0.158291245 -0.548815536,-0.158291245 -0.353553391,-0.353553391 Z\"\n id=\"\u8DEF\u5F84\"\n ></path>\n </g>\n </g>\n </svg>\n <svg width=\"16px\" height=\"16px\" viewBox=\"0 0 16 16\" style=\"float: right\" class=\"icon devui-cascader-loading\" *ngIf=\"option._loading\">\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <path\n d=\"M8,1 C8.55228475,1 9,1.44771525 9,2 L9,4 C9,4.55228475 8.55228475,5 8,5 C7.44771525,5 7,4.55228475 7,4 L7,2 C7,1.44771525 7.44771525,1 8,1 Z M8,11 C8.55228475,11 9,11.4477153 9,12 L9,14 C9,14.5522847 8.55228475,15 8,15 C7.44771525,15 7,14.5522847 7,14 L7,12 C7,11.4477153 7.44771525,11 8,11 Z M15,8 C15,8.55228475 14.5522847,9 14,9 L12,9 C11.4477153,9 11,8.55228475 11,8 C11,7.44771525 11.4477153,7 12,7 L14,7 C14.5522847,7 15,7.44771525 15,8 Z M5,8 C5,8.55228475 4.55228475,9 4,9 L2,9 C1.44771525,9 1,8.55228475 1,8 C1,7.44771525 1.44771525,7 2,7 L4,7 C4.55228475,7 5,7.44771525 5,8 Z M12.9497475,3.05025253 C13.3402718,3.44077682 13.3402718,4.0739418 12.9497475,4.46446609 L11.5355339,5.87867966 C11.1450096,6.26920395 10.5118446,6.26920395 10.1213203,5.87867966 C9.73079605,5.48815536 9.73079605,4.85499039 10.1213203,4.46446609 L11.5355339,3.05025253 C11.9260582,2.65972824 12.5592232,2.65972824 12.9497475,3.05025253 Z M5.87867966,10.1213203 C6.26920395,10.5118446 6.26920395,11.1450096 5.87867966,11.5355339 L4.46446609,12.9497475 C4.0739418,13.3402718 3.44077682,13.3402718 3.05025253,12.9497475 C2.65972824,12.5592232 2.65972824,11.9260582 3.05025253,11.5355339 L4.46446609,10.1213203 C4.85499039,9.73079605 5.48815536,9.73079605 5.87867966,10.1213203 Z M12.9497475,12.9497475 C12.5592232,13.3402718 11.9260582,13.3402718 11.5355339,12.9497475 L10.1213203,11.5355339 C9.73079605,11.1450096 9.73079605,10.5118446 10.1213203,10.1213203 C10.5118446,9.73079605 11.1450096,9.73079605 11.5355339,10.1213203 L12.9497475,11.5355339 C13.3402718,11.9260582 13.3402718,12.5592232 12.9497475,12.9497475 Z M5.87867966,5.87867966 C5.48815536,6.26920395 4.85499039,6.26920395 4.46446609,5.87867966 L3.05025253,4.46446609 C2.65972824,4.0739418 2.65972824,3.44077682 3.05025253,3.05025253 C3.44077682,2.65972824 4.0739418,2.65972824 4.46446609,3.05025253 L5.87867966,4.46446609 C6.26920395,4.85499039 6.26920395,5.48815536 5.87867966,5.87867966 Z\"\n fill=\"#293040\"\n ></path>\n </g>\n </svg>\n </div>\n</li>\n\n<li *ngIf=\"isLeaf\" role=\"menuitem\" (click)=\"clickLeaf()\" [ngClass]=\"{ disabled: option.disabled, 'devui-leaf-active': option.active }\">\n <div class=\"devui-dropdown-item\">\n <d-checkbox\n *ngIf=\"multiple\"\n class=\"devui-dropdown-item-checkbox\"\n [disabled]=\"option.disabled\"\n [isShowTitle]=\"false\"\n [halfchecked]=\"option.halfChecked\"\n (click)=\"clickCheckbox($event)\"\n [ngModel]=\"option.checked\"\n (ngModelChange)=\"updateValue($event)\"\n [beforeChange]=\"avoidCheckboxChange\"\n ></d-checkbox>\n <i *ngIf=\"option.icon\" class=\"icon {{ option.icon }}\"></i>\n <span class=\"dropdown-item-label\">\n <ng-template\n [ngTemplateOutlet]=\"dropDownItemTemplate || defaultDropdownItemTemplate\"\n [ngTemplateOutletContext]=\"{\n option: option,\n label: option.label\n }\"\n >\n </ng-template>\n </span>\n </div>\n</li>\n\n<ng-template #defaultDropdownItemTemplate let-label=\"label\">\n <span>{{ label }}</span>\n</ng-template>\n", styles: [".devui-font-size-base{font-size:var(--devui-font-size, 12px)}.devui-font-base{font-size:var(--devui-font-size, 12px);font-weight:var(--devui-font-content-weight, normal);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-modal-title{font-size:var(--devui-font-size-modal-title, 18px)}.devui-font-modal-title{font-size:var(--devui-font-size-modal-title, 18px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-page-title{font-size:var(--devui-font-size-page-title, 16px)}.devui-font-page-title{font-size:var(--devui-font-size-page-title, 16px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-secondary-title{font-size:var(--devui-font-size-card-title, 14px)}.devui-font-secondary-title{font-size:var(--devui-font-size-card-title, 14px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-dropdown-menu{padding-bottom:0}.devui-dropdown-item{height:32px;padding:8px 12px;cursor:pointer;display:flex!important;align-items:center}.devui-dropdown-item .dropdown-item-label{display:inline-block;color:var(--devui-text, #191919);flex:1;width:0;overflow:hidden;text-overflow:ellipsis}.devui-dropdown-item i{color:var(--devui-placeholder, #808080)}.devui-dropdown-item-checkbox{display:inline-block;margin-right:8px}.devui-dropdown-item .icon{font-size:var(--devui-font-size-icon, 16px);margin-right:4px;color:var(--devui-text, #191919);display:inline-block}.devui-dropdown-menu{border-left-color:var(--devui-base-bg, #ffffff)!important;box-shadow:var(--devui-shadow-length-connected-overlay, 0 4px 12px 0) var(--devui-shadow, rgba(0, 0, 0, .16))}.devui-dropdown-open{background-color:var(--devui-list-item-hover-bg, #f2f2f3)}.devui-dropdown-open span,.devui-dropdown-open i{color:var(--devui-link, #0950de)}.devui-leaf-active{background-color:var(--devui-list-item-hover-bg, #f2f2f3)}.devui-dropdown-menu{height:180px;overflow-y:auto}.disabled{background-color:var(--devui-disabled-bg, #f3f3f3)!important}.disabled .devui-dropdown-item{cursor:not-allowed}.disabled .devui-dropdown-item span,.disabled .devui-dropdown-item i{color:var(--devui-disabled-text, #c2c2c2)}.devui-dropdown-item:not(.disabled):active:hover .devui-cascader-icon-right polygon{fill:var(--devui-light-text, #ffffff)}.devui-cascader-loading{animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host:first-of-type>li{margin-top:0}:host>li{margin-top:4px}:host>li span{transition:color var(--devui-animation-duration-fast, .1s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1)),background-color var(--devui-animation-duration-fast, .1s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1))}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$1.CheckBoxComponent, selector: "d-checkbox", inputs: ["name", "label", "cssClass", "color", "disabled", "isShowTitle", "title", "labelTemplate", "halfchecked", "showAnimation", "showGlowStyle", "beforeChange"], outputs: ["change"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CascaderLiComponent, decorators: [{ type: Component, args: [{ selector: 'd-cascader-li', template: "<li *ngIf=\"!isLeaf\" role=\"menuitem\" [ngClass]=\"{ disabled: option.disabled, 'devui-leaf-active': option.active }\" (click)=\"clickItem()\">\n <div class=\"devui-dropdown-item\">\n <d-checkbox\n *ngIf=\"multiple\"\n class=\"devui-dropdown-item-checkbox\"\n [disabled]=\"option.disabled\"\n [isShowTitle]=\"false\"\n [halfchecked]=\"option.halfChecked\"\n (click)=\"clickCheckbox($event)\"\n [ngModel]=\"option.checked\"\n [beforeChange]=\"avoidCheckboxChange\"\n ></d-checkbox>\n <i *ngIf=\"option.icon\" class=\"icon {{ option.icon }}\"></i>\n <span class=\"dropdown-item-label\">\n <ng-template\n [ngTemplateOutlet]=\"dropDownItemTemplate || defaultDropdownItemTemplate\"\n [ngTemplateOutletContext]=\"{\n label: option.label,\n option: option\n }\"\n >\n </ng-template>\n </span>\n <svg\n *ngIf=\"!option._loading\"\n style=\"float: right\"\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n class=\"icon devui-cascader-icon-right\"\n >\n <g id=\"icon-icon-chevron-right-2\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <g\n id=\"ipd_right\"\n transform=\"translate(8.000000, 8.000000) scale(-1, 1) rotate(-270.000000) translate(-8.000000, -8.000000) translate(3.000000, 5.500000)\"\n fill=\"#71757F\"\n fill-rule=\"nonzero\"\n >\n <path\n d=\"M-0.353553391,-0.353553391 C-0.179987039,-0.527119742 0.0894373624,-0.546404893 0.284305503,-0.411408841 L0.353553391,-0.353553391 L4.999,4.293 L9.64644661,-0.353553391 C9.82001296,-0.527119742 10.0894374,-0.546404893 10.2843055,-0.411408841 L10.3535534,-0.353553391 C10.5271197,-0.179987039 10.5464049,0.0894373624 10.4114088,0.284305503 L10.3535534,0.353553391 L5.35355339,5.35355339 C5.17998704,5.52711974 4.91056264,5.54640489 4.7156945,5.41140884 L4.64644661,5.35355339 L-0.353553391,0.353553391 C-0.548815536,0.158291245 -0.548815536,-0.158291245 -0.353553391,-0.353553391 Z\"\n id=\"\u8DEF\u5F84\"\n ></path>\n </g>\n </g>\n </svg>\n <svg width=\"16px\" height=\"16px\" viewBox=\"0 0 16 16\" style=\"float: right\" class=\"icon devui-cascader-loading\" *ngIf=\"option._loading\">\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <path\n d=\"M8,1 C8.55228475,1 9,1.44771525 9,2 L9,4 C9,4.55228475 8.55228475,5 8,5 C7.44771525,5 7,4.55228475 7,4 L7,2 C7,1.44771525 7.44771525,1 8,1 Z M8,11 C8.55228475,11 9,11.4477153 9,12 L9,14 C9,14.5522847 8.55228475,15 8,15 C7.44771525,15 7,14.5522847 7,14 L7,12 C7,11.4477153 7.44771525,11 8,11 Z M15,8 C15,8.55228475 14.5522847,9 14,9 L12,9 C11.4477153,9 11,8.55228475 11,8 C11,7.44771525 11.4477153,7 12,7 L14,7 C14.5522847,7 15,7.44771525 15,8 Z M5,8 C5,8.55228475 4.55228475,9 4,9 L2,9 C1.44771525,9 1,8.55228475 1,8 C1,7.44771525 1.44771525,7 2,7 L4,7 C4.55228475,7 5,7.44771525 5,8 Z M12.9497475,3.05025253 C13.3402718,3.44077682 13.3402718,4.0739418 12.9497475,4.46446609 L11.5355339,5.87867966 C11.1450096,6.26920395 10.5118446,6.26920395 10.1213203,5.87867966 C9.73079605,5.48815536 9.73079605,4.85499039 10.1213203,4.46446609 L11.5355339,3.05025253 C11.9260582,2.65972824 12.5592232,2.65972824 12.9497475,3.05025253 Z M5.87867966,10.1213203 C6.26920395,10.5118446 6.26920395,11.1450096 5.87867966,11.5355339 L4.46446609,12.9497475 C4.0739418,13.3402718 3.44077682,13.3402718 3.05025253,12.9497475 C2.65972824,12.5592232 2.65972824,11.9260582 3.05025253,11.5355339 L4.46446609,10.1213203 C4.85499039,9.73079605 5.48815536,9.73079605 5.87867966,10.1213203 Z M12.9497475,12.9497475 C12.5592232,13.3402718 11.9260582,13.3402718 11.5355339,12.9497475 L10.1213203,11.5355339 C9.73079605,11.1450096 9.73079605,10.5118446 10.1213203,10.1213203 C10.5118446,9.73079605 11.1450096,9.73079605 11.5355339,10.1213203 L12.9497475,11.5355339 C13.3402718,11.9260582 13.3402718,12.5592232 12.9497475,12.9497475 Z M5.87867966,5.87867966 C5.48815536,6.26920395 4.85499039,6.26920395 4.46446609,5.87867966 L3.05025253,4.46446609 C2.65972824,4.0739418 2.65972824,3.44077682 3.05025253,3.05025253 C3.44077682,2.65972824 4.0739418,2.65972824 4.46446609,3.05025253 L5.87867966,4.46446609 C6.26920395,4.85499039 6.26920395,5.48815536 5.87867966,5.87867966 Z\"\n fill=\"#293040\"\n ></path>\n </g>\n </svg>\n </div>\n</li>\n\n<li *ngIf=\"isLeaf\" role=\"menuitem\" (click)=\"clickLeaf()\" [ngClass]=\"{ disabled: option.disabled, 'devui-leaf-active': option.active }\">\n <div class=\"devui-dropdown-item\">\n <d-checkbox\n *ngIf=\"multiple\"\n class=\"devui-dropdown-item-checkbox\"\n [disabled]=\"option.disabled\"\n [isShowTitle]=\"false\"\n [halfchecked]=\"option.halfChecked\"\n (click)=\"clickCheckbox($event)\"\n [ngModel]=\"option.checked\"\n (ngModelChange)=\"updateValue($event)\"\n [beforeChange]=\"avoidCheckboxChange\"\n ></d-checkbox>\n <i *ngIf=\"option.icon\" class=\"icon {{ option.icon }}\"></i>\n <span class=\"dropdown-item-label\">\n <ng-template\n [ngTemplateOutlet]=\"dropDownItemTemplate || defaultDropdownItemTemplate\"\n [ngTemplateOutletContext]=\"{\n option: option,\n label: option.label\n }\"\n >\n </ng-template>\n </span>\n </div>\n</li>\n\n<ng-template #defaultDropdownItemTemplate let-label=\"label\">\n <span>{{ label }}</span>\n</ng-template>\n", styles: [".devui-font-size-base{font-size:var(--devui-font-size, 12px)}.devui-font-base{font-size:var(--devui-font-size, 12px);font-weight:var(--devui-font-content-weight, normal);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-modal-title{font-size:var(--devui-font-size-modal-title, 18px)}.devui-font-modal-title{font-size:var(--devui-font-size-modal-title, 18px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-page-title{font-size:var(--devui-font-size-page-title, 16px)}.devui-font-page-title{font-size:var(--devui-font-size-page-title, 16px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-secondary-title{font-size:var(--devui-font-size-card-title, 14px)}.devui-font-secondary-title{font-size:var(--devui-font-size-card-title, 14px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-dropdown-menu{padding-bottom:0}.devui-dropdown-item{height:32px;padding:8px 12px;cursor:pointer;display:flex!important;align-items:center}.devui-dropdown-item .dropdown-item-label{display:inline-block;color:var(--devui-text, #191919);flex:1;width:0;overflow:hidden;text-overflow:ellipsis}.devui-dropdown-item i{color:var(--devui-placeholder, #808080)}.devui-dropdown-item-checkbox{display:inline-block;margin-right:8px}.devui-dropdown-item .icon{font-size:var(--devui-font-size-icon, 16px);margin-right:4px;color:var(--devui-text, #191919);display:inline-block}.devui-dropdown-menu{border-left-color:var(--devui-base-bg, #ffffff)!important;box-shadow:var(--devui-shadow-length-connected-overlay, 0 4px 12px 0) var(--devui-shadow, rgba(0, 0, 0, .16))}.devui-dropdown-open{background-color:var(--devui-list-item-hover-bg, #f2f2f3)}.devui-dropdown-open span,.devui-dropdown-open i{color:var(--devui-link, #0950de)}.devui-leaf-active{background-color:var(--devui-list-item-hover-bg, #f2f2f3)}.devui-dropdown-menu{height:180px;overflow-y:auto}.disabled{background-color:var(--devui-disabled-bg, #f3f3f3)!important}.disabled .devui-dropdown-item{cursor:not-allowed}.disabled .devui-dropdown-item span,.disabled .devui-dropdown-item i{color:var(--devui-disabled-text, #c2c2c2)}.devui-dropdown-item:not(.disabled):active:hover .devui-cascader-icon-right polygon{fill:var(--devui-light-text, #ffffff)}.devui-cascader-loading{animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host:first-of-type>li{margin-top:0}:host>li{margin-top:4px}:host>li span{transition:color var(--devui-animation-duration-fast, .1s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1)),background-color var(--devui-animation-duration-fast, .1s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1))}\n"] }] }], ctorParameters: () => [{ type: CascaderService }, { type: i0.ChangeDetectorRef }], propDecorators: { width: [{ type: Input }], trigger: [{ type: Input }], option: [{ type: Input }], multiple: [{ type: Input }], canSelectParent: [{ type: Input }], colIndex: [{ type: Input }], dropDownItemTemplate: [{ type: Input }], isLazyLoad: [{ type: Input }], checkboxRelation: [{ type: Input }], onClick: [{ type: HostListener, args: ['click', ['$event']] }], onMouseEnter: [{ type: HostListener, args: ['mouseenter', ['$event']] }] } }); const DEBOUNCE_TIME = 200; class CascaderComponent { set loadChildrenFn(fn) { this.isLazyLoad = !!fn; if (fn) { this.cascaderSrv.loadChildrenFn = fn; } } get hasGlowStyle() { return this.showGlowStyle; } get columnList() { return this.cascaderSrv.columnList; } get searchResultList() { return this.cascaderSrv.searchResultList; } constructor(cascaderSrv, i18n, devConfigService) { this.cascaderSrv = cascaderSrv; this.i18n = i18n; this.devConfigService = devConfigService; this.options = []; this.width = 200; this.placeholder = ''; this.trigger = 'hover'; this.disabled = false; this.multiple = false; this.showPath = false; this.allowClear = false; this.allowSearch = false; this.canSelectParent = false; this.checkboxRelation = { upward: true, downward: true }; this.dropdownPanelClass = ''; this.appendToBody = true; this.tagMaxWidth = '200px'; this.showAnimation = true; this.styleType = 'default'; this.toggleEvent = new EventEmitter(); this.showGlowStyle = true; this.subMenuDirections = [ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetX: 0, }, { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetX: 0, }, ]; this.unsubscribe$ = new Subject(); this.searchValueChange = new Subject(); this.multipleValueList = []; this.lazyloadValue = []; this.showSearchInput = false; this.onChange = Function.prototype; this.onTouched = Function.prototype; } // 获取整颗树的公共方法 getOptionTree() { return this.cascaderSrv.options; } ngOnInit() { this.dropdownWidth = this.dropdownWidth ? this.dropdownWidth : this.width; this.cascaderSrv.canSelectParent = this.canSelectParent; this.cascaderSrv.initOptions(this.options); this.cascaderSrv.isMultiple = this.multiple; this.cascaderSrv.isLazyLoad = this.isLazyLoad; this.cascaderSrv.checkboxRelation = this.checkboxRelation; this.initObservale(); this.initI18n(); } ngOnChanges(changes) { const { options, canSelectParent } = changes; if (options && !options.firstChange) { this.cascaderSrv.initOptions(this.options); } else if (canSelectParent && !canSelectParent.firstChange) { this.cascaderSrv.canSelectParent = this.canSelectParent; } } initI18n() { this.i18nCommonText = this.i18n.getI18nText().common; this.i18nSubscription = this.i18n.langChange().subscribe((data) => { this.i18nCommonText = data.common; }); } valueChanges(value) { this.searchValueChange.next(value); } chooseSearchResult(option) { if (option.checked) { return; } if (this.multiple) { // 多选模式下更新数据后需要刷新dropdown位置和重新聚焦input option.checked = true; this.writeValue([...this.cascaderSrv.multipleValue, option.valueList]); this.onChange(this.cascaderSrv.multipleValue); this.mainDropdown.updateCdkConnectedOverlayOrigin(); if (this.innerInput) { this.innerInput.nativeElement.focus(); } } else { this.writeValue(option.valueList); this.onChange(option.valueList); this.mainDropdown.isOpen = false; } } initObservale() { this.cascaderSrv.closeMianDropdown.pipe(takeUntil(this.unsubscribe$)).subscribe((res) => { if (this.mainDropdown) { this.mainDropdown.isOpen = false; } }); this.cascaderSrv.currentValueChange.pipe(takeUntil(this.unsubscribe$)).subscribe((res) => { this.writeValue(res); this.onChange(res); }); this.cascaderSrv.openDrawer.pipe(takeUntil(this.unsubscribe$)).subscribe((res) => { this.rePosition(); }); if (this.multiple) { this.cascaderSrv.updateTagList.pipe(takeUntil(this.unsubscribe$)).subscribe((res) => { const targetIndex = this.multipleValueList.findIndex((t) => t.value === res.option.value); if (res.isAdd) { if (targetIndex === -1) { this.multipleValueList.push(res.option); this.updateMultipleValuePathList(); } } else if (targetIndex !== -1) { this.multipleValueList.splice(targetIndex, 1); } if (res.isEmit) { this.onChange(this.cascaderSrv.currentMultipleValue); } // 当taglist变化导致input高度变化时,更新相对位置 setTimeout(() => { this.mainDropdown.updateCdkConnectedOverlayOrigin(); }); }); } if (this.allowSearch) { this.searchValueChange .pipe(takeUntil(this.unsubscribe$), debounceTime(DEBOUNCE_TIME), distinctUntilChanged(), filter((t) => t !== '')) .subscribe((value) => { clearTimeout(this.timer); if (this.mainDropdown && !this.mainDropdown.isOpen) { this.mainDropdown.isOpen = true; } if (this.multiple && this.innerInput) { this.innerInput.nativeElement.focus(); } this.cascaderSrv.searchResultList = []; this.cascaderSrv.searchByString(value); this.showSearchPanel = true; }); } if (this.isLazyLoad) { this.cascaderSrv.updateShowText.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { if (this.multiple) { this.multipleValueList = this.getMultipleValueFromValueList(this.lazyloadValue); this.cascaderSrv.multipleValue = this.lazyloadValue; } else { this.showTextValue = this.showPath ? this.getPathLabelFormValue(this.cascaderSrv._currentValue) : this.getLabelFromValue(this.cascaderSrv._currentValue); } }); } } deleteTag(tagEvent, option) { tagEvent.event.stopPropagation(); this.cascaderSrv.updateOptionCheckedStatus(option.value, false, this.checkboxRelation.upward, this.checkboxRelation.downward); this.multipleValueList = this.multipleValueList.filter((t) => t.value !== option.value); this.onChange(this.cascaderSrv.currentMultipleValue); // 当taglist变化导致input高度变化时,更新相对位置 setTimeout(() => { this.mainDropdown.updateCdkConnectedOverlayOrigin(); }); } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } writeValue(value) { if (!this.multiple) { if (!value) { this.showTextValue = ''; } else { this.showTextValue = this.showPath ? this.getPathLabelFormValue(value) : this.getLabelFromValue(value); this.cascaderSrv._currentValue = value; this.cascaderSrv.updateOptionByValue(); } } else { if (!value) { this.multipleValueList = []; } else { this.cascaderSrv.currentMultipleValue = value; this.lazyloadValue = value; this.multipleValueList = this.getMultipleValueFromValueList(value); this.updateMultipleValuePathList(); } } } updateMultipleValuePathList() { if (!this.showPath) { return; } const valueList = this.cascaderSrv.currentMultipleValue; valueList.forEach((item, index) => { const value = item[item.length - 1]; const target = this.multipleValueList.find((t) => t.value === value); if (target) { target.pathLabel = this.getPathLabelFormValue(item); } }); } // 从路径数组中获取所有tag的值,并让对应checkbox激活 getMultipleValueFromValueList(valueList) { const targetList = []; valueList.forEach((value) => { let cur; let cacheTarget; let list = this.cascaderSrv.options; for (let i = 0; i < value.length; i++) { cur = list.find((l) => l.value === value[i]); cacheTarget = cur; if (this.isLazyLoad && cur && !cur.children?.length && !cur.isLeaf) { this.cascaderSrv.lazyloadMultipleChild(cur, i); break; } else { list = (cur && cur.children) || []; } } if (cur && cur.isLeaf) { targetList.push(cur); cacheTarget = null; this.cascaderSrv.updateOptionCheckedStatus(cur.value, true, this.checkboxRelation.upward, this.checkboxRelation.downward); } if (cacheTarget) { targetList.push(cacheTarget); this.cascaderSrv.updateOptionCheckedStatus(cacheTarget.value, true, this.checkboxRelation.upward, this.checkboxRelation.downward); } }); return targetList; } // 获取路径末的label getLabelFromValue(value) { let cur; let list = this.cascaderSrv.options; value.forEach((item) => { cur = list.find((t) => t.value === item) || ''; list = cur.children || []; }); return (cur && cur.label) || ''; } // 将value数组转换为路径字符串 getPathLabelFormValue(value) { let path = ''; let cur; let list = this.cascaderSrv.options; value.forEach((item, index) => { cur = list.find((t) => t.value === item) || ''; if (cur) { path = path === '' ? path + cur.label : path + ' / ' + cur.label; } else if (this.cascaderSrv.columnList.length === index + 1) { path = path === '' ? path + item : path + ' / ' + item; } list = cur.children || []; }); return path; } clearTags() { if (this.multiple) { this.multipleValueList = []; this.cascaderSrv.resetStatus.next(); this.cascaderSrv.resetNodeStatus(); this.cascaderSrv.currentMultipleValue = []; } else { this.cascaderSrv.currentValue = []; } this.onChange([]); } onToggle(isOpen) { this.toggleEvent.emit(isOpen); if (isOpen && this.multiple) { this.cascaderSrv.clearTargetActive(this.cascaderSrv.options.find((t) => t.active)); this.cascaderSrv.columnList.splice(1); if (this.innerInput) { this.innerInput.nativeElement.focus(); } } if (isOpen && !this.multiple && this.allowSearch) { this.outerInput.nativeElement.focus(); } if (!isOpen && this.allowSearch) { if (!this.cascaderSrv.currentValue.length) { this.showTextValue = null; } else { this.writeValue(this.cascaderSrv.currentValue); } // 在动画结束后再设置参数,防止panel中内容突变,动画时间200 this.timer = setTimeout(() => { this.showSearchPanel = false; }, 200); } if (isOpen) { this.rePosition(); } } // 防止位置超出overlay边界 rePosition() { if (typeof window === undefined || !this.appendToBody) { return; } setTimeout(() => { this.dropdownComp.reposition(); const width = this.dropdownComp.overlay.overlayRef?.overlayElement.clientWidth; const offsetX = this.dropdownComp.overlay.overlayRef?.overlayElement.offsetLeft; const offsetRight = window.innerWidth - width - offsetX - 20; this.subMenuDirections.forEach((t) => { t.offsetX = offsetRight < 0 ? offsetRight : 0; }); this.dropdownComp.reposition(); }, 0); } ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); this.searchValueChange.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CascaderComponent, deps: [{ token: CascaderService }, { token: i2.I18nService }, { token: i3.DevConfigService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CascaderComponent, selector: "d-cascader", inputs: { options: "options", width: "width", dropdownWidth: "dropdownWidth", placeholder: "placeholder", trigger: "trigger", disabled: "disabled", multiple: "multiple", showPath: "showPath", allowClear: "allowClear", allowSearch: "allowSearch", canSelectParent: "canSelectParent", checkboxRelation: "checkboxRelation", dropDownItemTemplate: "dropDownItemTemplate", dropdownHeaderTemplate: "dropdownHeaderTemplate", hostTemplate: "hostTemplate", dropdownPanelClass: "dropdownPanelClass", appendToBody: "appendToBody", tagMaxWidth: "tagMaxWidth", showAnimation: "showAnimation", styleType: "styleType", loadChildrenFn: "loadChildrenFn", showGlowStyle: "showGlowStyle" }, outputs: { toggleEvent: "toggleEvent" }, host: { properties: { "class.devui-glow-style": "this.hasGlowStyle" } }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CascaderComponent), multi: true, }, CascaderService, ], viewQueries: [{ propertyName: "mainDropdown", first: true, predicate: ["mainDropdown"], descendants: true }, { propertyName: "innerInput", first: true, predicate: ["innerInput"], descendants: true }, { propertyName: "outerInput", first: true, predicate: ["outerInput"], descendants: true }, { propertyName: "dropdownComp", first: true, predicate: DropDownAppendToBodyComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\r\n [ngStyle]=\"{ width: width + 'px' }\"\r\n *ngIf=\"appendToBody\"\r\n dDropDown\r\n appendToBody\r\n [trigger]=\"disabled ? 'manually' : 'click'\"\r\n [closeScope]=\"'blank'\"\r\n #mainDropdown=\"d-dropdown\"\r\n (toggleEvent)=\"onToggle($event)\"\r\n [showAnimation]=\"showAnimation\"\r\n [appendToBodyDirections]=\"subMenuDirections\"\r\n [ngClass]=\"{ 'dropdown-show-clear': allowClear && (showTextValue || multipleValueList.length) }\"\r\n>\r\n <div dDropDownToggle (focus)=\"showSearchInput = true\" [class.devui-gray-style]=\"styleT