ui
ui copied to clipboard
How to add mask on input field?
I'm trying to add masks to inputs using the shadcn/ui forms integration with react hook form and zod. However, the libraries I've found so far aren't working very well.
Has anyone implemented this?
I came into a solution using maskito: https://maskito.dev/frameworks/react
And applying with the current Shadcn components would be something like:
The masked fields on your schema should be strings to make this work (you could process those values after the handleSubmit or something)
Here is an example for money mask https://gist.github.com/Sutil/5285f2e5a912dcf14fc23393dac97fed
"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>
);
}}
/>
);
}
And in your form:
<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}>
save
</Button>
</form>
</Form>
This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.
you can use: https://github.com/eduardoborges/use-mask-input
you can use: https://github.com/eduardoborges/use-mask-input
Do you have an example? In my case, trying to use it on an Input with the ref={useMask('')}
syntax says the types of ref are not compatible.
I made a wrapper.
https://gist.github.com/Sutil/5285f2e5a912dcf14fc23393dac97fed
thanks @Sutil , based on your solution i built my own just extending the input with a callback.
import * as React from 'react';
import { cn } from '@/lib/utils';
import { Input } from './ui/input';
const moneyFormatter = Intl.NumberFormat("pt-BR", {
currency: "BRL",
currencyDisplay: "symbol",
currencySign: "standard",
style: "currency",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
type CurrencyInputProps = {
className?: string;
initialValue?: string;
onCallback?: Function;
};
export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & CurrencyInputProps;
const CurrencyInput = React.forwardRef<HTMLInputElement, InputProps>(
({ className, initialValue = "", onCallback, ...props }, ref) => {
const [value, setValue] = React.useReducer(
(_: any, next: string) => {
const digits = next.replace(/\D/g, "");
return moneyFormatter.format(Number(digits) / 100);
},
initialValue
);
function handleChange(formattedValue: string) {
const digits = formattedValue.replace(/\D/g, "");
const realValue = Number(digits) / 100;
onCallback && onCallback(realValue);
}
return (
<Input
ref={ref}
className={cn('w-full', className)}
{...props}
value={value}
onChange={(ev) => {
setValue(ev.target.value);
handleChange(ev.target.value);
}}
/>
);
}
);
CurrencyInput.displayName = 'CurrencyInput';
export { CurrencyInput };
using
<FormField
control={form.control}
name="value"
render={({ field }) => (
<FormItem className="col-span-12 md:col-span-6 lg:col-span-4">
<FormLabel>Valor</FormLabel>
<FormControl>
<CurrencyInput {...field} onCallback={field.onChange} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>