web-toolkit
Version:
A GTK inspired toolkit designed to build awesome web apps
404 lines (349 loc) • 12.4 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectSpread2"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/createClass"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/inherits"));
var _createSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/createSuper"));
var _react = _interopRequireDefault(require("react"));
var _reactDom = require("react-dom");
var _core = require("@popperjs/core");
var _clsx = _interopRequireDefault(require("clsx"));
var NOOP = function NOOP() {};
function getInversePlacement(p) {
if (p.startsWith('top')) return 'bottom';
if (p.startsWith('bottom')) return 'top';
if (p.startsWith('left')) return 'right';
if (p.startsWith('right')) return 'left';
return 'top';
}
var Popover = /*#__PURE__*/function (_React$PureComponent) {
(0, _inherits2.default)(Popover, _React$PureComponent);
var _super = (0, _createSuper2.default)(Popover);
function Popover(props) {
var _this;
(0, _classCallCheck2.default)(this, Popover);
_this = _super.call(this, props);
_this.onContentResize = function () {
if (_this.popper) _this.popper.update();
};
_this.onRefPopover = function (ref) {
if (!ref) {
if (_this.popoverRef.current) {
_this.observer.unobserve(_this.popoverRef.current);
_this.popoverRef.current = null;
}
return;
}
_this.popoverRef.current = ref;
_this.observer.observe(_this.popoverRef.current);
};
_this.onDocumentClick = function (ev) {
if (_this.props.method !== 'click' && _this.props.method !== 'click-controlled') return;
if (!_this.isOpen()) return;
if (!(_this.triggerRef.current.contains(ev.target) || _this.popoverRef.current.contains(ev.target))) _this.close();
};
_this.onTransitionEnd = function () {
_this.setState({
closing: false
});
if (_this.state.open) _this.props.onDidOpen();else _this.props.onDidClose();
};
_this.onUpdatePopper = function (_ref) {
var state = _ref.state;
if (_this.state.actualPlacement !== state.placement) {
_this.setState({
actualPlacement: state.placement
});
}
if (_this.props.width) {
var trigger = state.elements.reference;
var rect = trigger.getBoundingClientRect();
if (_this.props.width === 'trigger') {
var currentWidth = _this.state.styles.width;
var newWidth = rect.width - 1;
if (currentWidth !== newWidth) _this.setState({
styles: {
width: newWidth
}
});
} else if (_this.props.width === 'trigger-min') {
var _currentWidth = _this.state.styles.minWidth;
var _newWidth = rect.width - 1;
if (_currentWidth !== _newWidth) _this.setState({
styles: {
minWidth: _newWidth
}
});
}
}
};
_this.onClick = function (ev) {
/* React bubbles event in portals up to the containing element */
if (!_this.triggerRef.current.contains(ev.target)) return;
if (_this.isOpen()) _this.close();else _this.open();
};
_this.onMouseOver = function () {
if (_this.closeTimeout) _this.closeTimeout = clearTimeout(_this.closeTimeout);
if (_this.state.open === false) {
// is closed
if (!_this.props.delay) {
_this.open();
} else {
_this.openTimeout = setTimeout(function () {
_this.open();
}, _this.props.delay);
}
}
};
_this.onMouseOut = function () {
if (_this.openTimeout) _this.openTimeout = clearTimeout(_this.openTimeout);
if (_this.state.open) {
if (!_this.props.delay) {
_this.close();
} else {
_this.closeTimeout = setTimeout(function () {
_this.close();
}, _this.props.delay);
}
}
};
_this.isControlled = function () {
return 'open' in _this.props;
};
_this.open = function () {
if (_this.props.open && _this.state.open) return; // This allows for call this.open() when props.open is true
if (_this.isControlled()) {
if (_this.props.open === false) return _this.props.onOpen();
}
_this.attachDomNode();
_this.updatePopperOptions();
_this.openTimeout = undefined;
_this.setState({
open: true
});
if (!_this.isControlled()) {
_this.props.onOpen();
}
};
_this.close = function () {
if (_this.isControlled()) {
if (_this.props.open === true) return _this.props.onClose();
}
_this.updatePopperOptions();
_this.setState({
open: false,
closing: true
});
if (!_this.isControlled()) _this.props.onClose();
};
_this.domNode = document.createElement('div');
_this.domNode.className = 'Popover__domNode';
_this.isDomNodeAttached = false;
_this.isEventListening = false;
_this.openTimeout = undefined;
_this.closeTimeout = undefined;
_this.state = {
open: false,
closing: false,
actualPlacement: props.placement,
styles: {}
};
_this.triggerRef = _react.default.createRef();
_this.popoverRef = _react.default.createRef();
_this.arrowRef = _react.default.createRef();
_this.observer = new ResizeObserver(_this.onContentResize);
return _this;
}
(0, _createClass2.default)(Popover, [{
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.detachDomNode();
this.detachPopper();
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
this.attachPopper();
if (this.props.shouldAttachEarly) this.attachDomNode();
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
if (prevProps.open !== this.props.open || prevState.open !== this.state.open) {
if (this.props.shouldUpdatePlacement && this.popper) this.popper.update();
}
}
}, {
key: "attachDomNode",
value: function attachDomNode() {
if (!this.isDomNodeAttached) {
document.body.append(this.domNode);
document.addEventListener('click', this.onDocumentClick);
this.isDomNodeAttached = true; // We need the classes to be applied before to enable the fade-in transition
this.forceUpdate();
}
}
}, {
key: "detachDomNode",
value: function detachDomNode() {
if (this.isDomNodeAttached) {
document.body.removeChild(this.domNode);
document.removeEventListener('click', this.onDocumentClick);
this.isDomNodeAttached = false;
}
}
}, {
key: "attachPopper",
value: function attachPopper() {
if (!this.popoverRef.current) return;
if (this.popper) return;
this.popper = (0, _core.createPopper)(this.triggerRef.current, this.popoverRef.current, this.getPopperOptions());
}
}, {
key: "detachPopper",
value: function detachPopper() {
if (this.popper) {
this.popper.destroy();
this.popper = null;
}
}
}, {
key: "updatePopperOptions",
value: function updatePopperOptions() {
if (!this.popper) return;
this.popper.setOptions(this.getPopperOptions());
}
}, {
key: "getPopperOptions",
value: function getPopperOptions() {
var hasArrow = this.props.arrow;
var isOpen = this.isOpen();
this.isEventListening = isOpen;
return {
placement: this.props.placement,
modifiers: [{
name: 'arrow',
enabled: hasArrow,
options: {
element: this.arrowRef.current,
padding: 15
}
}, {
/* Offset from the trigger */
name: 'offset',
options: {
offset: [0, hasArrow ? 10 : 0]
}
}, {
/* Avoids touching the edge of the window */
name: 'preventOverflow',
options: {
altAxis: true,
padding: 10
}
}, {
/* Custom modifier */
name: 'eventListeners',
enabled: isOpen
}, {
/* Custom modifier */
name: 'updateComponentState',
enabled: true,
phase: 'write',
fn: this.onUpdatePopper
}]
};
}
}, {
key: "isOpen",
value: function isOpen() {
var _this$props$open;
return (_this$props$open = this.props.open) !== null && _this$props$open !== void 0 ? _this$props$open : this.state.open;
}
}, {
key: "getContent",
value: function getContent() {
return !this.isDomNodeAttached ? null : typeof this.props.content === 'function' ? this.props.content() : this.props.content;
}
}, {
key: "getEventListeners",
value: function getEventListeners() {
var method = this.props.method;
return method === 'mouseover' ? {
onMouseOver: this.onMouseOver,
onMouseOut: this.onMouseOut
} : method === 'click' ? {
onClick: this.onClick
} : {};
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props = this.props,
method = _this$props.method,
arrow = _this$props.arrow,
children = _this$props.children,
className = _this$props.className;
var _this$state = this.state,
actualPlacement = _this$state.actualPlacement,
styles = _this$state.styles,
closing = _this$state.closing;
var open = this.isOpen();
var trigger = children;
if (this.props.open && !this.state.open) setTimeout(this.open, 0);
if (open !== this.isEventListening) this.updatePopperOptions();
var eventListeners = this.getEventListeners();
var props = (0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)({}, trigger.props), eventListeners), {}, {
className: (0, _clsx.default)(trigger.props.className, open ? "with-popover" : undefined, open ? "popover-".concat(actualPlacement) : undefined),
ref: function ref(node) {
if (node) _this2.triggerRef.current = (0, _reactDom.findDOMNode)(node);
if (trigger.ref) trigger.ref(node);
}
});
var arrowPlacement = getInversePlacement(actualPlacement);
var popoverEventListeners = method === 'mouseover' ? eventListeners : undefined;
var popoverClassName = (0, _clsx.default)('Popover popover', className, actualPlacement, arrow ? "arrow-".concat(arrowPlacement) : undefined, {
open: open,
arrow: arrow,
closing: closing
});
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, _react.default.cloneElement(trigger, props), (0, _reactDom.createPortal)( /*#__PURE__*/_react.default.createElement("div", Object.assign({
ref: this.onRefPopover,
className: popoverClassName,
onTransitionEnd: this.onTransitionEnd
}, popoverEventListeners), /*#__PURE__*/_react.default.createElement("div", {
className: "Popover__wrapper"
}, arrow && /*#__PURE__*/_react.default.createElement("div", {
className: (0, _clsx.default)('Popover__arrow', arrowPlacement),
ref: this.arrowRef
}), /*#__PURE__*/_react.default.createElement("div", {
className: "Popover__container",
style: styles
}, /*#__PURE__*/_react.default.createElement("div", {
className: "Popover__content"
}, this.getContent())))), this.domNode));
}
}]);
return Popover;
}(_react.default.PureComponent);
Popover.defaultProps = {
arrow: true,
placement: 'bottom',
align: 'right',
method: 'click',
delay: 200,
shouldUpdatePlacement: true,
onOpen: NOOP,
onClose: NOOP,
onDidOpen: NOOP,
onDidClose: NOOP
};
var _default = Popover;
exports.default = _default;
//# sourceMappingURL=Popover.js.map