72 lines
1.9 KiB
TypeScript
72 lines
1.9 KiB
TypeScript
import React, { useEffect, useRef } from 'react';
|
|
|
|
interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
label?: string;
|
|
error?: string;
|
|
autoResize?: boolean;
|
|
}
|
|
|
|
const Textarea: React.FC<TextareaProps> = ({
|
|
label,
|
|
error,
|
|
placeholder,
|
|
rows = 4,
|
|
maxLength,
|
|
autoResize = false,
|
|
value,
|
|
onChange,
|
|
className = '',
|
|
...props
|
|
}) => {
|
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (autoResize && textareaRef.current) {
|
|
textareaRef.current.style.height = 'auto';
|
|
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
|
|
}
|
|
}, [value, autoResize]);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-1.5 w-full">
|
|
{label && (
|
|
<label className="text-sm font-medium text-slate-700 dark:text-slate-300">
|
|
{label}
|
|
</label>
|
|
)}
|
|
|
|
<div className="relative">
|
|
<textarea
|
|
ref={textareaRef}
|
|
value={value}
|
|
onChange={onChange}
|
|
placeholder={placeholder}
|
|
rows={autoResize ? 1 : rows}
|
|
maxLength={maxLength}
|
|
className={`
|
|
w-full px-3 py-2 text-sm bg-white dark:bg-slate-900
|
|
border rounded-md outline-none transition-colors
|
|
${error
|
|
? 'border-red-500 focus:ring-1 focus:ring-red-500'
|
|
: 'border-slate-300 dark:border-slate-700 focus:border-blue-500 focus:ring-1 focus:ring-blue-500'
|
|
}
|
|
${className}
|
|
`}
|
|
{...props}
|
|
/>
|
|
|
|
{maxLength && value && (
|
|
<div className="absolute bottom-2 right-2 text-[10px] text-slate-400 pointer-events-none">
|
|
{value.length}/{maxLength}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{error && (
|
|
<span className="text-xs text-red-500">{error}</span>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Textarea; |