Add src/components/ui/liquid-glass-button.tsx

This commit is contained in:
2026-03-09 21:42:37 +00:00
parent 8fd46b134d
commit 1b7f30d181

View File

@@ -0,0 +1,154 @@
'use client';
import React, { useState, useRef, useEffect } from 'react';
export interface LiquidButtonProps {
text: string;
onClick?: () => void;
className?: string;
disabled?: boolean;
children?: React.ReactNode;
}
export const LiquidButton: React.FC<LiquidButtonProps> = ({
text,
onClick,
className = '',
disabled = false,
children,
}) => {
const [isHovered, setIsHovered] = useState(false);
return (
<button
onClick={onClick}
disabled={disabled}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`relative px-6 py-3 font-medium transition-all duration-300 ${
isHovered ? 'scale-105' : 'scale-100'
} ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'} ${className}`}
>
<div className="absolute inset-0 rounded-lg bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 opacity-75 blur-md group-hover:opacity-100 transition-opacity duration-300" />
<div className="relative z-10 flex items-center justify-center gap-2">
{children || text}
</div>
</button>
);
};
export interface ButtonProps extends LiquidButtonProps {
variant?: 'primary' | 'secondary' | 'outline';
size?: 'sm' | 'md' | 'lg';
}
export const Button: React.FC<ButtonProps> = ({
text,
onClick,
variant = 'primary',
size = 'md',
className = '',
disabled = false,
children,
}) => {
const baseClass =
'relative font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
const sizeClasses = {
sm: 'px-3 py-2 text-sm',
md: 'px-6 py-3 text-base',
lg: 'px-8 py-4 text-lg',
};
const variantClasses = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 disabled:bg-gray-400',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300 disabled:bg-gray-100',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50 disabled:border-gray-400 disabled:text-gray-400',
};
return (
<button
onClick={onClick}
disabled={disabled}
className={`${baseClass} ${sizeClasses[size]} ${variantClasses[variant]} ${className}`}
>
{children || text}
</button>
);
};
export interface GlassFilterProps {
children: React.ReactNode;
intensity?: number;
className?: string;
}
export const GlassFilter: React.FC<GlassFilterProps> = ({
children,
intensity = 0.1,
className = '',
}) => {
return (
<div
className={`backdrop-blur-md bg-white/10 rounded-xl border border-white/20 ${className}`}
style={{ backdropFilter: `blur(${intensity * 20}px)` }}
>
{children}
</div>
);
};
export interface MetalButtonProps extends ButtonProps {
shine?: boolean;
}
export const MetalButton: React.FC<MetalButtonProps> = ({
text,
onClick,
className = '',
disabled = false,
shine = true,
children,
}) => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const buttonRef = useRef<HTMLButtonElement>(null);
const handleMouseMove = (e: React.MouseEvent<HTMLButtonElement>) => {
if (!buttonRef.current) return;
const rect = buttonRef.current.getBoundingClientRect();
setMousePosition({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
};
return (
<button
ref={buttonRef}
onClick={onClick}
disabled={disabled}
onMouseMove={handleMouseMove}
className={`relative px-6 py-3 font-medium rounded-lg transition-all duration-200 overflow-hidden
bg-gradient-to-b from-gray-100 to-gray-300 text-gray-900 hover:from-gray-50 hover:to-gray-200
border border-gray-400 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed
${className}`}
>
{shine && (
<div
className="absolute inset-0 pointer-events-none"
style={{
background: `radial-gradient(circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(255,255,255,0.3), transparent 80%)`,
}}
/>
)}
<div className="relative z-10 flex items-center justify-center gap-2">
{children || text}
</div>
</button>
);
};
export default {
LiquidButton,
Button,
GlassFilter,
MetalButton,
};