Button (components/ui/button.tsx)¶
Overview¶
A versatile button component that provides customizable styling, interaction states, and loading functionality.
Features¶
- Multiple variants (default, outline, ghost, link)
- Size options (sm, md, lg)
- Loading state with spinner
- Icon support (leading/trailing)
- Keyboard navigation
- Disabled state handling
- Focus management
- Touch feedback
- Ripple effect
- Accessibility support
Dependencies¶
import * as React from 'react'
import { forwardRef } from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { Spinner } from './spinner'
import { cn } from '@/lib/utils'
Props¶
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
disabled?: boolean;
leadingIcon?: ReactNode;
trailingIcon?: ReactNode;
className?: string;
children: ReactNode;
onClick?: () => void;
}
Implementation¶
State Management¶
interface ButtonState {
isPressed: boolean;
isFocused: boolean;
ripples: Array<{
id: number;
x: number;
y: number;
}>;
}
const [buttonState, setButtonState] = useState<ButtonState>({
isPressed: false,
isFocused: false,
ripples: []
});
Methods¶
const handlePressStart = (event: React.PointerEvent) => {
setButtonState(prev => ({
...prev,
isPressed: true
}));
const rect = event.currentTarget.getBoundingClientRect();
const ripple = {
id: Date.now(),
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
setButtonState(prev => ({
...prev,
ripples: [...prev.ripples, ripple]
}));
};
const handlePressEnd = () => {
setButtonState(prev => ({
...prev,
isPressed: false
}));
};
Unique Functionality¶
- Variant styling system
- Ripple effect animation
- Size management
- Loading state handling
- Icon positioning
- Focus state management
HTML Structure¶
<button
className={cn(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
getVariantStyles(variant),
getSizeStyles(size),
className
)}
disabled={disabled || loading}
ref={ref}
{...props}
>
{loading && <Spinner className="mr-2 h-4 w-4 animate-spin" />}
{leadingIcon && <span className="mr-2">{leadingIcon}</span>}
{children}
{trailingIcon && <span className="ml-2">{trailingIcon}</span>}
</button>
API Integration¶
No direct API integration required.
Components Used¶
- Spinner
- Ripple
Notes¶
- Implements keyboard navigation
- Handles loading states
- Manages focus states
- Provides touch feedback
- Supports accessibility features
- Clean up ripple effects on unmount