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: HelperTextVariant
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
+ }