This commit is contained in:
2025-12-14 02:38:35 +07:00
commit 5343a8f2c3
84 changed files with 7406 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
import { forwardRef, type ButtonHTMLAttributes } from 'react'
import { clsx } from 'clsx'
import { Loader2 } from 'lucide-react'
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'danger' | 'ghost'
size?: 'sm' | 'md' | 'lg'
isLoading?: boolean
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'primary', size = 'md', isLoading, children, disabled, ...props }, ref) => {
return (
<button
ref={ref}
disabled={disabled || isLoading}
className={clsx(
'inline-flex items-center justify-center font-medium rounded-lg transition-colors',
'disabled:opacity-50 disabled:cursor-not-allowed',
{
'bg-primary-600 hover:bg-primary-700 text-white': variant === 'primary',
'bg-gray-700 hover:bg-gray-600 text-white': variant === 'secondary',
'bg-red-600 hover:bg-red-700 text-white': variant === 'danger',
'bg-transparent hover:bg-gray-800 text-gray-300': variant === 'ghost',
'px-3 py-1.5 text-sm': size === 'sm',
'px-4 py-2 text-base': size === 'md',
'px-6 py-3 text-lg': size === 'lg',
},
className
)}
{...props}
>
{isLoading && <Loader2 className="w-4 h-4 mr-2 animate-spin" />}
{children}
</button>
)
}
)
Button.displayName = 'Button'

View File

@@ -0,0 +1,54 @@
import { type ReactNode } from 'react'
import { clsx } from 'clsx'
interface CardProps {
children: ReactNode
className?: string
}
export function Card({ children, className }: CardProps) {
return (
<div className={clsx('bg-gray-800 rounded-xl p-6 shadow-lg', className)}>
{children}
</div>
)
}
interface CardHeaderProps {
children: ReactNode
className?: string
}
export function CardHeader({ children, className }: CardHeaderProps) {
return (
<div className={clsx('mb-4', className)}>
{children}
</div>
)
}
interface CardTitleProps {
children: ReactNode
className?: string
}
export function CardTitle({ children, className }: CardTitleProps) {
return (
<h3 className={clsx('text-xl font-bold text-white', className)}>
{children}
</h3>
)
}
interface CardContentProps {
children: ReactNode
className?: string
}
export function CardContent({ children, className }: CardContentProps) {
return (
<div className={clsx('text-gray-300', className)}>
{children}
</div>
)
}

View File

@@ -0,0 +1,36 @@
import { forwardRef, type InputHTMLAttributes } from 'react'
import { clsx } from 'clsx'
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label?: string
error?: string
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, label, error, id, ...props }, ref) => {
return (
<div className="w-full">
{label && (
<label htmlFor={id} className="block text-sm font-medium text-gray-300 mb-1">
{label}
</label>
)}
<input
ref={ref}
id={id}
className={clsx(
'w-full px-4 py-2 bg-gray-800 border rounded-lg text-white placeholder-gray-500',
'focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent',
'transition-colors',
error ? 'border-red-500' : 'border-gray-700',
className
)}
{...props}
/>
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
</div>
)
}
)
Input.displayName = 'Input'

View File

@@ -0,0 +1,3 @@
export { Button } from './Button'
export { Input } from './Input'
export { Card, CardHeader, CardTitle, CardContent } from './Card'