import * as React from "react"; import { NumberFormatter } from "@internationalized/number"; import { TextInput, TextInputProps } from "../TextInput"; export interface CurrencyInputProps extends Omit { /** * An integer value representing hundredths (cents) for the currency amount. */ value?: number; onChange?: (value: number) => unknown; locale?: string; currency?: string; } export const CurrencyInput = React.forwardRef( function CurrencyInput(props, ref) { const { name, value, onChange, locale = "en-US", currency = "USD", ...otherProps } = props; // This is a formatted input, so it can't _actually_ be uncontrolled. This // local value acts as the default `value` and allows `onChange` to still // function even when uncontrolled. const [localValue, setLocalValue] = React.useState(value ?? 0); React.useEffect(() => { setLocalValue(value ?? 0); }, [value]); const [formatter] = React.useMemo( () => [ new NumberFormatter(locale, { style: "currency", currency, minimumIntegerDigits: 1, maximumFractionDigits: 2, }), ], [locale, currency], ); const [formattedValue, setFormattedValue] = React.useState(() => localValue != null ? formatter.format(localValue / 100) : "", ); // TODO: Support number stepping with keyboard shortcuts. Up/Down for 1, // +Shift for 10, +Ctrl for 100. function handleKeyDown(event: React.KeyboardEvent) { let newValue = localValue; if (event.key === "Backspace") { if (event.metaKey || event.ctrlKey) { newValue = 0; } else { newValue = Math.floor(localValue / 10); } } else if (!Number.isNaN(Number(event.key))) { newValue = localValue * 10 + Number(event.key); } setLocalValue(newValue); setFormattedValue(formatter.format(newValue / 100)); onChange?.(newValue); } return ( <> {name != null ? ( // When a name is given, render a hidden input with the numeric value // of the currency so that uncontrolled forms are still able to // function as expected. ) : null} ); }, );