import { forwardRef, ReactElement, ReactNode, useCallback, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import classnames from 'classnames'; import { autoCompleteType } from './InternalTextField'; import styles from './VRInternalTextField.css'; import boxStyles from '../Box.css'; import FormErrorMessage from '../sharedSubcomponents/FormErrorMessage'; import FormHelperText from '../sharedSubcomponents/FormHelperText'; import { MaxLength } from '../TextField'; import TextUI from '../TextUI'; import typographyStyle from '../Typography.css'; type SizeType = 'sm' | 'md' | 'lg'; type Props = { // REQUIRED id: string; onChange: (arg1: { event: React.ChangeEvent; value: string }) => void; // OPTIONAL accessibilityControls?: string; accessibilityActiveDescendant?: string; autoComplete?: autoCompleteType; dataTestId?: string; disabled?: boolean; errorMessage?: ReactNode; hasError?: boolean; helperText?: string; iconButton?: ReactElement; label?: string; labelDisplay?: 'visible' | 'hidden'; max?: number; maxLength?: MaxLength | null | undefined; min?: number; mobileEnterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; mobileInputMode?: 'none' | 'text' | 'decimal' | 'numeric'; name?: string; onBlur?: (arg1: { event: React.FocusEvent; value: string }) => void; onClick?: (arg1: { event: React.MouseEvent; value: string }) => void; onFocus?: (arg1: { event: React.FocusEvent; value: string }) => void; onKeyDown?: (arg1: { event: React.KeyboardEvent; value: string }) => void; placeholder?: string; readOnly?: boolean; readOnlyNoIconButton?: boolean; size?: SizeType; step?: number; tags?: ReadonlyArray; type?: 'date' | 'email' | 'number' | 'password' | 'tel' | 'text' | 'url'; value?: string; }; const InternalTextFieldWithForwardRef = forwardRef(function TextField( { accessibilityControls, accessibilityActiveDescendant, autoComplete, dataTestId, disabled = false, errorMessage, hasError = false, helperText, id, iconButton, label, labelDisplay = 'visible', max, maxLength, mobileEnterKeyHint, mobileInputMode, min, name, onBlur, onChange, onClick, onFocus, onKeyDown, placeholder, readOnly, readOnlyNoIconButton, size = 'md', step, // eslint-disable-next-line @typescript-eslint/no-unused-vars tags, type = 'text', value, }: Props, ref, ) { const innerRef = useRef(null); const labelRef = useRef(null); // @ts-expect-error - TS2322 - Type 'HTMLDivElement | HTMLInputElement | null' is not assignable to type 'HTMLInputElement'. useImperativeHandle(ref, () => innerRef.current); const hasErrorMessage = Boolean(errorMessage); const isLabelVisible = labelDisplay === 'visible'; const isSM = size === 'sm'; const isMD = size === 'md'; const isLG = size === 'lg'; // ==== STATE ==== const [focused, setFocused] = useState(false); const [currentLength, setCurrentLength] = useState(value?.length ?? 0); const [ellipsisActive, setEllipsisActive] = useState(false); // ==== A11Y ==== let ariaDescribedby; if (hasErrorMessage) { ariaDescribedby = `${id}-error`; } if (helperText || maxLength) { ariaDescribedby = `${id}-helperText`; } const isEllipsisActive = (element: HTMLElement) => element.offsetHeight < element.scrollHeight || element.offsetWidth < element.scrollWidth; const checkEllipsisActive = useCallback(() => { if (labelRef.current && !ellipsisActive && isEllipsisActive(labelRef?.current)) { setEllipsisActive(true); } else if (labelRef.current && ellipsisActive && !isEllipsisActive(labelRef?.current)) { setEllipsisActive(false); } }, [ellipsisActive]); useEffect(() => { if (!label) return () => {}; checkEllipsisActive(); if (typeof window !== 'undefined') window.addEventListener('resize', checkEllipsisActive); return () => { if (typeof window !== 'undefined') window?.removeEventListener('resize', checkEllipsisActive); }; }, [label, checkEllipsisActive]); return (
{label && ( )} { setFocused(false); onBlur?.({ event, value: event.currentTarget.value }); }} onChange={(event) => { setCurrentLength(event.currentTarget.value?.length ?? 0); onChange({ event, value: event.currentTarget.value }); }} onClick={(event) => onClick?.({ event, value: event.currentTarget.value })} onFocus={(event) => { setFocused(true); onFocus?.({ event, value: event.currentTarget.value }); }} onKeyDown={(event) => onKeyDown?.({ event, value: event.currentTarget.value })} pattern={type === 'number' ? '\\d*' : undefined} placeholder={placeholder} readOnly={readOnly} spellCheck={['email', 'password'].includes(type) ? false : undefined} step={type === 'number' ? step : undefined} type={type} value={value} /> {!disabled && !readOnlyNoIconButton && iconButton}
{(helperText || maxLength) && !hasErrorMessage ? ( ) : null} {!disabled && hasErrorMessage ? ( ) : null}
); }); InternalTextFieldWithForwardRef.displayName = 'InternalTextField'; export default InternalTextFieldWithForwardRef;