import React from "react"; import FP from "../fp"; import type { InputProps } from "./form.types"; import { useDisabledState } from "../../hooks/use-disabled-state"; import { resolveDisabledState } from "../../utils/accessibility"; export type { InputProps } from "./form.types"; /** * Input component - Accessible text input with validation support * * A flexible input component that supports various input types, validation states, * and proper ARIA attributes for accessibility. Integrates seamlessly with the * Field component for complete form control composition. * * @component * @example * // Basic text input * * * @example * // Input with error state * * * @example * // Controlled input with validation * setPassword(e.target.value)} * minLength={8} * required * /> * * @param {InputProps} props - Component props * @returns {JSX.Element} Input element with proper accessibility attributes * * @see {@link https://www.w3.org/WAI/WCAG21/Understanding/error-identification.html|WCAG 3.3.1 Error Identification} * @see {@link https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html|WCAG 4.1.2 Name, Role, Value} */ export const Input = React.forwardRef( ( { type = "text", name, value, defaultValue, placeholder, id, styles, classes, isDisabled, // Legacy support disabled, readOnly, required = false, validationState = "none", errorMessage, hintText, onChange, onBlur, onFocus, onKeyDown, onEnter, maxLength, minLength, pattern, autoComplete, autoFocus = false, inputMode, ...props }, ref ) => { // Support both `disabled` and `isDisabled` props (legacy compatibility) const isInputDisabled = resolveDisabledState(disabled, isDisabled); // Use the disabled state hook with enhanced API for automatic className merging const { disabledProps, handlers } = useDisabledState( isInputDisabled, { handlers: { onChange, onBlur, onKeyDown: (e: React.KeyboardEvent) => { // Handle Enter key press for accessibility if (e.key === "Enter" && onEnter) { onEnter(e); } // Always call consumer's onKeyDown if provided if (onKeyDown) { onKeyDown(e); } }, // Note: onFocus is NOT wrapped to allow focus on disabled inputs // This is handled automatically by useDisabledState }, // Automatic className merging - hook combines disabled class with user classes className: classes, } ); // Determine aria-invalid based on validation state const isInvalid = validationState === "invalid"; // Generate describedby IDs for error and hint text const describedByIds: string[] = []; if (errorMessage && id) { describedByIds.push(`${id}-error`); } if (hintText && id) { describedByIds.push(`${id}-hint`); } const ariaDescribedBy = describedByIds.length > 0 ? describedByIds.join(" ") : undefined; // Generate accessible placeholder if not provided const accessiblePlaceholder = placeholder || (required ? `* ${type} input` : `${type} input`); return ( ); } ); Input.displayName = "Input"; export default Input;