File

src/lib/tooltip/tooltip-content.component.ts

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>

Inputs

animation

Default value: false

content

Type: string

hostElement

Type: HTMLElement

placement

Type: "top" | "bottom" | "left" | "right"

Default value: top

Constructor

constructor(element: any, cdr: ChangeDetectorRef)

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

Properties

isFade
isFade: boolean
Default value: false
isIn
isIn: boolean
Default value: false
left
left: number
top
top: number
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;
    }
}

results matching ""

    No results matching ""