UNPKG

@chayns-components/core

Version:

A set of beautiful React components for developing your own applications with chayns.

300 lines (298 loc) • 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = require("motion/react"); var _react2 = _interopRequireWildcard(require("react")); var _reactDom = require("react-dom"); var _uuid = require("../../hooks/uuid"); var _popup = require("../../types/popup"); var _AreaContextProvider = _interopRequireDefault(require("../area-provider/AreaContextProvider")); var _PopupContentWrapper = _interopRequireDefault(require("./popup-content-wrapper/PopupContentWrapper")); var _Popup = require("./Popup.styles"); var _element = require("../../hooks/element"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const Popup = /*#__PURE__*/(0, _react2.forwardRef)(({ alignment, content, onShow, container, onHide, children, isOpen, shouldHideOnChildrenLeave, shouldShowOnHover = false, shouldUseChildrenWidth = true, shouldScrollWithContent = true, shouldUseFullWidth = false, yOffset = 0, shouldBeOpen = false }, ref) => { const [coordinates, setCoordinates] = (0, _react2.useState)({ x: 0, y: 0 }); const [internalAlignment, setInternalAlignment] = (0, _react2.useState)(_popup.PopupAlignment.TopLeft); const [offset, setOffset] = (0, _react2.useState)(0); const [isInternallyOpen, setIsInternallyOpen] = (0, _react2.useState)(shouldBeOpen); const [portal, setPortal] = (0, _react2.useState)(); const [pseudoSize, setPseudoSize] = (0, _react2.useState)(); const [newContainer, setNewContainer] = (0, _react2.useState)(container ?? null); const [contentMaxHeight, setContentMaxHeight] = (0, _react2.useState)(undefined); const timeout = (0, _react2.useRef)(); const previousIsVisibleRef = (0, _react2.useRef)(false); const uuid = (0, _uuid.useUuid)(); const isControlled = typeof isOpen === 'boolean'; const isPopupOpen = isControlled ? isOpen : isInternallyOpen; const { height, width, measuredElement } = (0, _element.useMeasuredClone)({ content, shouldPreventTextWrapping: !shouldUseChildrenWidth }); const popupContentRef = (0, _react2.useRef)(null); const popupRef = (0, _react2.useRef)(null); (0, _react2.useEffect)(() => { if (popupRef.current && !container) { const el = popupRef.current; const element = el.closest('.dialog-inner, .page-provider, .tapp, body'); setNewContainer(element); } }, [container]); (0, _react2.useEffect)(() => { if (container instanceof Element) { setNewContainer(container); } }, [container]); (0, _react2.useEffect)(() => { setPseudoSize({ height, width }); }, [height, width]); const updatePopupPosition = (0, _react2.useCallback)(() => { if (popupRef.current && pseudoSize) { if (!newContainer) { return; } const HORIZONTAL_PADDING = 23; const { height: pseudoHeight, width: pseudoWidth } = pseudoSize; const { height: childrenHeight, left: childrenLeft, top: childrenTop, width: childrenWidth } = popupRef.current.getBoundingClientRect(); const element = shouldScrollWithContent ? newContainer : document.body; const { height: containerHeight, width: containerWidth, top, left } = element.getBoundingClientRect(); const zoomX = containerWidth / element.offsetWidth; const zoomY = containerHeight / element.offsetHeight; const childrenCenterX = childrenLeft + childrenWidth / 2; const x = (childrenCenterX - left) / zoomX + element.scrollLeft; const y = (childrenTop + childrenHeight / 2 - top) / zoomY + element.scrollTop - yOffset; // Use one coordinate space for all horizontal bounds checks. const boundaryLeft = element.scrollLeft; const boundaryWidth = containerWidth / zoomX; const relativeX = x - boundaryLeft; const shouldShowBottom = pseudoHeight > childrenTop - 25 || alignment === _popup.PopupAlignment.BottomLeft || alignment === _popup.PopupAlignment.BottomRight || alignment === _popup.PopupAlignment.BottomCenter; const shouldForceRight = shouldShowBottom ? alignment === _popup.PopupAlignment.BottomRight : alignment === _popup.PopupAlignment.TopRight; const shouldUseCenterAlignment = shouldShowBottom ? alignment === _popup.PopupAlignment.BottomCenter : alignment === _popup.PopupAlignment.TopCenter; const hasEnoughSpaceForCenter = pseudoWidth / 2 <= relativeX - HORIZONTAL_PADDING && pseudoWidth / 2 <= boundaryWidth - relativeX - HORIZONTAL_PADDING; if (shouldUseCenterAlignment && hasEnoughSpaceForCenter) { setInternalAlignment(shouldShowBottom ? _popup.PopupAlignment.BottomCenter : _popup.PopupAlignment.TopCenter); setOffset(0); setCoordinates({ x, y }); } else { let isRight = false; if (pseudoWidth > relativeX - HORIZONTAL_PADDING || shouldForceRight) { setInternalAlignment(shouldShowBottom ? _popup.PopupAlignment.BottomRight : _popup.PopupAlignment.TopRight); isRight = true; } else { setInternalAlignment(shouldShowBottom ? _popup.PopupAlignment.BottomLeft : _popup.PopupAlignment.TopLeft); } let newOffset; if (isRight) { newOffset = relativeX + pseudoWidth >= boundaryWidth - HORIZONTAL_PADDING ? relativeX + pseudoWidth - (boundaryWidth - HORIZONTAL_PADDING) : 0; } else { const right = boundaryWidth - relativeX; newOffset = right + pseudoWidth >= boundaryWidth + HORIZONTAL_PADDING ? right + pseudoWidth - (boundaryWidth + HORIZONTAL_PADDING) : 0; } setOffset(newOffset); const newX = x - newOffset; setCoordinates({ x: newX, y }); } } }, [alignment, newContainer, pseudoSize, shouldScrollWithContent, yOffset]); const handleShow = (0, _react2.useCallback)(() => { updatePopupPosition(); if (isControlled) { return; } setIsInternallyOpen(true); }, [isControlled, updatePopupPosition]); (0, _react2.useEffect)(() => { if (isControlled) { if (isOpen) { updatePopupPosition(); } return; } if (shouldBeOpen) { handleShow(); } }, [handleShow, isControlled, isOpen, shouldBeOpen, updatePopupPosition]); const handleReposition = (0, _react2.useCallback)(() => { if (isPopupOpen) { updatePopupPosition(); } }, [isPopupOpen, updatePopupPosition]); (0, _react2.useEffect)(() => { if (!isPopupOpen) { return undefined; } window.addEventListener('resize', handleReposition); window.addEventListener('scroll', handleReposition, true); return () => { window.removeEventListener('resize', handleReposition); window.removeEventListener('scroll', handleReposition, true); }; }, [handleReposition, isPopupOpen]); (0, _react2.useEffect)(() => { if (!newContainer || !popupRef.current) return; const viewHeight = newContainer.clientHeight; const childrenHeight = popupRef.current.getBoundingClientRect().height; if ([_popup.PopupAlignment.TopLeft, _popup.PopupAlignment.TopRight, _popup.PopupAlignment.TopCenter].includes(internalAlignment)) { setContentMaxHeight(coordinates.y - 20); } else { setContentMaxHeight(viewHeight - childrenHeight - coordinates.y - 20); } }, [coordinates.y, internalAlignment, newContainer]); const handleChildrenClick = () => { if (isControlled) { return; } handleShow(); }; const handleHide = (0, _react2.useCallback)(() => { if (isControlled) { return; } setIsInternallyOpen(false); }, [isControlled]); const handleMouseEnter = (0, _react2.useCallback)(() => { if (isControlled) { return; } if (shouldShowOnHover) { window.clearTimeout(timeout.current); handleShow(); } }, [handleShow, isControlled, shouldShowOnHover]); const handleMouseLeave = (0, _react2.useCallback)(() => { if (isControlled) { return; } if (!shouldShowOnHover) { return; } if (shouldHideOnChildrenLeave) { handleHide(); return; } timeout.current = window.setTimeout(() => { handleHide(); }, 500); }, [handleHide, isControlled, shouldHideOnChildrenLeave, shouldShowOnHover]); const handleDocumentClick = (0, _react2.useCallback)(event => { var _popupContentRef$curr; if (!((_popupContentRef$curr = popupContentRef.current) !== null && _popupContentRef$curr !== void 0 && _popupContentRef$curr.contains(event.target))) { handleHide(); } }, [handleHide]); const handleDocumentKeyDown = (0, _react2.useCallback)(event => { if (event.key === 'Escape') { handleHide(); } }, [handleHide]); (0, _react2.useImperativeHandle)(ref, () => ({ hide: handleHide, show: handleShow }), [handleHide, handleShow]); (0, _react2.useEffect)(() => { if (!isPopupOpen) { return undefined; } if (!isControlled && !shouldBeOpen) { document.addEventListener('click', handleDocumentClick, true); window.addEventListener('blur', handleHide); } document.addEventListener('keydown', handleDocumentKeyDown); return () => { document.removeEventListener('click', handleDocumentClick, true); window.removeEventListener('blur', handleHide); document.removeEventListener('keydown', handleDocumentKeyDown); }; }, [handleDocumentClick, handleDocumentKeyDown, handleHide, isControlled, isPopupOpen, shouldBeOpen]); (0, _react2.useEffect)(() => { if (previousIsVisibleRef.current === isPopupOpen) { return; } previousIsVisibleRef.current = isPopupOpen; if (isPopupOpen) { onShow === null || onShow === void 0 || onShow(); return; } onHide === null || onHide === void 0 || onHide(); }, [isPopupOpen, onHide, onShow]); (0, _react2.useEffect)(() => { if (!newContainer) { return; } setPortal(() => /*#__PURE__*/(0, _reactDom.createPortal)(/*#__PURE__*/_react2.default.createElement(_react.AnimatePresence, { initial: false }, isPopupOpen && /*#__PURE__*/_react2.default.createElement(_PopupContentWrapper.default, { width: (pseudoSize === null || pseudoSize === void 0 ? void 0 : pseudoSize.width) ?? 0, offset: offset, shouldScrollWithContent: shouldScrollWithContent, coordinates: coordinates, key: `tooltip_${uuid}`, maxHeight: contentMaxHeight, alignment: internalAlignment, ref: popupContentRef, onMouseLeave: handleMouseLeave, onMouseEnter: handleMouseEnter }, /*#__PURE__*/_react2.default.createElement(_AreaContextProvider.default, { shouldChangeColor: true }, content))), newContainer)); }, [contentMaxHeight, internalAlignment, newContainer, content, coordinates, handleMouseEnter, handleMouseLeave, isPopupOpen, offset, pseudoSize === null || pseudoSize === void 0 ? void 0 : pseudoSize.width, uuid, shouldScrollWithContent]); return /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, measuredElement, /*#__PURE__*/_react2.default.createElement(_Popup.StyledPopup, { className: "beta-chayns-popup", ref: popupRef, onClick: handleChildrenClick, onMouseLeave: handleMouseLeave, onMouseEnter: handleMouseEnter, $shouldUseChildrenWidth: shouldUseChildrenWidth, $shouldUseFullWidth: shouldUseFullWidth }, children), portal); }); Popup.displayName = 'Popup'; var _default = exports.default = Popup; //# sourceMappingURL=Popup.js.map