import type { ChangeEvent, CompositionEvent, CSSProperties, FocusEvent, KeyboardEvent, ReactNode, } from 'react'; import { forwardRef, useImperativeHandle, useRef } from 'react'; import { Input as BaseInput } from '@base-ui/react/input'; import { IconCloseCircleFilled } from '@tencentcloud/uikit-base-component-react'; import cs from 'classnames'; import styles from './Input.module.scss'; export interface InputProps { /** Outer wrapper class name. */ className?: string; /** Class name applied to the native element. */ inputClassName?: string; /** Controlled value. */ value?: string; /** Uncontrolled initial value. */ defaultValue?: string; placeholder?: string; disabled?: boolean; readOnly?: boolean; /** Show a clear button when there is a value. */ clearable?: boolean; prefix?: ReactNode; suffix?: ReactNode; /** Border style variant. */ border?: '' | 'bottom'; maxLength?: number; autoFocus?: boolean; /** Hint for the virtual keyboard's enter key (mobile). */ enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; /** * Native `autocomplete` attribute. Defaults to `'off'` so the browser * doesn't show its own "previously entered values" suggestion dropdown * (e.g. search boxes without a stable `name` can still trigger it). */ autoComplete?: string; style?: CSSProperties; onChange?: (event: ChangeEvent) => void; onFocus?: (event: FocusEvent) => void; onBlur?: (event: FocusEvent) => void; onKeyDown?: (event: KeyboardEvent) => void; /** Fired when the IME composition session starts. */ onCompositionStart?: (event: CompositionEvent) => void; /** Fired when the IME composition session ends (text committed). */ onCompositionEnd?: (event: CompositionEvent) => void; /** Fired when the clear button is clicked. */ onClear?: () => void; } export interface InputRef { focus: (options?: FocusOptions) => void; blur: () => void; input: HTMLInputElement | null; container: HTMLDivElement | null; } /** * Base UI powered text input. * * It is fully controlled and forwards native events (including * `onCompositionStart` / `onCompositionEnd`) so callers can implement * IME-aware behaviors such as skipping search while composing. * * Styles are scoped via CSS Modules with BEM naming to avoid global leakage. */ export const Input = forwardRef((props, ref) => { const { className, inputClassName, value, defaultValue, placeholder, disabled = false, readOnly = false, clearable = true, prefix, suffix, border = '', maxLength, autoFocus = false, enterKeyHint, autoComplete = 'off', style, onChange, onFocus, onBlur, onKeyDown, onCompositionStart, onCompositionEnd, onClear, } = props; const inputRef = useRef(null); const containerRef = useRef(null); const focus = (options?: FocusOptions) => { inputRef.current?.focus({ preventScroll: true, ...options }); }; useImperativeHandle(ref, () => ({ focus, blur: () => inputRef.current?.blur(), input: inputRef.current, container: containerRef.current, })); const showClear = clearable && !disabled && !!value; const handleClear = () => { onClear?.(); focus(); }; return (
{prefix && {prefix}} {suffix && {suffix}} {showClear && ( e.preventDefault()} onClick={handleClear} > )}
); }); Input.displayName = 'Input'; export default Input;