/* * @Author: 疯狂秀才(Lucas Huang) * @Date: 2019-08-14 15:22:21 * @LastEditors: 疯狂秀才(Lucas Huang) * @LastEditTime: 2019-08-15 16:44:15 * @QQ: 1055818239 * @Version: v0.0.1 */ import { Component, OnInit, Input, ContentChild, HostBinding, OnDestroy, ViewChild, Optional, NgZone, Injector, ElementRef, Renderer2 } from '@angular/core'; import { FarrisSectionTitleDirective } from './directive/section-title.directive'; import { FarrisSectionHeaderDirective } from './directive/section-header.directive'; import { FarrisSectionHcontentDirective } from './directive/section-hcontent.directive'; import { FarrisSectionToolbarDirective, FarrisSectionViewChangeDirective } from './directive/section-toolbar.directive'; import { FarrisSectionExtendDirective } from './directive/section-extend.directive'; import { FarrisSectionContentDirective } from './directive/section-content.directive'; import { Observable, Subject, Subscription, of } from 'rxjs'; import { ToolbarConfig, ButtonConfig } from './toolbar.config'; import ResizeObserver from 'resize-observer-polyfill'; import { map, debounceTime, } from 'rxjs/operators'; @Component({ selector: 'farris-section', templateUrl: './section.component.html', styleUrls: ['./section.component.css'] }) export class FarrisSectionComponent implements OnInit, OnDestroy { @Input() contentCls = ''; @HostBinding('class.f-section') baseCls = true; @HostBinding('class.f-section-maximize') maxStatus: boolean = false; @HostBinding('class.f-section-fill') get fillCls(): boolean { return this.fill; } @HostBinding('class.f-section-accordion') get enableAccordionCls(): boolean { return this.enableAccordion == 'default'; } @HostBinding('class.f-state-collapse') get enableCollapseCls(): boolean { if ( this.enableAccordion === 'default' || this.enableAccordion === 'custom' ) { return !this.expandStatus; } else { return false; } } @HostBinding('class.f-section-custom-accordion') get enableCustomAccordionCls(): boolean { return this.enableAccordion == 'custom'; } // 主标题 @Input() mainTitle: string; // 副标题 @Input() subTitle: string; // 是否显示header @Input() showHeader = true; // 是否支持最大化 @Input() enableMaximize = false; /** * 是否启用収折功能 * 默认不启用収折,值为'' * 启用默认収折,值为'default' * 启用自定义収折,值为 'custom' **/ @Input() enableAccordion = ''; // section是否铺满 @Input() fill = false; // 默认是是否展开 @Input() expandStatus = true; /** * 事件上下文,解决按钮点击时没有上下文的问题 */ @Input() context: any; @Input() index: any; // 就来按钮位置 toolbarPosition = ''; // toolbarBtns = []; // 工具栏按钮 private _toolbar: ToolbarConfig | null = null; @Input() set toolbar(value: ToolbarConfig | null) { if (value) { this._toolbar = value; // 计算位置 this.toolbarPosition = this.getToolbarPosition(); // this.toolbarBtns = [...this.toolbar.contents]; this.inMoreButtonContents = []; // 绑定事件 this.bindResizeEvent(); // 调整界面 this.afterBindToolbarData(); } else { this._toolbar = null; // 有位置 this.toolbarPosition = ''; this.toolbarBtns = []; this.inMoreButtonContents = []; // 移除绑定事件 this.unBindResizeEvent(); } } get toolbar() { return this._toolbar; } //标记按钮工具栏是否隐藏[更多]按钮 toolbarDpHidden = true; // section头部模板 @ContentChild(FarrisSectionHeaderDirective) headerDirective: any; // section头部标题区模板 @ContentChild(FarrisSectionTitleDirective) headerTitleDirective: any; //section头部可扩展区域模板 @ContentChild(FarrisSectionHcontentDirective) headerContentDirective: any; //section头部工具栏区域模板 @ContentChild(FarrisSectionToolbarDirective) headerToolbarDirective: any; //section可扩展区域模板——内容区上面的扩展,标题下面 @ContentChild(FarrisSectionExtendDirective) extendDirective: any; //section内容区域模板 @ContentChild(FarrisSectionContentDirective) contentDirective: any; //section头部ViewChange区域模板 @ContentChild(FarrisSectionViewChangeDirective) viewChangeDirective: any; // 参照Tab中这样的处理方式有问题----延后修改 // 是否被禁用的状态 // 按照之前的逻辑,如果没有找到key,就说明被禁用 _disableDatas: Observable> = new Subject(); @Input() set btnStates(values: Observable>) { if (values) { if (this._disableDatas&&this._disableDatas.hasOwnProperty('unsubscribe')) { this._disableDatas['unsubscribe'](); } this._disableDatas = values; } } // 按照之前的逻辑,如果没有找到key,就说明可见 _visibleDatas: Observable> = new Subject(); // 是否可见的状态 @Input() set btnVisible(values: Observable>) { if (values) { if (this._visibleDatas&&this._visibleDatas.hasOwnProperty('unsubscribe')) { this._visibleDatas['unsubscribe'](); } this._visibleDatas = values; } } @Input() clickThrottleTime = 350; // 通过按钮数组显示工具栏 @ViewChild('toolbarContainer') toolbarContainer; // 计算工具栏按钮宽度的辅助区域 @ViewChild('btnPlaceholder') btnPlaceHolder; private clickItems = new Subject(); private clickSubscription: Subscription; // 用于提供循环效率 trackByButton(index: number, btn: any): number { return btn.id; } // 用于计算响应时 private ro: ResizeObserver = null; private resizeEl = null; // 处理变化 private ngZone; // private renderer; inMoreButtonContents: ButtonConfig[] = []; constructor(@Optional() private inject: Injector) { if (this.inject) { this.ngZone = this.inject.get(NgZone, null); this.renderer = this.inject.get(Renderer2, null); } } ngOnInit() { // 标记点击事件 this.clickSubscription = this.clickItems.pipe(debounceTime(this.clickThrottleTime)) .subscribe((btnInfo: ButtonConfig) => { if (btnInfo.click && typeof btnInfo.click == 'function') { btnInfo.click({ context: this.context }); } }); // 绑定状态 this._disableDatas.subscribe(data => { // 调整按钮可用状态 this.changeState(data,'disable'); }); this._visibleDatas.subscribe(data=>{ // 调整按钮可见状态 this.changeState(data,'visible'); }); } /** * 获取按钮配置中,禁用、可见的属性 * @param id * @param stateProp */ getState(btnItem,stateProp){ if(btnItem.hasOwnProperty(stateProp)){ return btnItem[stateProp]; } // 不存在属性 if(stateProp=='visible'){ return true; } return false; } /** * 改变可用状态 * @param values */ private changeState(values,stateProp){ const idArray = Object.keys(values); idArray.forEach(id => { const state = values[id]; const item = this._findItemByID(id, this.toolbarBtns); if(item){ item[stateProp] = state; } }); this.inMoreButtonContents.forEach(item=>{ const findId=idArray.find(id=>id==item.id); if(findId){ item[stateProp]=values[findId]; } }); } /** * 查找 * @param id * @param datas */ private _findItemByID(id:string,datas:ButtonConfig[]){ return datas.find((item) => item.id==id); } ngOnDestroy() { if (this.clickSubscription) { this.clickSubscription.unsubscribe(); this.clickSubscription = null; } if (this._visibleDatas&&this._visibleDatas.hasOwnProperty('unsubscribe')) { this._visibleDatas['unsubscribe'](); this._visibleDatas=null; } if (this._disableDatas&&this._disableDatas.hasOwnProperty('unsubscribe')) { this._disableDatas['unsubscribe'](); this._disableDatas=null; } this.unBindResizeEvent(); } /** * 绑定限制 */ private bindResizeEvent() { // 标记响应时 if (this.ngZone && this.toolbar && this.toolbar.contents.length > 0) { this.ngZone.runOutsideAngular(() => { setTimeout(() => { if (!this.toolbarContainer) { return; } if (this.ro) { if (this.toolbarContainer.nativeElement.parentElement == this.resizeEl) { // 已绑定过 return; } else { this.unBindResizeEvent(); } } if (this.ro === null) { this.ro = new ResizeObserver((el) => { this._calculateToolbarDropdownIsShow(); this._calculateButtonsShow(); }); this.resizeEl = this.toolbarContainer.nativeElement.parentElement; this.ro.observe(this.resizeEl); } }, 0); }); } else { this.unBindResizeEvent(); } } /** * 按钮数据变更后触发检查 */ private afterBindToolbarData() { if (this.ngZone && this.toolbar && this.toolbar.contents.length > 0) { this.ngZone.runOutsideAngular(() => { setTimeout(() => { this._calculateToolbarDropdownIsShow(); this._calculateButtonsShow(); }, 0); }) } } /** * 判断下拉按钮是否显示 */ private _calculateToolbarDropdownIsShow() { if (!this.toolbarContainer || !this.toolbar || !this.toolbarPosition) { // 容器未显示,没有数据,没有按钮位置 this.toolbarDpHidden = true; return; } const btnParentsContainer = this.toolbarContainer.nativeElement.parentElement; if (this.btnPlaceHolder.nativeElement.scrollWidth > btnParentsContainer.offsetWidth) { this.toolbarDpHidden = false; } else { this.toolbarDpHidden = true; } } /** * 计算显示的按钮个数及更多中显示的按钮个数 */ private _calculateButtonsShow() { if (!this.toolbarContainer || !this.toolbar || !this.toolbarPosition) { // 容器未显示,没有数据,没有按钮位置 return; } if (this.toolbarDpHidden) { this.toolbarBtns = [...this.toolbar.contents]; this.inMoreButtonContents = []; return; } let toolbarParentContainerEl = this.toolbarContainer.nativeElement.parentElement; let divEl = this.toolbarContainer.nativeElement; let btnPlaceHolderEl = this.btnPlaceHolder.nativeElement; let btnPlaceHolderBtns = btnPlaceHolderEl.querySelectorAll('button'); // 更多按钮 const moreButton = toolbarParentContainerEl.querySelector('.morebtn'); // 计算按钮的宽度 let basicStart = parseInt(btnPlaceHolderEl.getBoundingClientRect().left); let basicWidth = toolbarParentContainerEl.offsetWidth - moreButton.offsetWidth - 12; let toolbarDropdownHidden = true; let k = 0 // 只有一个按钮 if (btnPlaceHolderBtns.length == 1) { if (btnPlaceHolderEl.offsetWidth > basicWidth) { toolbarDropdownHidden = false; } else { k = 1; } } else { for (k = 1; k <= btnPlaceHolderBtns.length - 1; k++) { // 如果内容超出,考虑到按钮之间的间距 if (k == btnPlaceHolderBtns.length - 1) { if (btnPlaceHolderEl.offsetWidth > basicWidth) { toolbarDropdownHidden = false; k = k - 1; break; } } else if (parseInt(btnPlaceHolderBtns[k].getBoundingClientRect().left) - basicStart > basicWidth) { toolbarDropdownHidden = false; k = k - 1; break; } } } this.toolbarDpHidden = toolbarDropdownHidden; let showBtns = divEl.querySelectorAll('button'); let resultBtns = this.toolbar.contents.filter( (item, index) => { return index < k; } ); this.toolbarBtns = resultBtns ? [...resultBtns] : []; // for (let m = k; m < showBtns.length; m++) { // this.renderer.setStyle(showBtns[m], 'display', 'none'); // } if (this.toolbar.contents && this.toolbar.contents.length) { this.inMoreButtonContents = this.toolbar.contents.filter( (item, index) => { return index >= k; } ); } } /** * 在按钮状态变化时解除限制 */ private unBindResizeEvent() { if (this.ro) { if (this.resizeEl) { this.ro.unobserve(this.resizeEl); } this.ro = null; } } /** * @return string * '' 不存在按钮,无所谓位置 * 'inHead' 按钮在头部 * 'inContent' 按钮在内容位置 */ private getToolbarPosition() { if (this.toolbar && this.toolbar.hasOwnProperty('position') && this.toolbar.hasOwnProperty('contents') && this.toolbar.contents.length > 0) { // 如果有属性,且按钮有值 if (this.toolbar.position === 'inHead') { return 'inHead'; } else { return 'inContent'; } } return ''; } /** * 判断是否显示、隐藏 * @param id * @param state */ getToolbarState(id: string, state: Observable = this.btnStates, defaultValue: boolean = false) { if (state) { return state.pipe( map((n: any) => { if (n[id] === undefined) { console.warn(`未找ID为:${id} 相关状态设置!`); return defaultValue; } return n[id]; }) ); } else { return of(defaultValue); } } clickHandler(btnInfo) { if (!btnInfo) { return; } this.clickItems.next(btnInfo); } }