import { clsx } from "clsx"; import React, { forwardRef, useEffect, useRef, useState } from "react"; import "./input.less"; interface InputDecorationProps { startDecoration?: React.ReactNode; endDecoration?: React.ReactNode; } interface InputProps { label?: string; value?: string; className?: string; onChange?: (value: string) => void; onKeyDown?: (event: React.KeyboardEvent) => void; onFocus?: () => void; onBlur?: () => void; placeholder?: string; defaultValue?: string; decoration?: InputDecorationProps; required?: boolean; maxLength?: number; autoFocus?: boolean; disabled?: boolean; isNumber?: boolean; inputRef?: React.MutableRefObject; } const Input = forwardRef( ( { label, value, className, onChange, onKeyDown, onFocus, onBlur, placeholder, defaultValue = "", decoration, required, maxLength, autoFocus, disabled, isNumber, inputRef, }: InputProps, ref ) => { const [focused, setFocused] = useState(false); const [internalValue, setInternalValue] = useState(defaultValue); const [error, setError] = useState(false); const [hasContent, setHasContent] = useState(Boolean(value || defaultValue)); const internalInputRef = useRef(null); useEffect(() => { if (value !== undefined) { setFocused(Boolean(value)); } }, [value]); const handleComponentFocus = () => { if (internalInputRef.current && !internalInputRef.current.contains(document.activeElement)) { internalInputRef.current.focus(); } }; const handleComponentBlur = () => { if (internalInputRef.current?.contains(document.activeElement)) { internalInputRef.current.blur(); } }; const handleSetInputRef = (elem: HTMLInputElement) => { if (inputRef) { inputRef.current = elem; } internalInputRef.current = elem; }; const handleFocus = () => { setFocused(true); onFocus && onFocus(); }; const handleBlur = () => { if (internalInputRef.current) { const inputValue = internalInputRef.current.value; if (required && !inputValue) { setError(true); setFocused(false); } else { setError(false); setFocused(false); } } onBlur && onBlur(); }; const handleInputChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; if (isNumber && inputValue !== "" && !/^\d*$/.test(inputValue)) { return; } if (required && !inputValue) { setError(true); setHasContent(false); } else { setError(false); setHasContent(Boolean(inputValue)); } if (value === undefined) { setInternalValue(inputValue); } onChange && onChange(inputValue); }; const inputValue = value ?? internalValue; return (
{decoration?.startDecoration && <>{decoration.startDecoration}}
{label && ( )}
{decoration?.endDecoration && <>{decoration.endDecoration}}
); } ); export { Input }; export type { InputDecorationProps, InputProps };