UNPKG

web-toolkit

Version:

A GTK inspired toolkit designed to build awesome web apps

404 lines (349 loc) 12.4 kB
"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