// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { ChangeEvent, forwardRef, ReactNode, Ref, useEffect, useRef, useState, } from 'react'; import { BaseProps } from '../Base'; import { Clear } from '../icons'; import InputWrapper from './InputWrapper'; import { StyledClear, StyledInput } from './Styled'; export type Size = 'sm' | 'md'; export interface InputProps extends Omit< React.InputHTMLAttributes, 'onChange' | 'value' | 'css' >, BaseProps { /** The callback fired when the state is changed. */ onChange(event: ChangeEvent): void; /** The callback fired when the input value is cleared. */ onClear?(): void; /** The icon in the input. */ leadingIcon?: ReactNode; /** The size of the input. */ sizing?: Size; /** The value of the input. */ value: string; /** The id of the input. */ id?: string; /** Whether or not the clear icon is shown, it defaults to `true`. */ showClear?: boolean; } export const Input = forwardRef( (props: InputProps, externalRef: Ref) => { const { type, value, sizing, onClear, onChange, className, leadingIcon, showClear = true, ...rest } = props; const [focused, setFocused] = useState(false); const focusedRef = useRef(false); const internalRef = useRef(null); const inputRef = (externalRef || internalRef) as React.MutableRefObject; const clearRef = useRef(null); const label = props['aria-label'] ? `clear ${props['aria-label']}` : 'clear'; const handleClear = () => { if (onClear) { onClear(); return; } const input = inputRef.current; const nativeSetter = Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, 'value' )?.set; if (nativeSetter && input) { nativeSetter.call(input, ''); input.dispatchEvent(new Event('input', { bubbles: true })); } input.focus(); }; useEffect(() => { let blurring = false; const onFocus = (e: any) => { if (!focusedRef.current) { return; } if (e.target !== clearRef.current && e.target !== inputRef.current) { focusedRef.current = false; setFocused(false); return; } if (blurring) { blurring = false; } }; const onFocusOut = (): void => { if (!focusedRef.current) { return; } blurring = true; setTimeout(() => { if (blurring) { focusedRef.current = false; setFocused(false); } blurring = false; }, 10); }; document.addEventListener('focusin', onFocus); document.addEventListener('focusout', onFocusOut); return () => { document.removeEventListener('focusin', onFocus); document.removeEventListener('focusout', onFocusOut); }; }, []); return ( { focusedRef.current = true; setFocused(true); }} /> {showClear && ( )} ); } ); Input.displayName = 'Input'; export default Input;