@catalystsoftware/ui 1.0.2 → 1.0.5
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.
- package/data/tailwind.config.js +261 -3821
- package/dist/components/catalyst-ui/buttons/burger.tsx +207 -0
- package/dist/components/catalyst-ui/core/data-display/timeline.tsx +210 -0
- package/dist/components/catalyst-ui/core/feedback/alert.tsx +491 -0
- package/dist/components/catalyst-ui/core/feedback/spinner-1.tsx +65 -0
- package/dist/components/catalyst-ui/core/feedback/toast.tsx +1857 -0
- package/dist/components/catalyst-ui/core/navigation/menu.tsx +164 -0
- package/dist/components/catalyst-ui/forms/toggle-class.tsx +176 -0
- package/dist/components/catalyst-ui/hooks/use-copy-to-clipboard.tsx +419 -0
- package/dist/components/catalyst-ui/hooks/use-counter.tsx +13 -0
- package/dist/components/catalyst-ui/hooks/use-event-listener.tsx +23 -0
- package/dist/components/catalyst-ui/hooks/use-export-markdown.tsx +47 -0
- package/dist/components/catalyst-ui/hooks/use-focus.tsx +17 -0
- package/dist/components/catalyst-ui/hooks/use-interval.tsx +23 -0
- package/dist/components/catalyst-ui/hooks/use-is-client.tsx +16 -0
- package/dist/components/catalyst-ui/hooks/use-media-query.tsx +19 -0
- package/dist/components/catalyst-ui/hooks/use-mobile.tsx +19 -0
- package/dist/components/catalyst-ui/hooks/use-resize-observer.tsx +81 -0
- package/dist/components/catalyst-ui/hooks/use-timeout.tsx +21 -0
- package/dist/components/catalyst-ui/hooks/use-timer.tsx +209 -0
- package/dist/components/catalyst-ui/hooks/use-toggle.tsx +12 -0
- package/dist/components/catalyst-ui/media/image.tsx +13 -0
- package/dist/components/catalyst-ui/overlays/dual-sidebar.tsx +4142 -0
- package/dist/components/catalyst-ui/overlays/sidebar-original.tsx +726 -0
- package/dist/components/catalyst-ui/primitives/accordion.tsx +250 -0
- package/dist/components/catalyst-ui/primitives/alert-dialog.tsx +126 -0
- package/dist/components/catalyst-ui/primitives/aspect-ratio.tsx +9 -0
- package/dist/components/catalyst-ui/primitives/avatar.tsx +296 -0
- package/dist/components/catalyst-ui/primitives/badge.tsx +57 -0
- package/dist/components/catalyst-ui/primitives/breadcrumb.tsx +101 -0
- package/dist/components/catalyst-ui/primitives/button.tsx +265 -0
- package/dist/components/catalyst-ui/primitives/calendar-v4.tsx +208 -0
- package/dist/components/catalyst-ui/primitives/calendar.tsx +295 -0
- package/dist/components/catalyst-ui/primitives/card.tsx +618 -0
- package/dist/components/catalyst-ui/primitives/carousel.tsx +238 -0
- package/dist/components/catalyst-ui/primitives/chart.tsx +347 -0
- package/dist/components/catalyst-ui/primitives/checkbox.tsx +225 -0
- package/dist/components/catalyst-ui/primitives/collapsible.tsx +212 -0
- package/dist/components/catalyst-ui/primitives/command.tsx +393 -0
- package/dist/components/catalyst-ui/primitives/context-menu.tsx +236 -0
- package/dist/components/catalyst-ui/primitives/dialog.tsx +471 -0
- package/dist/components/catalyst-ui/primitives/drawer.tsx +761 -0
- package/dist/components/catalyst-ui/primitives/dropdown-menu.tsx +290 -0
- package/dist/components/catalyst-ui/primitives/empty.tsx +104 -0
- package/dist/components/catalyst-ui/primitives/field.tsx +244 -0
- package/dist/components/catalyst-ui/primitives/hover-card.tsx +124 -0
- package/dist/components/catalyst-ui/primitives/input-otp.tsx +76 -0
- package/dist/components/catalyst-ui/primitives/input.tsx +64 -0
- package/dist/components/catalyst-ui/primitives/item.tsx +196 -0
- package/dist/components/catalyst-ui/primitives/kbd.tsx +75 -0
- package/dist/components/catalyst-ui/primitives/label.tsx +24 -0
- package/dist/components/catalyst-ui/primitives/navigation-menu.tsx +150 -0
- package/dist/components/catalyst-ui/primitives/pagination.tsx +198 -0
- package/dist/components/catalyst-ui/primitives/popover.tsx +232 -0
- package/dist/components/catalyst-ui/primitives/progress.tsx +34 -0
- package/dist/components/catalyst-ui/primitives/radio-group.tsx +43 -0
- package/dist/components/catalyst-ui/primitives/resizable.tsx +56 -0
- package/dist/components/catalyst-ui/primitives/select.tsx +155 -0
- package/dist/components/catalyst-ui/primitives/separator.tsx +74 -0
- package/dist/components/catalyst-ui/primitives/sheet.tsx +126 -0
- package/dist/components/catalyst-ui/primitives/skeleton.tsx +15 -0
- package/dist/components/catalyst-ui/primitives/slider.tsx +27 -0
- package/dist/components/catalyst-ui/primitives/switch.tsx +187 -0
- package/dist/components/catalyst-ui/primitives/tabs.tsx +335 -0
- package/dist/components/catalyst-ui/primitives/textarea.tsx +24 -0
- package/dist/components/catalyst-ui/primitives/toggle-group.tsx +55 -0
- package/dist/components/catalyst-ui/primitives/toggle.tsx +42 -0
- package/dist/components/catalyst-ui/primitives/tooltip.tsx +116 -0
- package/dist/components/catalyst-ui/utils/basic-auth.tsx +40 -0
- package/dist/components/catalyst-ui/utils/context-storage.tsx +19 -0
- package/dist/components/catalyst-ui/utils/cors-middleware.tsx +71 -0
- package/dist/components/catalyst-ui/utils/deferred-content.tsx +595 -0
- package/dist/components/catalyst-ui/utils/honeypot-middleware.tsx +38 -0
- package/dist/components/catalyst-ui/utils/incId.tsx +75 -0
- package/dist/components/catalyst-ui/utils/jwk-auth.tsx +36 -0
- package/dist/components/catalyst-ui/utils/request-id.tsx +14 -0
- package/dist/components/catalyst-ui/utils/secure-headers.tsx +37 -0
- package/dist/components/catalyst-ui/utils/server-timing.tsx +23 -0
- package/dist/components/catalyst-ui/utils/utils.ts +43 -0
- package/dist/components/catalyst-ui/utils/with-cookie.tsx +43 -0
- package/dist/components/catalyst-ui/x/accordian-x.tsx +428 -0
- package/dist/components/catalyst-ui/x/alert-x.tsx +413 -0
- package/dist/components/catalyst-ui/x/animated-text-x.tsx +2242 -0
- package/dist/components/catalyst-ui/x/avatar-x.tsx +515 -0
- package/dist/components/catalyst-ui/x/badge-x.tsx +670 -0
- package/dist/components/catalyst-ui/x/button-X.tsx +2857 -0
- package/dist/components/catalyst-ui/x/button-group-x.tsx +847 -0
- package/dist/components/catalyst-ui/x/calendar-x.tsx +1910 -0
- package/dist/components/catalyst-ui/x/card-x.tsx +2597 -0
- package/dist/components/catalyst-ui/x/checkbox-x.tsx +656 -0
- package/dist/components/catalyst-ui/x/collapsible-x.tsx +1360 -0
- package/dist/components/catalyst-ui/x/combobox-x.tsx +911 -0
- package/dist/components/catalyst-ui/x/data-table-x.tsx +1753 -0
- package/dist/components/catalyst-ui/x/date-picker-x.tsx +648 -0
- package/dist/components/catalyst-ui/x/dialog-x.tsx +659 -0
- package/dist/components/catalyst-ui/x/dropdown-menu-x.tsx +612 -0
- package/dist/components/catalyst-ui/x/hover-card-x.tsx +375 -0
- package/dist/components/catalyst-ui/x/icon-x.tsx +840 -0
- package/dist/components/catalyst-ui/x/input-mask-x.tsx +981 -0
- package/dist/components/catalyst-ui/x/input-otp-x.tsx +659 -0
- package/dist/components/catalyst-ui/x/loader-x.tsx +1757 -0
- package/dist/components/catalyst-ui/x/pagination-x.tsx +622 -0
- package/dist/components/catalyst-ui/x/popover-x.tsx +744 -0
- package/dist/components/catalyst-ui/x/radio-group-x.tsx +499 -0
- package/dist/components/catalyst-ui/x/select-x.tsx +1127 -0
- package/dist/components/catalyst-ui/x/sheet-x.tsx +668 -0
- package/dist/components/catalyst-ui/x/switch-x.tsx +681 -0
- package/dist/components/catalyst-ui/x/table-x.tsx +574 -0
- package/dist/components/catalyst-ui/x/tabs-x.tsx +839 -0
- package/dist/components/catalyst-ui/x/textarea-x.tsx +1263 -0
- package/dist/components/catalyst-ui/x/tooltip-x.tsx +396 -0
- package/dist/components/catalyst-ui/x/tracker-x.tsx +560 -0
- package/dist/data/bg-data.tsx +901 -0
- package/dist/data/buttons-data.tsx +2327 -0
- package/dist/data/charts-data.tsx +102 -0
- package/dist/data/chat-data.tsx +83 -0
- package/dist/data/code-data.tsx +1040 -0
- package/dist/data/comboboxes-data.tsx +1843 -0
- package/dist/data/command-data.tsx +1381 -0
- package/dist/data/core-data.tsx +15953 -0
- package/dist/data/crm-data.tsx +47 -0
- package/dist/data/data.tsx +159 -0
- package/dist/data/date-and-time-data.tsx +554 -0
- package/dist/data/dependencies.tsx +7 -0
- package/dist/data/ecommerce-data.tsx +1387 -0
- package/dist/data/forms-data.tsx +7890 -0
- package/dist/data/hooks-data.tsx +5487 -0
- package/dist/data/index.ts +34 -0
- package/dist/data/inputs-data.tsx +557 -0
- package/dist/data/interactive-data.tsx +5394 -0
- package/dist/data/lofi-data.tsx +18295 -0
- package/dist/data/marketing-data.tsx +2546 -0
- package/dist/data/media-data.tsx +1510 -0
- package/dist/data/motion-data.tsx +5801 -0
- package/dist/data/overlay-data.tsx +4136 -0
- package/dist/data/pdf-data.tsx +124 -0
- package/dist/data/pos-data.tsx +213 -0
- package/dist/data/postcss.config.js +6 -0
- package/dist/data/primitive-data.tsx +5170 -0
- package/dist/data/prompt-data.tsx +1226 -0
- package/dist/data/requiredLibs.ts +4 -0
- package/dist/data/sandbox-data.tsx +1 -0
- package/dist/data/sidebars-data.tsx +5421 -0
- package/dist/data/stacks-data.tsx +32 -0
- package/dist/data/table-data.tsx +706 -0
- package/dist/data/tailwind.config.js +270 -0
- package/dist/data/tailwind.config.ngin.js +3830 -0
- package/dist/data/tailwind.css +431 -0
- package/dist/data/tools-data.tsx +6910 -0
- package/dist/data/typography-data.tsx +2050 -0
- package/dist/data/utils-data.tsx +6500 -0
- package/dist/data/x-data.tsx +1171 -0
- package/dist/data.tsx +159 -0
- package/package.json +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -362
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useId, useState, useEffect } from 'react'
|
|
4
|
+
import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp'
|
|
5
|
+
import { Dot } from 'lucide-react'
|
|
6
|
+
import { cva } from 'class-variance-authority'
|
|
7
|
+
import { cn } from '~/components/catalyst-ui'
|
|
8
|
+
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from '~/components/catalyst-ui'
|
|
9
|
+
import { Field, FieldLabel, FieldDescription, FieldError } from '~/components/catalyst-ui'
|
|
10
|
+
|
|
11
|
+
// #region CVA VARIANTS
|
|
12
|
+
export const inputOtpVariants = cva(
|
|
13
|
+
'flex items-center gap-2 has-[:disabled]:opacity-50',
|
|
14
|
+
{
|
|
15
|
+
variants: {
|
|
16
|
+
variant: {
|
|
17
|
+
default: '',
|
|
18
|
+
outlined: 'gap-2 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border',
|
|
19
|
+
filled: '*:data-[slot=input-otp-slot]:bg-muted gap-2 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border *:data-[slot=input-otp-slot]:border-transparent *:data-[slot=input-otp-slot]:shadow-none',
|
|
20
|
+
minimal: 'gap-2 *:data-[active=true]:ring-0 *:data-[slot=input-otp-slot]:rounded-none *:data-[slot=input-otp-slot]:border-0 *:data-[slot=input-otp-slot]:border-b-2 *:data-[slot=input-otp-slot]:shadow-none *:dark:data-[slot=input-otp-slot]:bg-transparent',
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
sm: '*:data-[slot=input-otp-slot]:h-8 *:data-[slot=input-otp-slot]:w-8 *:data-[slot=input-otp-slot]:text-xs',
|
|
24
|
+
default: '*:data-[slot=input-otp-slot]:h-10 *:data-[slot=input-otp-slot]:w-10',
|
|
25
|
+
lg: '*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-12 *:data-[slot=input-otp-slot]:text-base',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
defaultVariants: {
|
|
29
|
+
variant: 'default',
|
|
30
|
+
size: 'default',
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
// #endregion
|
|
35
|
+
|
|
36
|
+
// #region PARENT FUNCTION
|
|
37
|
+
export function InputOtpX({ inputOtp = 'default', ...props }: any) {
|
|
38
|
+
switch (inputOtp) {
|
|
39
|
+
case 'Number':
|
|
40
|
+
return <Number {...props} />
|
|
41
|
+
case 'Alphanumeric':
|
|
42
|
+
return <Alphanumeric {...props} />
|
|
43
|
+
case 'WithResend':
|
|
44
|
+
return <WithResend {...props} />
|
|
45
|
+
case 'WithResendTimer':
|
|
46
|
+
return <WithResendTimer {...props} />
|
|
47
|
+
case 'Outlined':
|
|
48
|
+
return <Outlined {...props} />
|
|
49
|
+
case 'Filled':
|
|
50
|
+
return <Filled {...props} />
|
|
51
|
+
case 'Minimal':
|
|
52
|
+
return <Minimal {...props} />
|
|
53
|
+
case 'Grouped':
|
|
54
|
+
return <Grouped {...props} />
|
|
55
|
+
case 'OutlinedGrouped':
|
|
56
|
+
return <OutlinedGrouped {...props} />
|
|
57
|
+
case 'CustomSeparator':
|
|
58
|
+
return <CustomSeparator {...props} />
|
|
59
|
+
default:
|
|
60
|
+
return <DefaultInputOtp {...props} />
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// #endregion
|
|
64
|
+
|
|
65
|
+
// #region SUPPORTING FUNCTIONS
|
|
66
|
+
const DefaultInputOtp = ({ label, description, error, variant, size, className, maxLength = 4, ...props }: any) => {
|
|
67
|
+
const id = useId()
|
|
68
|
+
return (
|
|
69
|
+
<Field className='space-y-3'>
|
|
70
|
+
{label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
|
|
71
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
72
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
73
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
74
|
+
<InputOTPSlot key={index} index={index} />
|
|
75
|
+
))}
|
|
76
|
+
</InputOTPGroup>
|
|
77
|
+
</InputOTP>
|
|
78
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
79
|
+
{error && <FieldError>{error}</FieldError>}
|
|
80
|
+
</Field>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const Number = ({ label = 'Input OTP number', description, error, variant, size, className, maxLength = 4, ...props }: any) => {
|
|
85
|
+
const id = useId()
|
|
86
|
+
return (
|
|
87
|
+
<Field className='space-y-3'>
|
|
88
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
89
|
+
<InputOTP id={id} maxLength={maxLength} pattern={REGEXP_ONLY_DIGITS} className={className} {...props}>
|
|
90
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
91
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
92
|
+
<InputOTPSlot key={index} index={index} />
|
|
93
|
+
))}
|
|
94
|
+
</InputOTPGroup>
|
|
95
|
+
</InputOTP>
|
|
96
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
97
|
+
{error && <FieldError>{error}</FieldError>}
|
|
98
|
+
</Field>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const Alphanumeric = ({ label = 'Input OTP Alphanumeric', description, error, variant, size, className, maxLength = 4, ...props }: any) => {
|
|
103
|
+
const id = useId()
|
|
104
|
+
return (
|
|
105
|
+
<Field className='space-y-3'>
|
|
106
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
107
|
+
<InputOTP id={id} maxLength={maxLength} pattern={REGEXP_ONLY_DIGITS_AND_CHARS} className={className} {...props}>
|
|
108
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
109
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
110
|
+
<InputOTPSlot key={index} index={index} />
|
|
111
|
+
))}
|
|
112
|
+
</InputOTPGroup>
|
|
113
|
+
</InputOTP>
|
|
114
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
115
|
+
{error && <FieldError>{error}</FieldError>}
|
|
116
|
+
</Field>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const WithResend = ({ label = 'Input OTP with resend', description, error, variant, size, className, maxLength = 4, ...props }: any) => {
|
|
121
|
+
const id = useId()
|
|
122
|
+
return (
|
|
123
|
+
<Field className='space-y-3'>
|
|
124
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
125
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
126
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
127
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
128
|
+
<InputOTPSlot key={index} index={index} />
|
|
129
|
+
))}
|
|
130
|
+
</InputOTPGroup>
|
|
131
|
+
</InputOTP>
|
|
132
|
+
<p className='text-muted-foreground text-xs'>
|
|
133
|
+
Didn't get the code?{' '}
|
|
134
|
+
<a href='#' className='text-primary hover:underline'>
|
|
135
|
+
Resend code
|
|
136
|
+
</a>
|
|
137
|
+
</p>
|
|
138
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
139
|
+
{error && <FieldError>{error}</FieldError>}
|
|
140
|
+
</Field>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const WithResendTimer = ({ label = 'Input OTP with resend timer', description, error, variant, size, className, maxLength = 4, initialTimer = 30, ...props }: any) => {
|
|
145
|
+
const [timeLeft, setTimeLeft] = useState(initialTimer)
|
|
146
|
+
const id = useId()
|
|
147
|
+
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (timeLeft > 0) {
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
setTimeLeft(prev => prev - 1)
|
|
152
|
+
}, 1000)
|
|
153
|
+
|
|
154
|
+
return () => clearTimeout(timer)
|
|
155
|
+
}
|
|
156
|
+
}, [timeLeft])
|
|
157
|
+
|
|
158
|
+
const formatTime = (seconds: number) => {
|
|
159
|
+
const mins = Math.floor(seconds / 60)
|
|
160
|
+
const secs = seconds % 60
|
|
161
|
+
|
|
162
|
+
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const handleResend = () => {
|
|
166
|
+
setTimeLeft(60)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<Field className='space-y-3'>
|
|
171
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
172
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
173
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
174
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
175
|
+
<InputOTPSlot key={index} index={index} />
|
|
176
|
+
))}
|
|
177
|
+
</InputOTPGroup>
|
|
178
|
+
</InputOTP>
|
|
179
|
+
<p className='text-muted-foreground text-xs'>
|
|
180
|
+
{timeLeft > 0 ? (
|
|
181
|
+
`Resend available in ${formatTime(timeLeft)}`
|
|
182
|
+
) : (
|
|
183
|
+
<a
|
|
184
|
+
href='#'
|
|
185
|
+
onClick={e => {
|
|
186
|
+
e.preventDefault()
|
|
187
|
+
handleResend()
|
|
188
|
+
}}
|
|
189
|
+
className='hover:text-primary underline'
|
|
190
|
+
>
|
|
191
|
+
Resend code
|
|
192
|
+
</a>
|
|
193
|
+
)}
|
|
194
|
+
</p>
|
|
195
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
196
|
+
{error && <FieldError>{error}</FieldError>}
|
|
197
|
+
</Field>
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const Outlined = ({ label = 'Input OTP outlined', description, error, size, className, maxLength = 4, ...props }: any) => {
|
|
202
|
+
const id = useId()
|
|
203
|
+
return (
|
|
204
|
+
<Field className='space-y-3'>
|
|
205
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
206
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
207
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant: 'outlined', size }))}>
|
|
208
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
209
|
+
<InputOTPSlot key={index} index={index} />
|
|
210
|
+
))}
|
|
211
|
+
</InputOTPGroup>
|
|
212
|
+
</InputOTP>
|
|
213
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
214
|
+
{error && <FieldError>{error}</FieldError>}
|
|
215
|
+
</Field>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const Filled = ({ label = 'Input OTP filled', description, error, size, className, maxLength = 4, ...props }: any) => {
|
|
220
|
+
const id = useId()
|
|
221
|
+
return (
|
|
222
|
+
<Field className='space-y-3'>
|
|
223
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
224
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
225
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant: 'filled', size }))}>
|
|
226
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
227
|
+
<InputOTPSlot key={index} index={index} />
|
|
228
|
+
))}
|
|
229
|
+
</InputOTPGroup>
|
|
230
|
+
</InputOTP>
|
|
231
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
232
|
+
{error && <FieldError>{error}</FieldError>}
|
|
233
|
+
</Field>
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const Minimal = ({ label = 'Input OTP minimal', description, error, size, className, maxLength = 4, ...props }: any) => {
|
|
238
|
+
const id = useId()
|
|
239
|
+
return (
|
|
240
|
+
<Field className='space-y-3'>
|
|
241
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
242
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
243
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant: 'minimal', size }))}>
|
|
244
|
+
{Array.from({ length: maxLength }).map((_, index) => (
|
|
245
|
+
<InputOTPSlot key={index} index={index} />
|
|
246
|
+
))}
|
|
247
|
+
</InputOTPGroup>
|
|
248
|
+
</InputOTP>
|
|
249
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
250
|
+
{error && <FieldError>{error}</FieldError>}
|
|
251
|
+
</Field>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const Grouped = ({ label = 'Input OTP Grouped', description, error, variant, size, className, maxLength = 6, ...props }: any) => {
|
|
256
|
+
const id = useId()
|
|
257
|
+
const halfLength = Math.floor(maxLength / 2)
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<Field className='space-y-3'>
|
|
261
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
262
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
263
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
264
|
+
{Array.from({ length: halfLength }).map((_, index) => (
|
|
265
|
+
<InputOTPSlot key={index} index={index} />
|
|
266
|
+
))}
|
|
267
|
+
</InputOTPGroup>
|
|
268
|
+
<InputOTPSeparator />
|
|
269
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
270
|
+
{Array.from({ length: maxLength - halfLength }).map((_, index) => (
|
|
271
|
+
<InputOTPSlot key={index + halfLength} index={index + halfLength} />
|
|
272
|
+
))}
|
|
273
|
+
</InputOTPGroup>
|
|
274
|
+
</InputOTP>
|
|
275
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
276
|
+
{error && <FieldError>{error}</FieldError>}
|
|
277
|
+
</Field>
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const OutlinedGrouped = ({ label = 'Input OTP Outline Grouped', description, error, size, className, maxLength = 6, ...props }: any) => {
|
|
282
|
+
const id = useId()
|
|
283
|
+
const halfLength = Math.floor(maxLength / 2)
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<Field className='space-y-3'>
|
|
287
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
288
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
289
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant: 'outlined', size }))}>
|
|
290
|
+
{Array.from({ length: halfLength }).map((_, index) => (
|
|
291
|
+
<InputOTPSlot key={index} index={index} />
|
|
292
|
+
))}
|
|
293
|
+
</InputOTPGroup>
|
|
294
|
+
<InputOTPSeparator />
|
|
295
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant: 'outlined', size }))}>
|
|
296
|
+
{Array.from({ length: maxLength - halfLength }).map((_, index) => (
|
|
297
|
+
<InputOTPSlot key={index + halfLength} index={index + halfLength} />
|
|
298
|
+
))}
|
|
299
|
+
</InputOTPGroup>
|
|
300
|
+
</InputOTP>
|
|
301
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
302
|
+
{error && <FieldError>{error}</FieldError>}
|
|
303
|
+
</Field>
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const CustomSeparator = ({ label = 'Input OTP Custom Separator', description, error, variant, size, className, maxLength = 6, ...props }: any) => {
|
|
308
|
+
const id = useId()
|
|
309
|
+
const halfLength = Math.floor(maxLength / 2)
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<Field className='space-y-3'>
|
|
313
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
314
|
+
<InputOTP id={id} maxLength={maxLength} className={className} {...props}>
|
|
315
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
316
|
+
{Array.from({ length: halfLength }).map((_, index) => (
|
|
317
|
+
<InputOTPSlot key={index} index={index} />
|
|
318
|
+
))}
|
|
319
|
+
</InputOTPGroup>
|
|
320
|
+
<div role='separator' className='text-muted-foreground'>
|
|
321
|
+
<Dot />
|
|
322
|
+
</div>
|
|
323
|
+
<InputOTPGroup className={cn(inputOtpVariants({ variant, size }))}>
|
|
324
|
+
{Array.from({ length: maxLength - halfLength }).map((_, index) => (
|
|
325
|
+
<InputOTPSlot key={index + halfLength} index={index + halfLength} />
|
|
326
|
+
))}
|
|
327
|
+
</InputOTPGroup>
|
|
328
|
+
</InputOTP>
|
|
329
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
330
|
+
{error && <FieldError>{error}</FieldError>}
|
|
331
|
+
</Field>
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
// #endregion
|
|
335
|
+
|
|
336
|
+
// #region DEMO
|
|
337
|
+
export function InputOtpXDemo() {
|
|
338
|
+
return (
|
|
339
|
+
<div className='space-y-12 p-6 max-w-3xl mx-auto'>
|
|
340
|
+
<div>
|
|
341
|
+
<h3 className='text-lg font-semibold mb-4'>Default</h3>
|
|
342
|
+
<InputOtpX label='Default OTP' />
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<div>
|
|
346
|
+
<h3 className='text-lg font-semibold mb-4'>Number Only</h3>
|
|
347
|
+
<InputOtpX inputOtp='Number' />
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
<div>
|
|
351
|
+
<h3 className='text-lg font-semibold mb-4'>Alphanumeric</h3>
|
|
352
|
+
<InputOtpX inputOtp='Alphanumeric' />
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<div>
|
|
356
|
+
<h3 className='text-lg font-semibold mb-4'>With Resend</h3>
|
|
357
|
+
<InputOtpX inputOtp='WithResend' />
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
<div>
|
|
361
|
+
<h3 className='text-lg font-semibold mb-4'>With Resend Timer</h3>
|
|
362
|
+
<InputOtpX inputOtp='WithResendTimer' />
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<div>
|
|
366
|
+
<h3 className='text-lg font-semibold mb-4'>Outlined</h3>
|
|
367
|
+
<InputOtpX inputOtp='Outlined' />
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<div>
|
|
371
|
+
<h3 className='text-lg font-semibold mb-4'>Filled</h3>
|
|
372
|
+
<InputOtpX inputOtp='Filled' />
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
<div>
|
|
376
|
+
<h3 className='text-lg font-semibold mb-4'>Minimal</h3>
|
|
377
|
+
<InputOtpX inputOtp='Minimal' />
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<div>
|
|
381
|
+
<h3 className='text-lg font-semibold mb-4'>Grouped</h3>
|
|
382
|
+
<InputOtpX inputOtp='Grouped' />
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
<div>
|
|
386
|
+
<h3 className='text-lg font-semibold mb-4'>Outlined Grouped</h3>
|
|
387
|
+
<InputOtpX inputOtp='OutlinedGrouped' />
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
<div>
|
|
391
|
+
<h3 className='text-lg font-semibold mb-4'>Custom Separator</h3>
|
|
392
|
+
<InputOtpX inputOtp='CustomSeparator' />
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
// #endregion
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
export function InputOtpX({ inputOtp = 'default', children, ...props }: any) {
|
|
409
|
+
switch (inputOtp) {
|
|
410
|
+
case 'Closable':
|
|
411
|
+
break
|
|
412
|
+
default:
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const InputOTPNumberDemo = () => {
|
|
418
|
+
const id = useId()
|
|
419
|
+
|
|
420
|
+
return (
|
|
421
|
+
<div className='space-y-3'>
|
|
422
|
+
<Label htmlFor={id}>Input OTP number</Label>
|
|
423
|
+
<InputOTP id={id} maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
|
|
424
|
+
<InputOTPGroup>
|
|
425
|
+
<InputOTPSlot index={0} />
|
|
426
|
+
<InputOTPSlot index={1} />
|
|
427
|
+
<InputOTPSlot index={2} />
|
|
428
|
+
<InputOTPSlot index={3} />
|
|
429
|
+
</InputOTPGroup>
|
|
430
|
+
</InputOTP>
|
|
431
|
+
</div>
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const InputOTPAlphanumericDemo = () => {
|
|
436
|
+
const id = useId()
|
|
437
|
+
|
|
438
|
+
return (
|
|
439
|
+
<div className='space-y-3'>
|
|
440
|
+
<Label htmlFor={id}>Input OTP Alphanumeric</Label>
|
|
441
|
+
<InputOTP id={id} maxLength={4} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
|
|
442
|
+
<InputOTPGroup>
|
|
443
|
+
<InputOTPSlot index={0} />
|
|
444
|
+
<InputOTPSlot index={1} />
|
|
445
|
+
<InputOTPSlot index={2} />
|
|
446
|
+
<InputOTPSlot index={3} />
|
|
447
|
+
</InputOTPGroup>
|
|
448
|
+
</InputOTP>
|
|
449
|
+
</div>
|
|
450
|
+
)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const InputOTPWithResendDemo = () => {
|
|
454
|
+
const id = useId()
|
|
455
|
+
|
|
456
|
+
return (
|
|
457
|
+
<div className='space-y-3'>
|
|
458
|
+
<Label htmlFor={id}>Input OTP with resend</Label>
|
|
459
|
+
<InputOTP id={id} maxLength={4}>
|
|
460
|
+
<InputOTPGroup>
|
|
461
|
+
<InputOTPSlot index={0} />
|
|
462
|
+
<InputOTPSlot index={1} />
|
|
463
|
+
<InputOTPSlot index={2} />
|
|
464
|
+
<InputOTPSlot index={3} />
|
|
465
|
+
</InputOTPGroup>
|
|
466
|
+
</InputOTP>
|
|
467
|
+
<p className='text-muted-foreground text-xs'>
|
|
468
|
+
Didn't get the code?{' '}
|
|
469
|
+
<a href='#' className='text-primary hover:underline'>
|
|
470
|
+
Resend code
|
|
471
|
+
</a>
|
|
472
|
+
</p>
|
|
473
|
+
</div>
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const InputOTPWithResendTimerDemo = () => {
|
|
478
|
+
const [timeLeft, setTimeLeft] = useState(30)
|
|
479
|
+
const id = useId()
|
|
480
|
+
|
|
481
|
+
useEffect(() => {
|
|
482
|
+
if (timeLeft > 0) {
|
|
483
|
+
const timer = setTimeout(() => {
|
|
484
|
+
setTimeLeft(prev => prev - 1)
|
|
485
|
+
}, 1000)
|
|
486
|
+
|
|
487
|
+
return () => clearTimeout(timer)
|
|
488
|
+
}
|
|
489
|
+
}, [timeLeft])
|
|
490
|
+
|
|
491
|
+
const formatTime = (seconds: number) => {
|
|
492
|
+
const mins = Math.floor(seconds / 60)
|
|
493
|
+
const secs = seconds % 60
|
|
494
|
+
|
|
495
|
+
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const handleResend = () => {
|
|
499
|
+
setTimeLeft(60)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return (
|
|
503
|
+
<div className='space-y-3'>
|
|
504
|
+
<Label htmlFor={id}>Input OTP with resend timer</Label>
|
|
505
|
+
<InputOTP id={id} maxLength={4}>
|
|
506
|
+
<InputOTPGroup>
|
|
507
|
+
<InputOTPSlot index={0} />
|
|
508
|
+
<InputOTPSlot index={1} />
|
|
509
|
+
<InputOTPSlot index={2} />
|
|
510
|
+
<InputOTPSlot index={3} />
|
|
511
|
+
</InputOTPGroup>
|
|
512
|
+
</InputOTP>
|
|
513
|
+
<p className='text-muted-foreground text-xs'>
|
|
514
|
+
{timeLeft > 0 ? (
|
|
515
|
+
`Resend available in ${formatTime(timeLeft)}`
|
|
516
|
+
) : (
|
|
517
|
+
<a
|
|
518
|
+
href='#'
|
|
519
|
+
onClick={e => {
|
|
520
|
+
e.preventDefault()
|
|
521
|
+
handleResend()
|
|
522
|
+
}}
|
|
523
|
+
className='hover:text-primary underline'
|
|
524
|
+
>
|
|
525
|
+
Resend code
|
|
526
|
+
</a>
|
|
527
|
+
)}
|
|
528
|
+
</p>
|
|
529
|
+
</div>
|
|
530
|
+
)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const InputOTPOutlinedDemo = () => {
|
|
534
|
+
const id = useId()
|
|
535
|
+
|
|
536
|
+
return (
|
|
537
|
+
<div className='space-y-3'>
|
|
538
|
+
<Label htmlFor={id}>Input OTP outlined</Label>
|
|
539
|
+
<InputOTP id={id} maxLength={4}>
|
|
540
|
+
<InputOTPGroup className='gap-2 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border'>
|
|
541
|
+
<InputOTPSlot index={0} />
|
|
542
|
+
<InputOTPSlot index={1} />
|
|
543
|
+
<InputOTPSlot index={2} />
|
|
544
|
+
<InputOTPSlot index={3} />
|
|
545
|
+
</InputOTPGroup>
|
|
546
|
+
</InputOTP>
|
|
547
|
+
</div>
|
|
548
|
+
)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const InputOTPFilledDemo = () => {
|
|
552
|
+
const id = useId()
|
|
553
|
+
|
|
554
|
+
return (
|
|
555
|
+
<div className='space-y-3'>
|
|
556
|
+
<Label htmlFor={id}>Input OTP filled</Label>
|
|
557
|
+
<InputOTP id={id} maxLength={4}>
|
|
558
|
+
<InputOTPGroup className='*:data-[slot=input-otp-slot]:bg-muted gap-2 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border *:data-[slot=input-otp-slot]:border-transparent *:data-[slot=input-otp-slot]:shadow-none'>
|
|
559
|
+
<InputOTPSlot index={0} />
|
|
560
|
+
<InputOTPSlot index={1} />
|
|
561
|
+
<InputOTPSlot index={2} />
|
|
562
|
+
<InputOTPSlot index={3} />
|
|
563
|
+
</InputOTPGroup>
|
|
564
|
+
</InputOTP>
|
|
565
|
+
</div>
|
|
566
|
+
)
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const InputOTPMinimalDemo = () => {
|
|
570
|
+
const id = useId()
|
|
571
|
+
|
|
572
|
+
return (
|
|
573
|
+
<div className='space-y-3'>
|
|
574
|
+
<Label htmlFor={id}>Input OTP minimal</Label>
|
|
575
|
+
<InputOTP id={id} maxLength={4}>
|
|
576
|
+
<InputOTPGroup className='gap-2 *:data-[active=true]:ring-0 *:data-[slot=input-otp-slot]:rounded-none *:data-[slot=input-otp-slot]:border-0 *:data-[slot=input-otp-slot]:border-b-2 *:data-[slot=input-otp-slot]:shadow-none *:dark:data-[slot=input-otp-slot]:bg-transparent'>
|
|
577
|
+
<InputOTPSlot index={0} />
|
|
578
|
+
<InputOTPSlot index={1} />
|
|
579
|
+
<InputOTPSlot index={2} />
|
|
580
|
+
<InputOTPSlot index={3} />
|
|
581
|
+
</InputOTPGroup>
|
|
582
|
+
</InputOTP>
|
|
583
|
+
</div>
|
|
584
|
+
)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const InputOTPGroupedDemo = () => {
|
|
588
|
+
const id = useId()
|
|
589
|
+
|
|
590
|
+
return (
|
|
591
|
+
<div className='space-y-3'>
|
|
592
|
+
<Label htmlFor={id}>Input OTP Grouped</Label>
|
|
593
|
+
<InputOTP id={id} maxLength={6}>
|
|
594
|
+
<InputOTPGroup>
|
|
595
|
+
<InputOTPSlot index={0} />
|
|
596
|
+
<InputOTPSlot index={1} />
|
|
597
|
+
<InputOTPSlot index={2} />
|
|
598
|
+
</InputOTPGroup>
|
|
599
|
+
<InputOTPSeparator />
|
|
600
|
+
<InputOTPGroup>
|
|
601
|
+
<InputOTPSlot index={3} />
|
|
602
|
+
<InputOTPSlot index={4} />
|
|
603
|
+
<InputOTPSlot index={5} />
|
|
604
|
+
</InputOTPGroup>
|
|
605
|
+
</InputOTP>
|
|
606
|
+
</div>
|
|
607
|
+
)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const InputOTPOutlinedGroupedDemo = () => {
|
|
611
|
+
const id = useId()
|
|
612
|
+
|
|
613
|
+
return (
|
|
614
|
+
<div className='space-y-3'>
|
|
615
|
+
<Label htmlFor={id}>Input OTP Outline Grouped</Label>
|
|
616
|
+
<InputOTP id={id} maxLength={6}>
|
|
617
|
+
<InputOTPGroup className='gap-2 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border'>
|
|
618
|
+
<InputOTPSlot index={0} />
|
|
619
|
+
<InputOTPSlot index={1} />
|
|
620
|
+
<InputOTPSlot index={2} />
|
|
621
|
+
</InputOTPGroup>
|
|
622
|
+
<InputOTPSeparator />
|
|
623
|
+
<InputOTPGroup className='gap-2 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border'>
|
|
624
|
+
<InputOTPSlot index={3} />
|
|
625
|
+
<InputOTPSlot index={4} />
|
|
626
|
+
<InputOTPSlot index={5} />
|
|
627
|
+
</InputOTPGroup>
|
|
628
|
+
</InputOTP>
|
|
629
|
+
</div>
|
|
630
|
+
)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const InputOTPCustomSeparatorDemo = () => {
|
|
634
|
+
const id = useId()
|
|
635
|
+
|
|
636
|
+
return (
|
|
637
|
+
<div className='space-y-3'>
|
|
638
|
+
<Label htmlFor={id}>Input OTP Custom Separator</Label>
|
|
639
|
+
<InputOTP id={id} maxLength={6}>
|
|
640
|
+
<InputOTPGroup>
|
|
641
|
+
<InputOTPSlot index={0} />
|
|
642
|
+
<InputOTPSlot index={1} />
|
|
643
|
+
<InputOTPSlot index={2} />
|
|
644
|
+
</InputOTPGroup>
|
|
645
|
+
<div role='separator' className='text-muted-foreground'>
|
|
646
|
+
<DotIcon />
|
|
647
|
+
</div>
|
|
648
|
+
<InputOTPGroup>
|
|
649
|
+
<InputOTPSlot index={3} />
|
|
650
|
+
<InputOTPSlot index={4} />
|
|
651
|
+
<InputOTPSlot index={5} />
|
|
652
|
+
</InputOTPGroup>
|
|
653
|
+
</InputOTP>
|
|
654
|
+
</div>
|
|
655
|
+
)
|
|
656
|
+
} */
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
|