Skip to content

Commit

Permalink
fix: simplify onChange handler (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
omeralpi committed Mar 13, 2024
1 parent 236c3b1 commit 344f34d
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 100 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ export default function Hero() {
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8 flex flex-col items-start"
>
className="space-y-8 flex flex-col items-start">
<FormField
control={form.control}
name="phone"
Expand All @@ -88,6 +87,19 @@ export default function Hero() {
}
```

## Other Examples

### Optional Phone Input

```tsx
const FormSchema = z.object({
phone: z
.string()
.refine(isValidPhoneNumber, { message: "Invalid phone number" })
.or(z.literal("")),
});
```

## Documentation

You can find out more about the API and implementation in the
Expand Down
108 changes: 55 additions & 53 deletions app/(home)/sections/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,62 +41,64 @@ export default function Hero() {
}

return (
<section className="z-10 max-w-5xl w-full flex flex-col items-center text-center gap-5">
<div className="z-10 w-full flex flex-col items-center text-center gap-5">
<h1 className="scroll-m-20 text-4xl font-bold tracking-tight">Shadcn Phone Input</h1>
<p className="text-muted-foreground max-w-[450px]">
An implementation of a Phone Input component built on top of Shadcn UI&apos;s input component.
</p>
<div className="flex gap-2 mt-1">
<Link
href="#try"
className={`${buttonVariants({
variant: "default",
size: "lg",
})} min-w-[150px] shadow-sm`}
>
Try it out
</Link>
<Link
href={siteConfig.links.github}
className={`${buttonVariants({
variant: "secondary",
size: "lg",
})} shadow-sm`}
>
Github
</Link>
<>
<section className="z-10 max-w-5xl w-full flex flex-col items-center text-center gap-5">
<div className="z-10 w-full flex flex-col items-center text-center gap-5">
<h1 className="scroll-m-20 text-4xl font-bold tracking-tight">Shadcn Phone Input</h1>
<p className="text-muted-foreground max-w-[450px]">
An implementation of a Phone Input component built on top of Shadcn UI&apos;s input component.
</p>
<div className="flex gap-2 mt-1">
<Link
href="#try"
className={`${buttonVariants({
variant: "default",
size: "lg",
})} min-w-[150px] shadow-sm`}
>
Try it out
</Link>
<Link
href={siteConfig.links.github}
className={`${buttonVariants({
variant: "secondary",
size: "lg",
})} shadow-sm`}
>
Github
</Link>
</div>
</div>
</div>

<div id="try" className="w-full py-8">
<div className="w-full relative my-4 flex flex-col space-y-2">
<div className="preview flex min-h-[350px] w-full justify-center p-10 items-start mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative rounded-md border">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8 flex flex-col items-start">
<FormField
control={form.control}
name="phone"
render={({ field }) => (
<FormItem className="flex flex-col items-start">
<FormLabel className="text-left">Phone Number</FormLabel>
<FormControl className="w-full">
<PhoneInput placeholder="Enter a phone number" {...field} />
</FormControl>
<FormDescription className="text-left">Enter a phone number</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<pre>
<code className="text-foreground">{JSON.stringify(form.watch("phone"), null, 2)}</code>
</pre>
<Button type="submit">Submit</Button>
</form>
</Form>
<div id="try" className="w-full py-8">
<div className="w-full relative my-4 flex flex-col space-y-2">
<div className="preview flex min-h-[350px] w-full justify-center p-10 items-start mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative rounded-md border">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8 flex flex-col items-start">
<FormField
control={form.control}
name="phone"
render={({ field }) => (
<FormItem className="flex flex-col items-start">
<FormLabel className="text-left">Phone Number</FormLabel>
<FormControl className="w-full">
<PhoneInput placeholder="Enter a phone number" {...field} />
</FormControl>
<FormDescription className="text-left">Enter a phone number</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<pre>
<code className="text-foreground">{JSON.stringify(form.watch("phone"), null, 2)}</code>
</pre>
<Button type="submit">Submit</Button>
</form>
</Form>
</div>
</div>
</div>
</div>
</section>
</section>
</>
);
}
1 change: 0 additions & 1 deletion app/(home)/sections/variants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { PhoneInput } from "@/components/ui/phone-input";
import React, { useState } from "react";
import {
Country,
Value,
formatPhoneNumber,
formatPhoneNumberIntl,
getCountryCallingCode,
Expand Down
45 changes: 23 additions & 22 deletions components/ui/phone-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,34 @@ import { cn } from "@/lib/utils";

type PhoneInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "value"> &
Omit<RPNInput.Props<typeof RPNInput.default>, "onChange"> & {
onChange: (value: RPNInput.Value) => void;
value: RPNInput.Value;
onChange?: (value: RPNInput.Value) => void;
};

const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> = React.forwardRef<
React.ElementRef<typeof RPNInput.default>,
PhoneInputProps
>(({ className, onChange, ...props }, ref) => (
<RPNInput.default
ref={ref}
className={cn("flex", className)}
flagComponent={FlagComponent}
countrySelectComponent={CountrySelect}
inputComponent={InputComponent}
/**
* Handles the onChange event.
*
* react-phone-number-input might trigger the onChange event as undefined
* when a valid phone number is not entered. To prevent this,
* the value is coerced to an empty string.
*
* @param {E164Number | undefined} value - The entered value
*/
onChange={(value) => onChange(value || "")}
{...props}
/>
));
>(({ className, onChange, ...props }, ref) => {
return (
<RPNInput.default
ref={ref}
className={cn("flex", className)}
flagComponent={FlagComponent}
countrySelectComponent={CountrySelect}
inputComponent={InputComponent}
/**
* Handles the onChange event.
*
* react-phone-number-input might trigger the onChange event as undefined
* when a valid phone number is not entered. To prevent this,
* the value is coerced to an empty string.
*
* @param {E164Number | undefined} value - The entered value
*/
onChange={(value) => onChange?.(value || "")}
{...props}
/>
);
});
PhoneInput.displayName = "PhoneInput";

const InputComponent = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => (
Expand Down
45 changes: 23 additions & 22 deletions content/snippets/phone-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,33 @@ type PhoneInputProps = Omit<
"onChange" | "value"
> &
Omit<RPNInput.Props<typeof RPNInput.default>, "onChange"> & {
onChange: (value: RPNInput.Value) => void;
value: RPNInput.Value;
onChange?: (value: RPNInput.Value) => void;
};

const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> =
React.forwardRef<React.ElementRef<typeof RPNInput.default>, PhoneInputProps>(
({ className, onChange, ...props }, ref) => (
<RPNInput.default
ref={ref}
className={cn("flex", className)}
flagComponent={FlagComponent}
countrySelectComponent={CountrySelect}
inputComponent={InputComponent}
/**
* Handles the onChange event.
*
* react-phone-number-input might trigger the onChange event as undefined
* when a valid phone number is not entered. To prevent this,
* the value is coerced to an empty string.
*
* @param {E164Number | undefined} value - The entered value
*/
onChange={(value) => onChange(value || "")}
{...props}
/>
),
({ className, onChange, ...props }, ref) => {
return (
<RPNInput.default
ref={ref}
className={cn("flex", className)}
flagComponent={FlagComponent}
countrySelectComponent={CountrySelect}
inputComponent={InputComponent}
/**
* Handles the onChange event.
*
* react-phone-number-input might trigger the onChange event as undefined
* when a valid phone number is not entered. To prevent this,
* the value is coerced to an empty string.
*
* @param {E164Number | undefined} value - The entered value
*/
onChange={(value) => onChange?.(value || "")}
{...props}
/>
);
},
);
PhoneInput.displayName = "PhoneInput";

Expand Down

0 comments on commit 344f34d

Please sign in to comment.