summaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/ng2/components/popover
diff options
context:
space:
mode:
authorMichael Lando <ml636r@att.com>2017-06-09 03:19:04 +0300
committerMichael Lando <ml636r@att.com>2017-06-09 03:19:04 +0300
commited64b5edff15e702493df21aa3230b81593e6133 (patch)
treea4cb01fdaccc34930a8db403a3097c0d1e40914b /catalog-ui/src/app/ng2/components/popover
parent280f8015d06af1f41a3ef12e8300801c7a5e0d54 (diff)
[SDC-29] catalog 1707 rebase commit.
Change-Id: I43c3dc5cf44abf5da817649bc738938a3e8388c1 Signed-off-by: Michael Lando <ml636r@att.com>
Diffstat (limited to 'catalog-ui/src/app/ng2/components/popover')
-rw-r--r--catalog-ui/src/app/ng2/components/popover/popover-content.component.html24
-rw-r--r--catalog-ui/src/app/ng2/components/popover/popover-content.component.less73
-rw-r--r--catalog-ui/src/app/ng2/components/popover/popover-content.component.ts258
-rw-r--r--catalog-ui/src/app/ng2/components/popover/popover.component.ts159
-rw-r--r--catalog-ui/src/app/ng2/components/popover/popover.module.ts27
5 files changed, 541 insertions, 0 deletions
diff --git a/catalog-ui/src/app/ng2/components/popover/popover-content.component.html b/catalog-ui/src/app/ng2/components/popover/popover-content.component.html
new file mode 100644
index 0000000000..6d76f0ad06
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/popover/popover-content.component.html
@@ -0,0 +1,24 @@
+<div #popoverDiv class="popover {{ effectivePlacement }}"
+ [style.top]="top + 'px'"
+ [style.left]="left + 'px'"
+ [class.in]="isIn"
+ [class.fade]="animation"
+ style="display: block"
+ role="popover"
+ [ngClass]="{'hide-arrow':hideArrow}">
+ <div [hidden]="!closeOnMouseOutside" class="virtual-area"></div>
+ <div class="arrow" *ngIf="!hideArrow"></div>
+ <div class="popover-header">
+ <span class="title">{{ title }}</span>
+ <span class="close-button" (click)="popover.hide()"></span>
+ </div>
+ <ng-content></ng-content>
+ <div class="popover-footer">
+ <button *ngFor="let buttonName of buttonsNames"
+ class="tlv-btn {{buttons[buttonName].cssClass}}"
+ [disabled] = "buttons[buttonName].getDisabled && buttons[buttonName].getDisabled()"
+ (click) = "buttons[buttonName].callback()">{{buttons[buttonName].text}}</button>
+ </div>
+</div>
+
+
diff --git a/catalog-ui/src/app/ng2/components/popover/popover-content.component.less b/catalog-ui/src/app/ng2/components/popover/popover-content.component.less
new file mode 100644
index 0000000000..f7b62e91f7
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/popover/popover-content.component.less
@@ -0,0 +1,73 @@
+@import '../../../../assets/styles/variables';
+@import '../../../../assets/styles/mixins';
+@import '../../../../assets/styles/sprite-old';
+.popover .virtual-area {
+ height: 11px;
+ width: 100%;
+ position: absolute;
+}
+.popover.top .virtual-area {
+ bottom: -11px;
+}
+.popover.bottom .virtual-area {
+ top: -11px;
+}
+.popover.left .virtual-area {
+ right: -11px;
+}
+.popover.right .virtual-area {
+ left: -11px;
+}
+.popover.hide-arrow{
+ margin: 0;
+}
+
+.popover-header{
+ .m_14_m;
+ font-weight: bold;
+ -webkit-box-flex: 1;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ height: 40px;
+ line-height: 48px;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ text-align: left;
+ border-bottom: solid 1px @main_color_o;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ margin: 0px 20px;
+ .title{
+ -webkit-box-flex: 999;
+ -ms-flex-positive: 999;
+ flex-grow: 999;
+ }
+ .close-button{
+ .sprite;
+ .sprite.x-btn-black;
+ cursor: pointer;
+ }
+}
+
+.popover-footer{
+ background-color: @tlv_color_t;
+ height: 40px;
+ clear: both;
+ -webkit-box-flex: 1;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: end;
+ -ms-flex-pack: end;
+ justify-content: flex-end;
+ button{
+ margin: 8px 12px 8px 6px;
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/popover/popover-content.component.ts b/catalog-ui/src/app/ng2/components/popover/popover-content.component.ts
new file mode 100644
index 0000000000..c4489f59b9
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/popover/popover-content.component.ts
@@ -0,0 +1,258 @@
+import {Component, Input, Output, AfterViewInit, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, EventEmitter, Renderer } from "@angular/core";
+import {ButtonsModelMap} from "app/models";
+import {PopoverComponent} from "./popover.component";
+
+@Component({
+ selector: "popover-content",
+ templateUrl:'./popover-content.component.html',
+ styleUrls:['popover-content.component.less']
+})
+export class PopoverContentComponent implements AfterViewInit, OnDestroy {
+
+ @Input() public title: string;
+ @Input() public buttons:ButtonsModelMap;
+
+ @Input()
+ content: string;
+
+ @Input()
+ placement: "top"|"bottom"|"left"|"right"|"auto"|"auto top"|"auto bottom"|"auto left"|"auto right" = "bottom";
+
+ @Input()
+ animation: boolean = true;
+
+ @Input()
+ closeOnClickOutside: boolean = false;
+
+ @Input()
+ closeOnMouseOutside: boolean = false;
+
+ @Input()
+ hideArrow: boolean = false;
+
+ @ViewChild("popoverDiv")
+ popoverDiv: ElementRef;
+
+ buttonsNames:Array<string>;
+
+ popover: PopoverComponent;
+ onCloseFromOutside = new EventEmitter();
+ top: number = -10000;
+ left: number = -10000;
+ isIn: boolean = false;
+ displayType: string = "none";
+ effectivePlacement: string;
+
+ onDocumentMouseDown = (event: any) => {
+ const element = this.element.nativeElement;
+ if (!element || !this.popover) return;
+ if (element.contains(event.target) || this.popover.getElement().contains(event.target)) return;
+ this.hide();
+ this.onCloseFromOutside.emit(undefined);
+ };
+
+
+ constructor(protected element: ElementRef,
+ protected cdr: ChangeDetectorRef,
+ protected renderer: Renderer) {
+ }
+
+ listenClickFunc: any;
+ listenMouseFunc: any;
+
+ ngAfterViewInit(): void {
+ this.buttonsNames = Object.keys(this.buttons);
+ if (this.closeOnClickOutside)
+ this.listenClickFunc = this.renderer.listenGlobal("document", "mousedown", (event: any) => this.onDocumentMouseDown(event));
+ if (this.closeOnMouseOutside)
+ this.listenMouseFunc = this.renderer.listenGlobal("document", "mouseover", (event: any) => this.onDocumentMouseDown(event));
+
+ this.show();
+ this.cdr.detectChanges();
+ }
+
+ ngOnDestroy() {
+ if (this.closeOnClickOutside)
+ this.listenClickFunc();
+ if (this.closeOnMouseOutside)
+ this.listenMouseFunc();
+ }
+
+ // -------------------------------------------------------------------------
+ // Public Methods
+ // -------------------------------------------------------------------------
+
+ show(): void {
+ if (!this.popover || !this.popover.getElement())
+ return;
+
+ const p = this.positionElements(this.popover.getElement(), this.popoverDiv.nativeElement, this.placement);
+ this.displayType = "block";
+ this.top = p.top;
+ this.left = p.left;
+ this.isIn = true;
+ }
+
+ hide(): void {
+ this.top = -10000;
+ this.left = -10000;
+ this.isIn = true;
+ this.popover.hide();
+ }
+
+ hideFromPopover() {
+ this.top = -10000;
+ this.left = -10000;
+ this.isIn = true;
+ }
+
+ // -------------------------------------------------------------------------
+ // Protected Methods
+ // -------------------------------------------------------------------------
+
+ protected positionElements(hostEl: HTMLElement, targetEl: HTMLElement, positionStr: string, appendToBody: boolean = false): { 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;
+
+ this.effectivePlacement = pos0 = this.getEffectivePlacement(pos0, hostEl, targetEl);
+
+ let shiftWidth: any = {
+ center: function (): number {
+ return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
+ },
+ left: function (): number {
+ return hostElPos.left;
+ },
+ right: function (): number {
+ return hostElPos.left + hostElPos.width - targetElWidth;
+ }
+ };
+
+ let shiftHeight: any = {
+ 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 - targetElHeight;
+ }
+ };
+
+ let targetElPos: { top: number, left: number };
+ switch (pos0) {
+ case "right":
+ targetElPos = {
+ top: shiftHeight[pos1](),
+ left: hostElPos.left + hostElPos.width
+ };
+ break;
+
+ case "left":
+ targetElPos = {
+ top: shiftHeight[pos1](),
+ left: hostElPos.left - targetElWidth
+ };
+ break;
+
+ case "bottom":
+ targetElPos = {
+ top: hostElPos.top + hostElPos.height,
+ left: shiftWidth[pos1]()
+ };
+ break;
+
+ default:
+ targetElPos = {
+ top: hostElPos.top - targetElHeight,
+ left: shiftWidth[pos1]()
+ };
+ break;
+ }
+
+ return targetElPos;
+ }
+
+ protected 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
+ };
+ }
+
+ protected 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)
+ };
+ }
+
+ protected getStyle(nativeEl: HTMLElement, cssProp: string): string {
+ if ((nativeEl as any).currentStyle) // IE
+ return (nativeEl as any).currentStyle[cssProp];
+
+ if (window.getComputedStyle)
+ return (window.getComputedStyle as any)(nativeEl)[cssProp];
+
+ // finally try and get inline style
+ return (nativeEl.style as any)[cssProp];
+ }
+
+ protected isStaticPositioned(nativeEl: HTMLElement): boolean {
+ return (this.getStyle(nativeEl, "position") || "static" ) === "static";
+ }
+
+ protected 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;
+ }
+
+ protected getEffectivePlacement(placement: string, hostElement: HTMLElement, targetElement: HTMLElement): string {
+ const placementParts = placement.split(" ");
+ if (placementParts[0] !== "auto") {
+ return placement;
+ }
+
+ const hostElBoundingRect = hostElement.getBoundingClientRect();
+
+ const desiredPlacement = placementParts[1] || "bottom";
+
+ if (desiredPlacement === "top" && hostElBoundingRect.top - targetElement.offsetHeight < 0) {
+ return "bottom";
+ }
+ if (desiredPlacement === "bottom" && hostElBoundingRect.bottom + targetElement.offsetHeight > window.innerHeight) {
+ return "top";
+ }
+ if (desiredPlacement === "left" && hostElBoundingRect.left - targetElement.offsetWidth < 0) {
+ return "right";
+ }
+ if (desiredPlacement === "right" && hostElBoundingRect.right + targetElement.offsetWidth > window.innerWidth) {
+ return "left";
+ }
+
+ return desiredPlacement;
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/popover/popover.component.ts b/catalog-ui/src/app/ng2/components/popover/popover.component.ts
new file mode 100644
index 0000000000..a7e2881b29
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/popover/popover.component.ts
@@ -0,0 +1,159 @@
+import { Directive, HostListener, ComponentRef, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, Input, OnChanges, SimpleChange, Output, EventEmitter } from "@angular/core";
+import {PopoverContentComponent} from "./popover-content.component";
+
+@Directive({
+ selector: "[popover]",
+ exportAs: "popover"
+})
+export class PopoverComponent implements OnChanges {
+
+ protected PopoverComponent = PopoverContentComponent;
+ protected popover: ComponentRef<PopoverContentComponent>;
+ protected visible: boolean;
+
+
+ constructor(protected viewContainerRef: ViewContainerRef,
+ protected resolver: ComponentFactoryResolver) {
+ }
+
+ @Input("popover")
+ content: string|PopoverContentComponent;
+
+ @Input()
+ popoverDisabled: boolean;
+
+ @Input()
+ popoverAnimation: boolean;
+
+ @Input()
+ popoverPlacement: "top"|"bottom"|"left"|"right"|"auto"|"auto top"|"auto bottom"|"auto left"|"auto right";
+
+ @Input()
+ popoverTitle: string;
+
+ @Input()
+ popoverOnHover: boolean = false;
+
+ @Input()
+ popoverCloseOnClickOutside: boolean;
+
+ @Input()
+ popoverCloseOnMouseOutside: boolean;
+
+ @Input()
+ popoverDismissTimeout: number = 0;
+
+ @Output()
+ onShown = new EventEmitter<PopoverComponent>();
+
+ @Output()
+ onHidden = new EventEmitter<PopoverComponent>();
+
+ @HostListener("click")
+ showOrHideOnClick(): void {
+ if (this.popoverOnHover) return;
+ if (this.popoverDisabled) return;
+ this.toggle();
+ }
+
+ @HostListener("focusin")
+ @HostListener("mouseenter")
+ showOnHover(): void {
+ if (!this.popoverOnHover) return;
+ if (this.popoverDisabled) return;
+ this.show();
+ }
+
+ @HostListener("focusout")
+ @HostListener("mouseleave")
+ hideOnHover(): void {
+ if (this.popoverCloseOnMouseOutside) return;
+ if (!this.popoverOnHover) return;
+ if (this.popoverDisabled) return;
+ this.hide();
+ }
+
+ ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
+ if (changes["popoverDisabled"]) {
+ if (changes["popoverDisabled"].currentValue) {
+ this.hide();
+ }
+ }
+ }
+
+ toggle() {
+ if (!this.visible) {
+ this.show();
+ } else {
+ this.hide();
+ }
+ }
+
+ show() {
+ if (this.visible) return;
+
+ this.visible = true;
+ if (typeof this.content === "string") {
+ const factory = this.resolver.resolveComponentFactory(this.PopoverComponent);
+ if (!this.visible)
+ return;
+
+ this.popover = this.viewContainerRef.createComponent(factory);
+ const popover = this.popover.instance as PopoverContentComponent;
+ popover.popover = this;
+ popover.content = this.content as string;
+ if (this.popoverPlacement !== undefined)
+ popover.placement = this.popoverPlacement;
+ if (this.popoverAnimation !== undefined)
+ popover.animation = this.popoverAnimation;
+ if (this.popoverTitle !== undefined)
+ popover.title = this.popoverTitle;
+ if (this.popoverCloseOnClickOutside !== undefined)
+ popover.closeOnClickOutside = this.popoverCloseOnClickOutside;
+ if (this.popoverCloseOnMouseOutside !== undefined)
+ popover.closeOnMouseOutside = this.popoverCloseOnMouseOutside;
+
+ popover.onCloseFromOutside.subscribe(() => this.hide());
+ if (this.popoverDismissTimeout > 0)
+ setTimeout(() => this.hide(), this.popoverDismissTimeout);
+ } else {
+ const popover = this.content as PopoverContentComponent;
+ popover.popover = this;
+ if (this.popoverPlacement !== undefined)
+ popover.placement = this.popoverPlacement;
+ if (this.popoverAnimation !== undefined)
+ popover.animation = this.popoverAnimation;
+ if (this.popoverTitle !== undefined)
+ popover.title = this.popoverTitle;
+ if (this.popoverCloseOnClickOutside !== undefined)
+ popover.closeOnClickOutside = this.popoverCloseOnClickOutside;
+ if (this.popoverCloseOnMouseOutside !== undefined)
+ popover.closeOnMouseOutside = this.popoverCloseOnMouseOutside;
+
+ popover.onCloseFromOutside.subscribe(() => this.hide());
+ if (this.popoverDismissTimeout > 0)
+ setTimeout(() => this.hide(), this.popoverDismissTimeout);
+ popover.show();
+ }
+
+ this.onShown.emit(this);
+ }
+
+ hide() {
+ if (!this.visible) return;
+
+ this.visible = false;
+ if (this.popover)
+ this.popover.destroy();
+
+ if (this.content instanceof PopoverContentComponent)
+ (this.content as PopoverContentComponent).hideFromPopover();
+
+ this.onHidden.emit(this);
+ }
+
+ getElement() {
+ return this.viewContainerRef.element.nativeElement;
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/components/popover/popover.module.ts b/catalog-ui/src/app/ng2/components/popover/popover.module.ts
new file mode 100644
index 0000000000..4bd8426ce1
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/popover/popover.module.ts
@@ -0,0 +1,27 @@
+/**
+ * Created by rc2122 on 5/17/2017.
+ */
+import {NgModule} from "@angular/core";
+import { CommonModule } from '@angular/common';
+import {PopoverComponent} from "./popover.component";
+import {PopoverContentComponent} from "./popover-content.component";
+
+@NgModule({
+ declarations: [
+ PopoverComponent,
+ PopoverContentComponent
+ ],
+ imports: [
+ // PopoverComponent,
+ // PopoverContentComponent
+ CommonModule
+ ],
+ exports: [
+ PopoverComponent,
+ PopoverContentComponent
+ ],
+ providers: []
+})
+export class PopoverModule {
+
+}