feat: Добавить анимации и улучшения UX/UI в компоненты приложения, включая плавные переходы, эффекты при наведении и анимации для модальных окон. Обновить карточки и таблицы с новыми анимациями для улучшения визуального восприятия и доступности.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m42s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m42s
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* FormField - универсальное поле формы с валидацией
|
||||
* Поддерживает иконки, подсказки, ошибки и success состояния
|
||||
*/
|
||||
function FormField({
|
||||
label,
|
||||
name,
|
||||
type = 'text',
|
||||
value,
|
||||
onChange,
|
||||
onBlur,
|
||||
error,
|
||||
success,
|
||||
helpText,
|
||||
required,
|
||||
disabled,
|
||||
placeholder,
|
||||
icon: Icon,
|
||||
className = '',
|
||||
inputClassName = '',
|
||||
rows, // для textarea
|
||||
options, // для select
|
||||
...inputProps
|
||||
}) {
|
||||
const inputId = `field-${name}`;
|
||||
const hasError = !!error;
|
||||
const hasSuccess = !!success && !error;
|
||||
const isTextarea = type === 'textarea';
|
||||
const isSelect = type === 'select';
|
||||
|
||||
const inputClasses = `form-control ${hasError ? 'is-invalid' : ''} ${hasSuccess ? 'is-valid' : ''} ${inputClassName}`;
|
||||
|
||||
const handleChange = (e) => {
|
||||
onChange?.(e.target.value, e);
|
||||
};
|
||||
|
||||
const handleBlur = (e) => {
|
||||
onBlur?.(e);
|
||||
};
|
||||
|
||||
const renderInput = () => {
|
||||
if (isTextarea) {
|
||||
return (
|
||||
<textarea
|
||||
id={inputId}
|
||||
name={name}
|
||||
className={inputClasses}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
rows={rows || 3}
|
||||
aria-invalid={hasError}
|
||||
aria-describedby={error ? `${inputId}-error` : helpText ? `${inputId}-help` : undefined}
|
||||
{...inputProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isSelect) {
|
||||
return (
|
||||
<select
|
||||
id={inputId}
|
||||
name={name}
|
||||
className={inputClasses.replace('form-control', 'form-select')}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
aria-invalid={hasError}
|
||||
aria-describedby={error ? `${inputId}-error` : helpText ? `${inputId}-help` : undefined}
|
||||
{...inputProps}
|
||||
>
|
||||
{options?.map((option) => (
|
||||
<option key={option.value} value={option.value} disabled={option.disabled}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
id={inputId}
|
||||
name={name}
|
||||
type={type}
|
||||
className={inputClasses}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
aria-invalid={hasError}
|
||||
aria-describedby={error ? `${inputId}-error` : helpText ? `${inputId}-help` : undefined}
|
||||
{...inputProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`mb-3 ${className}`}>
|
||||
{label && (
|
||||
<label htmlFor={inputId} className="form-label">
|
||||
{label}
|
||||
{required && <span className="text-danger ms-1" aria-label="обязательное поле">*</span>}
|
||||
</label>
|
||||
)}
|
||||
|
||||
{Icon ? (
|
||||
<div className="input-icon">
|
||||
<span className="input-icon-addon">
|
||||
<Icon size={18} />
|
||||
</span>
|
||||
{renderInput()}
|
||||
</div>
|
||||
) : (
|
||||
renderInput()
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div id={`${inputId}-error`} className="invalid-feedback d-block" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasSuccess && (
|
||||
<div className="valid-feedback d-block">
|
||||
{success}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{helpText && !error && !success && (
|
||||
<div id={`${inputId}-help`} className="form-text">
|
||||
{helpText}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormField;
|
||||
|
||||
Reference in New Issue
Block a user