File
Metadata
| selector |
tooltip-content |
| template |
<div class="tooltip show tooltip-{{ placement }} no-wrap" [style.top]="top + 'px'" [style.left]="left + 'px'" role="tooltip"> <div class="tooltip-inner"> <ng-content></ng-content> <div [innerHTML]="content"></div> </div> </div> |
animation
|
Default value: false
|
hostElement
|
Type: HTMLElement
|
placement
|
Type: "top" | "bottom" | "left" | "right"
Default value: top
|
Methods
|
show
|
show()
|
|
Returns: void
|
|
hide
|
hide()
|
|
Returns: void
|
|
Private positionElements
|
positionElements(hostEl: HTMLElement, targetEl: HTMLElement, positionStr: string, appendToBody: boolean)
|
|
Returns: { top: number; left: number; }
|
|
Private position
|
position(nativeEl: HTMLElement)
|
|
Returns: { width: number; height: number; top: number; left: number; }
|
|
Private offset
|
offset(nativeEl: any)
|
|
Returns: { width: number; height: number; top: number; left: number; }
|
|
Private getStyle
|
getStyle(nativeEl: HTMLElement, cssProp: string)
|
|
Returns: string
|
|
Private isStaticPositioned
|
isStaticPositioned(nativeEl: HTMLElement)
|
|
Returns: boolean
|
|
Private parentOffsetEl
|
parentOffsetEl(nativeEl: HTMLElement)
|
|
Returns: any
|
|
isFade
|
isFade: boolean
|
Default value: false
|
|
isIn
|
isIn: boolean
|
Default value: false
|
import { Component, Input, AfterViewInit, ElementRef, ChangeDetectorRef } from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: 'tooltip-content',
template: `<div class="tooltip show tooltip-{{ placement }} no-wrap"
[style.top]="top + 'px'"
[style.left]="left + 'px'"
role="tooltip">
<div class="tooltip-inner">
<ng-content></ng-content>
<div [innerHTML]="content"></div>
</div>
</div>`
})
export class TooltipContentComponent implements AfterViewInit {
// -------------------------------------------------------------------------
// Inputs / Outputs
// -------------------------------------------------------------------------
@Input()
hostElement: HTMLElement;
@Input()
content: string;
@Input()
placement: 'top' | 'bottom' | 'left' | 'right' = 'top';
@Input()
animation = false;
// -------------------------------------------------------------------------
// Properties
// -------------------------------------------------------------------------
top = -100000;
left = -100000;
isIn = false;
isFade = false;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(
private element: ElementRef,
private cdr: ChangeDetectorRef) {
}
// -------------------------------------------------------------------------
// Lifecycle callbacks
// -------------------------------------------------------------------------
ngAfterViewInit(): void {
this.show();
this.cdr.detectChanges();
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
show(): void {
if (!this.hostElement) {
return;
}
const p = this.positionElements(this.hostElement, this.element.nativeElement.children[0], this.placement);
this.top = p.top;
this.left = p.left;
this.isIn = true;
if (this.animation) {
this.isFade = true;
}
}
hide(): void {
this.top = -100000;
this.left = -100000;
this.isIn = true;
if (this.animation) {
this.isFade = false;
}
}
// -------------------------------------------------------------------------
// Private Methods
// -------------------------------------------------------------------------
private positionElements(hostEl: HTMLElement, targetEl: HTMLElement, positionStr: string, appendToBody = false): { top: number, left: number } {
const positionStrParts = positionStr.split('-');
const pos0 = positionStrParts[0];
const pos1 = positionStrParts[1] || 'center';
const hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
const targetElWidth = targetEl.offsetWidth;
const targetElHeight = targetEl.offsetHeight;
const shiftWidth: any = {
center(): number {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left(): number {
return hostElPos.left;
},
right(): number {
return hostElPos.left + hostElPos.width;
}
};
const shiftHeight: any = {
center(): number {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top(): number {
return hostElPos.top;
},
bottom(): number {
return hostElPos.top + hostElPos.height;
}
};
let targetElPos: { top: number, left: number };
switch (pos0) {
case 'right':
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case 'left':
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case 'bottom':
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
private position(nativeEl: HTMLElement): { width: number, height: number, top: number, left: number } {
let offsetParentBCR = { top: 0, left: 0 };
const elBCR = this.offset(nativeEl);
const offsetParentEl = this.parentOffsetEl(nativeEl);
if (offsetParentEl !== window.document) {
offsetParentBCR = this.offset(offsetParentEl);
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
const boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
}
private offset(nativeEl: any): { width: number, height: number, top: number, left: number } {
const boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: boundingClientRect.top + (window.pageYOffset || window.document.documentElement.scrollTop),
left: boundingClientRect.left + (window.pageXOffset || window.document.documentElement.scrollLeft)
};
}
private getStyle(nativeEl: HTMLElement, cssProp: string): string {
if ((nativeEl as any).currentStyle) { // IE
return (nativeEl as any).currentStyle[cssProp];
}
if (window.getComputedStyle) {
return (window.getComputedStyle(nativeEl) as any)[cssProp];
}
// finally try and get inline style
return (nativeEl.style as any)[cssProp];
}
private isStaticPositioned(nativeEl: HTMLElement): boolean {
return (this.getStyle(nativeEl, 'position') || 'static') === 'static';
}
private parentOffsetEl(nativeEl: HTMLElement): any {
let offsetParent: any = nativeEl.offsetParent || window.document;
while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || window.document;
}
}