aural-ui 2.1.8 → 2.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -9,7 +9,7 @@ import { cn } from "@lib/utils"
|
|
|
9
9
|
type HelperTextVariant = "default" | "error" | "warning" | "success"
|
|
10
10
|
|
|
11
11
|
interface HelperTextProps {
|
|
12
|
-
variant
|
|
12
|
+
variant?: HelperTextVariant
|
|
13
13
|
className?: string
|
|
14
14
|
children: ReactNode
|
|
15
15
|
disabled?: boolean
|
|
@@ -17,7 +17,7 @@ interface HelperTextProps {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const HelperText = forwardRef<HTMLSpanElement, HelperTextProps>(
|
|
20
|
-
({ variant, className = "", children, disabled, id }, ref) => {
|
|
20
|
+
({ variant = "default", className = "", children, disabled, id }, ref) => {
|
|
21
21
|
return (
|
|
22
22
|
<SwitchCase value={variant}>
|
|
23
23
|
<Case value="error">
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import React, { useState } from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
|
|
4
|
+
import OtpInputs from "."
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof OtpInputs> = {
|
|
7
|
+
title: "Components/UI/OtpInputs",
|
|
8
|
+
component: OtpInputs,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: "centered",
|
|
11
|
+
backgrounds: {
|
|
12
|
+
default: "dark",
|
|
13
|
+
values: [
|
|
14
|
+
{ name: "dark", value: "#0a0a0a" },
|
|
15
|
+
{ name: "light", value: "#ffffff" },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
docs: {
|
|
19
|
+
description: {
|
|
20
|
+
component: `
|
|
21
|
+
# OTP Input Component
|
|
22
|
+
|
|
23
|
+
A flexible and accessible OTP (One-Time Password) input component that allows users to enter verification codes with individual input fields.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Customizable Length**: Configure the number of input fields (default: 6)
|
|
28
|
+
- **Number/Text Input**: Support for both numeric and alphanumeric OTP codes
|
|
29
|
+
- **Keyboard Navigation**: Full keyboard support with arrow keys, backspace, and delete
|
|
30
|
+
- **Auto-focus Management**: Automatic focus progression when typing
|
|
31
|
+
- **Accessibility**: Proper ARIA attributes and keyboard navigation
|
|
32
|
+
- **Custom Styling**: Flexible styling options for inputs and container
|
|
33
|
+
- **Disabled State**: Support for disabled state
|
|
34
|
+
|
|
35
|
+
## Usage Examples
|
|
36
|
+
|
|
37
|
+
### Basic OTP Input
|
|
38
|
+
\`\`\`tsx
|
|
39
|
+
<OtpInputs
|
|
40
|
+
length={6}
|
|
41
|
+
onChangeOTP={(otp) => console.log(otp)}
|
|
42
|
+
/>
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
### Alphanumeric OTP
|
|
46
|
+
\`\`\`tsx
|
|
47
|
+
<OtpInputs
|
|
48
|
+
length={6}
|
|
49
|
+
isNumberInput={false}
|
|
50
|
+
onChangeOTP={(otp) => console.log(otp)}
|
|
51
|
+
/>
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
## Props Overview
|
|
55
|
+
|
|
56
|
+
- **length**: Number of input fields (required)
|
|
57
|
+
- **isNumberInput**: Whether to restrict input to numbers only (default: true)
|
|
58
|
+
- **disabled**: Disable all inputs (default: false)
|
|
59
|
+
- **onChangeOTP**: Callback when OTP value changes (required)
|
|
60
|
+
- **inputStyle**: Inline styles for individual inputs
|
|
61
|
+
`,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
tags: ["autodocs"],
|
|
66
|
+
argTypes: {
|
|
67
|
+
length: {
|
|
68
|
+
control: { type: "number", min: 1, max: 10 },
|
|
69
|
+
description: "Number of input fields",
|
|
70
|
+
},
|
|
71
|
+
isNumberInput: {
|
|
72
|
+
control: { type: "boolean" },
|
|
73
|
+
description: "Whether to restrict input to numbers only",
|
|
74
|
+
},
|
|
75
|
+
disabled: {
|
|
76
|
+
control: { type: "boolean" },
|
|
77
|
+
description: "Disable all inputs",
|
|
78
|
+
},
|
|
79
|
+
onChangeOTP: {
|
|
80
|
+
action: "otpChanged",
|
|
81
|
+
description: "Callback when OTP value changes",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default meta
|
|
87
|
+
type Story = StoryObj<typeof OtpInputs>
|
|
88
|
+
|
|
89
|
+
export const Default: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
length: 6,
|
|
92
|
+
isNumberInput: true,
|
|
93
|
+
disabled: false,
|
|
94
|
+
onChangeOTP: (otp: string) => console.log("OTP changed:", otp),
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const FourDigits: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
length: 4,
|
|
101
|
+
isNumberInput: true,
|
|
102
|
+
disabled: false,
|
|
103
|
+
onChangeOTP: (otp: string) => console.log("OTP changed:", otp),
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const EightDigits: Story = {
|
|
108
|
+
args: {
|
|
109
|
+
length: 8,
|
|
110
|
+
isNumberInput: true,
|
|
111
|
+
onChangeOTP: (otp: string) => console.log("OTP changed:", otp),
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const Alphanumeric: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
length: 6,
|
|
118
|
+
isNumberInput: false,
|
|
119
|
+
onChangeOTP: (otp: string) => console.log("OTP changed:", otp),
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const Disabled: Story = {
|
|
124
|
+
args: {
|
|
125
|
+
length: 6,
|
|
126
|
+
isNumberInput: true,
|
|
127
|
+
disabled: true,
|
|
128
|
+
onChangeOTP: (otp: string) => console.log("OTP changed:", otp),
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const Interactive: Story = {
|
|
133
|
+
render: () => {
|
|
134
|
+
const [otpValue, setOtpValue] = useState("")
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div className="space-y-4">
|
|
138
|
+
<OtpInputs length={6} onChangeOTP={setOtpValue} />
|
|
139
|
+
<p className="text-sm text-gray-600">
|
|
140
|
+
Current OTP: {otpValue || "Empty"}
|
|
141
|
+
</p>
|
|
142
|
+
</div>
|
|
143
|
+
)
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const WithValidation: Story = {
|
|
148
|
+
render: () => {
|
|
149
|
+
const [isValid, setIsValid] = useState<boolean | null>(null)
|
|
150
|
+
|
|
151
|
+
const handleOtpChange = (otp: string) => {
|
|
152
|
+
if (otp.length === 6) {
|
|
153
|
+
setIsValid(otp === "123456")
|
|
154
|
+
} else {
|
|
155
|
+
setIsValid(null)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<OtpInputs
|
|
161
|
+
length={6}
|
|
162
|
+
onChangeOTP={handleOtpChange}
|
|
163
|
+
isValid={isValid}
|
|
164
|
+
messages={{
|
|
165
|
+
error: "Invalid OTP, try: 123456",
|
|
166
|
+
}}
|
|
167
|
+
/>
|
|
168
|
+
)
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const AllVariants: Story = {
|
|
173
|
+
render: () => {
|
|
174
|
+
const [otp4, setOtp4] = useState("")
|
|
175
|
+
const [otp6, setOtp6] = useState("")
|
|
176
|
+
const [otp8, setOtp8] = useState("")
|
|
177
|
+
const [alphanumericOtp, setAlphanumericOtp] = useState("")
|
|
178
|
+
const [validatedOtp, setValidatedOtp] = useState("")
|
|
179
|
+
const [isValid, setIsValid] = useState<boolean | null>(null)
|
|
180
|
+
|
|
181
|
+
const handleValidation = (otp: string) => {
|
|
182
|
+
setValidatedOtp(otp)
|
|
183
|
+
if (otp.length === 6) {
|
|
184
|
+
setIsValid(otp === "123456")
|
|
185
|
+
} else {
|
|
186
|
+
setIsValid(null)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<div className="max-w-4xl space-y-8 p-6">
|
|
192
|
+
<div className="space-y-2">
|
|
193
|
+
<h2 className="text-2xl font-bold text-white">
|
|
194
|
+
OTP Input - All Variants
|
|
195
|
+
</h2>
|
|
196
|
+
<p className="text-gray-400">
|
|
197
|
+
Comprehensive showcase of all OTP input configurations, states, and
|
|
198
|
+
use cases.
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
{/* Basic Length Variants */}
|
|
203
|
+
<div className="space-y-4">
|
|
204
|
+
<h3 className="text-xl font-semibold text-white">Length Variants</h3>
|
|
205
|
+
|
|
206
|
+
<div className="space-y-3">
|
|
207
|
+
<div>
|
|
208
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
209
|
+
4-Digit OTP (Common for SMS)
|
|
210
|
+
</label>
|
|
211
|
+
<OtpInputs
|
|
212
|
+
length={4}
|
|
213
|
+
isNumberInput={true}
|
|
214
|
+
onChangeOTP={setOtp4}
|
|
215
|
+
/>
|
|
216
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
217
|
+
Current: {otp4 || "Empty"}
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div>
|
|
222
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
223
|
+
6-Digit OTP (Standard)
|
|
224
|
+
</label>
|
|
225
|
+
<OtpInputs
|
|
226
|
+
length={6}
|
|
227
|
+
isNumberInput={true}
|
|
228
|
+
onChangeOTP={setOtp6}
|
|
229
|
+
/>
|
|
230
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
231
|
+
Current: {otp6 || "Empty"}
|
|
232
|
+
</p>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div>
|
|
236
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
237
|
+
8-Digit OTP (Extended Security)
|
|
238
|
+
</label>
|
|
239
|
+
<OtpInputs
|
|
240
|
+
length={8}
|
|
241
|
+
isNumberInput={true}
|
|
242
|
+
onChangeOTP={setOtp8}
|
|
243
|
+
/>
|
|
244
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
245
|
+
Current: {otp8 || "Empty"}
|
|
246
|
+
</p>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
{/* Input Type Variants */}
|
|
252
|
+
<div className="space-y-4">
|
|
253
|
+
<h3 className="text-xl font-semibold text-white">
|
|
254
|
+
Input Type Variants
|
|
255
|
+
</h3>
|
|
256
|
+
|
|
257
|
+
<div className="space-y-3">
|
|
258
|
+
<div>
|
|
259
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
260
|
+
Numeric Only (Default)
|
|
261
|
+
</label>
|
|
262
|
+
<OtpInputs
|
|
263
|
+
length={6}
|
|
264
|
+
isNumberInput={true}
|
|
265
|
+
onChangeOTP={(otp) => console.log("Numeric OTP:", otp)}
|
|
266
|
+
/>
|
|
267
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
268
|
+
Accepts only numbers (0-9)
|
|
269
|
+
</p>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<div>
|
|
273
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
274
|
+
Alphanumeric (Letters + Numbers)
|
|
275
|
+
</label>
|
|
276
|
+
<OtpInputs
|
|
277
|
+
length={6}
|
|
278
|
+
isNumberInput={false}
|
|
279
|
+
onChangeOTP={setAlphanumericOtp}
|
|
280
|
+
/>
|
|
281
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
282
|
+
Current: {alphanumericOtp || "Empty"} | Accepts letters and
|
|
283
|
+
numbers
|
|
284
|
+
</p>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
{/* State Variants */}
|
|
290
|
+
<div className="space-y-4">
|
|
291
|
+
<h3 className="text-xl font-semibold text-white">State Variants</h3>
|
|
292
|
+
|
|
293
|
+
<div className="space-y-3">
|
|
294
|
+
<div>
|
|
295
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
296
|
+
Enabled State (Default)
|
|
297
|
+
</label>
|
|
298
|
+
<OtpInputs
|
|
299
|
+
length={6}
|
|
300
|
+
isNumberInput={true}
|
|
301
|
+
onChangeOTP={(otp) => console.log("Enabled OTP:", otp)}
|
|
302
|
+
/>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div>
|
|
306
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
307
|
+
Disabled State
|
|
308
|
+
</label>
|
|
309
|
+
<OtpInputs
|
|
310
|
+
length={6}
|
|
311
|
+
isNumberInput={true}
|
|
312
|
+
disabled={true}
|
|
313
|
+
onChangeOTP={(otp) => console.log("Disabled OTP:", otp)}
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
{/* Validation States */}
|
|
320
|
+
<div className="space-y-4">
|
|
321
|
+
<h3 className="text-xl font-semibold text-white">
|
|
322
|
+
Validation States
|
|
323
|
+
</h3>
|
|
324
|
+
|
|
325
|
+
<div className="space-y-3">
|
|
326
|
+
<div>
|
|
327
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
328
|
+
Neutral State (No validation)
|
|
329
|
+
</label>
|
|
330
|
+
<OtpInputs
|
|
331
|
+
length={6}
|
|
332
|
+
isNumberInput={true}
|
|
333
|
+
onChangeOTP={(otp) => console.log("Neutral OTP:", otp)}
|
|
334
|
+
isValid={null}
|
|
335
|
+
messages={{
|
|
336
|
+
neutral: "Enter your 6-digit verification code",
|
|
337
|
+
}}
|
|
338
|
+
/>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<div>
|
|
342
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
343
|
+
Success State (Valid OTP)
|
|
344
|
+
</label>
|
|
345
|
+
<OtpInputs
|
|
346
|
+
length={6}
|
|
347
|
+
isNumberInput={true}
|
|
348
|
+
onChangeOTP={(otp) => console.log("Success OTP:", otp)}
|
|
349
|
+
isValid={true}
|
|
350
|
+
messages={{
|
|
351
|
+
success: "✓ Verification successful!",
|
|
352
|
+
}}
|
|
353
|
+
/>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<div>
|
|
357
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
358
|
+
Error State (Invalid OTP)
|
|
359
|
+
</label>
|
|
360
|
+
<OtpInputs
|
|
361
|
+
length={6}
|
|
362
|
+
isNumberInput={true}
|
|
363
|
+
onChangeOTP={(otp) => console.log("Error OTP:", otp)}
|
|
364
|
+
isValid={false}
|
|
365
|
+
messages={{
|
|
366
|
+
error: "✗ Invalid code. Please try again.",
|
|
367
|
+
}}
|
|
368
|
+
/>
|
|
369
|
+
</div>
|
|
370
|
+
|
|
371
|
+
<div>
|
|
372
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
373
|
+
Interactive Validation (Try: 123456)
|
|
374
|
+
</label>
|
|
375
|
+
<OtpInputs
|
|
376
|
+
length={6}
|
|
377
|
+
isNumberInput={true}
|
|
378
|
+
onChangeOTP={handleValidation}
|
|
379
|
+
isValid={isValid}
|
|
380
|
+
messages={{
|
|
381
|
+
neutral: "Enter 6-digit code",
|
|
382
|
+
success: "✓ Code verified successfully!",
|
|
383
|
+
error: "✗ Invalid code. Try: 123456",
|
|
384
|
+
}}
|
|
385
|
+
/>
|
|
386
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
387
|
+
Current: {validatedOtp || "Empty"} | Status:{" "}
|
|
388
|
+
{isValid === null
|
|
389
|
+
? "Neutral"
|
|
390
|
+
: isValid === true
|
|
391
|
+
? "Valid"
|
|
392
|
+
: "Invalid"}
|
|
393
|
+
</p>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
|
|
398
|
+
{/* Custom Styling Examples */}
|
|
399
|
+
<div className="space-y-4">
|
|
400
|
+
<h3 className="text-xl font-semibold text-white">Custom Styling</h3>
|
|
401
|
+
|
|
402
|
+
<div className="space-y-3">
|
|
403
|
+
<div>
|
|
404
|
+
<label className="mb-2 block text-sm font-medium text-gray-300">
|
|
405
|
+
Custom Input Styling
|
|
406
|
+
</label>
|
|
407
|
+
<OtpInputs
|
|
408
|
+
length={6}
|
|
409
|
+
isNumberInput={true}
|
|
410
|
+
onChangeOTP={(otp) => console.log("Custom styled OTP:", otp)}
|
|
411
|
+
inputStyle={{
|
|
412
|
+
backgroundColor: "#1f2937",
|
|
413
|
+
borderColor: "#374151",
|
|
414
|
+
color: "#f9fafb",
|
|
415
|
+
}}
|
|
416
|
+
inputClassName="border-2 border-blue-500 focus:border-blue-400"
|
|
417
|
+
/>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
|
|
422
|
+
{/* Usage Examples */}
|
|
423
|
+
<div className="space-y-4">
|
|
424
|
+
<h3 className="text-xl font-semibold text-white">Common Use Cases</h3>
|
|
425
|
+
|
|
426
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
427
|
+
<div className="rounded-lg bg-gray-800 p-4">
|
|
428
|
+
<h4 className="mb-2 font-medium text-white">SMS Verification</h4>
|
|
429
|
+
<OtpInputs
|
|
430
|
+
length={4}
|
|
431
|
+
isNumberInput={true}
|
|
432
|
+
onChangeOTP={(otp) => console.log("SMS OTP:", otp)}
|
|
433
|
+
messages={{
|
|
434
|
+
neutral: "Enter SMS code",
|
|
435
|
+
}}
|
|
436
|
+
/>
|
|
437
|
+
</div>
|
|
438
|
+
|
|
439
|
+
<div className="rounded-lg bg-gray-800 p-4">
|
|
440
|
+
<h4 className="mb-2 font-medium text-white">
|
|
441
|
+
Email Verification
|
|
442
|
+
</h4>
|
|
443
|
+
<OtpInputs
|
|
444
|
+
length={6}
|
|
445
|
+
isNumberInput={true}
|
|
446
|
+
onChangeOTP={(otp) => console.log("Email OTP:", otp)}
|
|
447
|
+
messages={{
|
|
448
|
+
neutral: "Enter email code",
|
|
449
|
+
}}
|
|
450
|
+
/>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
<div className="rounded-lg bg-gray-800 p-4">
|
|
454
|
+
<h4 className="mb-2 font-medium text-white">
|
|
455
|
+
2FA Authentication
|
|
456
|
+
</h4>
|
|
457
|
+
<OtpInputs
|
|
458
|
+
length={6}
|
|
459
|
+
isNumberInput={true}
|
|
460
|
+
onChangeOTP={(otp) => console.log("2FA OTP:", otp)}
|
|
461
|
+
messages={{
|
|
462
|
+
neutral: "Enter authenticator code",
|
|
463
|
+
}}
|
|
464
|
+
/>
|
|
465
|
+
</div>
|
|
466
|
+
|
|
467
|
+
<div className="rounded-lg bg-gray-800 p-4">
|
|
468
|
+
<h4 className="mb-2 font-medium text-white">Alphanumeric Code</h4>
|
|
469
|
+
<OtpInputs
|
|
470
|
+
length={6}
|
|
471
|
+
isNumberInput={false}
|
|
472
|
+
onChangeOTP={(otp) => console.log("Alphanumeric OTP:", otp)}
|
|
473
|
+
messages={{
|
|
474
|
+
neutral: "Enter alphanumeric code",
|
|
475
|
+
}}
|
|
476
|
+
/>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
)
|
|
482
|
+
},
|
|
483
|
+
parameters: {
|
|
484
|
+
docs: {
|
|
485
|
+
description: {
|
|
486
|
+
story: `
|
|
487
|
+
This comprehensive story demonstrates all available variants, states, and configurations of the OTP Input component:`,
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
}
|