Presupuestador_web/client/src/components/Forms/FormCurrencyField.tsx
2024-07-17 20:10:07 +02:00

151 lines
4.9 KiB
TypeScript

import { cn } from "@/lib/utils";
import { FormControl, FormDescription, FormField, FormItem, InputProps } from "@/ui";
import { CurrencyData, Language, MoneyValue, MoneyValueObject } from "@shared/contexts";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import CurrencyInput, { CurrencyInputOnChangeValues } from "react-currency-input-field";
import { FieldPath, FieldValues, UseControllerProps, useFormContext } from "react-hook-form";
import { FormErrorMessage } from "./FormErrorMessage";
import { FormLabel, FormLabelProps } from "./FormLabel";
import { FormInputProps, FormInputWithIconProps } from "./FormProps";
const formCurrencyFieldVariants = cva(
"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 ",
outline: "focus-visible:border focus-visible:border-input",
},
},
defaultVariants: {
variant: "default",
},
}
);
export type FormCurrencyFieldProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
button?: (props?: React.PropsWithChildren) => React.ReactNode;
defaultValue?: any;
} & InputProps &
FormInputProps &
Partial<FormLabelProps> &
FormInputWithIconProps &
UseControllerProps<TFieldValues, TName> &
VariantProps<typeof formCurrencyFieldVariants> & {
currency: CurrencyData;
language: Language;
scale: number;
};
export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrencyFieldProps>(
(props, ref) => {
const {
name,
label,
hint,
description,
placeholder,
className,
disabled,
defaultValue,
rules,
readOnly,
scale,
currency,
language,
variant,
} = props;
const { control } = useFormContext();
const [inputValue, setInputValue] = React.useState<string>("");
const transform = {
input: (value: MoneyValueObject) => {
if (typeof value !== "object") {
return value;
}
const moneyOrError = MoneyValue.create(value);
if (moneyOrError.isFailure) {
throw moneyOrError.error;
}
const result = moneyOrError.object.toString();
return inputValue.endsWith(",") ? result.replace(/.0$/, ",") : result;
},
output: (value: string | undefined, name?: string, values?: CurrencyInputOnChangeValues) => {
const { value: amount } = values ?? { value: null };
setInputValue(amount ?? "");
const moneyOrError = MoneyValue.createFromFormattedValue(amount, currency.code);
if (moneyOrError.isFailure) {
throw moneyOrError.error;
}
return moneyOrError.object.toObject();
},
};
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")}>
{label && (
<FormLabel label={label} hint={hint} required={Boolean(rules?.required ?? false)} />
)}
<FormControl>
<CurrencyInput
intlConfig={{
locale: language.code,
}}
name={field.name}
//ref={field.ref} <-- no activar que hace cosas raras
onBlur={field.onBlur}
disabled={field.disabled}
readOnly={readOnly}
className={cn(formCurrencyFieldVariants({ variant, className }))}
suffix={` ${currency?.symbol}`}
groupSeparator='.'
decimalSeparator=','
placeholder={placeholder}
//allowDecimals={scale !== 0}
decimalsLimit={scale}
decimalScale={scale}
//fixedDecimalLength={scale} <- no activar para que sea más cómodo escribir las cantidades
step={1}
// { ...field }
value={transform.input(field.value)}
//onChange={() => {}}
onValueChange={(value, name, values) =>
field.onChange(transform.output(value, name, values))
}
/>
</FormControl>
{description && <FormDescription>{description}</FormDescription>}
<FormErrorMessage />
</FormItem>
);
}}
/>
);
}
);
FormCurrencyField.displayName = "FormCurrencyField";