@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
JavaScript
;
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