Add src/components/ui/liquid-glass-button.tsx
This commit is contained in:
154
src/components/ui/liquid-glass-button.tsx
Normal file
154
src/components/ui/liquid-glass-button.tsx
Normal 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,
|
||||
};
|
||||
Reference in New Issue
Block a user