import type { HTMLInputProps, NumericInputProps } from '@blueprintjs/core'; import { Classes, NumericInput } from '@blueprintjs/core'; import debounce from 'lodash/debounce.js'; import type { ForwardedRef } from 'react'; import { forwardRef, isValidElement, useEffect, useMemo, useRef, useState, } from 'react'; import useCombinedRefs from '../hooks/useCombinedRefs.js'; interface ValueProps extends Pick, 'name' | 'style'>, Pick { checkValue?: (element?: number) => boolean; debounceTime?: number; autoSelect?: boolean; } interface UseInputProps extends Omit { ref: ForwardedRef; } export interface NumberInput2Props extends Omit< Omit & NumericInputProps, 'value' | 'onValueChange' >, ValueProps { format?: () => (element: string) => number | string; } function useNumberInput(props: UseInputProps) { const { value: externalValue, debounceTime, onValueChange, ref, autoSelect, checkValue, } = props; const [internalValue, setValue] = useState(); const localRef = useRef(null); const innerRef = useCombinedRefs([ref, localRef]); const value = debounceTime ? internalValue : externalValue; const [isDebounced, setDebouncedStatus] = useState(false); const debounceOnValueChange = useMemo( () => debounce( ( valueAsNumber: number, valueAsString: string, inputElement: HTMLInputElement | null, ) => { onValueChange?.(valueAsNumber, valueAsString, inputElement); setDebouncedStatus(true); }, debounceTime, ), [debounceTime, onValueChange], ); useEffect(() => { if (debounceTime) { setValue(externalValue); } }, [debounceTime, externalValue]); useEffect(() => { if (autoSelect) { innerRef?.current?.select(); } }, [autoSelect, innerRef]); function handleValueChange( valueAsNumber: number, valueAsString: string, inputElement: HTMLInputElement | null, ) { setDebouncedStatus(false); if (!checkValue || checkValue?.(valueAsNumber)) { if (debounceTime) { setValue(valueAsString); debounceOnValueChange(valueAsNumber, valueAsString, inputElement); } else { onValueChange?.(valueAsNumber, valueAsString, inputElement); } } } return { handleValueChange, debounceOnValueChange, isDebounced, value, innerRef, }; } function getIcon(leftIcon: any) { if (isValidElement(leftIcon)) { return {leftIcon}; } return leftIcon; } function getClasses(isDebounced: boolean) { const classes = ['numeric-input']; if (isDebounced) { classes.push('debounce-end'); } return classes.join(' '); } function InnerNumberInput(props: NumberInput2Props, ref: any) { const { debounceTime = 0, onValueChange, checkValue, value: externalValue, leftIcon, autoSelect = false, ...otherInputProps } = props; const { handleValueChange, isDebounced, value, innerRef } = useNumberInput({ value: externalValue, debounceTime, autoSelect, ref, onValueChange, checkValue, }); const icon = getIcon(leftIcon); const classes = getClasses(isDebounced); return ( ); } const NumberInput2 = forwardRef(InnerNumberInput); export { NumberInput2 };