145 lines
4.4 KiB
JavaScript
145 lines
4.4 KiB
JavaScript
import ngModule from '../../module';
|
|
import Popup from '../popup';
|
|
import isMobile from '../../lib/is-mobile';
|
|
import './style.scss';
|
|
|
|
/**
|
|
* A simple popover.
|
|
*
|
|
* @property {HTMLElement} parent The parent element to show drop down relative to
|
|
* @property {HTMLElement} content Element holding the popover content
|
|
*
|
|
* @event open Thrown when popover is displayed
|
|
* @event close Thrown when popover is hidden
|
|
*/
|
|
export default class Popover extends Popup {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this.displayMode = isMobile ? 'centered' : 'relative';
|
|
}
|
|
|
|
/**
|
|
* Shows the popover emitting the open signal. If a parent is specified
|
|
* it is shown in a visible relative position to it.
|
|
*
|
|
* @param {HTMLElement|Event} parent Overrides the parent property
|
|
* @param {String} direction - Direction [left]
|
|
*/
|
|
show(parent, direction) {
|
|
if (parent instanceof Event)
|
|
parent = event.target;
|
|
|
|
if (parent) this.parent = parent;
|
|
if (direction) this.direction = direction;
|
|
|
|
super.show();
|
|
this.content = this.popup.querySelector('.content');
|
|
this.$timeout(() => this.relocate(), 10);
|
|
}
|
|
|
|
hide() {
|
|
this.content = null;
|
|
super.hide();
|
|
}
|
|
|
|
get parent() {
|
|
return this.__parent;
|
|
}
|
|
|
|
// Bug #2147 Popover loses parent location
|
|
set parent(value) {
|
|
this.__parent = value;
|
|
if (!value) return;
|
|
|
|
const parentRect = value.getBoundingClientRect();
|
|
this.parentRect = {};
|
|
for (let prop in parentRect)
|
|
this.parentRect[prop] = parentRect[prop];
|
|
}
|
|
|
|
/**
|
|
* Repositions the popover to a correct location relative to the parent.
|
|
*/
|
|
relocate() {
|
|
if (!(this.parent && this._shown && this.displayMode == 'relative'))
|
|
return;
|
|
|
|
let margin = 10;
|
|
let arrow = this.popup.querySelector('.arrow');
|
|
|
|
let style = this.windowEl.style;
|
|
style.width = '';
|
|
style.height = '';
|
|
|
|
let arrowStyle = arrow.style;
|
|
arrowStyle.top = '';
|
|
arrowStyle.bottom = '';
|
|
|
|
let parentRect = this.parentRect;
|
|
let popoverRect = this.windowEl.getBoundingClientRect();
|
|
let arrowRect = arrow.getBoundingClientRect();
|
|
let clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
|
|
let arrowHeight = Math.floor(arrowRect.height / 2);
|
|
let arrowOffset = arrowHeight + margin / 2;
|
|
|
|
let docEl = this.document.documentElement;
|
|
let maxRight = Math.min(window.innerWidth, docEl.clientWidth) - margin;
|
|
let maxBottom = Math.min(window.innerHeight, docEl.clientHeight) - margin;
|
|
let maxWith = maxRight - margin;
|
|
let maxHeight = maxBottom - margin - arrowHeight;
|
|
|
|
let width = clamp(popoverRect.width, parentRect.width, maxWith);
|
|
let height = popoverRect.height;
|
|
|
|
let left;
|
|
if (this.direction == 'left') {
|
|
left = parentRect.left + parentRect.width;
|
|
left = clamp(left, margin, maxRight - width);
|
|
} else {
|
|
left = parentRect.left + parentRect.width / 2 - width / 2;
|
|
left = clamp(left, margin, maxRight - width);
|
|
}
|
|
|
|
let top;
|
|
if (this.direction == 'left')
|
|
top = parentRect.top;
|
|
else
|
|
top = parentRect.top + parentRect.height + arrowOffset;
|
|
let showTop = top + height > maxBottom;
|
|
if (showTop) top = parentRect.top - height - arrowOffset;
|
|
top = Math.max(top, margin);
|
|
|
|
if (this.direction == 'left')
|
|
arrowStyle.left = `0`;
|
|
else if (showTop)
|
|
arrowStyle.bottom = `0`;
|
|
else
|
|
arrowStyle.top = `0`;
|
|
|
|
let arrowLeft;
|
|
if (this.direction == 'left') {
|
|
arrowLeft = 0;
|
|
let arrowTop = arrowOffset;
|
|
arrowStyle.top = `${arrowTop}px`;
|
|
} else {
|
|
arrowLeft = (parentRect.left - left) + parentRect.width / 2;
|
|
arrowLeft = clamp(arrowLeft, arrowHeight, width - arrowHeight);
|
|
}
|
|
arrowStyle.left = `${arrowLeft}px`;
|
|
|
|
style.top = `${top}px`;
|
|
style.left = `${left}px`;
|
|
style.width = `${width}px`;
|
|
if (height > maxHeight) style.height = `${maxHeight}px`;
|
|
}
|
|
}
|
|
|
|
ngModule.vnComponent('vnPopover', {
|
|
controller: Popover
|
|
});
|
|
|
|
ngModule.run(['$compile', function($compile) {
|
|
Popover.prototype.contentLinkFn = $compile(require('./index.html'));
|
|
}]);
|