123 lines
4.1 KiB
TypeScript
123 lines
4.1 KiB
TypeScript
|
|
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";
|