<!DOCTYPE html>
<html>
<head>
<title>Angular 2 Tooltip - Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSS file -->
<link rel="stylesheet" type="text/css" href="style.css">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" >
<!-- IE polyfills, keep the order please -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
<!-- Agular 2 -->
<script src="https://code.angularjs.org/2.0.0-beta.7/angular2-polyfills.js"></script>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.7/Rx.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.7/angular2.dev.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.min.js"></script>
<!-- Config Agular 2 and Typescript -->
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'app': {defaultExtension: 'ts'}}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
</head>
<!-- Run the application -->
<body>
<h1>Angular 2 Tooltip - Stars</h1>
<my-app class="container" style="display: block">Loading Sample...</my-app>
<div style="padding-top:50px">
<a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Tutorial">
<img src="http://www.angulartypescript.com/wp-content/uploads/2016/03/learn-more-angular-2.png" alt="Smiley face" height="200" width="500">
</a>
<ul class="nav nav-pills nav-stacked" >
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Home"> Angular 2 Tutorial </a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-introduction/">Angular 2 Introduction</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-architecture/">Angular 2 Architecture</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-annotations/">Angular 2 Annotations</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-getting-started/">Angular 2 Setup</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-hello-world/">Angular 2 Hello World</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-components/">Angular 2 Components</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-template-syntax/">Angular 2 Template Syntax</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-data-binding/">Angular 2 Data Binding</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-forms/">Angular 2 Forms</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-formbuilder-example/">Angular 2 Formbuilder</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-router-example/">Angular 2 Router</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-http-example-typescript/">Angular 2 HTTP</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-services/">Angular 2 Service</a></li>
</ul>
</div>
</body>
</html>
<!--
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/
-->
/* Styles go here */
/**
* Created by Tareq Boulakjar. from angulartypescript.com
*/
import {bootstrap} from 'angular2/platform/browser';
import {Angular2Tooltip} from './tooltipe-example';
import {
enableProdMode,
} from 'angular2/core';
enableProdMode();
bootstrap(Angular2Tooltip);
/*
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/
*/
import {Directive, TemplateRef, ViewContainerRef, Inject} from 'angular2/core';
export interface IAttribute {
[key: string]: any;
}
@Directive({
selector: '[ngTransclude]',
properties: ['ngTransclude']
})
export class NgTransclude {
private _ngTransclude: TemplateRef;
private set ngTransclude(templateRef:TemplateRef) {
this._ngTransclude = templateRef;
if (templateRef) {
this.viewRef.createEmbeddedView(templateRef);
}
}
private get ngTransclude() {
return this._ngTransclude;
}
constructor(@Inject(ViewContainerRef) public viewRef:ViewContainerRef) {
}
}
import {
Injectable,
ElementRef
} from 'angular2/core';
import {IAttribute} from './IAttribute';
export class PositionService {
private get window():any {
return window;
}
private get document():any {
return window.document;
}
private getStyle(nativeEl:any, cssProp:string):any {
// IE
if (nativeEl.currentStyle) {
return nativeEl.currentStyle[cssProp];
}
if (this.window.getComputedStyle) {
return this.window.getComputedStyle(nativeEl)[cssProp];
}
// finally try and get inline style
return nativeEl.style[cssProp];
}
/**
* Checks if a given element is statically positioned
* @param nativeEl - raw DOM element
*/
private isStaticPositioned(nativeEl:any):any {
return (this.getStyle(nativeEl, 'position') || 'static' ) === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param nativeEl
*/
private parentOffsetEl(nativeEl:any) {
let offsetParent = nativeEl.offsetParent || this.document;
while (offsetParent && offsetParent !== this.document &&
this.isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || this.document;
};
/**
* Provides read-only equivalent of jQuery's position function:
* http://api.jquery.com/position/
*/
public position(nativeEl:any):{width: number, height: number, top: number, left: number} {
let elBCR = this.offset(nativeEl);
let offsetParentBCR = {top: 0, left: 0};
let offsetParentEl = this.parentOffsetEl(nativeEl);
if (offsetParentEl !== this.document) {
offsetParentBCR = this.offset(offsetParentEl);
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
let boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
}
/**
* Provides read-only equivalent of jQuery's offset function:
* http://api.jquery.com/offset/
*/
public offset(nativeEl:any):{width: number, height: number, top: number, left: number} {
let boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: boundingClientRect.top + (this.window.pageYOffset || this.document.documentElement.scrollTop),
left: boundingClientRect.left + (this.window.pageXOffset || this.document.documentElement.scrollLeft)
};
}
/**
* Provides coordinates for the targetEl in relation to hostEl
*/
public positionElements(hostEl:any, targetEl:any, positionStr:any, appendToBody:any):{top: number, left: number} {
let positionStrParts = positionStr.split('-');
let pos0 = positionStrParts[0];
let pos1 = positionStrParts[1] || 'center';
let hostElPos = appendToBody ?
this.offset(hostEl) :
this.position(hostEl);
let targetElWidth = targetEl.offsetWidth;
let targetElHeight = targetEl.offsetHeight;
let shiftWidth:IAttribute = {
center: function () {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function () {
return hostElPos.left;
},
right: function () {
return hostElPos.left + hostElPos.width;
}
};
let shiftHeight:IAttribute = {
center: function ():number {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function ():number {
return hostElPos.top;
},
bottom: function ():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;
}
}
export const positionService = new PositionService();
import {
Directive,
OnInit, Input, HostListener,
ElementRef, EventEmitter,
DynamicComponentLoader, ComponentRef,
Provider,
Injectable, forwardRef, ResolvedBinding, Injector
} from 'angular2/core';
import {NgClass, NgStyle} from 'angular2/common';
import {TooltipOptions} from './tooltip-options.class';
import {TooltipContainer} from './tooltip-container.component';
import {IAttribute} from './IAttribute';
@Directive({selector: '[tooltip]'})
export class Tooltip implements OnInit {
@Input('tooltip') public content:string;
@Input('tooltipPlacement') public placement:string = 'top';
@Input('tooltipIsOpen') public isOpen:boolean;
@Input('tooltipEnable') public enable:boolean;
@Input('tooltipAppendToBody') public appendToBody:boolean;
private visible:boolean = false;
private tooltip:Promise<ComponentRef>;
constructor(public element:ElementRef,
public loader:DynamicComponentLoader) {
}
ngOnInit() {
}
@HostListener('focusin', ['$event', '$target'])
@HostListener('mouseenter', ['$event', '$target'])
show() {
if (this.visible) {
return;
}
this.visible = true;
let options = new TooltipOptions({
content: this.content,
placement: this.placement
});
let binding = Injector.resolve([
new Provider(TooltipOptions, {useValue: options})
]);
this.tooltip = this.loader
.loadNextToLocation(TooltipContainer, this.element, binding)
.then((componentRef:ComponentRef) => {
componentRef.instance.position(this.element);
return componentRef;
});
}
@HostListener('focusout', ['$event', '$target'])
@HostListener('mouseleave', ['$event', '$target'])
hide() {
if (!this.visible) {
return;
}
this.visible = false;
this.tooltip.then((componentRef:ComponentRef) => {
componentRef.dispose();
return componentRef;
});
}
}
import {
Component,
OnInit, Input, HostListener,
ElementRef, EventEmitter,
DynamicComponentLoader, ComponentRef, Inject, AfterViewChecked
} from 'angular2/core';
import {NgClass, NgStyle} from 'angular2/common';
import {positionService} from './position';
import {TooltipOptions} from './tooltip-options.class';
@Component({
selector: 'tooltip-container',
directives: [NgClass, NgStyle],
template: `<div class="tooltip" role="tooltip"
[ngStyle]="{top: top, left: left, display: display}"
[ngClass]="classMap">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">
{{content}}
</div>
</div>`
})
export class TooltipContainer implements AfterViewChecked {
private classMap:any;
private positionMap:any;
private top:string;
private left:string;
private display:string;
private content:string;
private placement:string;
private appendToBody:boolean;
private isOpen:boolean;
private hostEl:ElementRef;
constructor(public element:ElementRef, @Inject(TooltipOptions) options:TooltipOptions) {
Object.assign(this, options);
this.classMap = {'in': false};
this.classMap[options.placement] = true;
}
ngAfterViewChecked() {
if (this.hostEl !== null) {
let p = positionService
.positionElements(this.hostEl.nativeElement,
this.element.nativeElement.children[0],
this.placement, this.appendToBody);
this.top = p.top + 'px';
this.left = p.left + 'px';
this.classMap['in'] = true;
}
}
public position(hostEl:ElementRef) {
this.display = 'block';
this.top = '-1000px';
this.left = '-1000px';
this.hostEl = hostEl;
}
}
import {Injectable} from 'angular2/core';
@Injectable()
export class TooltipOptions {
public placement:string;
public popupClass:string;
public animation:boolean;
public isOpen:boolean;
constructor(options:Object) {
Object.assign(this, options);
}
}
/**
* Created by Tareq Boulakjar. from angulartypescript.com
*/
import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {Tooltip} from './tooltip.directive';
import {TooltipContainer} from './tooltip-container.component';
/*Angular 2 Tooltip Example*/
@Component({
selector: 'my-app',
template:`
<h4 style="color: #e85c45">Angular 2 Tooltip Example</h4>
<p>
Angular 2 rating <a href="#" tooltipPlacement="left" tooltip="stars (left)!">stars</a> will allow
the <a href="#" tooltipPlacement="right" tooltip="visitors (right)!">visitors</a>
of your website to <a href="#" tooltipPlacement="bottom" tooltip="rate (bottom)!">rate</a>
a <a href="#" tooltipPopupDelay='1000' tooltip='document (delay = 1000)'>document</a>
, <a href="#" tooltipPlacement="top" tooltip="image (top)!"> image </a> or something else by clicking on the stars.
</p>
<h4 style="color: #e86f4a">Angular 2 (Form) Tooltip Example</h4>
<form role="form">
<div class="form-group">
<label>Tooltip on <b>Input</b>: </label>
<input type="text" value="Show the tooltip !"
tooltip="Please enter a value : ) (i'm a tooltip)"
tooltipPlacement="bottom"
class="form-control" />
</div>
</form>
`,
directives: [Tooltip, TooltipContainer, CORE_DIRECTIVES, FORM_DIRECTIVES],
styles: [`
/* Specify styling for tooltip contents */
.tooltip.customClass .tooltip-inner {
color: #880000;
background-color: #ffff66;
box-shadow: 0 6px 12px rgba(0,0,0,.175);
}
/* Hide arrow */
.tooltip.customClass .tooltip-arrow {
display: none;
}
`]
})
export class Angular2Tooltip {
}