69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
import React from 'react'
|
|
|
|
interface ValidatedTextInputProps {
|
|
value: string
|
|
onChange: (value: string) => void
|
|
maxLength: number
|
|
placeholder: string
|
|
rows?: number
|
|
className?: string
|
|
showCounter?: boolean
|
|
warningThreshold?: number
|
|
errorThreshold?: number
|
|
}
|
|
|
|
export const ValidatedTextInput: React.FC<ValidatedTextInputProps> = ({
|
|
value,
|
|
onChange,
|
|
maxLength,
|
|
placeholder,
|
|
rows = 4,
|
|
className = '',
|
|
showCounter = true,
|
|
warningThreshold = 0.8,
|
|
errorThreshold = 0.95
|
|
}) => {
|
|
const currentLength = value.length
|
|
const isNearLimit = currentLength >= maxLength * warningThreshold
|
|
const isAtLimit = currentLength >= maxLength * errorThreshold
|
|
|
|
const getBorderColor = () => {
|
|
if (isAtLimit) return 'border-red-300 focus:ring-red-500'
|
|
if (isNearLimit) return 'border-yellow-300 focus:ring-yellow-500'
|
|
return 'border-gray-300 focus:ring-blue-500'
|
|
}
|
|
|
|
const getCounterColor = () => {
|
|
if (isAtLimit) return 'text-red-600'
|
|
if (isNearLimit) return 'text-yellow-600'
|
|
return 'text-gray-500'
|
|
}
|
|
|
|
return (
|
|
<div className={`relative ${className}`}>
|
|
<textarea
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={placeholder}
|
|
rows={rows}
|
|
maxLength={maxLength}
|
|
className={`w-full p-4 rounded-lg focus:ring-2 focus:outline-none transition-colors resize-none ${getBorderColor()}`}
|
|
/>
|
|
{showCounter && (
|
|
<div className={`absolute bottom-2 right-2 text-sm ${getCounterColor()}`}>
|
|
{currentLength}/{maxLength}
|
|
</div>
|
|
)}
|
|
{isAtLimit && (
|
|
<div className="mt-1 text-sm text-red-600">
|
|
已達到字數限制
|
|
</div>
|
|
)}
|
|
{isNearLimit && !isAtLimit && (
|
|
<div className="mt-1 text-sm text-yellow-600">
|
|
接近字數限制
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
} |