aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/ng2/components/tooltip
diff options
context:
space:
mode:
Diffstat (limited to 'catalog-ui/src/app/ng2/components/tooltip')
-rw-r--r--catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html12
-rw-r--r--catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less11
-rw-r--r--catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts195
-rw-r--r--catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts81
-rw-r--r--catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts25
5 files changed, 324 insertions, 0 deletions
diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html
new file mode 100644
index 0000000000..1fbf45e39f
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html
@@ -0,0 +1,12 @@
+<div class="tooltip {{ placement }}"
+ [style.top]="top + 'px'"
+ [style.left]="left + 'px'"
+ [class.in]="isIn"
+ [class.fade]="isFade"
+ role="tooltip">
+ <div class="tooltip-arrow"></div>
+ <div class="tooltip-inner">
+ <ng-content></ng-content>
+ {{ content }}
+ </div>
+</div>
diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less
new file mode 100644
index 0000000000..1ff496f840
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less
@@ -0,0 +1,11 @@
+.tooltip-inner {
+ word-wrap: break-word;
+ max-width: 300px;
+}
+
+.tooltip.bottom .tooltip-arrow {
+ border-bottom-color: #000 !important;
+}
+
+
+
diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts
new file mode 100644
index 0000000000..6e3e8065bb
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts
@@ -0,0 +1,195 @@
+import {Component, AfterViewInit, Input, ElementRef, ChangeDetectorRef} from "@angular/core";
+
+@Component
+({
+ selector: "tooltip-content",
+ templateUrl: "./tooltip-content.component.html",
+ styleUrls: ["./tooltip-content.component.less"]
+})
+
+export class TooltipContentComponent implements AfterViewInit {
+
+ // -------------------------------------------------------------------------
+ // Inputs / Outputs
+ // -------------------------------------------------------------------------
+
+ @Input() hostElement: HTMLElement;
+ @Input() content: string;
+ @Input() placement: "top"|"bottom"|"left"|"right" = "bottom";
+ @Input() animation: boolean = true;
+
+ // -------------------------------------------------------------------------
+ // Properties
+ // -------------------------------------------------------------------------
+
+ top: number = -100000;
+ left: number = -100000;
+ isIn: boolean = false;
+ isFade: boolean = 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 position = this.positionElement(this.hostElement, this.element.nativeElement.children[0], this.placement);
+ this.top = position.top;
+ this.left = position.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 positionElement(hostElem: HTMLElement, targetElem: HTMLElement, positionStr: string, appendToBody: boolean = false): {top: number, left: number} {
+ let positionStrParts = positionStr.split("-");
+ let pos0 = positionStrParts[0];
+ let pos1 = positionStrParts[1] || "center";
+ let hostElemPosition = appendToBody ? this.offset(hostElem) : this.position(hostElem);
+ let targetElemWidth = targetElem.offsetWidth;
+ let targetElemHeight = targetElem.offsetHeight;
+ let shiftWidth: any = {
+ center(): number {
+ return hostElemPosition.left + hostElemPosition.width / 2 - targetElemWidth / 2;
+ },
+ left(): number {
+ return hostElemPosition.left;
+ },
+ right(): number {
+ return hostElemPosition.left + hostElemPosition.width;
+ }
+ };
+
+ let shiftHeight: any = {
+ center: function (): number {
+ return hostElemPosition.top + hostElemPosition.height / 2 - targetElemHeight / 2;
+ },
+ top: function (): number {
+ return hostElemPosition.top;
+ },
+ bottom: function (): number {
+ return hostElemPosition.top + hostElemPosition.height;
+ }
+ }
+
+ let targetElemPosition: {top: number, left: number};
+
+ switch (pos0) {
+ case "right":
+ targetElemPosition = {
+ top: shiftHeight[pos1](),
+ left: shiftWidth[pos0]()
+ };
+ break;
+
+ case "left":
+ targetElemPosition = {
+ top: shiftHeight[pos1](),
+ left: hostElemPosition.left - targetElemWidth
+ };
+ break;
+
+ case "bottom":
+ targetElemPosition = {
+ top: shiftHeight[pos0](),
+ left: shiftWidth[pos1]()
+ };
+ break;
+
+ default:
+ targetElemPosition = {
+ top: hostElemPosition.top - targetElemHeight,
+ left: shiftWidth[pos1]()
+ };
+ break;
+ }
+
+ return targetElemPosition;
+ }
+
+
+ private position(nativeElem: HTMLElement): {width: number, height: number, top: number, left: number} {
+ let offsetParentCBR = {top: 0, left: 0};
+ const elemBCR = this.offset(nativeElem);
+ const offsetParentElem = this.parentOffsetElem(nativeElem);
+ if(offsetParentElem !== window.document) {
+ offsetParentCBR = this.offset(offsetParentElem);
+ offsetParentCBR.top += offsetParentElem.clientTop - offsetParentElem.scrollTop;
+ offsetParentCBR.left += offsetParentElem.clientLeft - offsetParentElem.scrollTop;
+ }
+
+ const boundingClientRect = nativeElem.getBoundingClientRect();
+
+ return {
+ width: boundingClientRect.width || nativeElem.offsetWidth,
+ height: boundingClientRect.height || nativeElem.offsetHeight,
+ top: elemBCR.top - offsetParentCBR.top,
+ left: elemBCR.left - offsetParentCBR.left
+ };
+ }
+
+ private offset(nativeElem:any): {width: number, height: number, top: number, left: number} {
+ const boundingClientRect = nativeElem.getBoundingClientRect();
+ return {
+ width: boundingClientRect.width || nativeElem.offsetWidth,
+ height: boundingClientRect.height || nativeElem.offsetHeight,
+ top: boundingClientRect.top + (window.pageYOffset || window.document.documentElement.scrollTop),
+ left: boundingClientRect.left + (window.pageXOffset || window.document.documentElement.scrollLeft)
+ };
+ }
+
+ private getStyle(nativeElem: HTMLElement, cssProperty: string): string {
+ if(window.getComputedStyle) {
+ return (window.getComputedStyle(nativeElem) as any)[cssProperty];
+ }
+
+ return (nativeElem.style as any)[cssProperty];
+ }
+
+ private isStaticPositioned(nativeElem: HTMLElement): boolean {
+ return (this.getStyle(nativeElem, "position") || "static") === "static";
+ }
+
+ private parentOffsetElem(nativeElem: HTMLElement): any {
+ let offsetParent: any = nativeElem.offsetParent || window.document;
+ while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) {
+ offsetParent = offsetParent.offsetParent;
+ }
+
+ return offsetParent || window.document;
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts b/catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts
new file mode 100644
index 0000000000..e98b69003e
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts
@@ -0,0 +1,81 @@
+import {
+ Directive, ComponentRef, ViewContainerRef, ComponentFactoryResolver, Input, HostListener
+} from "@angular/core";
+import {TooltipContentComponent} from "./tooltip-content.component";
+
+@Directive ({
+ selector: "[tooltip]"
+})
+export class TooltipComponent {
+
+ // -------------------------------------------------------------------------
+ // Properties
+ // -------------------------------------------------------------------------
+
+ private tooltip: ComponentRef<TooltipContentComponent>;
+ private visible: boolean;
+
+ // -------------------------------------------------------------------------
+ // Constructor
+ // -------------------------------------------------------------------------
+
+ constructor(private viewContainerRef: ViewContainerRef,
+ private resolver: ComponentFactoryResolver) {
+ }
+
+ // -------------------------------------------------------------------------
+ // Inputs / Outputs
+ // -------------------------------------------------------------------------
+
+ @Input("tooltip") content: string|TooltipContentComponent;
+ @Input() tooltipDisabled: boolean;
+ @Input() tooltipAnimation: boolean = true;
+ @Input() tooltipPlacement: "top"|"bottom"|"left"|"right" = "bottom";
+
+ // -------------------------------------------------------------------------
+ // Public Methods
+ // -------------------------------------------------------------------------
+
+ @HostListener("mouseenter")
+ show(): void {
+ if(this.tooltipDisabled || this.visible || this.content === "") {
+ return;
+ }
+
+ this.visible = true;
+ if (typeof this.content === "string") {
+ const factory = this.resolver.resolveComponentFactory(TooltipContentComponent);
+ if (!this.visible) {
+ return;
+ }
+
+ this.tooltip = this.viewContainerRef.createComponent(factory);
+ this.tooltip.instance.hostElement = this.viewContainerRef.element.nativeElement;
+ this.tooltip.instance.content = this.content as string;
+ this.tooltip.instance.placement = this.tooltipPlacement;
+ this.tooltip.instance.animation = this.tooltipAnimation;
+ } else {
+ const tooltip = this.content as TooltipContentComponent;
+ tooltip.hostElement = this.viewContainerRef.element.nativeElement;
+ tooltip.placement = this.tooltipPlacement;
+ tooltip.animation = this.tooltipAnimation;
+ tooltip.show();
+ }
+ }
+
+ @HostListener("mouseleave")
+ hide(): void {
+ if (!this.visible) {
+ return;
+ }
+
+ this.visible = false;
+ if (this.tooltip) {
+ this.tooltip.destroy();
+ }
+ if (this.content instanceof TooltipContentComponent) {
+ (this.content as TooltipContentComponent).hide();
+ }
+ }
+}
+
diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts b/catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts
new file mode 100644
index 0000000000..69976da6af
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts
@@ -0,0 +1,25 @@
+import {NgModule} from "@angular/core";
+import {TooltipContentComponent} from "./tooltip-content.component";
+import {TooltipComponent} from "./tooltip.component";
+import {CommonModule} from "@angular/common";
+
+@NgModule({
+ declarations: [
+ TooltipComponent,
+ TooltipContentComponent,
+ ],
+ imports: [
+ CommonModule
+ ],
+ exports: [
+ TooltipComponent,
+ TooltipContentComponent,
+ ],
+ entryComponents: [
+ TooltipContentComponent
+ ],
+ providers: []
+})
+export class TooltipModule {
+
+}