Presupuestador_web/client/src/components/Forms/FormCurrencyField.tsx

151 lines
4.9 KiB
TypeScript
Raw Normal View History

2024-07-10 18:54:33 +00:00
import { cn } from "@/lib/utils";
import { FormControl, FormDescription, FormField, FormItem, InputProps } from "@/ui";
2024-07-17 11:03:02 +00:00
import { CurrencyData, Language, MoneyValue, MoneyValueObject } from "@shared/contexts";
2024-07-10 18:54:33 +00:00
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
2024-07-16 17:17:52 +00:00
import CurrencyInput, { CurrencyInputOnChangeValues } from "react-currency-input-field";
2024-07-10 18:54:33 +00:00
import { FieldPath, FieldValues, UseControllerProps, useFormContext } from "react-hook-form";
import { FormErrorMessage } from "./FormErrorMessage";
import { FormLabel, FormLabelProps } from "./FormLabel";
import { FormInputProps, FormInputWithIconProps } from "./FormProps";
2024-07-11 11:08:53 +00:00
const formCurrencyFieldVariants = cva(
2024-07-10 18:54:33 +00:00
"flex h-10 w-full rounded-md bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
{
variants: {
variant: {
default:
"border border-input ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ",
2024-07-11 11:08:53 +00:00
outline: "focus-visible:border focus-visible:border-input",
2024-07-10 18:54:33 +00:00
},
},
defaultVariants: {
variant: "default",
},
}
);
export type FormCurrencyFieldProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
button?: (props?: React.PropsWithChildren) => React.ReactNode;
2024-07-11 11:08:53 +00:00
defaultValue?: any;
2024-07-10 18:54:33 +00:00
} & InputProps &
FormInputProps &
Partial<FormLabelProps> &
FormInputWithIconProps &
UseControllerProps<TFieldValues, TName> &
VariantProps<typeof formCurrencyFieldVariants> & {
currency: CurrencyData;
2024-07-17 11:03:02 +00:00
language: Language;
2024-07-16 17:17:52 +00:00
scale: number;
2024-07-10 18:54:33 +00:00
};
export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrencyFieldProps>(
(props, ref) => {
const {
name,
label,
hint,
description,
2024-07-11 11:08:53 +00:00
placeholder,
2024-07-10 18:54:33 +00:00
className,
disabled,
defaultValue,
rules,
2024-07-11 16:40:46 +00:00
readOnly,
2024-07-16 17:17:52 +00:00
scale,
2024-07-10 18:54:33 +00:00
currency,
2024-07-17 11:03:02 +00:00
language,
2024-07-10 18:54:33 +00:00
variant,
} = props;
const { control } = useFormContext();
2024-07-17 11:03:02 +00:00
const [inputValue, setInputValue] = React.useState<string>("");
2024-07-16 18:18:17 +00:00
2024-07-11 16:40:46 +00:00
const transform = {
2024-07-17 11:03:02 +00:00
input: (value: MoneyValueObject) => {
2024-07-11 16:40:46 +00:00
if (typeof value !== "object") {
return value;
}
2024-07-10 18:54:33 +00:00
2024-07-11 16:40:46 +00:00
const moneyOrError = MoneyValue.create(value);
2024-07-16 17:17:52 +00:00
2024-07-11 16:40:46 +00:00
if (moneyOrError.isFailure) {
throw moneyOrError.error;
}
2024-07-10 18:54:33 +00:00
2024-07-17 11:03:02 +00:00
const result = moneyOrError.object.toString();
return inputValue.endsWith(",") ? result.replace(/.0$/, ",") : result;
2024-07-11 16:40:46 +00:00
},
2024-07-16 17:17:52 +00:00
output: (value: string | undefined, name?: string, values?: CurrencyInputOnChangeValues) => {
const { value: amount } = values ?? { value: null };
2024-07-11 16:40:46 +00:00
2024-07-17 11:03:02 +00:00
setInputValue(amount ?? "");
2024-07-16 18:18:17 +00:00
2024-07-16 17:17:52 +00:00
const moneyOrError = MoneyValue.createFromFormattedValue(amount, currency.code);
2024-07-11 16:40:46 +00:00
if (moneyOrError.isFailure) {
throw moneyOrError.error;
}
return moneyOrError.object.toObject();
},
2024-07-10 18:54:33 +00:00
};
return (
<FormField
defaultValue={defaultValue}
control={control}
name={name}
disabled={disabled}
rules={rules}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
render={({ field, fieldState, formState }) => {
return (
<FormItem ref={ref} className={cn(className, "space-y-3")}>
2024-07-11 16:40:46 +00:00
{label && (
<FormLabel label={label} hint={hint} required={Boolean(rules?.required ?? false)} />
)}
2024-07-10 18:54:33 +00:00
<FormControl>
<CurrencyInput
2024-07-17 18:10:07 +00:00
intlConfig={{
locale: language.code,
}}
2024-07-10 18:54:33 +00:00
name={field.name}
//ref={field.ref} <-- no activar que hace cosas raras
onBlur={field.onBlur}
disabled={field.disabled}
2024-07-11 16:40:46 +00:00
readOnly={readOnly}
2024-07-10 18:54:33 +00:00
className={cn(formCurrencyFieldVariants({ variant, className }))}
suffix={` ${currency?.symbol}`}
groupSeparator='.'
decimalSeparator=','
2024-07-11 11:08:53 +00:00
placeholder={placeholder}
2024-07-16 17:17:52 +00:00
//allowDecimals={scale !== 0}
decimalsLimit={scale}
decimalScale={scale}
//fixedDecimalLength={scale} <- no activar para que sea más cómodo escribir las cantidades
step={1}
// { ...field }
2024-07-11 16:40:46 +00:00
value={transform.input(field.value)}
2024-07-16 17:17:52 +00:00
//onChange={() => {}}
onValueChange={(value, name, values) =>
field.onChange(transform.output(value, name, values))
}
2024-07-10 18:54:33 +00:00
/>
</FormControl>
2024-07-16 18:18:17 +00:00
2024-07-10 18:54:33 +00:00
{description && <FormDescription>{description}</FormDescription>}
<FormErrorMessage />
</FormItem>
);
}}
/>
);
}
);
FormCurrencyField.displayName = "FormCurrencyField";