UNPKG

@kubit-ui-web/react-components

Version:

Kubit React Components is a customizable, accessible library of React web components, designed to enhance your application's user experience

156 lines 8.17 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PopoverControlled = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const stylesName_1 = require("../../constants/stylesName/stylesName"); const useClickOutside_1 = require("../../hooks/useClickOutside/useClickOutside"); const useEscPressedV2_1 = require("../../hooks/useEscPressedV2/useEscPressedV2"); const useMediaDevice_1 = require("../../hooks/useMediaDevice/useMediaDevice"); const useScrollBlock_1 = require("../../hooks/useScrollBlock/useScrollBlock"); const useStyles_1 = require("../../hooks/useStyles/useStyles"); const useTrapFocus_1 = require("../../hooks/useTrapFocus/useTrapFocus"); const string_utility_1 = require("../../utils/stringUtility/string.utility"); const errorBoundary_1 = require("../../provider/errorBoundary/errorBoundary"); const fallbackComponent_1 = require("../../provider/errorBoundary/fallbackComponent"); const focusHandlers_1 = require("../../utils/focusHandlers/focusHandlers"); const cssAnimation_1 = require("../cssAnimation/types/cssAnimation"); const popoverStandAlone_1 = require("./popoverStandAlone"); const component_1 = require("./types/component"); const getAnimationConfig_1 = require("./utils/getAnimationConfig"); const MILISECONDS = 1000; const PopoverControlledComponent = react_1.default.forwardRef(({ variant, component = component_1.PopoverComponentType.DIALOG, focusFirstDescendantAutomatically = true, focusLastElementFocusedAfterClose = true, focusScreenFirstDescendantAfterClose = false, onCloseInternally, blockBack, clickOverlayClose = true, pressEscapeClose = true, preventScrollOnCloseFocus = false, trapFocusInsideModal = true, ctv, ...props }, ref) => { const { blockScroll, allowScroll } = (0, useScrollBlock_1.useScrollBlock)(); const currentFocus = react_1.default.useRef(null); const innerRef = react_1.default.useRef(null); const forwardedRef = react_1.default.useRef(null); const [forwardedElementInitialized, setForwardedElementInitialized] = react_1.default.useState(false); const setForwareddRef = react_1.default.useCallback(node => { if (node && focusFirstDescendantAutomatically) { (0, focusHandlers_1.focusFirstDescendant)(node); } if (props.forwardedRef) { props.forwardedRef(node); } if (node && blockBack) { blockScroll({ elementsToOmit: [node] }); } if (!node && blockBack) { allowScroll(); } setForwardedElementInitialized(!!node); forwardedRef.current = node; }, [props.forwardedRef, focusFirstDescendantAutomatically]); const styles = (0, useStyles_1.useStyles)(stylesName_1.STYLES_NAME.POPOVER, variant, ctv); const device = (0, useMediaDevice_1.useMediaDevice)(); const animationConfig = (0, getAnimationConfig_1.getAnimationConfig)(styles, device, props.animation, props.animationOptions); const [showAnimationEnd, setShowAnimationEnd] = react_1.default.useState(false); const [openAnimation, setOpenAnimation] = react_1.default.useState(props.open); const openAnimationRef = react_1.default.useRef(props.open); // Expose the ref to the parent component react_1.default.useImperativeHandle(ref, () => { return innerRef.current; }, []); // To improve: this ref has been created to avoid update a state when the component does not longer exists. // Check waitForAnimation and setOpenAnimation in the onClose method const componentAlive = react_1.default.useRef(true); react_1.default.useEffect(() => { componentAlive.current = true; return () => { componentAlive.current = false; }; }, []); const onClose = async () => { if (!openAnimationRef.current) { return; } if (animationConfig && animationConfig?.animation) { setShowAnimationEnd(true); await waitForAnimation(); } afterModalFocus(); openAnimationRef.current = false; if (componentAlive.current) { setOpenAnimation(false); } }; const handleClickOutside = async () => { if (clickOverlayClose) { await onClose(); onCloseInternally?.(); } }; const handlePressScape = async () => { if (pressEscapeClose) { await onClose(); onCloseInternally?.(); } }; (0, useClickOutside_1.useClickOutside)(forwardedRef, handleClickOutside, props.preventCloseOnClickElements); (0, useEscPressedV2_1.useEscPressedV2)({ ref: innerRef, onEscPress: handlePressScape, disablePreventDefault: !pressEscapeClose, disableStopPropagation: !pressEscapeClose, }); const beforeModalFocus = () => { currentFocus.current = document.activeElement; setShowAnimationEnd(false); }; const afterModalFocus = () => { if (focusScreenFirstDescendantAfterClose) { (0, focusHandlers_1.focusFirstDescendant)(document.body); return; } if (focusLastElementFocusedAfterClose) { currentFocus.current?.focus({ preventScroll: preventScrollOnCloseFocus }); } }; (0, useTrapFocus_1.useTrapFocus)({ ref: forwardedRef, trapFocus: forwardedElementInitialized && trapFocusInsideModal, }); react_1.default.useEffect(() => { if (!props.open && openAnimationRef.current) { onClose(); } if (props.open) { if (!openAnimationRef.current) { beforeModalFocus(); openAnimationRef.current = true; setOpenAnimation(true); } } }, [props.open]); // deprecated - Remove the condition when `exitDuration`, `duration` and `delay' are type string const animationExitDuration = () => { if (typeof animationConfig?.animationOptions?.exitDuration === 'string' || typeof animationConfig?.animationOptions?.duration === 'string' || typeof animationConfig?.animationOptions?.delay === 'string') { const exitDuration = (0, string_utility_1.convertDurationToNumber)(animationConfig?.animationOptions?.exitDuration); const duration = (0, string_utility_1.convertDurationToNumber)(animationConfig?.animationOptions?.duration); const delay = (0, string_utility_1.convertDurationToNumber)(animationConfig?.animationOptions?.delay); return (exitDuration || duration || 0) + (delay || 0); } return (((animationConfig?.animationOptions?.exitDuration || animationConfig?.animationOptions?.duration || 0) + (animationConfig?.animationOptions?.delay || 0)) * MILISECONDS); }; const waitForAnimation = () => { return new Promise(resolve => { setTimeout(() => { resolve(); }, animationExitDuration()); }); }; return ((0, jsx_runtime_1.jsx)(popoverStandAlone_1.PopoverStandAlone, { ...props, ref: innerRef, animationConfig: animationConfig, animationExecution: showAnimationEnd ? cssAnimation_1.CssAnimationExecuteOption.END : cssAnimation_1.CssAnimationExecuteOption.START, component: component, device: device, forwardedRef: setForwareddRef, open: openAnimation, styles: styles })); }); PopoverControlledComponent.displayName = 'PopoverControlledComponent'; const PopoverBoundary = (props, ref) => ((0, jsx_runtime_1.jsx)(errorBoundary_1.ErrorBoundary, { fallBackComponent: (0, jsx_runtime_1.jsx)(fallbackComponent_1.FallbackComponent, { children: (0, jsx_runtime_1.jsx)(popoverStandAlone_1.PopoverStandAlone, { ...props, ref: ref }) }), children: (0, jsx_runtime_1.jsx)(PopoverControlledComponent, { ...props, ref: ref }) })); exports.PopoverControlled = react_1.default.forwardRef(PopoverBoundary); //# sourceMappingURL=popoverControlled.js.map