Skip to content

Instantly share code, notes, and snippets.

@Sutil
Last active April 26, 2024 05:28
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Sutil/5285f2e5a912dcf14fc23393dac97fed to your computer and use it in GitHub Desktop.
Save Sutil/5285f2e5a912dcf14fc23393dac97fed to your computer and use it in GitHub Desktop.
Shandcn UI Money Mask Input - NextJS.
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import MoneyInput from "src/components/custom/money-input";
import { Button } from "src/components/ui/button";
import { Form } from "src/components/ui/form";
import * as z from "zod";
const schema = z.object({
value: z.coerce.number().min(0.01, "Required"),
});
export default function PlanForm() {
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
defaultValues: {
value: 0,
},
mode: "onTouched",
});
function onSubmit(values: z.infer<typeof schema>) {
// handle submit
}
return (
<Form {...form}>
<form
className="flex flex-col gap-8"
onSubmit={form.handleSubmit(onSubmit)}
>
<MoneyInput
form={form}
label="Valor"
name="value"
placeholder="Valor do plano"
/>
<Button type="submit" disabled={!form.formState.isValid}>
Submit
</Button>
</form>
</Form>
);
}
"use client";
import { useReducer } from "react";
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "../ui/form"; // Shadcn UI import
import { Input } from "../ui/input"; // Shandcn UI Input
import { UseFormReturn } from "react-hook-form";
type TextInputProps = {
form: UseFormReturn<any>;
name: string;
label: string;
placeholder: string;
};
// Brazilian currency config
const moneyFormatter = Intl.NumberFormat("pt-BR", {
currency: "BRL",
currencyDisplay: "symbol",
currencySign: "standard",
style: "currency",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
export default function MoneyInput(props: TextInputProps) {
const initialValue = props.form.getValues()[props.name]
? moneyFormatter.format(props.form.getValues()[props.name])
: "";
const [value, setValue] = useReducer((_: any, next: string) => {
const digits = next.replace(/\D/g, "");
return moneyFormatter.format(Number(digits) / 100);
}, initialValue);
function handleChange(realChangeFn: Function, formattedValue: string) {
const digits = formattedValue.replace(/\D/g, "");
const realValue = Number(digits) / 100;
realChangeFn(realValue);
}
return (
<FormField
control={props.form.control}
name={props.name}
render={({ field }) => {
field.value = value;
const _change = field.onChange;
return (
<FormItem>
<FormLabel>{props.label}</FormLabel>
<FormControl>
<Input
placeholder={props.placeholder}
type="text"
{...field}
onChange={(ev) => {
setValue(ev.target.value);
handleChange(_change, ev.target.value);
}}
value={value}
/>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
);
}
@hspotted
Copy link

Funciona muito bem quando estás a introduzir os dados, mas a apagar ele não funciona

@Sutil
Copy link
Author

Sutil commented Jan 15, 2024

Funciona muito bem quando estás a introduzir os dados, mas a apagar ele não funciona

@hspotted, It worked fine here. Let me know what behavior you expect on this.

Screen.Recording.2024-01-15.at.10.14.44.mov

@DaviJat
Copy link

DaviJat commented Feb 27, 2024

Estou usando o componente e ele funciona perfeitamente, mas quando faço form.reset(data) para recuperar os dados, é o único campo que não atualiza

@Sutil
Copy link
Author

Sutil commented Feb 29, 2024

Estou usando o componente e ele funciona perfeitamente, mas quando faço form.reset(data) para recuperar os dados, é o único campo que não atualiza

Não previ este caso. A linha 51 sempre vai atrapalhar.

Vou ter que mudar a implementação

@DaviJat
Copy link

DaviJat commented Feb 29, 2024

Eu consegui contornar a situação aqui, adicionei um useEffect no componente para atualizar o valor, e estou passando o value pelo formulário como uma props do componente:

useEffect(() => {
    if (props.value) {
      setValue((Number(props.value) * 100).toString());
    }
  }, [props.form, props.value]);
const [balanceValue, setBalanceValue] = useState('');

// setBalanceValue usei onde pego os dados da API

<MoneyInput
            form={form}
            value={balanceValue}
            label="Saldo"
            name="balance"
            placeholder={!isDataLoading ? 'Saldo da carteira...' : 'Carregando...'}
          />

@thailonlucas
Copy link

Eu consegui contornar a situação aqui, adicionei um useEffect no componente para atualizar o valor, e estou passando o value pelo formulário como uma props do componente:

useEffect(() => {
    if (props.value) {
      setValue((Number(props.value) * 100).toString());
    }
  }, [props.form, props.value]);
const [balanceValue, setBalanceValue] = useState('');

// setBalanceValue usei onde pego os dados da API

<MoneyInput
            form={form}
            value={balanceValue}
            label="Saldo"
            name="balance"
            placeholder={!isDataLoading ? 'Saldo da carteira...' : 'Carregando...'}
          />

Eu resolvi dessa forma e deixou o código um pouco mais limpo:

useEffect(() => { setValue(initialValue); }, [initialValue]);

Mas isso porque nao passo o value como prop e continuo utilizando apenas o form como fonte de dado.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment