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 };