xlibs-components
Version:
Modern React component library with Aceternity, MagicUI, and ShadCN components for building beautiful web applications
120 lines (119 loc) • 7.71 kB
JavaScript
"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva } from "class-variance-authority";
import { cn } from '../../lib/utils.js';
import { motion, AnimatePresence } from "motion/react";
const unifiedButtonVariants = cva("inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 relative overflow-hidden group font-sans", {
variants: {
variant: {
default: "btn-primary shadow-lg hover:shadow-xl transform hover:-translate-y-0.5",
destructive: "btn-destructive shadow-lg hover:shadow-xl transform hover:-translate-y-0.5",
outline: "btn-outline shadow-sm hover:shadow-md transform hover:-translate-y-0.5",
secondary: "btn-secondary shadow-lg hover:shadow-xl transform hover:-translate-y-0.5",
ghost: "text-foreground hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
success: "btn-success shadow-lg hover:shadow-xl transform hover:-translate-y-0.5",
warning: "btn-warning shadow-lg hover:shadow-xl transform hover:-translate-y-0.5",
info: "btn-info shadow-lg hover:shadow-xl transform hover:-translate-y-0.5",
magic: "bg-gradient-to-r from-primary via-chart-2 to-chart-3 text-primary-foreground shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 hover:scale-105",
glass: "backdrop-blur-sm border border-border bg-card/50 hover:bg-card/80 hover:text-card-foreground shadow-lg hover:shadow-xl transform hover:-translate-y-0.5",
neon: "bg-primary text-primary-foreground shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 border border-primary/50 hover:border-primary shadow-[0_0_20px_rgba(var(--primary),0.3)] hover:shadow-[0_0_30px_rgba(var(--primary),0.5)]",
shiny: "bg-gradient-to-r from-primary to-chart-2 text-primary-foreground shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 relative overflow-hidden",
pulsating: "btn-primary shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 animate-pulse",
interactive: "btn-primary shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 hover:scale-105",
subscribe: "btn-primary shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-300",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
magic: "h-12 px-6 py-3 text-base",
xl: "h-14 px-10 py-4 text-lg",
},
effect: {
none: "",
ripple: "relative overflow-hidden",
shine: "relative overflow-hidden",
pulse: "animate-pulse",
glow: "shadow-[0_0_20px_rgba(var(--primary),0.3)] hover:shadow-[0_0_30px_rgba(var(--primary),0.5)]",
float: "animate-float",
scale: "hover:scale-105",
bounce: "hover:animate-bounce",
}
},
defaultVariants: {
variant: "default",
size: "default",
effect: "none",
},
});
const UnifiedButton = React.forwardRef(({ className, variant, size, effect, asChild = false, loading = false, ripple = false, shine = false, pulse = false, glow = false, float = false, scale = false, bounce = false, children, ...props }, ref) => {
// Filter out custom props to prevent them from being passed to DOM
const domProps = { ...props };
delete domProps.ripple;
delete domProps.shine;
delete domProps.pulse;
delete domProps.glow;
delete domProps.float;
delete domProps.scale;
delete domProps.bounce;
const Comp = asChild ? Slot : "button";
const [ripples, setRipples] = React.useState([]);
const [isShining, setIsShining] = React.useState(false);
const [isSubscribed, setIsSubscribed] = React.useState(false);
// Determine effect based on variant and props
const activeEffect = React.useMemo(() => {
if (effect !== "none")
return effect;
if (ripple)
return "ripple";
if (shine || variant === "shiny")
return "shine";
if (pulse || variant === "pulsating")
return "pulse";
if (glow || variant === "neon")
return "glow";
if (float)
return "float";
if (scale || variant === "interactive")
return "scale";
if (bounce)
return "bounce";
return "none";
}, [effect, ripple, shine, pulse, glow, float, scale, bounce, variant]);
const handleClick = (event) => {
if (ripple || activeEffect === "ripple") {
const rect = event.currentTarget.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const id = Date.now();
setRipples(prev => [...prev, { id, x, y }]);
setTimeout(() => {
setRipples(prev => prev.filter(ripple => ripple.id !== id));
}, 600);
}
if (variant === "subscribe") {
setIsSubscribed(!isSubscribed);
}
if (shine || activeEffect === "shine") {
setIsShining(true);
setTimeout(() => setIsShining(false), 600);
}
};
return (_jsxs(Comp, { className: cn(unifiedButtonVariants({
variant,
size,
effect: activeEffect,
className
})), ref: ref, onClick: handleClick, disabled: loading || domProps.disabled, ...domProps, children: [loading && (_jsx("div", { className: "mr-2 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" })), _jsx(AnimatePresence, { mode: "wait", children: variant === "subscribe" ? (_jsx(motion.span, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2 }, className: "font-sans", children: isSubscribed ? "Subscribed" : "Subscribe" }, isSubscribed ? "subscribed" : "subscribe")) : (_jsx("span", { className: "font-sans", children: children })) }), (ripple || activeEffect === "ripple") && ripples.map((ripple) => (_jsx(motion.span, { className: "absolute rounded-full bg-white/30", initial: { scale: 0, opacity: 1 }, animate: { scale: 4, opacity: 0 }, transition: { duration: 0.6 }, style: {
left: ripple.x - 10,
top: ripple.y - 10,
width: 20,
height: 20,
} }, ripple.id))), (shine || activeEffect === "shine" || variant === "shiny") && (_jsx(motion.div, { className: "absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent", initial: { x: "-100%" }, animate: { x: isShining ? "100%" : "-100%" }, transition: { duration: 0.6, ease: "easeInOut" } })), variant === "magic" && (_jsx("div", { className: "absolute inset-0 rounded-lg bg-gradient-to-r from-primary/20 via-chart-2/20 to-chart-3/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300" })), (variant === "interactive" || activeEffect === "scale") && (_jsx(motion.div, { className: "absolute inset-0 bg-white/10 rounded-lg", initial: { scale: 0, opacity: 0 }, whileHover: { scale: 1, opacity: 1 }, transition: { duration: 0.2 } }))] }));
});
UnifiedButton.displayName = "UnifiedButton";
export { UnifiedButton, unifiedButtonVariants };