import * as React from "react" import { Input } from "./input" interface AmountInputProps extends Omit, 'value' | 'onChange'> { value?: string | number; onChange?: (e: React.ChangeEvent) => void; } const AmountInput = React.forwardRef( ({ value, onChange, name, ...props }, ref) => { const formatNumber = (val: string) => { if (!val) return ""; // Remove all commas first const clean = val.replace(/,/g, ""); const parts = clean.split("."); // Format the integer part parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); return parts.join("."); }; const [cursor, setCursor] = React.useState(null); const inputRef = React.useRef(null); // Sync ref React.useImperativeHandle(ref, () => inputRef.current!); const handleChange = (e: React.ChangeEvent) => { const inputVal = e.target.value; // Allow only numbers, dots and commas if (/[^\d.,]/.test(inputVal)) return; const stripped = inputVal.replace(/,/g, ""); const formatted = formatNumber(stripped); // Track cursor position changes due to formatting const selectionStart = e.target.selectionStart || 0; const commasBefore = (inputVal.substring(0, selectionStart).match(/,/g) || []).length; const commasAfter = (formatted.substring(0, selectionStart).match(/,/g) || []).length; const diff = commasAfter - commasBefore; setCursor(selectionStart + diff); if (onChange) { // We pass the stripped value back to handlers (like react-hook-form) // so they can treat it as a normal numeric string const event = { ...e, target: { ...e.target, name: name, value: stripped } } as React.ChangeEvent; onChange(event); } }; // Restore cursor position after render React.useLayoutEffect(() => { if (inputRef.current && cursor !== null) { inputRef.current.setSelectionRange(cursor, cursor); } }); const displayValue = formatNumber(value?.toString() || ""); return ( ); } ); AmountInput.displayName = "AmountInput"; export { AmountInput };