@miksu/react-tiny-popover
Version:
A simple and highly customizable popover react higher order component with no other dependencies! Typescript friendly.
329 lines • 16.5 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var React = require("react");
var react_dom_1 = require("react-dom");
var util_1 = require("./util");
var ArrowContainer_1 = require("./ArrowContainer");
exports.ArrowContainer = ArrowContainer_1.ArrowContainer;
var Portal_1 = require("./Portal");
var underscore_1 = require("underscore");
var Popover = /** @class */ (function (_super) {
__extends(Popover, _super);
function Popover(props) {
var _this = _super.call(this, props) || this;
_this.target = null;
_this.targetRect = null;
_this.targetPositionIntervalHandler = null;
_this.popoverDiv = null;
_this.positionOrder = null;
_this.willUnmount = false;
_this.willMount = false;
_this.onResize = function (e) {
_this.renderPopover();
};
_this.onClick = function (e) {
var _a = _this.props, onClickOutside = _a.onClickOutside, isOpen = _a.isOpen;
if (!_this.willUnmount && !_this.willMount && !_this.popoverDiv.contains(e.target) && !_this.target.contains(e.target) && onClickOutside && isOpen) {
onClickOutside(e);
}
};
_this.state = {
popoverInfo: null,
};
_this.willUnmount = false;
_this.willMount = true;
return _this;
}
Popover.prototype.componentDidMount = function () {
var _this = this;
window.setTimeout(function () { return _this.willMount = false; });
var _a = this.props, position = _a.position, isOpen = _a.isOpen;
this.target = react_dom_1.findDOMNode(this);
this.positionOrder = this.getPositionPriorityOrder(position);
this.updatePopover(isOpen);
};
Popover.prototype.componentDidUpdate = function (prevProps) {
if (this.target == null) {
this.target = react_dom_1.findDOMNode(this);
}
var prevIsOpen = prevProps.isOpen, prevPosition = prevProps.position, prevBody = prevProps.content;
var _a = this.props, isOpen = _a.isOpen, content = _a.content, position = _a.position;
this.positionOrder = this.getPositionPriorityOrder(this.props.position);
var hasNewDestination = prevProps.contentDestination !== this.props.contentDestination;
if (prevIsOpen !== isOpen ||
prevBody !== content ||
prevPosition !== position ||
hasNewDestination) {
if (hasNewDestination) {
this.removePopover();
this.popoverDiv && this.popoverDiv.remove();
}
this.updatePopover(isOpen);
}
};
Popover.prototype.componentWillUnmount = function () {
this.willUnmount = true;
this.removePopover();
};
Popover.prototype.render = function () {
var content = this.props.content;
var popoverInfo = this.state.popoverInfo;
var popoverContent = null;
if (this.props.isOpen && this.popoverDiv && popoverInfo) {
var getContent = function (args) {
return typeof content === 'function'
? content(args)
: content;
};
popoverContent = React.createElement(Portal_1.Portal, { element: this.popoverDiv, container: this.props.contentDestination || window.document.body }, getContent(popoverInfo));
}
return (React.createElement(React.Fragment, null,
this.props.children,
popoverContent));
};
Popover.prototype.updatePopover = function (isOpen) {
if (isOpen && this.target != null) {
if (!this.popoverDiv || !this.popoverDiv.parentNode) {
var transitionDuration = this.props.transitionDuration;
this.popoverDiv = this.createContainer();
this.popoverDiv.style.opacity = '0';
this.popoverDiv.style.transition = "opacity " + (transitionDuration || util_1.Constants.FADE_TRANSITION) + "s";
}
window.addEventListener('resize', this.onResize);
window.addEventListener('click', this.onClick);
this.renderPopover();
}
else if (this.popoverDiv && this.popoverDiv.parentNode) {
this.removePopover();
}
};
Popover.prototype.renderPopover = function (positionIndex) {
var _this = this;
if (positionIndex === void 0) { positionIndex = 0; }
if (positionIndex >= this.positionOrder.length) {
this.removePopover();
return;
}
this.renderWithPosition({ position: this.positionOrder[positionIndex], targetRect: this.target.getBoundingClientRect() }, function (violation, rect) {
var _a;
var _b = _this.props, disableReposition = _b.disableReposition, contentLocation = _b.contentLocation;
if (violation && !disableReposition && !(typeof contentLocation === 'object')) {
_this.renderPopover(positionIndex + 1);
}
else {
var _c = _this.props, contentLocation_1 = _c.contentLocation, align = _c.align;
var _d = _this.getNudgedPopoverPosition(rect), nudgedTop = _d.top, nudgedLeft = _d.left;
var rectTop = rect.top, rectLeft = rect.left;
var position = _this.positionOrder[positionIndex];
var _e = disableReposition ? { top: rectTop, left: rectLeft } : { top: nudgedTop, left: nudgedLeft }, top_1 = _e.top, left = _e.left;
if (contentLocation_1) {
var targetRect = _this.target.getBoundingClientRect();
var popoverRect = _this.popoverDiv.getBoundingClientRect();
(_a = typeof contentLocation_1 === 'function' ? contentLocation_1({ targetRect: targetRect, popoverRect: popoverRect, position: position, align: align, nudgedLeft: nudgedLeft, nudgedTop: nudgedTop }) : contentLocation_1, top_1 = _a.top, left = _a.left);
_this.popoverDiv.style.left = left.toFixed() + "px";
_this.popoverDiv.style.top = top_1.toFixed() + "px";
}
else {
var destinationTopOffset = 0;
var destinationLeftOffset = 0;
if (_this.props.contentDestination) {
var destRect = _this.props.contentDestination.getBoundingClientRect();
destinationTopOffset = -destRect.top;
destinationLeftOffset = -destRect.left;
}
var _f = [top_1 + window.pageYOffset, left + window.pageXOffset], absoluteTop = _f[0], absoluteLeft = _f[1];
var finalLeft = absoluteLeft + destinationTopOffset;
var finalTop = absoluteTop + destinationLeftOffset;
_this.popoverDiv.style.left = finalLeft.toFixed() + "px";
_this.popoverDiv.style.top = finalTop.toFixed() + "px";
}
_this.popoverDiv.style.width = null;
_this.popoverDiv.style.height = null;
_this.renderWithPosition({
position: position,
nudgedTop: nudgedTop - rect.top,
nudgedLeft: nudgedLeft - rect.left,
targetRect: _this.target.getBoundingClientRect(),
popoverRect: _this.popoverDiv.getBoundingClientRect(),
}, function () {
_this.startTargetPositionListener(10);
if (_this.popoverDiv.style.opacity !== '1') {
_this.popoverDiv.style.opacity = '1';
}
});
}
});
};
Popover.prototype.startTargetPositionListener = function (checkInterval) {
var _this = this;
if (this.targetPositionIntervalHandler === null) {
this.targetPositionIntervalHandler = window.setInterval(function () {
var newTargetRect = _this.target.getBoundingClientRect();
if (_this.targetPositionHasChanged(_this.targetRect, newTargetRect)) {
_this.renderPopover();
}
_this.targetRect = newTargetRect;
}, checkInterval);
}
};
Popover.prototype.renderWithPosition = function (_a, callback) {
var _this = this;
var position = _a.position, _b = _a.nudgedLeft, nudgedLeft = _b === void 0 ? 0 : _b, _c = _a.nudgedTop, nudgedTop = _c === void 0 ? 0 : _c, _d = _a.targetRect, targetRect = _d === void 0 ? util_1.Constants.EMPTY_CLIENT_RECT : _d, _e = _a.popoverRect, popoverRect = _e === void 0 ? util_1.Constants.EMPTY_CLIENT_RECT : _e;
var _f = this.props, padding = _f.windowBorderPadding, content = _f.content, align = _f.align;
var popoverInfo = { position: position, nudgedLeft: nudgedLeft, nudgedTop: nudgedTop, targetRect: targetRect, popoverRect: popoverRect, align: align };
if (!underscore_1.isEqual(this.state.popoverInfo, popoverInfo)) {
this.setState({
popoverInfo: popoverInfo,
}, function () {
if (_this.willUnmount) {
return;
}
targetRect = _this.target.getBoundingClientRect();
popoverRect = _this.popoverDiv.getBoundingClientRect();
var _a = _this.getLocationForPosition(position, targetRect, popoverRect), top = _a.top, left = _a.left;
callback(position === 'top' && top < padding ||
position === 'left' && left < padding ||
position === 'right' && left + popoverRect.width > window.innerWidth - padding ||
position === 'bottom' && top + popoverRect.height > window.innerHeight - padding, { width: popoverRect.width, height: popoverRect.height, top: top, left: left });
});
}
};
Popover.prototype.getNudgedPopoverPosition = function (_a) {
var top = _a.top, left = _a.left, width = _a.width, height = _a.height;
var padding = this.props.windowBorderPadding;
top = top < padding ? padding : top;
top = top + height > window.innerHeight - padding ? window.innerHeight - padding - height : top;
left = left < padding ? padding : left;
left = left + width > window.innerWidth - padding ? window.innerWidth - padding - width : left;
return { top: top, left: left };
};
Popover.prototype.removePopover = function () {
var _this = this;
if (this.popoverDiv) {
var transitionDuration = this.props.transitionDuration;
this.popoverDiv.style.opacity = '0';
var remove = function () {
if (_this.willUnmount || !_this.props.isOpen || !_this.popoverDiv.parentNode) {
window.clearInterval(_this.targetPositionIntervalHandler);
window.removeEventListener('resize', _this.onResize);
window.removeEventListener('click', _this.onClick);
_this.targetPositionIntervalHandler = null;
}
};
if (!this.willUnmount) {
window.setTimeout(remove, (transitionDuration || util_1.Constants.FADE_TRANSITION) * 1000);
}
else {
remove();
}
}
};
Popover.prototype.getPositionPriorityOrder = function (position) {
if (position && typeof position !== 'string') {
if (util_1.Constants.DEFAULT_POSITIONS.every(function (defaultPosition) { return position.find(function (p) { return p === defaultPosition; }) !== undefined; })) {
return util_1.arrayUnique(position);
}
else {
var remainingPositions = util_1.Constants.DEFAULT_POSITIONS.filter(function (defaultPosition) { return position.find(function (p) { return p === defaultPosition; }) === undefined; });
return util_1.arrayUnique(position.concat(remainingPositions));
}
}
else if (position && typeof position === 'string') {
var remainingPositions = util_1.Constants.DEFAULT_POSITIONS.filter(function (defaultPosition) { return defaultPosition !== position; });
return util_1.arrayUnique([position].concat(remainingPositions));
}
};
Popover.prototype.createContainer = function () {
var _a = this.props, containerStyle = _a.containerStyle, containerClassName = _a.containerClassName;
var container = window.document.createElement('div');
container.style.overflow = 'hidden';
if (containerStyle) {
Object.keys(containerStyle).forEach(function (key) { return container.style[key] = containerStyle[key]; });
}
container.className = containerClassName;
container.style.position = 'absolute';
container.style.top = '0';
container.style.left = '0';
return container;
};
Popover.prototype.getLocationForPosition = function (position, newTargetRect, popoverRect) {
var _a = this.props, padding = _a.padding, align = _a.align;
var targetMidX = newTargetRect.left + (newTargetRect.width / 2);
var targetMidY = newTargetRect.top + (newTargetRect.height / 2);
var top;
var left;
switch (position) {
case 'top':
top = newTargetRect.top - popoverRect.height - padding;
left = targetMidX - (popoverRect.width / 2);
if (align === 'start') {
left = newTargetRect.left;
}
if (align === 'end') {
left = newTargetRect.right - popoverRect.width;
}
break;
case 'left':
top = targetMidY - (popoverRect.height / 2);
left = newTargetRect.left - padding - popoverRect.width;
if (align === 'start') {
top = newTargetRect.top;
}
if (align === 'end') {
top = newTargetRect.bottom - popoverRect.height;
}
break;
case 'bottom':
top = newTargetRect.bottom + padding;
left = targetMidX - (popoverRect.width / 2);
if (align === 'start') {
left = newTargetRect.left;
}
if (align === 'end') {
left = newTargetRect.right - popoverRect.width;
}
break;
case 'right':
top = targetMidY - (popoverRect.height / 2);
left = newTargetRect.right + padding;
if (align === 'start') {
top = newTargetRect.top;
}
if (align === 'end') {
top = newTargetRect.bottom - popoverRect.height;
}
break;
}
return { top: top, left: left };
};
Popover.prototype.targetPositionHasChanged = function (oldTargetRect, newTargetRect) {
return oldTargetRect === null
|| oldTargetRect.left !== newTargetRect.left
|| oldTargetRect.top !== newTargetRect.top
|| oldTargetRect.width !== newTargetRect.width
|| oldTargetRect.height !== newTargetRect.height;
};
Popover.defaultProps = {
padding: util_1.Constants.DEFAULT_PADDING,
windowBorderPadding: util_1.Constants.DEFAULT_WINDOW_PADDING,
position: ['top', 'right', 'left', 'bottom'],
align: 'center',
containerClassName: util_1.Constants.POPOVER_CONTAINER_CLASS_NAME,
};
return Popover;
}(React.Component));
exports.default = Popover;
//# sourceMappingURL=Popover.js.map