UNPKG

nudge-components-library

Version:

A library of nudge UI components

132 lines (129 loc) 6.49 kB
import { jsxs, jsx } from 'react/jsx-runtime'; import { useContext, useState, useRef, useEffect, useLayoutEffect } from 'react'; import '../../styles/tokens.css.js'; import '../../styles/globals.css.js'; import { ThemeContext } from '../../theme/ThemeContext.js'; import { FiX } from '../../node_modules/react-icons/fi/index.js'; import styles from './Tooltip.module.css.js'; function Tooltip({ id, text, visible, defaultVisible = false, onOpen, onClose, position = "top", autoClose = false, autoCloseDelay = 3000, renderContent, children, ariaLabel, dismissible = false, animationType = "fade", animationDuration = 300, closeOnHover = true, openOnHover = true, closeOutside = false, buttonText, onButtonClick, icon, }) { const theme = useContext(ThemeContext); const [isVisible, setIsVisible] = useState(false); const [shouldRender, setShouldRender] = useState(defaultVisible); const [finalPosition, setFinalPosition] = useState("top"); const timerRef = useRef(null); const containerRef = useRef(null); const tooltipRef = useRef(null); useEffect(() => { if (visible !== undefined) { if (visible) { setShouldRender(true); setTimeout(() => { setIsVisible(true); }, 100); onOpen?.(); } else { setIsVisible(false); const timer = setTimeout(() => { setShouldRender(false); onClose?.(); }, animationDuration); return () => clearTimeout(timer); } } }, [visible, animationDuration, onOpen, onClose]); // Auto-close logic useEffect(() => { if (autoClose && isVisible) { timerRef.current = setTimeout(() => { setIsVisible(false); onClose?.(); }, autoCloseDelay); return () => { if (timerRef.current) clearTimeout(timerRef.current); }; } }, [autoClose, autoCloseDelay, isVisible, onClose]); // Close when clicking outside useEffect(() => { if (closeOutside && isVisible) { const handleClickOutside = (event) => { if (containerRef.current && !containerRef.current.contains(event.target)) { handleClose(); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); } }, [closeOutside, isVisible]); // Hover/focus handlers const handleOpen = (event) => { if (visible === undefined) { setShouldRender(true); setTimeout(() => { setIsVisible(true); }, 100); } onOpen?.(); }; const handleClose = (event) => { if (visible === undefined) { setIsVisible(false); const timer = setTimeout(() => { setShouldRender(false); onClose?.(); }, animationDuration); return () => clearTimeout(timer); } onClose?.(); }; useLayoutEffect(() => { if (position === "dynamic" && containerRef.current && tooltipRef.current) { const containerRect = containerRef.current.getBoundingClientRect(); const tooltipRect = tooltipRef.current.getBoundingClientRect(); const margin = 8; if (containerRect.top >= tooltipRect.height + margin) { setFinalPosition("top"); } else if (window.innerHeight - containerRect.bottom >= tooltipRect.height + margin) { setFinalPosition("bottom"); } else if (containerRect.left >= tooltipRect.width + margin) { setFinalPosition("left"); } else if (window.innerWidth - containerRect.right >= tooltipRect.width + margin) { setFinalPosition("right"); } else { setFinalPosition("top"); } } }, [position, isVisible]); const pos = position === "dynamic" ? finalPosition : position; const animationStyle = { "--animation-duration": `${animationDuration}ms`, }; const positionClass = (() => { switch (pos) { case "top": return styles.tooltipTop; case "bottom": return styles.tooltipBottom; case "left": return styles.tooltipLeft; case "right": return styles.tooltipRight; default: return ""; } })(); const animationClass = animationType === "fade" ? styles.fade : styles.slide; const visibilityClass = isVisible ? styles.visible : styles.hidden; return (jsxs("div", { ref: containerRef, className: styles.tooltipDisplayContainer, id: id, children: [jsx("div", { onMouseEnter: openOnHover ? handleOpen : undefined, onFocus: openOnHover ? handleOpen : undefined, onMouseLeave: closeOnHover ? handleClose : undefined, onBlur: closeOnHover ? handleClose : undefined, "aria-label": ariaLabel, children: children }), shouldRender && (jsx("div", { ref: tooltipRef, className: `${styles.tooltipContainer} ${positionClass} ${animationClass} ${visibilityClass}`, role: "tooltip", style: { ...animationStyle, ...theme.tooltip?.container }, children: jsxs("div", { className: `${styles.tooltipContentContainer} ${icon ? styles.gridWithIcon : ""}`, style: theme.tooltip?.content, children: [icon && (jsx("div", { className: styles.tooltipIconContainer, style: theme.tooltip?.icon, children: icon })), jsxs("div", { className: styles.tooltipTextContainer, style: theme.tooltip?.message, children: [renderContent ? renderContent() : jsx("span", { children: text }), buttonText && !renderContent && (jsx("button", { className: styles.tooltipActionButton, style: theme.tooltip?.actionButton, onClick: onButtonClick, children: buttonText }))] }), dismissible && (jsx("div", { className: styles.tooltipCloseButtonContainer, style: theme.tooltip?.closeButtonContainer, children: jsx("button", { onClick: handleClose, "aria-label": "Close tooltip", style: theme.tooltip?.closeButton, children: jsx(FiX, {}) }) }))] }) }))] })); } export { Tooltip }; //# sourceMappingURL=Tooltip.js.map