import { useState, useEffect, useRef } from 'react'; import { IconCheck, IconX, IconAlertCircle } from '@tabler/icons-react'; /** * FormField - универсальное поле формы с валидацией * Объединяет функциональность FormField и ValidatedInput * Поддерживает: иконки, подсказки, ошибки, success состояния, debounce валидацию */ function FormField({ label, name, type = 'text', value, onChange, onBlur, onValidate, // функция валидации (опционально): (value) => { valid: boolean, message: string } error, success, helpText, required, disabled, placeholder, autoFocus, icon: Icon, className = '', inputClassName = '', rows, // для textarea options, // для select debounceMs = 300, // debounce для валидации showValidationIcon = true, ...inputProps }) { const inputId = `field-${name}`; const [localValue, setLocalValue] = useState(value || ''); const [validation, setValidation] = useState({ valid: null, message: '' }); const [isDirty, setIsDirty] = useState(false); const timerRef = useRef(null); const hasExternalError = !!error; const hasValidationError = isDirty && validation.valid === false; const hasError = hasExternalError || hasValidationError; const hasExternalSuccess = !!success && !hasExternalError; const hasValidationSuccess = isDirty && validation.valid === true && !hasExternalError; const hasSuccess = hasExternalSuccess || hasValidationSuccess; const isTextarea = type === 'textarea'; const isSelect = type === 'select'; // Синхронизация с внешним value useEffect(() => { setLocalValue(value || ''); }, [value]); // Валидация с debounce useEffect(() => { if (!isDirty || !onValidate) return; if (timerRef.current) { clearTimeout(timerRef.current); } timerRef.current = setTimeout(() => { const result = onValidate(localValue); setValidation(result); }, debounceMs); return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, [localValue, isDirty, onValidate, debounceMs]); const inputClasses = `form-control ${hasError ? 'is-invalid' : ''} ${hasSuccess ? 'is-valid' : ''} ${inputClassName}`; const handleChange = (e) => { const newValue = e.target.value; setLocalValue(newValue); setIsDirty(true); onChange?.(newValue, e); }; const handleBlur = (e) => { setIsDirty(true); onBlur?.(e); }; const renderInput = () => { if (isTextarea) { return (