aethex-forge/client/components/ui/button.tsx
sirpiglr eea2856485 Update buttons and remove unused code from realm selector
Refactors the IsometricRealmSelector component by removing unused gradient code and updating CTA buttons to use the Button component. Also, enhances the Button component's ripple effect to correctly handle the `asChild` prop and ensure consistent behavior across all click events.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: cc85e0aa-eae4-424c-8e21-ce55c6963530
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/OuNSijY
Replit-Helium-Checkpoint-Created: true
2025-12-13 00:18:33 +00:00

99 lines
3.4 KiB
TypeScript

import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:scale-105 active:scale-95 relative overflow-hidden",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-lg hover:shadow-primary/25",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90 hover:shadow-lg hover:shadow-destructive/25",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-aethex-400/50 hover:shadow-md",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80 hover:shadow-lg hover:shadow-secondary/25",
ghost: "hover:bg-accent hover:text-accent-foreground hover:shadow-md",
link: "text-primary underline-offset-4 hover:underline hover:text-aethex-400",
},
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",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, onClick, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
const target = e.currentTarget;
if (target instanceof HTMLElement) {
const rect = target.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const ripple = document.createElement("span");
ripple.style.position = "absolute";
ripple.style.borderRadius = "50%";
ripple.style.transform = "scale(0)";
ripple.style.animation = "ripple 0.6s linear";
ripple.style.backgroundColor = "rgba(255, 255, 255, 0.3)";
ripple.style.pointerEvents = "none";
const rippleSize = Math.max(rect.width, rect.height);
ripple.style.width = ripple.style.height = rippleSize + "px";
ripple.style.left = x - rippleSize / 2 + "px";
ripple.style.top = y - rippleSize / 2 + "px";
target.appendChild(ripple);
setTimeout(() => {
ripple.remove();
}, 600);
}
if (onClick) onClick(e as React.MouseEvent<HTMLButtonElement>);
};
return (
<>
<style>{`
@keyframes ripple {
to {
transform: scale(4);
opacity: 0;
}
}
`}</style>
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
onClick={handleClick}
{...props}
/>
</>
);
},
);
Button.displayName = "Button";
export { Button, buttonVariants };