erdoganpeker's picture
v0.3.0 — multimodal vehicle damage MVP
e327f0d
import { forwardRef, useId, type InputHTMLAttributes, type ReactNode } from 'react';
import { cn } from '../utils/cn';
interface Props extends InputHTMLAttributes<HTMLInputElement> {
label?: string;
hint?: string;
error?: string;
leftIcon?: ReactNode;
rightIcon?: ReactNode;
containerClassName?: string;
}
export const Input = forwardRef<HTMLInputElement, Props>(function Input(
{
label,
hint,
error,
leftIcon,
rightIcon,
className,
containerClassName,
id,
required,
...rest
},
ref,
) {
const generatedId = useId();
const inputId = id || generatedId;
const describedBy = error
? `${inputId}-error`
: hint
? `${inputId}-hint`
: undefined;
return (
<div className={cn('flex flex-col gap-1.5', containerClassName)}>
{label && (
<label htmlFor={inputId} className="text-sm font-medium text-slate-800">
{label}
{required && (
<span className="ml-0.5 text-red-600" aria-hidden>
*
</span>
)}
</label>
)}
<div className="relative">
{leftIcon && (
<span
className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"
aria-hidden
>
{leftIcon}
</span>
)}
<input
ref={ref}
id={inputId}
aria-invalid={!!error || undefined}
aria-describedby={describedBy}
required={required}
className={cn(
'block w-full rounded-md border bg-white px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400',
'transition-colors focus:outline-none focus:ring-2 focus:ring-offset-0',
error
? 'border-red-400 focus:border-red-500 focus:ring-red-200'
: 'border-slate-300 focus:border-brand-500 focus:ring-brand-200',
'disabled:cursor-not-allowed disabled:bg-slate-50 disabled:text-slate-400',
leftIcon && 'pl-9',
rightIcon && 'pr-9',
className,
)}
{...rest}
/>
{rightIcon && (
<span
className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-slate-400"
aria-hidden
>
{rightIcon}
</span>
)}
</div>
{hint && !error && (
<p id={`${inputId}-hint`} className="text-xs text-slate-500">
{hint}
</p>
)}
{error && (
<p id={`${inputId}-error`} className="text-xs font-medium text-red-600">
{error}
</p>
)}
</div>
);
});