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

121 lines
3.6 KiB
JavaScript
Raw Normal View History

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 {
2020-04-25 09:50:04 +00:00
constructor(...args) {
super(...args);
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.
*
2020-04-25 09:50:04 +00:00
* @param {HTMLElement|Event} parent Overrides the parent property
*/
show(parent) {
2020-04-25 09:50:04 +00:00
if (parent instanceof Event)
parent = event.target;
if (parent) this.parent = parent;
super.show();
this.content = this.popup.querySelector('.content');
this.$timeout(() => this.relocate(), 10);
}
hide() {
this.content = null;
super.hide();
}
2020-02-26 06:02:03 +00:00
get parent() {
return this.__parent;
}
2020-02-26 10:37:58 +00:00
// Bug #2147 Popover loses parent location
2020-02-26 06:02:03 +00:00
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 = '';
2020-02-26 06:02:03 +00:00
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 = 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
});