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

123 lines
4.1 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";
import { CurrencyData, MoneyValue } from "@shared/contexts";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import CurrencyInput 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";
export 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:
"ring-offset-background focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 ",
},
},
defaultVariants: {
variant: "default",
},
}
);
export type FormCurrencyFieldProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
button?: (props?: React.PropsWithChildren) => React.ReactNode;
} & InputProps &
FormInputProps &
Partial<FormLabelProps> &
FormInputWithIconProps &
UseControllerProps<TFieldValues, TName> &
VariantProps<typeof formCurrencyFieldVariants> & {
currency: CurrencyData;
precision: number;
};
export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrencyFieldProps>(
(props, ref) => {
const {
name,
label,
hint,
description,
className,
disabled,
defaultValue,
rules,
precision,
currency,
variant,
} = props;
const { control } = useFormContext();
const transformToInput = (value: any) => {
if (typeof value !== "object") {
return value;
}
const moneyOrError = MoneyValue.create(value);
if (moneyOrError.isFailure) {
throw moneyOrError.error;
}
return moneyOrError.object
.convertPrecision(precision ?? value.precision)
.toUnit()
.toString();
};
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={rules?.required ?? false} />}
<FormControl>
<CurrencyInput
name={field.name}
//ref={field.ref} <-- no activar que hace cosas raras
onBlur={field.onBlur}
disabled={field.disabled}
className={cn(formCurrencyFieldVariants({ variant, className }))}
suffix={` ${currency?.symbol}`}
groupSeparator='.'
decimalSeparator=','
//placeholder={`0 ${fieldCurrenty.value?.symbol}`}
//fixedDecimalLength={precision} <- no activar para que sea más cómodo escribir las cantidades
decimalsLimit={precision}
decimalScale={precision}
value={transformToInput(field.value)}
onValueChange={(value) => {
// "value" ya viene con los "0" de la precisión
field.onChange(value ?? "");
}}
/>
</FormControl>
{description && <FormDescription>{description}</FormDescription>}
<FormErrorMessage />
</FormItem>
);
}}
/>
);
}
);
FormCurrencyField.displayName = "FormCurrencyField";