66 lines
1.7 KiB
TypeScript
66 lines
1.7 KiB
TypeScript
"use client"
|
|
|
|
import { useId } from 'react'
|
|
import { AlertCircle } from 'lucide-react'
|
|
|
|
type Props = {
|
|
label: string
|
|
type?: string
|
|
value: string
|
|
onChange: (v: string) => void
|
|
placeholder?: string
|
|
required?: boolean
|
|
min?: number
|
|
max?: number
|
|
step?: number
|
|
disabled?: boolean
|
|
error?: string
|
|
}
|
|
|
|
export default function FormField({
|
|
label,
|
|
type = 'text',
|
|
value,
|
|
onChange,
|
|
placeholder,
|
|
required,
|
|
min,
|
|
max,
|
|
step,
|
|
disabled,
|
|
error
|
|
}: Props) {
|
|
const id = useId()
|
|
return (
|
|
<div className="mb-4">
|
|
<label htmlFor={id} className="mb-2 block text-sm font-semibold text-gray-900">
|
|
{label} {required ? <span className="text-[#FF0000]" aria-hidden>*</span> : null}
|
|
</label>
|
|
<input
|
|
id={id}
|
|
className={`w-full rounded-lg border-2 px-4 py-3 text-base outline-none transition-colors ${
|
|
error
|
|
? 'border-[#FF0000] bg-[#fff5f5] focus-visible:ring-2 focus-visible:ring-[#FF0000]/30'
|
|
: 'border-gray-300 focus-visible:ring-2 focus-visible:ring-[#006600]/30'
|
|
}`}
|
|
type={type}
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={placeholder}
|
|
required={required}
|
|
min={min}
|
|
max={max}
|
|
step={step}
|
|
disabled={disabled}
|
|
aria-invalid={!!error}
|
|
aria-describedby={error ? `${id}-error` : undefined}
|
|
/>
|
|
{error ? (
|
|
<div id={`${id}-error`} className="mt-2 flex items-center gap-2 text-sm text-[#991b1b] font-medium">
|
|
<AlertCircle className="h-4 w-4 flex-shrink-0" style={{ color: '#FF0000' }} />
|
|
{error}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|