aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.ts
diff options
context:
space:
mode:
Diffstat (limited to 'catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.ts')
-rw-r--r--catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.ts278
1 files changed, 278 insertions, 0 deletions
diff --git a/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.ts b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.ts
new file mode 100644
index 0000000000..6eb36287d0
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.ts
@@ -0,0 +1,278 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+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;
+ }
+}