lightswind
Version:
A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.
359 lines • 15.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TooltipProvider = exports.TooltipContent = exports.Tooltips = exports.TooltipTrigger = exports.Tooltip = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const React = __importStar(require("react"));
const utils_1 = require("@/components/lib/utils"); // Assuming utils.ts is in lib/
const framer_motion_1 = require("framer-motion");
const react_dom_1 = __importDefault(require("react-dom"));
const TooltipContext = React.createContext(undefined);
// --- Tooltip Component ---
const Tooltip = ({ children, content, defaultOpen = false, open: controlledOpen, onOpenChange, delayDuration = 300, hideDelay = 100, side = "top", align = "center", sideOffset = 8, variant = "default", hideArrow = false, maxWidth = "20rem", asChild = false, disabled = false, }) => {
// Manage uncontrolled open state
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
// Determine if the component is controlled or uncontrolled
const isControlled = controlledOpen !== undefined;
const open = isControlled ? controlledOpen : uncontrolledOpen;
// Ref for the trigger element, passed via context to TooltipContentDisplay
const triggerRef = React.useRef(null);
// Function to update the open state, handling both controlled and uncontrolled modes
const setOpen = React.useCallback((value) => {
if (!isControlled) {
setUncontrolledOpen(value);
}
if (onOpenChange) {
// Calculate the new value if a function is passed
const newValue = typeof value === "function" ? value(open) : value;
onOpenChange(newValue);
}
}, [isControlled, onOpenChange, open]);
// Refs for managing show/hide timeouts
const showTimeoutRef = React.useRef(null);
const hideTimeoutRef = React.useRef(null);
// Memoize configuration object to prevent unnecessary re-renders
const config = React.useMemo(() => ({
side,
align,
sideOffset,
variant,
hideArrow,
maxWidth,
}), [side, align, sideOffset, variant, hideArrow, maxWidth]);
// Clean up any pending timeouts on component unmount
React.useEffect(() => {
return () => {
if (showTimeoutRef.current)
clearTimeout(showTimeoutRef.current);
if (hideTimeoutRef.current)
clearTimeout(hideTimeoutRef.current);
};
}, []);
const handleMouseEnter = React.useCallback(() => {
if (disabled)
return;
// Always clear both timers to prevent race conditions
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
hideTimeoutRef.current = null;
}
if (showTimeoutRef.current) {
clearTimeout(showTimeoutRef.current);
showTimeoutRef.current = null;
}
showTimeoutRef.current = setTimeout(() => setOpen(true), delayDuration);
}, [disabled, setOpen, delayDuration]);
const handleMouseLeave = React.useCallback(() => {
if (disabled)
return;
// Always clear both timers to prevent race conditions
if (showTimeoutRef.current) {
clearTimeout(showTimeoutRef.current);
showTimeoutRef.current = null;
}
if (hideTimeoutRef.current) {
clearTimeout(hideTimeoutRef.current);
hideTimeoutRef.current = null;
}
hideTimeoutRef.current = setTimeout(() => setOpen(false), hideDelay);
}, [disabled, setOpen, hideDelay]);
// Memoize the context value to prevent unnecessary re-renders of consumers
const contextValue = React.useMemo(() => ({
open,
setOpen,
content,
config,
triggerRef,
showTimeoutRef,
hideTimeoutRef,
handleMouseEnter,
handleMouseLeave,
}), [open, setOpen, content, config, triggerRef, handleMouseEnter, handleMouseLeave]);
return ((0, jsx_runtime_1.jsxs)(TooltipContext.Provider, { value: contextValue, children: [disabled ? children : ((0, jsx_runtime_1.jsx)(TooltipTrigger, { asChild: asChild, triggerRef: triggerRef, children: children })), (0, jsx_runtime_1.jsx)(TooltipContentDisplay, {})] }));
};
exports.Tooltip = Tooltip;
// --- TooltipTrigger Component ---
const TooltipTrigger = React.forwardRef(({ children, asChild = false, triggerRef }, ref) => {
const context = React.useContext(TooltipContext);
if (!context) {
throw new Error("TooltipTrigger must be used within a Tooltip");
}
// Combined ref to set both the forwarded ref and the internal triggerRef
const combinedRef = React.useCallback((node) => {
if (typeof ref === 'function') {
ref(node);
}
else if (ref) {
ref.current = node;
}
if (triggerRef) {
triggerRef.current = node;
}
}, [ref, triggerRef]);
// Props to be applied to the trigger element
const triggerProps = {
ref: combinedRef, // Use the combined ref
onMouseEnter: context.handleMouseEnter,
onMouseLeave: context.handleMouseLeave,
onFocus: () => context.setOpen(true),
onBlur: () => context.setOpen(false),
};
// If asChild is true, clone props onto the child element
if (asChild && React.isValidElement(children)) {
return React.cloneElement(children, triggerProps);
}
// Default rendering if asChild is false
return ((0, jsx_runtime_1.jsx)("div", { className: "inline-block relative" // Keeps the trigger as a block for correct positioning
, ...triggerProps, children: children }));
});
exports.TooltipTrigger = TooltipTrigger;
TooltipTrigger.displayName = "TooltipTrigger";
// --- TooltipContentDisplay Component ---
// This component renders the actual tooltip content and handles its positioning.
const TooltipContentDisplay = () => {
const context = React.useContext(TooltipContext);
if (!context) {
throw new Error("TooltipContentDisplay must be used within a Tooltip");
}
const { open, content, config, triggerRef } = context;
const [position, setPosition] = React.useState({ x: 0, y: 0 });
const contentRef = React.useRef(null);
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
const getAnimationVariants = React.useCallback(() => {
const { side } = config;
const distance = 8;
return {
hidden: {
opacity: 0,
scale: 0.8,
x: side === "left" ? distance : side === "right" ? -distance : 0,
y: side === "top" ? distance : side === "bottom" ? -distance : 0,
},
visible: {
opacity: 1,
scale: 1,
x: 0,
y: 0,
transition: {
type: "spring",
damping: 20,
stiffness: 400,
},
},
exit: {
opacity: 0,
scale: 0.8,
transition: { duration: 0.15, ease: "easeIn" },
pointerEvents: "none",
},
};
}, [config]);
const updatePosition = React.useCallback(() => {
if (!contentRef.current || !triggerRef.current)
return;
const triggerRect = triggerRef.current.getBoundingClientRect();
const contentRect = contentRef.current.getBoundingClientRect();
let x = 0;
let y = 0;
const { side, align, sideOffset } = config;
switch (side) {
case "top":
y = triggerRect.top - contentRect.height - sideOffset;
break;
case "bottom":
y = triggerRect.bottom + sideOffset;
break;
case "left":
x = triggerRect.left - contentRect.width - sideOffset;
break;
case "right":
x = triggerRect.right + sideOffset;
break;
}
if (side === "top" || side === "bottom") {
switch (align) {
case "start":
x = triggerRect.left;
break;
case "end":
x = triggerRect.right - contentRect.width;
break;
default:
x = triggerRect.left + triggerRect.width / 2 - contentRect.width / 2;
break;
}
}
else {
switch (align) {
case "start":
y = triggerRect.top;
break;
case "end":
y = triggerRect.bottom - contentRect.height;
break;
default:
y = triggerRect.top + triggerRect.height / 2 - contentRect.height / 2;
break;
}
}
const padding = 8;
if (x < padding)
x = padding;
if (x + contentRect.width > window.innerWidth - padding)
x = window.innerWidth - contentRect.width - padding;
if (y < padding)
y = padding;
if (y + contentRect.height > window.innerHeight - padding)
y = window.innerHeight - contentRect.height - padding;
setPosition({ x, y });
}, [config, triggerRef]);
React.useEffect(() => {
if (open) {
const id = requestAnimationFrame(updatePosition);
window.addEventListener("resize", updatePosition);
window.addEventListener("scroll", updatePosition, true);
return () => {
cancelAnimationFrame(id);
window.removeEventListener("resize", updatePosition);
window.removeEventListener("scroll", updatePosition, true);
};
}
}, [open, updatePosition]);
const getArrowStyle = React.useCallback(() => {
const { side, align } = config;
const arrowSize = 8;
let style = {
position: "absolute",
width: arrowSize,
height: arrowSize,
transform: "rotate(45deg)",
zIndex: -1,
};
switch (side) {
case "top":
style.bottom = -arrowSize / 2;
style.left = align === "center" ? "50%" : align === "start" ? "15%" : undefined;
if (align === "end")
style.right = "15%";
if (align === "center")
style.transform = "translateX(-50%) rotate(45deg)";
break;
case "bottom":
style.top = -arrowSize / 2;
style.left = align === "center" ? "50%" : align === "start" ? "15%" : undefined;
if (align === "end")
style.right = "15%";
if (align === "center")
style.transform = "translateX(-50%) rotate(45deg)";
break;
case "left":
style.right = -arrowSize / 2;
style.top = align === "center" ? "50%" : align === "start" ? "15%" : undefined;
if (align === "end")
style.bottom = "15%";
if (align === "center")
style.transform = "translateY(-50%) rotate(45deg)";
break;
case "right":
style.left = -arrowSize / 2;
style.top = align === "center" ? "50%" : align === "start" ? "15%" : undefined;
if (align === "end")
style.bottom = "15%";
if (align === "center")
style.transform = "translateY(-50%) rotate(45deg)";
break;
}
return style;
}, [config]);
const getVariantClasses = React.useCallback(() => {
const { variant } = config;
switch (variant) {
case "info": return "bg-primarylw text-white border-[color-mix(in_srgb,var(--primarylw)_20%,transparent)]";
case "success": return "bg-emerald-600 text-white border-emerald-400/20";
case "warning": return "bg-amber-500 text-black border-amber-400/20";
case "error": return "bg-rose-600 text-white border-rose-400/20";
default: return "bg-popover/90 text-popover-foreground border border-gray-200 dark:border-gray-800 backdrop-blur-md shadow-xl";
}
}, [config]);
if (!open || !mounted)
return null;
return react_dom_1.default.createPortal((0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { children: (0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { ref: contentRef, onMouseEnter: context.handleMouseEnter, onMouseLeave: context.handleMouseLeave, style: {
position: "fixed",
top: position.y,
left: position.x,
maxWidth: config.maxWidth,
zIndex: 9999,
}, initial: "hidden", animate: "visible", exit: "exit", variants: getAnimationVariants(), className: (0, utils_1.cn)("rounded-md px-3 py-1.5 text-xs font-medium", getVariantClasses()), children: [!config.hideArrow && ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("absolute w-2 h-2", getVariantClasses(), "border-0"), style: getArrowStyle() })), content] }) }), document.body);
};
// For legacy compatibility (if needed, otherwise remove)
exports.Tooltips = Tooltip;
// Deprecated TooltipContent, kept for backward compatibility with a warning
exports.TooltipContent = React.forwardRef((props, _ref) => {
console.warn("TooltipContent is deprecated. Use the Tooltip component with content prop instead.");
return (0, jsx_runtime_1.jsx)("div", { ...props });
});
exports.TooltipContent.displayName = "TooltipContent";
// Backward compatibility for TooltipProvider (if needed, otherwise remove)
const TooltipProvider = ({ children }) => {
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
};
exports.TooltipProvider = TooltipProvider;
//# sourceMappingURL=tooltip.js.map