whisper-money/resources/js/components/ui/amount-input.tsx

119 lines
3.2 KiB
TypeScript

import * as React from 'react';
import { Input } from '@/components/ui/input';
interface AmountInputProps {
value: number;
onChange: (valueInCents: number) => void;
currencyCode: string;
disabled?: boolean;
required?: boolean;
placeholder?: string;
id?: string;
}
const getCurrencySymbol = (currencyCode: string): string => {
const symbols: Record<string, string> = {
USD: '$',
EUR: '€',
GBP: '£',
JPY: '¥',
};
return symbols[currencyCode] || currencyCode;
};
const formatCurrency = (value: number): string => {
const amount = value / 100;
return new Intl.NumberFormat(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(amount);
};
const parseInputValue = (input: string): number => {
const cleaned = input.replace(/[^\d.,]/g, '');
const normalized = cleaned.replace(',', '.');
const parsed = parseFloat(normalized);
if (isNaN(parsed)) {
return 0;
}
return Math.round(parsed * 100);
};
export const AmountInput = React.forwardRef<HTMLInputElement, AmountInputProps>(
(
{
value,
onChange,
currencyCode,
disabled = false,
required = false,
placeholder = '0.00',
id,
},
ref,
) => {
const [displayValue, setDisplayValue] = React.useState<string>('');
const [isFocused, setIsFocused] = React.useState<boolean>(false);
React.useEffect(() => {
if (!isFocused) {
if (value === 0) {
setDisplayValue('');
} else {
setDisplayValue(formatCurrency(value));
}
}
}, [value, isFocused]);
const handleFocus = () => {
setIsFocused(true);
if (value > 0) {
const amount = (value / 100).toFixed(2);
setDisplayValue(amount);
} else {
setDisplayValue('');
}
};
const handleBlur = () => {
setIsFocused(false);
const valueInCents = parseInputValue(displayValue);
onChange(valueInCents);
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setDisplayValue(e.target.value);
};
const currencySymbol = getCurrencySymbol(currencyCode);
return (
<div className="relative">
<span className="-translate-y-1/2 absolute top-1/2 left-3 text-muted-foreground text-sm">
{currencySymbol}
</span>
<Input
ref={ref}
id={id}
type="text"
inputMode="decimal"
value={displayValue}
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={placeholder}
disabled={disabled}
required={required}
className="bg-background pl-9"
/>
</div>
);
},
);
AmountInput.displayName = 'AmountInput';