ngx-smart-popover
Version:
Simple popover control for your angular (v4+) applications using bootstrap4 with smart reflection logic when overflowing the viewport. This is a continuation of ngx-popover (https://github.com/pleerock/ngx-popover). If you want to use it without bootstrap
1,146 lines (1,138 loc) • 42.8 kB
JavaScript
import { EventEmitter, Component, ElementRef, ChangeDetectorRef, Renderer2, Input, ViewChild, HostListener, Directive, ViewContainerRef, ComponentFactoryResolver, ApplicationRef, Injector, Output, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* Generated from: lib/popover.placement.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class PopoverPlacement {
}
PopoverPlacement.Bottom = 'bottom';
PopoverPlacement.BottomLeft = 'bottom-left';
PopoverPlacement.BottomRight = 'bottom-right';
PopoverPlacement.Left = 'left';
PopoverPlacement.Right = 'right';
PopoverPlacement.Top = 'top';
PopoverPlacement.TopLeft = 'top-left';
PopoverPlacement.TopRight = 'top-right';
/**
* @fileoverview added by tsickle
* Generated from: lib/popover-content.component.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class PopoverContentComponent {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
/**
* @param {?} element
* @param {?} cdr
* @param {?} renderer
*/
constructor(element, cdr, renderer) {
this.element = element;
this.cdr = cdr;
this.renderer = renderer;
this.placement = PopoverPlacement.Top;
this.animation = true;
this.closeOnClickOutside = false;
this.closeOnMouseOutside = false;
this.appendToBody = false;
this.size = 'small';
this.onCloseFromOutside = new EventEmitter();
this.top = -10000;
this.left = -10000;
this.isIn = false;
this.opacity = 0;
this.transitionEnabled = false;
this.windowWidth = window.innerWidth;
this.windowHeight = window.innerHeight;
// -------------------------------------------------------------------------
// Anonymous
// -------------------------------------------------------------------------
/**
* Closes dropdown if user clicks outside of this directive.
*/
this.onDocumentMouseDown = (/**
* @param {?} event
* @return {?}
*/
(event) => {
/** @type {?} */
const element = this.element.nativeElement;
if (!element || !this.popover) {
return;
}
if (element.contains(event.target) || this.popover.getElement().contains(event.target)) {
return;
}
this.onCloseFromOutside.emit(undefined);
});
}
// -------------------------------------------------------------------------
// Lifecycle callbacks
// -------------------------------------------------------------------------
/**
* @return {?}
*/
ngAfterViewInit() {
if (this.closeOnClickOutside) {
this.listenClickFunc = this.renderer.listen('document', 'mousedown', (/**
* @param {?} event
* @return {?}
*/
(event) => this.onDocumentMouseDown(event)));
}
if (this.closeOnMouseOutside) {
this.listenMouseFunc = this.renderer.listen('document', 'mouseover', (/**
* @param {?} event
* @return {?}
*/
(event) => this.onDocumentMouseDown(event)));
}
// Always close on mobile touch event outside.
this.listenTouchFunc = this.renderer.listen('document', 'touchstart', (/**
* @param {?} event
* @return {?}
*/
(event) => this.onDocumentMouseDown(event)));
this.show();
this.cdr.detectChanges();
}
/**
* @return {?}
*/
ngOnDestroy() {
if (this.closeOnClickOutside && this.listenClickFunc) {
this.listenClickFunc();
}
if (this.closeOnMouseOutside && this.listenMouseFunc) {
this.listenMouseFunc();
}
if (!!this.listenTouchFunc) {
this.listenTouchFunc();
}
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* @param {?} event
* @return {?}
*/
onResize(event) {
this.windowWidth = window.innerWidth;
this.windowHeight = window.innerHeight;
}
/**
* @return {?}
*/
updatePosition() {
// if visible, reposition
if (this.opacity) {
/** @type {?} */
const p = this.positionElements(this.popover.getElement(), this.popoverDiv.nativeElement, this.placement);
this.top = p.top;
this.left = p.left;
}
}
/**
* @return {?}
*/
show() {
if (!this.popover || !this.popover.getElement()) {
return;
}
/** @type {?} */
const p = this.positionElements(this.popover.getElement(), this.popoverDiv.nativeElement, this.placement);
this.top = p.top;
this.left = p.left;
this.isIn = true;
this.transitionEnabled = true;
this.opacity = 1;
}
/**
* @return {?}
*/
hide() {
this.top = -10000;
this.left = -10000;
this.isIn = true;
this.popover.hide();
}
/**
* @return {?}
*/
hideFromPopover() {
this.top = -10000;
this.left = -10000;
this.isIn = true;
this.transitionEnabled = false;
this.opacity = 0;
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* @protected
* @param {?} hostEl
* @param {?} targetEl
* @param {?} positionStr
* @param {?=} appendToBody
* @return {?}
*/
positionElements(hostEl, targetEl, positionStr, appendToBody = false) {
/** @type {?} */
const positionStrParts = ((/** @type {?} */ (positionStr))).split(' ');
/** @type {?} */
let pos0 = positionStrParts[0];
/** @type {?} */
const pos1 = positionStrParts[1] || 'center';
/** @type {?} */
const hostElPos = this.appendToBody || appendToBody ? this.offset(hostEl) : this.position(hostEl);
/** @type {?} */
const targetElWidth = targetEl.offsetWidth;
/** @type {?} */
const targetElHeight = targetEl.offsetHeight;
this.effectivePlacement = pos0 = this.getEffectivePlacement(pos0, hostEl, targetEl);
/** @type {?} */
const shiftWidth = {
center: (/**
* @return {?}
*/
function () {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
}),
left: (/**
* @return {?}
*/
function () {
return hostElPos.left;
}),
right: (/**
* @return {?}
*/
function () {
return hostElPos.left + hostElPos.width;
}),
topOrBottomRight: (/**
* @return {?}
*/
function () {
return hostElPos.left + hostElPos.width / 2;
}),
topOrBottomLeft: (/**
* @return {?}
*/
function () {
return hostElPos.left - targetElWidth + hostElPos.width / 2;
})
};
/** @type {?} */
const shiftHeight = {
center: (/**
* @return {?}
*/
function () {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
}),
top: (/**
* @return {?}
*/
function () {
return hostElPos.top;
}),
bottom: (/**
* @return {?}
*/
function () {
return hostElPos.top + hostElPos.height;
})
};
/** @type {?} */
let targetElPos;
switch (pos0) {
case PopoverPlacement.Right:
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case PopoverPlacement.Left:
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case PopoverPlacement.Bottom:
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
case PopoverPlacement.TopLeft:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth['topOrBottomLeft']()
};
break;
case PopoverPlacement.TopRight:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth['topOrBottomRight']()
};
break;
case PopoverPlacement.BottomLeft:
targetElPos = {
top: shiftHeight[PopoverPlacement.Bottom](),
left: shiftWidth['topOrBottomLeft']()
};
break;
case PopoverPlacement.BottomRight:
targetElPos = {
top: shiftHeight[PopoverPlacement.Bottom](),
left: shiftWidth['topOrBottomRight']()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
/**
* @protected
* @param {?} nativeEl
* @return {?}
*/
position(nativeEl) {
/** @type {?} */
let offsetParentBCR = { top: 0, left: 0 };
/** @type {?} */
const elBCR = this.offset(nativeEl);
/** @type {?} */
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;
}
/** @type {?} */
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
* @param {?} nativeEl
* @return {?}
*/
offset(nativeEl) {
/** @type {?} */
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
* @param {?} nativeEl
* @param {?} cssProp
* @return {?}
*/
getStyle(nativeEl, cssProp) {
if (((/** @type {?} */ (nativeEl))).currentStyle) { // IE
return ((/** @type {?} */ (nativeEl))).currentStyle[cssProp];
}
if (window.getComputedStyle) {
return ((/** @type {?} */ (window.getComputedStyle)))(nativeEl)[cssProp];
}
// finally try and get inline style
return ((/** @type {?} */ (nativeEl.style)))[cssProp];
}
/**
* @protected
* @param {?} nativeEl
* @return {?}
*/
isStaticPositioned(nativeEl) {
return (this.getStyle(nativeEl, 'position') || 'static') === 'static';
}
/**
* @protected
* @param {?} nativeEl
* @return {?}
*/
parentOffsetEl(nativeEl) {
/** @type {?} */
let offsetParent = nativeEl.offsetParent || window.document;
while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || window.document;
}
// Check for overflow of the viewport and reflect the position if necessary.
/**
* @protected
* @param {?} placement
* @param {?} hostElement
* @param {?} targetElement
* @return {?}
*/
getEffectivePlacement(placement, hostElement, targetElement) {
/** @type {?} */
const hostElBoundingRect = hostElement.getBoundingClientRect();
/** @type {?} */
const desiredPlacement = placement || PopoverPlacement.Top;
// Determines if a popover overflows in a direction when in a specific position.
/** @type {?} */
const overflows = {
positionTop: {
top: hostElBoundingRect.top - targetElement.offsetHeight < 0,
right: hostElBoundingRect.left + hostElBoundingRect.width / 2 + targetElement.offsetWidth / 2 > this.windowWidth,
left: hostElBoundingRect.left + hostElBoundingRect.width / 2 - targetElement.offsetWidth / 2 < 0
},
positionTopRight: {
top: hostElBoundingRect.top - targetElement.offsetHeight < 0,
right: hostElBoundingRect.right + targetElement.offsetWidth > this.windowWidth
},
positionRight: {
top: hostElBoundingRect.top + hostElBoundingRect.height / 2 - targetElement.offsetHeight / 2 < 0,
right: hostElBoundingRect.right + targetElement.offsetWidth > this.windowWidth,
bottom: hostElBoundingRect.top + hostElBoundingRect.height / 2 + targetElement.offsetHeight / 2 > this.windowHeight
},
positionBottomRight: {
right: hostElBoundingRect.right + targetElement.offsetWidth > this.windowWidth,
bottom: hostElBoundingRect.bottom + targetElement.offsetHeight > this.windowHeight
},
positionBottom: {
right: hostElBoundingRect.left + hostElBoundingRect.width / 2 + targetElement.offsetWidth / 2 > this.windowWidth,
bottom: hostElBoundingRect.bottom + targetElement.offsetHeight > this.windowHeight,
left: hostElBoundingRect.left + hostElBoundingRect.width / 2 - targetElement.offsetWidth / 2 < 0
},
positionBottomLeft: {
bottom: hostElBoundingRect.bottom + targetElement.offsetHeight > this.windowHeight,
left: hostElBoundingRect.left - targetElement.offsetWidth < 0
},
positionLeft: {
left: hostElBoundingRect.left < targetElement.offsetWidth,
top: hostElBoundingRect.top + hostElBoundingRect.height / 2 - targetElement.offsetHeight / 2 < 0,
bottom: hostElBoundingRect.top + hostElBoundingRect.height / 2 + targetElement.offsetHeight / 2 > this.windowHeight
},
positionTopLeft: {
top: hostElBoundingRect.top - targetElement.offsetHeight < 0,
left: hostElBoundingRect.left - targetElement.offsetWidth < 0
}
};
if (desiredPlacement === PopoverPlacement.Top) {
// If it overflows on the top AND left, go to bottom-right.
if (overflows.positionTop.top && overflows.positionTop.left) {
return PopoverPlacement.BottomRight;
// If it overflows on the top AND right, go to bottom-left.
}
else if (overflows.positionTop.top && overflows.positionTop.right) {
return PopoverPlacement.BottomLeft;
// If it only overflows on the top, go to bottom.
}
else if (overflows.positionTop.top) {
return PopoverPlacement.Bottom;
// If it only overflows to the right, go to top-left.
}
else if (overflows.positionTop.right) {
return PopoverPlacement.TopLeft;
// If it only overflows to the left, go to top-right.
}
else if (overflows.positionTop.left) {
return PopoverPlacement.TopRight;
}
else {
return PopoverPlacement.Top;
}
}
if (desiredPlacement === PopoverPlacement.TopRight) {
// If it overflows on the top AND the right, try in the order: bottom, Bottom-left, left.
if (overflows.positionTopRight.top && overflows.positionTopRight.right) {
if (overflows.positionBottom.bottom) {
return PopoverPlacement.Left;
}
else if (overflows.positionBottom.right) {
return PopoverPlacement.BottomLeft;
}
else {
return PopoverPlacement.Bottom;
}
// If it only overflows on the top, try in the order: right, bottom-right.
}
else if (overflows.positionTopRight.top) {
if (overflows.positionRight.top) {
return PopoverPlacement.BottomRight;
}
else {
return PopoverPlacement.Right;
}
// If it only overflows on the right, try in the order: top, top-left.
}
else if (overflows.positionTopRight.right) {
if (overflows.positionTop.right) {
return PopoverPlacement.TopLeft;
}
else {
return PopoverPlacement.Top;
}
}
else {
return PopoverPlacement.TopRight;
}
}
if (desiredPlacement === PopoverPlacement.Right) {
// If it overflows on the right AND the top, try in the order: bottom-right, bottom, bottom-left.
if (overflows.positionRight.right && overflows.positionRight.top) {
if (overflows.positionBottomRight.right && overflows.positionBottom.right) {
return PopoverPlacement.BottomLeft;
}
else if (overflows.positionBottomRight.right) {
return PopoverPlacement.Bottom;
}
else {
return PopoverPlacement.BottomRight;
}
// If it overflows on the right AND the bottom, try in the order: top-right, top, top-left.
}
else if (overflows.positionRight.right && overflows.positionRight.bottom) {
if (overflows.positionTopRight.right && overflows.positionTop.right) {
return PopoverPlacement.TopLeft;
}
else if (overflows.positionTopRight.right) {
return PopoverPlacement.Top;
}
else {
return PopoverPlacement.TopRight;
}
// If it only overflows on the right, try all top positions from right to left, then try all bottom positions right to left.
}
else if (overflows.positionRight.right) {
if (overflows.positionTop.top) {
if (overflows.positionBottom.right) {
return PopoverPlacement.BottomLeft;
}
else if (overflows.positionBottomRight.right) {
return PopoverPlacement.Bottom;
}
else {
return PopoverPlacement.BottomRight;
}
}
else {
if (overflows.positionTop.right) {
return PopoverPlacement.TopLeft;
}
else if (overflows.positionTopRight.right) {
return PopoverPlacement.Top;
}
else {
return PopoverPlacement.TopRight;
}
}
// If it only over flows on the top, go bottom-right.
}
else if (overflows.positionRight.top) {
return PopoverPlacement.BottomRight;
// If it only overflows on the bottom, go top-right.
}
else if (overflows.positionRight.bottom) {
return PopoverPlacement.TopRight;
}
else {
return PopoverPlacement.Right;
}
}
if (desiredPlacement === PopoverPlacement.BottomRight) {
// If it overflows on the bottom AND the right, try in the order: top, top-left, left.
if (overflows.positionBottomRight.bottom && overflows.positionBottomRight.right) {
if (overflows.positionTop.top) {
return PopoverPlacement.Left;
}
else if (overflows.positionTop.right) {
return PopoverPlacement.TopLeft;
}
else {
return PopoverPlacement.Top;
}
// If it only overflows on the bottom, try in the order: right, top-right.
}
else if (overflows.positionBottomRight.bottom) {
if (overflows.positionRight.bottom) {
return PopoverPlacement.TopRight;
}
else {
return PopoverPlacement.Right;
}
// If it only overflows on the right, try in the order: bottom, bottom-left.
}
else if (overflows.positionBottomRight.right) {
if (overflows.positionBottom.right) {
return PopoverPlacement.BottomLeft;
}
else {
return PopoverPlacement.Bottom;
}
}
else {
return PopoverPlacement.BottomRight;
}
}
if (desiredPlacement === PopoverPlacement.Bottom) {
// If it overflows on the bottom AND left, go to top-right.
if (overflows.positionBottom.bottom && overflows.positionBottom.left) {
return PopoverPlacement.TopRight;
// If it overflows on the bottom AND right, go to top-left.
}
else if (overflows.positionBottom.bottom && overflows.positionBottom.right) {
return PopoverPlacement.TopLeft;
// If it only overflows on the bottom, go to top.
}
else if (overflows.positionBottom.bottom) {
return PopoverPlacement.Top;
// If it only overflows to the right, go to bottom-left.
}
else if (overflows.positionBottom.right) {
return PopoverPlacement.BottomLeft;
// If it only overflows to the left, go to bottom-right.
}
else if (overflows.positionBottom.left) {
return PopoverPlacement.BottomRight;
}
else {
return PopoverPlacement.Bottom;
}
}
if (desiredPlacement === PopoverPlacement.BottomLeft) {
// If it overflows on the bottom AND the left, try in the order: top, top-right, right.
if (overflows.positionBottomLeft.bottom && overflows.positionBottomLeft.left) {
if (overflows.positionTop.top) {
return PopoverPlacement.Right;
}
else if (overflows.positionTop.left) {
return PopoverPlacement.TopRight;
}
else {
return PopoverPlacement.Top;
}
// If it only overflows on the bottom, try in the order: left, top-left.
}
else if (overflows.positionBottomLeft.bottom) {
if (overflows.positionLeft.bottom) {
return PopoverPlacement.TopLeft;
}
else {
return PopoverPlacement.Left;
}
// If it only overflows on the left, try in the order: bottom, bottom-right.
}
else if (overflows.positionBottomLeft.left) {
if (overflows.positionBottom.left) {
return PopoverPlacement.BottomRight;
}
else {
return PopoverPlacement.Bottom;
}
}
else {
return PopoverPlacement.BottomLeft;
}
}
if (desiredPlacement === PopoverPlacement.Left) {
// If it overflows on the left AND the top, try in the order: bottom-left, bottom, bottom-right.
if (overflows.positionLeft.left && overflows.positionLeft.top) {
if (overflows.positionBottomLeft.left && overflows.positionBottom.left) {
return PopoverPlacement.BottomRight;
}
else if (overflows.positionBottomRight.right) {
return PopoverPlacement.Bottom;
}
else {
return PopoverPlacement.BottomLeft;
}
// If it overflows on the left AND the bottom, try in the order: top-left, top, top-right.
}
else if (overflows.positionLeft.left && overflows.positionLeft.bottom) {
if (overflows.positionTopLeft.left && overflows.positionTop.left) {
return PopoverPlacement.TopRight;
}
else if (overflows.positionTopLeft.left) {
return PopoverPlacement.Top;
}
else {
return PopoverPlacement.TopLeft;
}
// If it only overflows on the left, try all top positions from left to right, then try all bottom positions left to right.
}
else if (overflows.positionLeft.left) {
if (overflows.positionTop.top) {
if (overflows.positionBottom.left) {
return PopoverPlacement.BottomRight;
}
else if (overflows.positionBottomLeft.left) {
return PopoverPlacement.Bottom;
}
else {
return PopoverPlacement.BottomLeft;
}
}
else {
if (overflows.positionTop.left) {
return PopoverPlacement.TopRight;
}
else if (overflows.positionTopLeft.left) {
return PopoverPlacement.Top;
}
else {
return PopoverPlacement.TopLeft;
}
}
// If it only over flows on the top, go bottom-left.
}
else if (overflows.positionLeft.top) {
return PopoverPlacement.BottomLeft;
// If it only overflows on the bottom, go top-left.
}
else if (overflows.positionLeft.bottom) {
return PopoverPlacement.TopLeft;
}
else {
return PopoverPlacement.Left;
}
}
if (desiredPlacement === PopoverPlacement.TopLeft) {
// If it overflows on the top AND the left, try in the order: bottom, Bottom-right, right.
if (overflows.positionTopLeft.top && overflows.positionTopLeft.left) {
if (overflows.positionBottom.bottom) {
return PopoverPlacement.Right;
}
else if (overflows.positionBottom.left) {
return PopoverPlacement.BottomRight;
}
else {
return PopoverPlacement.Bottom;
}
// If it only overflows on the top, try in the order: left, bottom-left.
}
else if (overflows.positionTopLeft.top) {
if (overflows.positionLeft.top) {
return PopoverPlacement.BottomLeft;
}
else {
return PopoverPlacement.Left;
}
// If it only overflows on the left, try in the order: top, top-right.
}
else if (overflows.positionTopLeft.left) {
if (overflows.positionTop.left) {
return PopoverPlacement.TopRight;
}
else {
return PopoverPlacement.Top;
}
}
else {
return PopoverPlacement.TopLeft;
}
}
return desiredPlacement;
}
}
PopoverContentComponent.decorators = [
{ type: Component, args: [{
selector: 'popover-content',
template: `
<div #popoverDiv
class="bs-popover-{{ effectivePlacement }} popover-content popover {{ parentClass }}"
[ngClass]="{
'sm': size === 'small',
'md-sm': size === 'medium-small',
'md': size === 'medium',
'lg': size === 'large',
'in': isIn
}"
[style.top.px]="top"
[style.left.px]="left"
[style.transition]="(transitionEnabled ? '0.15s opacity' : '')"
[style.opacity]="opacity"
[style.display]="'block'"
[attr.aria-hidden]="opacity === 0"
role="popover">
<div [hidden]="!closeOnMouseOutside" class="virtual-area"></div>
<div class="arrow"></div>
<div class="popover-header" [hidden]="!title">{{ title }}</div>
<div class="popover-body">
<ng-content></ng-content>
{{ content }}
</div>
</div>
`
}] }
];
/** @nocollapse */
PopoverContentComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: ChangeDetectorRef },
{ type: Renderer2 }
];
PopoverContentComponent.propDecorators = {
content: [{ type: Input }],
placement: [{ type: Input }],
title: [{ type: Input }],
parentClass: [{ type: Input }],
animation: [{ type: Input }],
closeOnClickOutside: [{ type: Input }],
closeOnMouseOutside: [{ type: Input }],
appendToBody: [{ type: Input }],
size: [{ type: Input }],
popoverDiv: [{ type: ViewChild, args: ['popoverDiv', { static: true },] }],
onResize: [{ type: HostListener, args: ['window:resize', ['$event'],] }]
};
/**
* @fileoverview added by tsickle
* Generated from: lib/popover.directive.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* \@group Basic Toolkit
* \@component Popover Directive
*/
class PopoverDirective {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
/**
* @param {?} viewContainerRef
* @param {?} cdr
* @param {?} resolver
* @param {?} appRef
* @param {?} injector
*/
constructor(viewContainerRef, cdr, resolver, appRef, injector) {
this.viewContainerRef = viewContainerRef;
this.cdr = cdr;
this.resolver = resolver;
this.appRef = appRef;
this.injector = injector;
// -------------------------------------------------------------------------
// Properties
// -------------------------------------------------------------------------
this.popoverContentComponent = PopoverContentComponent;
this.popoverOnHover = true;
this.popoverDismissTimeout = 0;
this.onShown = new EventEmitter();
this.onHidden = new EventEmitter();
}
// -------------------------------------------------------------------------
// Event listeners
// -------------------------------------------------------------------------
/**
* @param {?} evt
* @return {?}
*/
showOrHideOnClick(evt) {
if (this.popoverOnHover) {
return;
}
if (this.popoverDisabled) {
return;
}
evt.stopImmediatePropagation();
this.toggle();
}
/**
* @param {?} evt
* @return {?}
*/
showOrHideOnTouch(evt) {
evt.stopImmediatePropagation();
if (!this.popoverOnHover) {
return;
}
if (this.popoverDisabled) {
return;
}
this.toggle();
}
/**
* @return {?}
*/
showOnHover() {
if (!this.popoverOnHover) {
return;
}
if (this.popoverDisabled) {
return;
}
this.show();
}
/**
* @return {?}
*/
hideOnHover() {
if (this.popoverCloseOnMouseOutside) {
return; // don't do anything since we do not control this
}
if (!this.popoverOnHover) {
return;
}
if (this.popoverDisabled) {
return;
}
this.hide();
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (changes['popoverDisabled']) {
if (changes['popoverDisabled'].currentValue) {
this.hide();
}
}
}
/**
* @protected
* @param {?} component
* @return {?}
*/
createComponent(component) {
/** @type {?} */
const factory = this.resolver.resolveComponentFactory(component);
// Create a component reference from the component
/** @type {?} */
const componentRef = this.appendToBody
? factory.create(this.injector)
: this.viewContainerRef.createComponent(factory);
if (this.appendToBody) {
// Attach component to the appRef so that it's inside the ng component tree
this.appRef.attachView(componentRef.hostView);
// Get DOM element from component
/** @type {?} */
const domElem = (/** @type {?} */ (((/** @type {?} */ (componentRef.hostView)))
.rootNodes[0]));
// Append DOM element to the body
document.body.appendChild(domElem);
}
return componentRef;
}
/**
* @protected
* @param {?} componentRef
* @return {?}
*/
removeComponent(componentRef) {
if (this.popover) {
if (this.appendToBody) {
this.appRef.detachView(componentRef.hostView);
}
componentRef.destroy();
}
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* @return {?}
*/
toggle() {
if (!this.visible) {
this.show();
}
else {
this.hide();
}
}
/**
* @return {?}
*/
show() {
if (this.visible) {
return;
}
this.visible = true;
if (typeof this.content === 'string') {
if (!this.visible) {
return;
}
this.popover = this.createComponent(this.popoverContentComponent);
/** @type {?} */
const popover = (/** @type {?} */ (this.popover.instance));
popover.popover = this;
popover.content = (/** @type {?} */ (this.content));
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;
}
if (this.popoverSize) {
popover.size = this.popoverSize;
}
popover.appendToBody = this.appendToBody;
popover.onCloseFromOutside.subscribe((/**
* @return {?}
*/
() => this.hide()));
// if dismissTimeout option is set, then this popover will be dismissed in dismissTimeout time
if (this.popoverDismissTimeout > 0) {
setTimeout((/**
* @return {?}
*/
() => this.hide()), this.popoverDismissTimeout);
}
}
else {
/** @type {?} */
const popover = (/** @type {?} */ (this.content));
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;
}
if (this.popoverSize) {
popover.size = this.popoverSize;
}
popover.appendToBody = this.appendToBody;
popover.onCloseFromOutside.subscribe((/**
* @return {?}
*/
() => this.hide()));
// if dismissTimeout option is set, then this popover will be dismissed in dismissTimeout time
if (this.popoverDismissTimeout > 0) {
setTimeout((/**
* @return {?}
*/
() => this.hide()), this.popoverDismissTimeout);
}
popover.show();
}
this.cdr.detectChanges();
this.onShown.emit(this);
}
/**
* @return {?}
*/
hide() {
if (!this.visible) {
return;
}
this.visible = false;
this.removeComponent(this.popover);
if (this.content instanceof PopoverContentComponent) {
((/** @type {?} */ (this.content))).hideFromPopover();
}
this.cdr.detectChanges();
this.onHidden.emit(this);
}
/**
* @return {?}
*/
getElement() {
return this.viewContainerRef.element.nativeElement;
}
}
PopoverDirective.decorators = [
{ type: Directive, args: [{
selector: '[popover]',
exportAs: 'popover'
},] }
];
/** @nocollapse */
PopoverDirective.ctorParameters = () => [
{ type: ViewContainerRef },
{ type: ChangeDetectorRef },
{ type: ComponentFactoryResolver },
{ type: ApplicationRef },
{ type: Injector }
];
PopoverDirective.propDecorators = {
content: [{ type: Input, args: ['popover',] }],
popoverSize: [{ type: Input }],
popoverDisabled: [{ type: Input }],
popoverAnimation: [{ type: Input }],
popoverPlacement: [{ type: Input }],
popoverTitle: [{ type: Input }],
popoverOnHover: [{ type: Input }],
popoverCloseOnClickOutside: [{ type: Input }],
popoverCloseOnMouseOutside: [{ type: Input }],
popoverDismissTimeout: [{ type: Input }],
appendToBody: [{ type: Input }],
onShown: [{ type: Output }],
onHidden: [{ type: Output }],
showOrHideOnClick: [{ type: HostListener, args: ['click', ['$event'],] }],
showOrHideOnTouch: [{ type: HostListener, args: ['touchend', ['$event'],] }],
showOnHover: [{ type: HostListener, args: ['focusin',] }, { type: HostListener, args: ['mouseenter',] }],
hideOnHover: [{ type: HostListener, args: ['focusout',] }, { type: HostListener, args: ['mouseleave',] }]
};
/**
* @fileoverview added by tsickle
* Generated from: lib/popover.module.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class PopoverModule {
}
PopoverModule.decorators = [
{ type: NgModule, args: [{
declarations: [
PopoverDirective,
PopoverContentComponent
],
imports: [
CommonModule,
],
exports: [
PopoverContentComponent,
PopoverDirective
],
entryComponents: [
PopoverContentComponent
]
},] }
];
/**
* @fileoverview added by tsickle
* Generated from: public_api.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* Generated from: ngx-smart-popover.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { PopoverContentComponent, PopoverDirective, PopoverModule, PopoverPlacement };
//# sourceMappingURL=ngx-smart-popover.js.map