import { OnInit, Directive, Input, ElementRef, AfterViewInit, Renderer2, OnDestroy, TemplateRef, ComponentFactory, ViewContainerRef, ComponentFactoryResolver, NgZone, ComponentRef } from '@angular/core'; import { TooltipComponent } from './tooltip-component/tooltip.component'; @Directive({ selector: '[farrisTooltip]' }) export class FarrisTooltipDirective implements OnInit, AfterViewInit, OnDestroy { private tooltipEnable: boolean = true; private _componentRef: ComponentRef; // 5月7日追加这种写法,控制禁用、启用 // 原来的时候value是空字符串 @Input('farrisTooltip') set enableTooltip(value: any) { if (typeof value == 'string' && value == '') { this.enableOrDisable(true); } else { this.enableOrDisable(value); } } get enableTooltip() { return this.tooltipEnable; } /* 提示文本 */ @Input() text = ''; // 提示框样式 @Input() tipCls = ''; // 提示框宽度 @Input() tipWidth = ''; /* 位置 */ @Input() placement = 'top'; private triggerMode = 'hover'; /* 触发方式 */ @Input() set trigger(value) { if (value) { if (value !== this.triggerMode) { this.triggerMode = value; this.unbindEvent(); this.bindEvent(); } } } get trigger() { return this.triggerMode; } /* 禁用---跟其他控件的属性容易冲突,逐渐废弃。 * 比如按钮可以设置禁用不触发点击,但想给出点击提示,这样就冲突了 */ @Input() set disable(value) { this.enableOrDisable(!value); } get disable() { return !this.tooltipEnable; } @Input() showTip: boolean = true; // 横向纠正的参照 @Input() rectifyReferenceH: Element | ElementRef | string; // 纵向纠正的参照 @Input() rectifyReferenceV: Element | ElementRef | string; @Input() delay = 0; /* 内容模板 */ @Input() content: string | TemplateRef; mouseenterEvent: any; mouseleaveEvent: any; clickEvent: any; clickOutEvent: any; focusDownEvent: any; focusUpEvent: any; docBody: any; // 是否绑定了事件 private hasBindEvent = false; private delayTimer = null; /* 生成提示组件 */ componentFactory: ComponentFactory = this.resolver.resolveComponentFactory(TooltipComponent); tooltipRef: any; tooltip: TooltipComponent; constructor( private el: ElementRef, private render: Renderer2, private viewContainerRef: ViewContainerRef, private resolver: ComponentFactoryResolver ) { } ngOnInit() { this.bindEvent(); } ngAfterViewInit(): void { } ngOnDestroy() { this.unbindEvent(); this.removeTooltip(); } private enableOrDisable(value) { this.tooltipEnable = value; if (this.tooltipEnable) { this.bindEvent(); } else { this.unbindEvent(); } } bindEvent() { if (!this.tooltipEnable) { return; } // 是否已绑定事件 if (this.hasBindEvent) { return; } if (this.trigger === 'click') { // 鼠标点击 this.clickEvent = this.render.listen(this.el.nativeElement, 'click', (e: MouseEvent) => { e.stopPropagation(); this.appendTooltip(); this.clickOutEvent = this.render.listen(document, 'click', () => { this.removeTooltip(); this.clickOutEvent(); }); }); } else if (this.trigger === 'focus') { // 聚焦 // @Todo 需要把input等组件和普通组件分开 this.focusDownEvent = this.render.listen(this.el.nativeElement, 'mousedown', (e: MouseEvent) => { e.preventDefault(); this.appendTooltip(); }); this.focusUpEvent = this.render.listen(this.el.nativeElement, 'mouseup', (e: MouseEvent) => { e.stopPropagation(); this.removeTooltip(); }); } else { // 鼠标移动上去 this.mouseenterEvent = this.render.listen(this.el.nativeElement, 'mouseenter', (e: MouseEvent) => { e.stopPropagation(); if (this.delay) { if (this.delayTimer) { clearTimeout(this.delayTimer); } this.delayTimer = setTimeout(() => { this.appendTooltip() clearTimeout(this.delayTimer); }, this.delay) } else { this.appendTooltip(); } }); this.mouseleaveEvent = this.render.listen(this.el.nativeElement, 'mouseleave', (e: MouseEvent) => { e.stopPropagation(); if (this.delayTimer) { clearTimeout(this.delayTimer); } this.removeTooltip(); }); } this.hasBindEvent = true; } /* 解绑事件 */ unbindEvent() { // 鼠标移上去 if (this.mouseenterEvent) { this.mouseenterEvent(); } if (this.mouseleaveEvent) { this.mouseleaveEvent(); } if (this.clickEvent) { this.clickEvent(); } if (this.clickOutEvent) { this.clickOutEvent(); } if (this.focusDownEvent) { this.focusDownEvent(); } if (this.focusUpEvent) { this.focusUpEvent(); } this.hasBindEvent = false; } /* body 移除tooltip */ removeTooltip() { if (this.tooltip) { this.render.removeChild(document.body, this.tooltip.el.nativeElement); this._componentRef.changeDetectorRef.markForCheck(); this._componentRef.changeDetectorRef.detectChanges(); this._componentRef.destroy(); this.viewContainerRef.clear(); this.tooltip = null; this._componentRef = null; } } private appendTooltip() { if (!this.showTip) return; this.generateTooltip(); this.updateViewProps(this.placement, this.content, this.el.nativeElement.getBoundingClientRect(),this.getReferencePosition()); } /* 更新tooltip组件属性 */ private updateViewProps(placement: string, content: string | TemplateRef, hostBoundingClientRect: object,referenceBoundingRect) { this.tooltip.placement = placement; this.tooltip.content = content; this.tooltip.hostBoundingClientRect = hostBoundingClientRect; this.tooltip.tipCls = this.tipCls; this.tooltip.tipWidth = this.tipWidth; this.tooltip.referenceBoundingRect=referenceBoundingRect; this._componentRef.changeDetectorRef.markForCheck(); this._componentRef.changeDetectorRef.detectChanges(); } /* 构造tooltip结构 */ private generateTooltip() { this._componentRef = this.viewContainerRef.createComponent(this.componentFactory) this.tooltip = this._componentRef.instance; // 父元素中移除 添加到body中 this.render.removeChild(this.render.parentNode(this.el.nativeElement), this.tooltip.el.nativeElement); this.render.appendChild(document.body, this.tooltip.el.nativeElement); } /** * 确认参照的边界 */ private getReferencePosition() { let rRight = document.documentElement.clientWidth; let rBottom = document.documentElement.clientHeight; let rTop = 0; let rLeft = 0; // 横向参照 if (this.rectifyReferenceH) { let rectifyReferenceHEl = this.getRectifyReferenceElement(this.rectifyReferenceH); rRight = rectifyReferenceHEl.getBoundingClientRect().right; rLeft = rectifyReferenceHEl.getBoundingClientRect().left; } // 纵向参照 if (this.rectifyReferenceV) { let rectifyReferenceVEl = this.getRectifyReferenceElement(this.rectifyReferenceV); rBottom = rectifyReferenceVEl.getBoundingClientRect().bottom; rTop = rectifyReferenceVEl.getBoundingClientRect().top; } return { top: rTop, left: rLeft, right: rRight, bottom: rBottom }; } /** * 获取纠正元素 */ private getRectifyReferenceElement(referenceEl) { if (referenceEl instanceof ElementRef) { return referenceEl.nativeElement; } else if (typeof referenceEl == 'string') { return document.querySelector(referenceEl as string) } return referenceEl } }