mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-07-09 06:55:07 +00:00
feat: add input-otp
This commit is contained in:
@@ -293,4 +293,3 @@ All other props are passed through to the underlying `<Textarea />` component.
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
```
|
||||
|
||||
|
||||
@@ -7,11 +7,7 @@ links:
|
||||
doc: https://input-otp.rodz.dev
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
styleName="base-nova"
|
||||
name="input-otp-demo"
|
||||
description="An 6 digits input OTP."
|
||||
/>
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-demo" />
|
||||
|
||||
## About
|
||||
|
||||
@@ -27,16 +23,10 @@ Input OTP is built on top of [input-otp](https://github.com/guilhermerodz/input-
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps className="mb-0 pt-2">
|
||||
|
||||
<Step>Run the following command:</Step>
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add input-otp
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
@@ -92,150 +82,66 @@ import {
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Pattern
|
||||
## Pattern
|
||||
|
||||
Use the `pattern` prop to define a custom pattern for the OTP input.
|
||||
|
||||
<ComponentPreview
|
||||
styleName="base-nova"
|
||||
name="input-otp-pattern"
|
||||
description="An input OTP with alphanumeric pattern."
|
||||
/>
|
||||
|
||||
```tsx showLineNumbers {1,7}
|
||||
```tsx showLineNumbers {1,5}
|
||||
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
|
||||
|
||||
...
|
||||
|
||||
<InputOTP
|
||||
maxLength={6}
|
||||
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
{/* ... */}
|
||||
</InputOTPGroup>
|
||||
;<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
|
||||
...
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-pattern" />
|
||||
|
||||
## Examples
|
||||
|
||||
### Separator
|
||||
|
||||
You can use the `<InputOTPSeparator />` component to add a separator between the input groups.
|
||||
Use the `<InputOTPSeparator />` component to add a separator between input groups.
|
||||
|
||||
<ComponentPreview
|
||||
styleName="base-nova"
|
||||
name="input-otp-separator"
|
||||
description="An input OTP with custom separator."
|
||||
/>
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-separator" />
|
||||
|
||||
```tsx showLineNumbers {4,15}
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/components/ui/input-otp"
|
||||
### Disabled
|
||||
|
||||
...
|
||||
Use the `disabled` prop to disable the input.
|
||||
|
||||
<InputOTP maxLength={4}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
```
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-disabled" />
|
||||
|
||||
### Controlled
|
||||
|
||||
You can use the `value` and `onChange` props to control the input value.
|
||||
Use the `value` and `onChange` props to control the input value.
|
||||
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-controlled" />
|
||||
|
||||
### Invalid
|
||||
|
||||
Use `aria-invalid` on the slots to show an error state.
|
||||
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-invalid" />
|
||||
|
||||
### Four Digits
|
||||
|
||||
A common pattern for PIN codes. This uses the `pattern={REGEXP_ONLY_DIGITS}` prop.
|
||||
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-four-digits" />
|
||||
|
||||
### Alphanumeric
|
||||
|
||||
Use `REGEXP_ONLY_DIGITS_AND_CHARS` to accept both letters and numbers.
|
||||
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-alphanumeric" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview styleName="base-nova" name="input-otp-form" />
|
||||
<ComponentPreview
|
||||
styleName="base-nova"
|
||||
name="input-otp-form"
|
||||
previewClassName="h-[30rem]"
|
||||
/>
|
||||
|
||||
## Changelog
|
||||
## API Reference
|
||||
|
||||
### 2024-03-19 Composition
|
||||
|
||||
We've made some updates and replaced the render props pattern with composition. Here's how to update your code if you prefer the composition pattern.
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Note:** You are not required to update your code if you are using the
|
||||
`render` prop. It is still supported.
|
||||
</Callout>
|
||||
|
||||
<Steps className="mb-0 pt-2">
|
||||
|
||||
<Step>Update to the latest version of `input-otp`.</Step>
|
||||
|
||||
```bash
|
||||
npm install input-otp@latest
|
||||
```
|
||||
|
||||
<Step>Update `input-otp.tsx`</Step>
|
||||
|
||||
```diff showLineNumbers title="input-otp.tsx" {2,8-11}
|
||||
- import { OTPInput, SlotProps } from "input-otp"
|
||||
+ import { OTPInput, OTPInputContext } from "input-otp"
|
||||
|
||||
const InputOTPSlot = React.forwardRef<
|
||||
React.ElementRef<"div">,
|
||||
- SlotProps & React.ComponentPropsWithoutRef<"div">
|
||||
- >(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {
|
||||
+ React.ComponentPropsWithoutRef<"div"> & { index: number }
|
||||
+ >(({ index, className, ...props }, ref) => {
|
||||
+ const inputOTPContext = React.useContext(OTPInputContext)
|
||||
+ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
|
||||
```
|
||||
|
||||
<Step>Then replace the `render` prop in your code.</Step>
|
||||
|
||||
```diff showLineNumbers {2-12}
|
||||
<InputOTP maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
### 2024-03-19 Disabled
|
||||
|
||||
To add a disabled state to the input, update `<InputOTP />` as follows:
|
||||
|
||||
```tsx showLineNumbers title="input-otp.tsx" {4,7-11}
|
||||
const InputOTP = React.forwardRef<
|
||||
React.ElementRef<typeof OTPInput>,
|
||||
React.ComponentPropsWithoutRef<typeof OTPInput>
|
||||
>(({ className, containerClassName, ...props }, ref) => (
|
||||
<OTPInput
|
||||
ref={ref}
|
||||
containerClassName={cn(
|
||||
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
||||
containerClassName
|
||||
)}
|
||||
className={cn("disabled:cursor-not-allowed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
InputOTP.displayName = "InputOTP"
|
||||
```
|
||||
See the [input-otp](https://input-otp.rodz.dev) documentation for more information.
|
||||
|
||||
@@ -7,11 +7,7 @@ links:
|
||||
doc: https://input-otp.rodz.dev
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
styleName="radix-nova"
|
||||
name="input-otp-demo"
|
||||
description="An 6 digits input OTP."
|
||||
/>
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-demo" />
|
||||
|
||||
## About
|
||||
|
||||
@@ -27,16 +23,10 @@ Input OTP is built on top of [input-otp](https://github.com/guilhermerodz/input-
|
||||
</TabsList>
|
||||
<TabsContent value="cli">
|
||||
|
||||
<Steps className="mb-0 pt-2">
|
||||
|
||||
<Step>Run the following command:</Step>
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add input-otp
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
@@ -92,150 +82,66 @@ import {
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Pattern
|
||||
## Pattern
|
||||
|
||||
Use the `pattern` prop to define a custom pattern for the OTP input.
|
||||
|
||||
<ComponentPreview
|
||||
styleName="radix-nova"
|
||||
name="input-otp-pattern"
|
||||
description="An input OTP with alphanumeric pattern."
|
||||
/>
|
||||
|
||||
```tsx showLineNumbers {1,7}
|
||||
```tsx showLineNumbers {1,5}
|
||||
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
|
||||
|
||||
...
|
||||
|
||||
<InputOTP
|
||||
maxLength={6}
|
||||
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
{/* ... */}
|
||||
</InputOTPGroup>
|
||||
;<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
|
||||
...
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-pattern" />
|
||||
|
||||
## Examples
|
||||
|
||||
### Separator
|
||||
|
||||
You can use the `<InputOTPSeparator />` component to add a separator between the input groups.
|
||||
Use the `<InputOTPSeparator />` component to add a separator between input groups.
|
||||
|
||||
<ComponentPreview
|
||||
styleName="radix-nova"
|
||||
name="input-otp-separator"
|
||||
description="An input OTP with custom separator."
|
||||
/>
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-separator" />
|
||||
|
||||
```tsx showLineNumbers {4,15}
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/components/ui/input-otp"
|
||||
### Disabled
|
||||
|
||||
...
|
||||
Use the `disabled` prop to disable the input.
|
||||
|
||||
<InputOTP maxLength={4}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
```
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-disabled" />
|
||||
|
||||
### Controlled
|
||||
|
||||
You can use the `value` and `onChange` props to control the input value.
|
||||
Use the `value` and `onChange` props to control the input value.
|
||||
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-controlled" />
|
||||
|
||||
### Invalid
|
||||
|
||||
Use `aria-invalid` on the slots to show an error state.
|
||||
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-invalid" />
|
||||
|
||||
### Four Digits
|
||||
|
||||
A common pattern for PIN codes. This uses the `pattern={REGEXP_ONLY_DIGITS}` prop.
|
||||
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-four-digits" />
|
||||
|
||||
### Alphanumeric
|
||||
|
||||
Use `REGEXP_ONLY_DIGITS_AND_CHARS` to accept both letters and numbers.
|
||||
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-alphanumeric" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview styleName="radix-nova" name="input-otp-form" />
|
||||
<ComponentPreview
|
||||
styleName="radix-nova"
|
||||
name="input-otp-form"
|
||||
previewClassName="h-[30rem ]"
|
||||
/>
|
||||
|
||||
## Changelog
|
||||
## API Reference
|
||||
|
||||
### 2024-03-19 Composition
|
||||
|
||||
We've made some updates and replaced the render props pattern with composition. Here's how to update your code if you prefer the composition pattern.
|
||||
|
||||
<Callout className="mt-6">
|
||||
**Note:** You are not required to update your code if you are using the
|
||||
`render` prop. It is still supported.
|
||||
</Callout>
|
||||
|
||||
<Steps className="mb-0 pt-2">
|
||||
|
||||
<Step>Update to the latest version of `input-otp`.</Step>
|
||||
|
||||
```bash
|
||||
npm install input-otp@latest
|
||||
```
|
||||
|
||||
<Step>Update `input-otp.tsx`</Step>
|
||||
|
||||
```diff showLineNumbers title="input-otp.tsx" {2,8-11}
|
||||
- import { OTPInput, SlotProps } from "input-otp"
|
||||
+ import { OTPInput, OTPInputContext } from "input-otp"
|
||||
|
||||
const InputOTPSlot = React.forwardRef<
|
||||
React.ElementRef<"div">,
|
||||
- SlotProps & React.ComponentPropsWithoutRef<"div">
|
||||
- >(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {
|
||||
+ React.ComponentPropsWithoutRef<"div"> & { index: number }
|
||||
+ >(({ index, className, ...props }, ref) => {
|
||||
+ const inputOTPContext = React.useContext(OTPInputContext)
|
||||
+ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
|
||||
```
|
||||
|
||||
<Step>Then replace the `render` prop in your code.</Step>
|
||||
|
||||
```diff showLineNumbers {2-12}
|
||||
<InputOTP maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
### 2024-03-19 Disabled
|
||||
|
||||
To add a disabled state to the input, update `<InputOTP />` as follows:
|
||||
|
||||
```tsx showLineNumbers title="input-otp.tsx" {4,7-11}
|
||||
const InputOTP = React.forwardRef<
|
||||
React.ElementRef<typeof OTPInput>,
|
||||
React.ComponentPropsWithoutRef<typeof OTPInput>
|
||||
>(({ className, containerClassName, ...props }, ref) => (
|
||||
<OTPInput
|
||||
ref={ref}
|
||||
containerClassName={cn(
|
||||
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
||||
containerClassName
|
||||
)}
|
||||
className={cn("disabled:cursor-not-allowed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
InputOTP.displayName = "InputOTP"
|
||||
```
|
||||
See the [input-otp](https://input-otp.rodz.dev) documentation for more information.
|
||||
|
||||
@@ -2930,19 +2930,6 @@ export const ExamplesIndex: Record<string, Record<string, any>> = {
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-fields": {
|
||||
name: "input-otp-fields",
|
||||
filePath: "examples/radix/input-otp-fields.tsx",
|
||||
component: React.lazy(async () => {
|
||||
const mod = await import("./radix/input-otp-fields")
|
||||
const exportName =
|
||||
Object.keys(mod).find(
|
||||
(key) =>
|
||||
typeof mod[key] === "function" || typeof mod[key] === "object"
|
||||
) || "input-otp-fields"
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-form": {
|
||||
name: "input-otp-form",
|
||||
filePath: "examples/radix/input-otp-form.tsx",
|
||||
@@ -3008,32 +2995,6 @@ export const ExamplesIndex: Record<string, Record<string, any>> = {
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-simple": {
|
||||
name: "input-otp-simple",
|
||||
filePath: "examples/radix/input-otp-simple.tsx",
|
||||
component: React.lazy(async () => {
|
||||
const mod = await import("./radix/input-otp-simple")
|
||||
const exportName =
|
||||
Object.keys(mod).find(
|
||||
(key) =>
|
||||
typeof mod[key] === "function" || typeof mod[key] === "object"
|
||||
) || "input-otp-simple"
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-with-separator": {
|
||||
name: "input-otp-with-separator",
|
||||
filePath: "examples/radix/input-otp-with-separator.tsx",
|
||||
component: React.lazy(async () => {
|
||||
const mod = await import("./radix/input-otp-with-separator")
|
||||
const exportName =
|
||||
Object.keys(mod).find(
|
||||
(key) =>
|
||||
typeof mod[key] === "function" || typeof mod[key] === "object"
|
||||
) || "input-otp-with-separator"
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-required": {
|
||||
name: "input-required",
|
||||
filePath: "examples/radix/input-required.tsx",
|
||||
@@ -9304,19 +9265,6 @@ export const ExamplesIndex: Record<string, Record<string, any>> = {
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-fields": {
|
||||
name: "input-otp-fields",
|
||||
filePath: "examples/base/input-otp-fields.tsx",
|
||||
component: React.lazy(async () => {
|
||||
const mod = await import("./base/input-otp-fields")
|
||||
const exportName =
|
||||
Object.keys(mod).find(
|
||||
(key) =>
|
||||
typeof mod[key] === "function" || typeof mod[key] === "object"
|
||||
) || "input-otp-fields"
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-form": {
|
||||
name: "input-otp-form",
|
||||
filePath: "examples/base/input-otp-form.tsx",
|
||||
@@ -9382,32 +9330,6 @@ export const ExamplesIndex: Record<string, Record<string, any>> = {
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-simple": {
|
||||
name: "input-otp-simple",
|
||||
filePath: "examples/base/input-otp-simple.tsx",
|
||||
component: React.lazy(async () => {
|
||||
const mod = await import("./base/input-otp-simple")
|
||||
const exportName =
|
||||
Object.keys(mod).find(
|
||||
(key) =>
|
||||
typeof mod[key] === "function" || typeof mod[key] === "object"
|
||||
) || "input-otp-simple"
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-otp-with-separator": {
|
||||
name: "input-otp-with-separator",
|
||||
filePath: "examples/base/input-otp-with-separator.tsx",
|
||||
component: React.lazy(async () => {
|
||||
const mod = await import("./base/input-otp-with-separator")
|
||||
const exportName =
|
||||
Object.keys(mod).find(
|
||||
(key) =>
|
||||
typeof mod[key] === "function" || typeof mod[key] === "object"
|
||||
) || "input-otp-with-separator"
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},
|
||||
"input-required": {
|
||||
name: "input-required",
|
||||
filePath: "examples/base/input-required.tsx",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Field, FieldDescription, FieldLabel } from "@/examples/base/ui/field"
|
||||
"use client"
|
||||
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
@@ -9,26 +10,18 @@ import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
|
||||
|
||||
export function InputOTPAlphanumeric() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="alphanumeric">Alphanumeric</FieldLabel>
|
||||
<FieldDescription>Accepts both letters and numbers.</FieldDescription>
|
||||
<InputOTP
|
||||
id="alphanumeric"
|
||||
maxLength={6}
|
||||
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/base/ui/input-otp"
|
||||
|
||||
export default function InputOTPDemo() {
|
||||
export function InputOTPDemo() {
|
||||
return (
|
||||
<InputOTP maxLength={6}>
|
||||
<InputOTP maxLength={6} defaultValue="123456">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
|
||||
@@ -8,21 +8,18 @@ import {
|
||||
|
||||
export function InputOTPDisabled() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="disabled">Disabled</FieldLabel>
|
||||
<InputOTP id="disabled" maxLength={6} disabled value="123456">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<InputOTP id="disabled" maxLength={6} disabled value="123456">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { useState } from "react"
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
} from "@/examples/base/ui/field"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/base/ui/input-otp"
|
||||
import { REGEXP_ONLY_DIGITS } from "input-otp"
|
||||
|
||||
export function InputOTPFields() {
|
||||
const [value, setValue] = useState("")
|
||||
const [pinValue, setPinValue] = useState("")
|
||||
|
||||
return (
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-basic">Verification Code</FieldLabel>
|
||||
<InputOTP id="otp-basic" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-with-desc">Enter OTP</FieldLabel>
|
||||
<InputOTP
|
||||
id="otp-with-desc"
|
||||
maxLength={6}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
Enter the 6-digit code sent to your email.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-separator">
|
||||
Two-Factor Authentication
|
||||
</FieldLabel>
|
||||
<InputOTP id="otp-separator" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
Enter the code from your authenticator app.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-pin">PIN Code</FieldLabel>
|
||||
<InputOTP
|
||||
id="otp-pin"
|
||||
maxLength={4}
|
||||
pattern={REGEXP_ONLY_DIGITS}
|
||||
value={pinValue}
|
||||
onChange={setPinValue}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
Enter your 4-digit PIN (numbers only).
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field data-invalid>
|
||||
<FieldLabel htmlFor="otp-invalid">Invalid OTP</FieldLabel>
|
||||
<InputOTP id="otp-invalid" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} aria-invalid />
|
||||
<InputOTPSlot index={1} aria-invalid />
|
||||
<InputOTPSlot index={2} aria-invalid />
|
||||
<InputOTPSlot index={3} aria-invalid />
|
||||
<InputOTPSlot index={4} aria-invalid />
|
||||
<InputOTPSlot index={5} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
This OTP field contains validation errors.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field data-disabled>
|
||||
<FieldLabel htmlFor="otp-disabled-field">Disabled OTP</FieldLabel>
|
||||
<InputOTP id="otp-disabled-field" maxLength={6} disabled>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
This OTP field is currently disabled.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
)
|
||||
}
|
||||
@@ -27,49 +27,49 @@ export function InputOTPForm() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form>
|
||||
<Field>
|
||||
<div className="flex items-center justify-between">
|
||||
<FieldLabel htmlFor="otp-verification">
|
||||
Verification code
|
||||
</FieldLabel>
|
||||
<Button variant="outline" size="xs">
|
||||
<RefreshCwIcon />
|
||||
Resend Code
|
||||
</Button>
|
||||
</div>
|
||||
<InputOTP maxLength={6} id="otp-verification" required>
|
||||
<InputOTPGroup className="style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-vega:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-mira:*:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup className="style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-vega:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-mira:*:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
<a href="#">I no longer have access to this email address.</a>
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
</form>
|
||||
<Field>
|
||||
<div className="flex items-center justify-between">
|
||||
<FieldLabel htmlFor="otp-verification">
|
||||
Verification code
|
||||
</FieldLabel>
|
||||
<Button variant="outline" size="xs">
|
||||
<RefreshCwIcon />
|
||||
Resend Code
|
||||
</Button>
|
||||
</div>
|
||||
<InputOTP maxLength={6} id="otp-verification" required>
|
||||
<InputOTPGroup className="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator className="mx-2" />
|
||||
<InputOTPGroup className="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
<a href="#">I no longer have access to this email address.</a>
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col gap-2">
|
||||
<Button type="submit" className="w-full">
|
||||
Verify
|
||||
</Button>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
Having trouble signing in?{" "}
|
||||
<a
|
||||
href="#"
|
||||
className="hover:text-primary underline underline-offset-4 transition-colors"
|
||||
>
|
||||
Contact support
|
||||
</a>
|
||||
</div>
|
||||
<CardFooter>
|
||||
<Field>
|
||||
<Button type="submit" className="w-full">
|
||||
Verify
|
||||
</Button>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
Having trouble signing in?{" "}
|
||||
<a
|
||||
href="#"
|
||||
className="hover:text-primary underline underline-offset-4 transition-colors"
|
||||
>
|
||||
Contact support
|
||||
</a>
|
||||
</div>
|
||||
</Field>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Field, FieldDescription, FieldLabel } from "@/examples/base/ui/field"
|
||||
"use client"
|
||||
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
@@ -8,17 +9,13 @@ import { REGEXP_ONLY_DIGITS } from "input-otp"
|
||||
|
||||
export function InputOTPFourDigits() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="four-digits">4 Digits</FieldLabel>
|
||||
<FieldDescription>Common pattern for PIN codes.</FieldDescription>
|
||||
<InputOTP id="four-digits" maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<InputOTP maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldError,
|
||||
FieldLabel,
|
||||
} from "@/examples/base/ui/field"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
@@ -18,28 +12,21 @@ export function InputOTPInvalid() {
|
||||
const [value, setValue] = React.useState("000000")
|
||||
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="invalid">Invalid State</FieldLabel>
|
||||
<FieldDescription>
|
||||
Example showing the invalid error state.
|
||||
</FieldDescription>
|
||||
<InputOTP id="invalid" maxLength={6} value={value} onChange={setValue}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} aria-invalid />
|
||||
<InputOTPSlot index={1} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} aria-invalid />
|
||||
<InputOTPSlot index={3} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={4} aria-invalid />
|
||||
<InputOTPSlot index={5} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldError errors={[{ message: "Invalid code. Please try again." }]} />
|
||||
</Field>
|
||||
<InputOTP maxLength={6} value={value} onChange={setValue}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} aria-invalid />
|
||||
<InputOTPSlot index={1} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} aria-invalid />
|
||||
<InputOTPSlot index={3} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={4} aria-invalid />
|
||||
<InputOTPSlot index={5} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { REGEXP_ONLY_DIGITS } from "input-otp"
|
||||
|
||||
export function InputOTPPattern() {
|
||||
return (
|
||||
<Field>
|
||||
<Field className="w-fit">
|
||||
<FieldLabel htmlFor="digits-only">Digits Only</FieldLabel>
|
||||
<InputOTP id="digits-only" maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
|
||||
<InputOTPGroup>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Field, FieldLabel } from "@/examples/base/ui/field"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/base/ui/input-otp"
|
||||
|
||||
export function InputOTPSimple() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="simple">Simple</FieldLabel>
|
||||
<InputOTP id="simple" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Field, FieldLabel } from "@/examples/base/ui/field"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/base/ui/input-otp"
|
||||
|
||||
export function InputOTPWithSeparator() {
|
||||
const [value, setValue] = React.useState("123456")
|
||||
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="with-separator">With Separator</FieldLabel>
|
||||
<InputOTP
|
||||
id="with-separator"
|
||||
maxLength={6}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Field, FieldDescription, FieldLabel } from "@/examples/radix/ui/field"
|
||||
"use client"
|
||||
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
@@ -9,26 +10,18 @@ import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
|
||||
|
||||
export function InputOTPAlphanumeric() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="alphanumeric">Alphanumeric</FieldLabel>
|
||||
<FieldDescription>Accepts both letters and numbers.</FieldDescription>
|
||||
<InputOTP
|
||||
id="alphanumeric"
|
||||
maxLength={6}
|
||||
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/radix/ui/input-otp"
|
||||
|
||||
export default function InputOTPDemo() {
|
||||
export function InputOTPDemo() {
|
||||
return (
|
||||
<InputOTP maxLength={6}>
|
||||
<InputOTP maxLength={6} defaultValue="123456">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Field, FieldLabel } from "@/examples/radix/ui/field"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
@@ -8,21 +7,18 @@ import {
|
||||
|
||||
export function InputOTPDisabled() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="disabled">Disabled</FieldLabel>
|
||||
<InputOTP id="disabled" maxLength={6} disabled value="123456">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<InputOTP id="disabled" maxLength={6} disabled value="123456">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { useState } from "react"
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
} from "@/examples/radix/ui/field"
|
||||
import { Input } from "@/examples/radix/ui/input"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/radix/ui/input-otp"
|
||||
import { REGEXP_ONLY_DIGITS } from "input-otp"
|
||||
|
||||
export function InputOTPFields() {
|
||||
const [value, setValue] = useState("")
|
||||
const [pinValue, setPinValue] = useState("")
|
||||
|
||||
return (
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-basic">Verification Code</FieldLabel>
|
||||
<InputOTP id="otp-basic" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-with-desc">Enter OTP</FieldLabel>
|
||||
<InputOTP
|
||||
id="otp-with-desc"
|
||||
maxLength={6}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
Enter the 6-digit code sent to your email.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-separator">
|
||||
Two-Factor Authentication
|
||||
</FieldLabel>
|
||||
<InputOTP id="otp-separator" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
Enter the code from your authenticator app.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="otp-pin">PIN Code</FieldLabel>
|
||||
<InputOTP
|
||||
id="otp-pin"
|
||||
maxLength={4}
|
||||
pattern={REGEXP_ONLY_DIGITS}
|
||||
value={pinValue}
|
||||
onChange={setPinValue}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
Enter your 4-digit PIN (numbers only).
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field data-invalid>
|
||||
<FieldLabel htmlFor="otp-invalid">Invalid OTP</FieldLabel>
|
||||
<InputOTP id="otp-invalid" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} aria-invalid />
|
||||
<InputOTPSlot index={1} aria-invalid />
|
||||
<InputOTPSlot index={2} aria-invalid />
|
||||
<InputOTPSlot index={3} aria-invalid />
|
||||
<InputOTPSlot index={4} aria-invalid />
|
||||
<InputOTPSlot index={5} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
This OTP field contains validation errors.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field data-disabled>
|
||||
<FieldLabel htmlFor="otp-disabled-field">Disabled OTP</FieldLabel>
|
||||
<InputOTP id="otp-disabled-field" maxLength={6} disabled>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
This OTP field is currently disabled.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
)
|
||||
}
|
||||
@@ -27,49 +27,49 @@ export function InputOTPForm() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form>
|
||||
<Field>
|
||||
<div className="flex items-center justify-between">
|
||||
<FieldLabel htmlFor="otp-verification">
|
||||
Verification code
|
||||
</FieldLabel>
|
||||
<Button variant="outline" size="xs">
|
||||
<RefreshCwIcon />
|
||||
Resend Code
|
||||
</Button>
|
||||
</div>
|
||||
<InputOTP maxLength={6} id="otp-verification" required>
|
||||
<InputOTPGroup className="style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-vega:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-mira:*:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup className="style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-vega:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-mira:*:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
<a href="#">I no longer have access to this email address.</a>
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
</form>
|
||||
<Field>
|
||||
<div className="flex items-center justify-between">
|
||||
<FieldLabel htmlFor="otp-verification">
|
||||
Verification code
|
||||
</FieldLabel>
|
||||
<Button variant="outline" size="xs">
|
||||
<RefreshCwIcon />
|
||||
Resend Code
|
||||
</Button>
|
||||
</div>
|
||||
<InputOTP maxLength={6} id="otp-verification" required>
|
||||
<InputOTPGroup className="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator className="mx-2" />
|
||||
<InputOTPGroup className="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldDescription>
|
||||
<a href="#">I no longer have access to this email address.</a>
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col gap-2">
|
||||
<Button type="submit" className="w-full">
|
||||
Verify
|
||||
</Button>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
Having trouble signing in?{" "}
|
||||
<a
|
||||
href="#"
|
||||
className="hover:text-primary underline underline-offset-4 transition-colors"
|
||||
>
|
||||
Contact support
|
||||
</a>
|
||||
</div>
|
||||
<CardFooter>
|
||||
<Field>
|
||||
<Button type="submit" className="w-full">
|
||||
Verify
|
||||
</Button>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
Having trouble signing in?{" "}
|
||||
<a
|
||||
href="#"
|
||||
className="hover:text-primary underline underline-offset-4 transition-colors"
|
||||
>
|
||||
Contact support
|
||||
</a>
|
||||
</div>
|
||||
</Field>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Field, FieldDescription, FieldLabel } from "@/examples/radix/ui/field"
|
||||
"use client"
|
||||
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
@@ -8,17 +9,13 @@ import { REGEXP_ONLY_DIGITS } from "input-otp"
|
||||
|
||||
export function InputOTPFourDigits() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="four-digits">4 Digits</FieldLabel>
|
||||
<FieldDescription>Common pattern for PIN codes.</FieldDescription>
|
||||
<InputOTP id="four-digits" maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
<InputOTP maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldError,
|
||||
FieldLabel,
|
||||
} from "@/examples/radix/ui/field"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
@@ -18,28 +12,21 @@ export function InputOTPInvalid() {
|
||||
const [value, setValue] = React.useState("000000")
|
||||
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="invalid">Invalid State</FieldLabel>
|
||||
<FieldDescription>
|
||||
Example showing the invalid error state.
|
||||
</FieldDescription>
|
||||
<InputOTP id="invalid" maxLength={6} value={value} onChange={setValue}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} aria-invalid />
|
||||
<InputOTPSlot index={1} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} aria-invalid />
|
||||
<InputOTPSlot index={3} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={4} aria-invalid />
|
||||
<InputOTPSlot index={5} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
<FieldError errors={[{ message: "Invalid code. Please try again." }]} />
|
||||
</Field>
|
||||
<InputOTP maxLength={6} value={value} onChange={setValue}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} aria-invalid />
|
||||
<InputOTPSlot index={1} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} aria-invalid />
|
||||
<InputOTPSlot index={3} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={4} aria-invalid />
|
||||
<InputOTPSlot index={5} aria-invalid />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { REGEXP_ONLY_DIGITS } from "input-otp"
|
||||
|
||||
export function InputOTPPattern() {
|
||||
return (
|
||||
<Field>
|
||||
<Field className="w-fit">
|
||||
<FieldLabel htmlFor="digits-only">Digits Only</FieldLabel>
|
||||
<InputOTP id="digits-only" maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
|
||||
<InputOTPGroup>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Field, FieldLabel } from "@/examples/radix/ui/field"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/radix/ui/input-otp"
|
||||
|
||||
export function InputOTPSimple() {
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="simple">Simple</FieldLabel>
|
||||
<InputOTP id="simple" maxLength={6}>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Field, FieldLabel } from "@/examples/radix/ui/field"
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSeparator,
|
||||
InputOTPSlot,
|
||||
} from "@/examples/radix/ui/input-otp"
|
||||
|
||||
export function InputOTPWithSeparator() {
|
||||
const [value, setValue] = React.useState("123456")
|
||||
|
||||
return (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="with-separator">With Separator</FieldLabel>
|
||||
<InputOTP
|
||||
id="with-separator"
|
||||
maxLength={6}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={2} />
|
||||
<InputOTPSlot index={3} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</Field>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user