salix/front/core/components/popover/index.js

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
});