61 lines
1.4 KiB
TypeScript
61 lines
1.4 KiB
TypeScript
/**
|
|
* Input Component
|
|
* Reusable text input with validation states
|
|
*/
|
|
|
|
import React, { InputHTMLAttributes, ReactNode } from 'react';
|
|
|
|
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
label?: string;
|
|
error?: string;
|
|
helperText?: string;
|
|
icon?: ReactNode;
|
|
fullWidth?: boolean;
|
|
}
|
|
|
|
export const Input: React.FC<InputProps> = ({
|
|
label,
|
|
error,
|
|
helperText,
|
|
icon,
|
|
fullWidth = false,
|
|
className = '',
|
|
...props
|
|
}) => {
|
|
const inputStyles = `
|
|
w-full px-4 py-2 bg-gray-900 border rounded-lg
|
|
text-gray-100 placeholder-gray-500
|
|
focus:outline-none focus:ring-2 focus:ring-purple-500
|
|
transition-all duration-200
|
|
${error ? 'border-red-500' : 'border-gray-700'}
|
|
${icon ? 'pl-10' : ''}
|
|
${className}
|
|
`;
|
|
|
|
return (
|
|
<div className={fullWidth ? 'w-full' : ''}>
|
|
{label && (
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">
|
|
{label}
|
|
</label>
|
|
)}
|
|
<div className="relative">
|
|
{icon && (
|
|
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
|
{icon}
|
|
</div>
|
|
)}
|
|
<input
|
|
className={inputStyles}
|
|
{...props}
|
|
/>
|
|
</div>
|
|
{error && (
|
|
<p className="mt-1 text-sm text-red-500">{error}</p>
|
|
)}
|
|
{helperText && !error && (
|
|
<p className="mt-1 text-sm text-gray-500">{helperText}</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|