102 lines
3.2 KiB
JavaScript
102 lines
3.2 KiB
JavaScript
import ngModule from '../../module';
|
|
import Popup from '../popup';
|
|
import template from './index.html';
|
|
import isMobile from '../../lib/is-mobile';
|
|
import './style.scss';
|
|
|
|
/**
|
|
* A simple popover.
|
|
*
|
|
* @property {HTMLElement} parent The parent element to show drop down relative to
|
|
*
|
|
* @event open Thrown when popover is displayed
|
|
* @event close Thrown when popover is hidden
|
|
*/
|
|
export default class Popover extends Popup {
|
|
constructor($element, $, $transclude) {
|
|
super($element, $, $transclude);
|
|
this.displayMode = isMobile ? 'centered' : 'relative';
|
|
this.template = template;
|
|
}
|
|
|
|
/**
|
|
* Shows the popover emitting the open signal. If a parent is specified
|
|
* it is shown in a visible relative position to it.
|
|
*
|
|
* @param {HTMLElement} parent Overrides the parent property
|
|
*/
|
|
show(parent) {
|
|
if (parent) this.parent = parent;
|
|
super.show();
|
|
this.content = this.popup.querySelector('.content');
|
|
this.$timeout(() => this.relocate(), 10);
|
|
}
|
|
|
|
hide() {
|
|
this.content = null;
|
|
super.hide();
|
|
}
|
|
|
|
/**
|
|
* 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.parent.getBoundingClientRect();
|
|
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 = parentRect.left + parentRect.width / 2 - width / 2;
|
|
left = clamp(left, margin, maxRight - width);
|
|
|
|
let top = parentRect.top + parentRect.height + arrowOffset;
|
|
let showTop = top + height > maxBottom;
|
|
if (showTop) top = parentRect.top - height - arrowOffset;
|
|
top = Math.max(top, margin);
|
|
|
|
if (showTop)
|
|
arrowStyle.bottom = `0`;
|
|
else
|
|
arrowStyle.top = `0`;
|
|
|
|
let 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
|
|
});
|