119 lines
3 KiB
TypeScript
119 lines
3 KiB
TypeScript
import React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface FormFieldProps {
|
|
label: string;
|
|
error?: string;
|
|
required?: boolean;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
helpText?: string;
|
|
}
|
|
|
|
export const FormField: React.FC<FormFieldProps> = ({
|
|
label,
|
|
error,
|
|
required = false,
|
|
children,
|
|
className,
|
|
helpText,
|
|
}) => {
|
|
return (
|
|
<div className={cn('space-y-2', className)}>
|
|
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
{label}
|
|
{required && <span className="text-red-500 ml-1">*</span>}
|
|
</label>
|
|
{children}
|
|
{helpText && !error && (
|
|
<p className="text-xs text-gray-500 dark:text-gray-400">{helpText}</p>
|
|
)}
|
|
{error && (
|
|
<p className="text-xs text-red-500 dark:text-red-400">{error}</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
error?: boolean;
|
|
}
|
|
|
|
export const Input: React.FC<InputProps> = ({
|
|
error = false,
|
|
className,
|
|
...props
|
|
}) => {
|
|
return (
|
|
<input
|
|
className={cn(
|
|
'w-full px-3 py-2 border rounded-md shadow-sm transition-colors',
|
|
'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent',
|
|
'dark:bg-gray-800 dark:border-gray-600 dark:text-white',
|
|
error
|
|
? 'border-red-500 focus:ring-red-500'
|
|
: 'border-gray-300 dark:border-gray-600',
|
|
className
|
|
)}
|
|
{...props}
|
|
/>
|
|
);
|
|
};
|
|
|
|
interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
error?: boolean;
|
|
}
|
|
|
|
export const Textarea: React.FC<TextareaProps> = ({
|
|
error = false,
|
|
className,
|
|
...props
|
|
}) => {
|
|
return (
|
|
<textarea
|
|
className={cn(
|
|
'w-full px-3 py-2 border rounded-md shadow-sm transition-colors resize-none',
|
|
'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent',
|
|
'dark:bg-gray-800 dark:border-gray-600 dark:text-white',
|
|
error
|
|
? 'border-red-500 focus:ring-red-500'
|
|
: 'border-gray-300 dark:border-gray-600',
|
|
className
|
|
)}
|
|
{...props}
|
|
/>
|
|
);
|
|
};
|
|
|
|
interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
|
error?: boolean;
|
|
options: Array<{ value: string; label: string }>;
|
|
}
|
|
|
|
export const Select: React.FC<SelectProps> = ({
|
|
error = false,
|
|
className,
|
|
options,
|
|
...props
|
|
}) => {
|
|
return (
|
|
<select
|
|
className={cn(
|
|
'w-full px-3 py-2 border rounded-md shadow-sm transition-colors',
|
|
'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent',
|
|
'dark:bg-gray-800 dark:border-gray-600 dark:text-white',
|
|
error
|
|
? 'border-red-500 focus:ring-red-500'
|
|
: 'border-gray-300 dark:border-gray-600',
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{options.map((option) => (
|
|
<option key={option.value} value={option.value}>
|
|
{option.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
);
|
|
};
|