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 6.96 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useTooltip = void 0; 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 useStyles_1 = require("../../../hooks/useStyles/useStyles"); const breakpoints_1 = require("../../../types/breakpoints/breakpoints"); const focusHandlers_1 = require("../../../utils/focusHandlers/focusHandlers"); const computePosition_1 = require("../positioning/computePosition"); // floating const arrow_1 = require("../positioning/middlewares/arrow"); const flip_1 = require("../positioning/middlewares/flip"); const shift_1 = require("../positioning/middlewares/shift"); const tooltipAlign_1 = require("../types/tooltipAlign"); /** * @name useTooltip * @description * Hook to handle the tooltip functionality * @param {UseTooltipType} props * @returns {UseTooltipReturnType} */ const useTooltip = ({ align = tooltipAlign_1.TooltipAlignType.TOP, ...props }) => { const mediaDevice = (0, useMediaDevice_1.useMediaDevice)(); const lastFocus = react_1.default.useRef(null); // Avoid tooltip is opened automatically after closing the tooltip const allowFocusOpenTooltip = react_1.default.useRef(true); const openRef = react_1.default.useRef(false); const [open, setOpen] = react_1.default.useState(false); // Need the styles to calc the arrow tooltip position const styles = (0, useStyles_1.useStyles)(stylesName_1.STYLES_NAME.TOOLTIP, props.variant, props.ctv); // Avoid to show tooltip when scrolling react_1.default.useEffect(() => { const handleResize = () => { updateTooltipPosition(); }; window.addEventListener('scroll', onScroll, true); window.addEventListener('resize', handleResize); return () => { window.removeEventListener('scroll', onScroll, true); window.removeEventListener('resize', handleResize); }; }, []); const onScroll = (e) => { if (!props.tooltipRef.current?.contains(e.target)) { updateTooltipPosition(); } }; const showTooltip = () => { if (openRef.current) { return; } openRef.current = true; // Save last focusable element lastFocus.current = document.activeElement; if (mediaDevice === breakpoints_1.DeviceBreakpointsType.DESKTOP) { if (!props.tooltipRef.current) { return; } props.tooltipRef.current.style.display = 'flex'; updateTooltipPosition(); (0, focusHandlers_1.focusElementOrFirstDescendant)(props.tooltipRef.current); } setOpen(true); props.onOpenClose?.(true); }; const hideTooltip = react_1.default.useCallback(() => { if (!openRef.current) { return; } // Apply focus in last focusable element openRef.current = false; allowFocusOpenTooltip.current = false; // Only focus in the last element if the tooltip is a modal if (props.tooltipAsModal) { lastFocus.current?.focus(); } allowFocusOpenTooltip.current = true; if (mediaDevice === breakpoints_1.DeviceBreakpointsType.DESKTOP) { if (!props.tooltipRef.current) { return; } props.tooltipRef.current.style.display = 'none'; } setOpen(false); props.onOpenClose?.(false); }, [mediaDevice, props.onOpenClose, props.tooltipAsModal]); const handleEscPress = react_1.default.useCallback((event) => { if (!openRef.current) { return; } // Only stop propagation if the tooltip is opened and its going to be closed event.stopPropagation(); hideTooltip(); }, [hideTooltip]); // Because desktop do not use popover component, we need to manually call manually to close on scape and click outside functionality (0, useEscPressedV2_1.useEscPressedV2)({ ref: props.labelRef, onEscPress: handleEscPress, disableStopPropagation: true, }); // Prevent to be closed when clicking the label, it will be handled by the tooltip label click in order to open/close the tooltip (0, useClickOutside_1.useClickOutside)(props.tooltipRef, hideTooltip, [props.labelRef.current]); // This function calc the position position where the tooltip should be displayed const updateTooltipPosition = () => { if (!props.labelRef.current || !props.tooltipRef.current) { return; } const arrowElement = props.tooltipRef.current.lastElementChild; const { x, y, placement, middlewareData } = (0, computePosition_1.computePosition)(props.labelRef.current, props.tooltipRef.current, { placement: align, middleware: [ // Offset is handle internally with a padding // Avoiding onMouseLeave label close the tooltip when you want to put the mouse inside // offset(props.styles.DESKTOP?.tooltip_offset), (0, flip_1.flip)(), (0, shift_1.shift)({ crossAxis: true }), (0, arrow_1.arrow)({ element: arrowElement }), ], }); Object.assign(props.tooltipRef.current.style, { left: `${x}px`, top: `${y}px`, }); // Arrow pos const { x: arrowX, y: arrowY } = middlewareData.arrow; const staticSide = { top: 'bottom', right: 'left', bottom: 'top', left: 'right', }[placement.split('-')[0]]; const arrowStyle = { left: arrowX !== null ? `${arrowX}px` : '', top: arrowY !== null ? `${arrowY}px` : '', right: '', bottom: '', [staticSide]: `calc(${styles.tooltipExternalContainer?.[breakpoints_1.DeviceBreakpointsType.DESKTOP]?.padding ?? 0} - ${styles.arrowContainer?.arrow_position ?? '0px'} / 2)`, }; if (arrowElement && arrowElement.style) { arrowElement.style.removeProperty('left'); arrowElement.style.removeProperty('top'); arrowElement.style.removeProperty('right'); arrowElement.style.removeProperty('bottom'); Object.assign(arrowElement?.style, arrowStyle); } }; return { showTooltip, hideTooltip, allowFocusOpenTooltip, open }; }; exports.useTooltip = useTooltip; //# sourceMappingURL=useTooltip.js.map