app/frontend/src/components/ui/Input.tsx

69 lines
1.9 KiB
TypeScript
Raw Normal View History

2025-10-13 17:35:07 +02:00
import React, { FC, InputHTMLAttributes } from "react";
import classNames from "classnames";
interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
label?: string;
error?: string;
helperText?: string;
fullWidth?: boolean;
}
const Input: FC<InputProps> = ({
label,
error,
helperText,
fullWidth = true,
className,
id,
required,
...props
}) => {
const inputId = id || label?.toLowerCase().replace(/\s+/g, '-');
const inputStyles = classNames(
"px-3 py-2 rounded border-2 transition-colors",
"bg-gray-600 text-secondary",
"focus:outline-none focus:ring-2 focus:ring-offset-1",
{
"border-secondary focus:border-primary focus:ring-primary-500": !error,
"border-danger focus:border-danger focus:ring-danger-500": error,
"w-full": fullWidth,
"opacity-50 cursor-not-allowed": props.disabled,
},
className
);
const labelStyles = classNames(
"block text-sm font-medium mb-1",
{
"text-secondary": !error,
"text-danger": error,
}
);
return (
<div className={fullWidth ? "w-full" : ""}>
{label && (
<label htmlFor={inputId} className={labelStyles}>
{label}
{required && <span className="text-danger ml-1">*</span>}
</label>
)}
<input
id={inputId}
className={inputStyles}
required={required}
{...props}
/>
{error && (
<p className="mt-1 text-sm text-danger">{error}</p>
)}
{helperText && !error && (
<p className="mt-1 text-sm text-gray-400">{helperText}</p>
)}
</div>
);
};
export default Input;