@zohodesk/hooks
Version:
Unified Component Library - Hooks
140 lines (123 loc) • 5.64 kB
JavaScript
import { useLayoutEffect, useCallback } from 'react';
import usePopupHandlers from "./usePopupHandlers";
import useClickOutside from "../utils/useClickOutside";
import useEscape from "../utils/useEscape";
import useScrollParents from "../scroll/useScrollParents";
import useScrollStopper from "../scroll/useScrollStopper";
import useFixedPopupScrollHandling from "./useFixedPopupScrollHandling";
import usePopupObservers from "./usePopupObservers";
import usePopupFloating from "./usePopupFloating";
/**
A custom React hook that provides advanced functionality for handling popups.
@param {Object} props - An object containing optional configuration for the popup.
@param {boolean} props.isPopupOpen - A boolean indicating whether the popup should be open or not.
@param {boolean} props.isAbsolutePosition - A boolean indicating whether the popup should be positioned absolutely or fixed.
@param {boolean} props.isArrow - A boolean indicating whether the popup should have an arrow pointing to the target element.
@param {Array} props.customPositionOrder - An array of preferred positions for the popup, in order of priority.
@param {string} props.defaultPosition - A string indicating the default position for the popup.
@param {Function} props.getTargetElement - A function that returns the target element for the popup.
@param {Function} props.getDropdownElement - A function that returns the dropdown element for the popup.
@param {boolean} props.needPositionUpdateOnScroll - A boolean indicating whether the popup position should be updated on scroll events.
@param {Function} props.onTargetMovement - A function that is called when the target element moves.
@param {boolean} props.isMobileView - A boolean indicating whether the popup should be optimized for mobile view.
@param {string} groupName - A string indicating the name of the popup group.
@returns {Object} An object containing the current state of the popup and functions to open, close, or toggle the popup.
*/
export default function useAdvancedPopup() {
let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
let groupName = arguments.length > 1 ? arguments[1] : undefined;
const {
isPopupOpen: propsPopupOpen,
isAbsolutePosition,
isArrow,
customPositionOrder,
defaultPosition,
getTargetElement,
getDropdownElement,
needPositionUpdateOnScroll = false,
onTargetMovement,
isMobileView = false
} = props; // --- Popup state and function ---
const {
isPopupOpen,
handleOpenPopup,
handleClosePopup,
handleTogglePopup
} = usePopupHandlers(propsPopupOpen, groupName); // --- Scroll Parents ---
const {
immediateScrollParent,
otherScrollParents
} = useScrollParents(getTargetElement()); // --- Floating data ---
const {
floating,
reCalcFloating
} = usePopupFloating({
getTargetElement,
getDropdownElement,
defaultPosition,
isAbsolutePosition,
isArrow,
customPositionOrder,
onTargetMovement
}); // --- Calculate floating when popup opens ---
useLayoutEffect(() => {
if (isPopupOpen && !isMobileView) {
reCalcFloating();
}
}, [isPopupOpen]); // --- Resize handlings ---
usePopupObservers({
isObserve: isPopupOpen && !isMobileView,
isAbsolutePosition,
handleClosePopup,
reCalcFloating,
getTargetElement,
getDropdownElement
}); // --- Exclude Elements ---
const getExcludeElements = useCallback(() => {
return [getTargetElement(), getDropdownElement()];
}, []); // --- Close popup on outside click ---
useClickOutside({
getElements: getExcludeElements,
callback: handleClosePopup,
isRegister: isPopupOpen
}); // --- Close popup on escape key press ---
useEscape({
callback: handleClosePopup,
isRegister: isPopupOpen
}); // --- Scroll handlings for fixed popup ---
useScrollStopper({
getExcludeElements,
element: immediateScrollParent,
isStop: !isAbsolutePosition && !needPositionUpdateOnScroll && isPopupOpen && !isMobileView
}); // --- Update popup position on parent scroll & Close popup on other parent's scroll ---
useFixedPopupScrollHandling({
isScrollHandleNeed: !(isAbsolutePosition || isMobileView) && isPopupOpen,
immediateScrollParent,
otherScrollParents,
immediateParentScrollHandler: needPositionUpdateOnScroll ? reCalcFloating : null,
otherParentsScrollHandler: handleClosePopup
}); // --- Return ---
return {
isPopupOpen,
handleOpenPopup,
handleClosePopup,
handleTogglePopup,
floating
};
}
/*
To Do:
- Update floating on scroll for fixed popup - DONE
- Close popup when target element hides inside scroll for fixed popup - DONE
- Block all scrolls for fixed popup - DONE
- Hide dropbox when scroll the other parents - DONE
- Update floating and visibility on window resize for fixed popup - DONE
- Update floating and visibility on window resize on scroll - InterSectionObserver - DONE
- Update floating state only change happens on floating data in reCalcFloating - PARTIALLY DONE (Using JSON.stringifiy)
- Update floating and visibility on target & dropbox resize - ResizeObserver - PARTIALLY DONE
- Use popup inside another popup & Need to maintain order - PARTIALLY DONE
- Resizes & floating should not be calculated for mobile case - Need flag - DONE
- Close dropbox on parent elements scroll - Get parent with data attr - WILL CHECK
- Close popup once complete the animation - WILL CHECK
- $ Floating data equal validation will not be useful, bcoz while scroll popupOffset (fixed popup's top position is changing)
*/