Files
leocrm/frontend/src/components/ui/Input.tsx
T

58 lines
1.9 KiB
TypeScript
Raw Normal View History

import React, { useId } from 'react';
import clsx from 'clsx';
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
helperText?: string;
required?: boolean;
}
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ label, error, helperText, required, className, id, ...props }, ref) => {
const generatedId = useId();
const inputId = id || generatedId;
const errorId = `${inputId}-error`;
const helperId = `${inputId}-helper`;
return (
<div className="w-full">
{label && (
<label htmlFor={inputId} className="block text-sm font-medium text-secondary-700 mb-1">
{label}
{required && <span className="text-danger-500 ml-1" aria-label="required">*</span>}
</label>
)}
<input
ref={ref}
id={inputId}
className={clsx(
'block w-full rounded-md border px-3 py-2 text-base min-h-touch',
'focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500',
'motion-safe:transition-colors',
error
? 'border-danger-500 text-danger-900 placeholder-danger-300'
: 'border-secondary-300 text-secondary-900 placeholder-secondary-400',
className
)}
aria-invalid={!!error}
aria-describedby={clsx(error && errorId, helperText && helperId) || undefined}
aria-required={required}
{...props}
/>
{error && (
<p id={errorId} className="mt-1 text-sm text-danger-600" role="alert">
{error}
</p>
)}
{helperText && !error && (
<p id={helperId} className="mt-1 text-sm text-secondary-500">
{helperText}
</p>
)}
</div>
);
}
);
Input.displayName = 'Input';