import { OnDestroy, NgZone, ChangeDetectionStrategy, Renderer2, OnChanges, SimpleChanges } from '@angular/core'; import { AfterViewInit, AfterViewChecked, ChangeDetectorRef } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core'; import { Button } from './button'; import { Subject } from 'rxjs'; import { style } from '@angular/animations'; @Component({ selector: 'farris-button-group', templateUrl: `./button-group.component.html`, changeDetection: ChangeDetectionStrategy.OnPush }) export class FarrisButtonGroupComponent implements OnInit, OnChanges, AfterViewInit, AfterViewChecked, OnDestroy { @ViewChild('dpBtn') dpBtn: ElementRef; @ViewChild('dpMenu') dpMenu: ElementRef; // 横向纠正的参照 @Input() rectifyReferenceH: HTMLElement; // 纵向纠正的参照 @Input() rectifyReferenceV: HTMLElement; // 是否自动纠正位置 @Input() autoRectify = true; // 计算方向的真正placement private realPlacement = 'bottom-right'; // 重新计算后的placement rectifyPlacement = 'bottom-right'; /* 按钮信息 */ @Input() data: Button[]; /* 显示的按钮数量 默认为2 */ @Input() count = 2; /* 按钮大小 */ @Input() size = 'small'; /* 按钮样式 */ @Input() type = 'primary'; /* 按钮展示位置 */ private _placement = "bottom"; @Input() set placement(value) { if (!value) { return; } if (value !== this._placement) { this.realPlacement = this.getRealPlacement(value); this._placement = value; } } get placement() { return this._placement; } /* menu size */ /** *2021.06.12 废弃,因为使用者不知道在中英文情况下,下拉面板的宽度和高度 */ @Input() width = 120; @Input() height = 250; private _menuWidth = 0; private _menuHeight = 0; /* 下拉按钮 触发事件 */ @Output() changeState = new EventEmitter(); /* 下拉菜单项点击事件 */ @Output() change = new EventEmitter(); @Output() click = new EventEmitter(); /* 触发下拉按钮面板区域之外的事件 */ @Output() clickMenuOut = new EventEmitter(); /** * 2021.06.12 废弃,因为当autoSize为true的时候,按钮显示不出来。 */ @Input() autoSize = false; /* 下拉面板显示 */ show = false; /* 显示出来的按钮组 */ get flatButtons() { return this.data && this.data.slice(0, this.count); } get dpButtons() { // this.height = this.dpButtons.length * 35; return this.data && this.data.slice(this.count); } setPosition$ = new Subject(); event: any; /* 下拉面板显示标志 */ dpFlag: boolean; /* 事件 */ mouseenterEvent: any; mouseleaveEvent: any; documentClickEvent: any; /* 三个点按钮尺寸 */ btnSize: any; /* */ setTimeObj: any; constructor(private changeRef: ChangeDetectorRef, private ngZone: NgZone) { } ngOnInit() { // 初始化数据 } ngOnChanges(simpleChanges: SimpleChanges) { if (simpleChanges['data']) { // this.flatButtons = this.data.slice(0, this.count); // this.dpButtons = this.data.slice(this.count); // this.height = this.dpButtons.length * 35; } } ngAfterViewInit() { } ngAfterViewChecked() { if (this.show) { // const { height, width } = this.dpMenu.nativeElement.getBoundingClientRect(); this.setPosition(this.event) // this.setPosition$.subscribe(res => this.setPosition(res, width, height)); } } clickEvent(e: any) { e.stopPropagation(); this.show = !this.show; // body添加面板 if (this.show) { this.appendBody(); // this.setPosition(e); // this.setPosition$.next(e); this.event = e; // 绑定相应的事件 this.ngZone.runOutsideAngular(() => { this.bindMenuMouseenter(); this.bindiMenuMouseleave(); // this.bindDocClick(); this.mouseNotEnterLeave(); }); } // 面板显示 脏值检测 this.changeRef.detectChanges(); this.changeRef.markForCheck(); // this.changeState.emit({ originEvent: e, show: this.show }); this.changeState.emit(this.show); } /* 按钮触发事件 */ toggle(e: any, btn) { e.stopPropagation(); if (btn.disabled) return; this.show = false; // this.unbindDocClick(); // 关闭下拉按钮面板 脏值检测 this.changeRef.detectChanges(); this.changeRef.markForCheck(); this.change.emit(btn.id); this.click.emit(btn); } // 下拉按钮显示到body中 可改变面板方向 appendBody() { if (this.dpMenu.nativeElement) { // 添加到body 便于全部显示 document.body.appendChild(this.dpMenu.nativeElement); } } /** * 当下拉超出边界时 转换方向, * 并未处理,边界不够下拉展示的情况 * @param btnSize */ changePlacement(btnSize: any) { if (!this.autoRectify) { return; } let referPosition = this.getReferencePosition(); let newPlacement = this.realPlacement; if (newPlacement.indexOf('bottom') > -1) { if (this._menuHeight > referPosition.bottom - btnSize.bottom) { newPlacement = newPlacement.replace('bottom', 'top'); } } else if (newPlacement.indexOf('top') > -1) { if (this._menuHeight > btnSize.top-referPosition.top ) { newPlacement = newPlacement.replace('top', 'bottom'); } } if (newPlacement.indexOf('left') > -1) { if (this._menuWidth > btnSize.left-referPosition.left ) { newPlacement = newPlacement.replace('left', 'right'); } } else if (newPlacement.indexOf('right') > -1) { if (this._menuWidth > referPosition.right - btnSize.right) { newPlacement = newPlacement.replace('right', 'left'); } } this.rectifyPlacement=newPlacement; } /** * 确认参照的边界 */ private getReferencePosition() { let rRight = document.documentElement.clientWidth; let rBottom = document.documentElement.clientHeight; let rTop = 0; let rLeft = 0; // 横向参照 if (this.rectifyReferenceH) { rRight = this.rectifyReferenceH.getBoundingClientRect().right; rLeft = this.rectifyReferenceH.getBoundingClientRect().left; } // 纵向参照 if (this.rectifyReferenceV) { rBottom = this.rectifyReferenceV.getBoundingClientRect().bottom; rTop = this.rectifyReferenceV.getBoundingClientRect().top; } return { top: rTop, left: rLeft, right: rRight, bottom: rBottom }; } /** * 变化对应的class * @param position */ _getClsName(position) { let className = ''; switch (position) { case 'top-right': case 'top': // 朝上,朝上-朝右 className = 'dropup'; break; case 'top-left': // 朝上-朝左 className = 'dropup-left'; break; case 'left-bottom': case 'left': // 横向——朝左——朝下 className = 'dropleft'; break; case 'left-top': // 横向——朝左——朝上 className = 'dropleft-up'; break; case 'right-bottom': case 'right': // 横向——朝右——朝下 className = 'dropright'; break; case 'right-top': // 横向——朝右——朝上 className = 'dropright-up'; break; case 'bottom-left': // 朝下——朝左 className = 'dropdown-left'; break; case 'bottom-right': className = 'dropdown'; break; default: // 朝下,朝下——朝右 className = 'dropdown'; } return className; } private getRealPlacement(pment) { var result = 'bottom-right'; switch (pment) { case 'top': result = 'top-right'; break; case 'left': result = 'left-bottom'; break; case 'right': result = 'right-bottom'; break; case 'bottom': result = 'bottom-right'; break; default: result = pment; } return result; } /* * 计算的位置区分忒细化 */ private changePosition(btnSize: any) { let rplacement=''; if(this.autoRectify){ rplacement=this.rectifyPlacement; }else{ rplacement=this.realPlacement; } let styleTop = 0; let styleLeft = 0; if (rplacement.indexOf('top') > -1) { styleTop = btnSize.top - this._menuHeight; }else if (rplacement.indexOf('bottom') > -1) { styleTop = btnSize.bottom; } if (rplacement.indexOf('right') > -1) { styleLeft = btnSize.right; }else if(rplacement.indexOf('left') > -1) { styleLeft = btnSize.left - this._menuWidth; } // 开头 if (rplacement.indexOf('-top')>-1) { styleTop= styleTop-btnSize.height; }else if(rplacement.indexOf('-bottom')>-1){ styleTop= styleTop+btnSize.height; } this.dpMenu.nativeElement.style.top = styleTop + 'px'; this.dpMenu.nativeElement.style.left = styleLeft + 'px'; } /* 绑定下拉面板鼠标进入事件 */ bindMenuMouseenter() { this.mouseenterEvent = this.changeFlagToTrue.bind(this); this.dpMenu.nativeElement.addEventListener('mouseenter', this.mouseenterEvent); } /* 绑定下拉面板鼠标离开事件 */ bindiMenuMouseleave() { this.mouseleaveEvent = this.mouseLeave.bind(this); this.dpMenu.nativeElement.addEventListener('mouseleave', this.mouseleaveEvent); } /* 绑定点击面板区域之外触发的事件 */ // bindDocClick() { // this.documentClickEvent = this.clickDoc.bind(this); // document.addEventListener('click', this.documentClickEvent); // } /* 解绑事件 */ unbindMenuMouseenter() { if (this.mouseenterEvent) { this.dpMenu.nativeElement.removeEventListener('mouseenter', this.mouseenterEvent); } } unbindiMenuMouseleave() { if (this.mouseleaveEvent) { this.dpMenu.nativeElement.removeEventListener('mouseleave', this.mouseleaveEvent); } } // unbindDocClick() { // if (this.documentClickEvent) { // document.removeEventListener('click', this.documentClickEvent); // } // } /* flag true */ changeFlagToTrue() { this.dpFlag = true; } /* flag false */ changeFlagToFalse() { this.dpFlag = false; } /* 鼠标离开时 关闭menu */ mouseLeave() { if (this.dpFlag) { this.changeFlagToFalse(); // this.unbindDocClick(); this.unbindiMenuMouseleave(); this.unbindMenuMouseenter(); this.close(); if (this.setTimeObj) { this.ngZone.runOutsideAngular(() => { clearTimeout(this.setTimeObj); }); } } } /* 鼠标没有进入到面板 一段时间后面板自动消失 */ mouseNotEnterLeave() { this.ngZone.runOutsideAngular(() => { this.setTimeObj = setTimeout(() => { if (!this.dpFlag) { this.changeFlagToFalse(); // this.unbindDocClick(); this.unbindiMenuMouseleave(); this.unbindMenuMouseenter(); this.close(); if (this.setTimeObj) { clearTimeout(this.setTimeObj); } } }, 2000); }); } /* 点击面板之外区域 */ // clickDoc() { // this.unbindDocClick(); // this.unbindiMenuMouseleave(); // this.unbindMenuMouseenter(); // this.close(); // if (this.setTimeObj) { // clearTimeout(this.setTimeObj); // } // this.clickMenuOut.emit(this.show); // } /* 关闭下拉面板 */ close() { this.show = false; // 关闭下拉按钮面板 脏值检测 if (!this.changeRef['destroyed']) { this.changeRef.detectChanges(); this.changeRef.markForCheck(); } this.dpBtn.nativeElement.blur(); } /* 动态指定menu在body中的位置 */ private setPosition(e) { // 下拉按钮 const btnSize = this.dpBtn.nativeElement.getBoundingClientRect(); // 下拉面板 let menuRect = this.dpMenu.nativeElement.getBoundingClientRect(); this._menuHeight = menuRect.height; this._menuWidth = menuRect.width; // 如果要自动纠正方向 if (this.autoRectify) { this.changePlacement(btnSize); } this.changePosition(btnSize); } ngOnDestroy() { } }