@catalystsoftware/ui 1.0.0
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/README.md +7 -0
- package/components/catalyst-ui/buttons/burger.tsx +207 -0
- package/components/catalyst-ui/core/data-display/timeline.tsx +210 -0
- package/components/catalyst-ui/core/feedback/alert.tsx +491 -0
- package/components/catalyst-ui/core/feedback/spinner-1.tsx +65 -0
- package/components/catalyst-ui/core/feedback/toast.tsx +1857 -0
- package/components/catalyst-ui/core/navigation/menu.tsx +164 -0
- package/components/catalyst-ui/forms/toggle-class.tsx +176 -0
- package/components/catalyst-ui/hooks/use-copy-to-clipboard.tsx +419 -0
- package/components/catalyst-ui/hooks/use-counter.tsx +13 -0
- package/components/catalyst-ui/hooks/use-event-listener.tsx +23 -0
- package/components/catalyst-ui/hooks/use-export-markdown.tsx +47 -0
- package/components/catalyst-ui/hooks/use-focus.tsx +17 -0
- package/components/catalyst-ui/hooks/use-interval.tsx +23 -0
- package/components/catalyst-ui/hooks/use-is-client.tsx +16 -0
- package/components/catalyst-ui/hooks/use-media-query.tsx +19 -0
- package/components/catalyst-ui/hooks/use-mobile.tsx +19 -0
- package/components/catalyst-ui/hooks/use-resize-observer.tsx +81 -0
- package/components/catalyst-ui/hooks/use-timeout.tsx +21 -0
- package/components/catalyst-ui/hooks/use-timer.tsx +209 -0
- package/components/catalyst-ui/hooks/use-toggle.tsx +12 -0
- package/components/catalyst-ui/media/image.tsx +13 -0
- package/components/catalyst-ui/overlays/dual-sidebar.tsx +4142 -0
- package/components/catalyst-ui/overlays/sidebar-original.tsx +726 -0
- package/components/catalyst-ui/primitives/accordion.tsx +250 -0
- package/components/catalyst-ui/primitives/alert-dialog.tsx +126 -0
- package/components/catalyst-ui/primitives/aspect-ratio.tsx +9 -0
- package/components/catalyst-ui/primitives/avatar.tsx +296 -0
- package/components/catalyst-ui/primitives/badge.tsx +57 -0
- package/components/catalyst-ui/primitives/breadcrumb.tsx +101 -0
- package/components/catalyst-ui/primitives/button.tsx +265 -0
- package/components/catalyst-ui/primitives/calendar-v4.tsx +208 -0
- package/components/catalyst-ui/primitives/calendar.tsx +295 -0
- package/components/catalyst-ui/primitives/card.tsx +618 -0
- package/components/catalyst-ui/primitives/carousel.tsx +238 -0
- package/components/catalyst-ui/primitives/chart.tsx +347 -0
- package/components/catalyst-ui/primitives/checkbox.tsx +225 -0
- package/components/catalyst-ui/primitives/collapsible.tsx +212 -0
- package/components/catalyst-ui/primitives/command.tsx +393 -0
- package/components/catalyst-ui/primitives/context-menu.tsx +236 -0
- package/components/catalyst-ui/primitives/dialog.tsx +471 -0
- package/components/catalyst-ui/primitives/drawer.tsx +761 -0
- package/components/catalyst-ui/primitives/dropdown-menu.tsx +290 -0
- package/components/catalyst-ui/primitives/empty.tsx +104 -0
- package/components/catalyst-ui/primitives/field.tsx +244 -0
- package/components/catalyst-ui/primitives/hover-card.tsx +124 -0
- package/components/catalyst-ui/primitives/input-otp.tsx +76 -0
- package/components/catalyst-ui/primitives/input.tsx +64 -0
- package/components/catalyst-ui/primitives/item.tsx +196 -0
- package/components/catalyst-ui/primitives/kbd.tsx +75 -0
- package/components/catalyst-ui/primitives/label.tsx +24 -0
- package/components/catalyst-ui/primitives/navigation-menu.tsx +150 -0
- package/components/catalyst-ui/primitives/pagination.tsx +198 -0
- package/components/catalyst-ui/primitives/popover.tsx +232 -0
- package/components/catalyst-ui/primitives/progress.tsx +34 -0
- package/components/catalyst-ui/primitives/radio-group.tsx +43 -0
- package/components/catalyst-ui/primitives/resizable.tsx +56 -0
- package/components/catalyst-ui/primitives/select.tsx +155 -0
- package/components/catalyst-ui/primitives/separator.tsx +74 -0
- package/components/catalyst-ui/primitives/sheet.tsx +126 -0
- package/components/catalyst-ui/primitives/skeleton.tsx +15 -0
- package/components/catalyst-ui/primitives/slider.tsx +27 -0
- package/components/catalyst-ui/primitives/switch.tsx +187 -0
- package/components/catalyst-ui/primitives/tabs.tsx +335 -0
- package/components/catalyst-ui/primitives/textarea.tsx +24 -0
- package/components/catalyst-ui/primitives/toggle-group.tsx +55 -0
- package/components/catalyst-ui/primitives/toggle.tsx +42 -0
- package/components/catalyst-ui/primitives/tooltip.tsx +116 -0
- package/components/catalyst-ui/utils/basic-auth.tsx +40 -0
- package/components/catalyst-ui/utils/context-storage.tsx +19 -0
- package/components/catalyst-ui/utils/cors-middleware.tsx +71 -0
- package/components/catalyst-ui/utils/deferred-content.tsx +595 -0
- package/components/catalyst-ui/utils/honeypot-middleware.tsx +38 -0
- package/components/catalyst-ui/utils/incId.tsx +75 -0
- package/components/catalyst-ui/utils/jwk-auth.tsx +36 -0
- package/components/catalyst-ui/utils/request-id.tsx +14 -0
- package/components/catalyst-ui/utils/secure-headers.tsx +37 -0
- package/components/catalyst-ui/utils/server-timing.tsx +23 -0
- package/components/catalyst-ui/utils/utils.ts +43 -0
- package/components/catalyst-ui/utils/with-cookie.tsx +43 -0
- package/components/catalyst-ui/x/accordian-x.tsx +428 -0
- package/components/catalyst-ui/x/alert-x.tsx +413 -0
- package/components/catalyst-ui/x/animated-text-x.tsx +2242 -0
- package/components/catalyst-ui/x/avatar-x.tsx +515 -0
- package/components/catalyst-ui/x/badge-x.tsx +670 -0
- package/components/catalyst-ui/x/button-X.tsx +2857 -0
- package/components/catalyst-ui/x/button-group-x.tsx +847 -0
- package/components/catalyst-ui/x/calendar-x.tsx +1910 -0
- package/components/catalyst-ui/x/card-x.tsx +2597 -0
- package/components/catalyst-ui/x/checkbox-x.tsx +656 -0
- package/components/catalyst-ui/x/collapsible-x.tsx +1360 -0
- package/components/catalyst-ui/x/combobox-x.tsx +911 -0
- package/components/catalyst-ui/x/data-table-x.tsx +1753 -0
- package/components/catalyst-ui/x/date-picker-x.tsx +648 -0
- package/components/catalyst-ui/x/dialog-x.tsx +659 -0
- package/components/catalyst-ui/x/dropdown-menu-x.tsx +612 -0
- package/components/catalyst-ui/x/hover-card-x.tsx +375 -0
- package/components/catalyst-ui/x/icon-x.tsx +840 -0
- package/components/catalyst-ui/x/input-mask-x.tsx +981 -0
- package/components/catalyst-ui/x/input-otp-x.tsx +659 -0
- package/components/catalyst-ui/x/loader-x.tsx +1757 -0
- package/components/catalyst-ui/x/pagination-x.tsx +622 -0
- package/components/catalyst-ui/x/popover-x.tsx +744 -0
- package/components/catalyst-ui/x/radio-group-x.tsx +499 -0
- package/components/catalyst-ui/x/select-x.tsx +1127 -0
- package/components/catalyst-ui/x/sheet-x.tsx +668 -0
- package/components/catalyst-ui/x/switch-x.tsx +681 -0
- package/components/catalyst-ui/x/table-x.tsx +574 -0
- package/components/catalyst-ui/x/tabs-x.tsx +839 -0
- package/components/catalyst-ui/x/textarea-x.tsx +1263 -0
- package/components/catalyst-ui/x/tooltip-x.tsx +396 -0
- package/components/catalyst-ui/x/tracker-x.tsx +560 -0
- package/data/bg-data.tsx +901 -0
- package/data/buttons-data.tsx +2327 -0
- package/data/charts-data.tsx +102 -0
- package/data/chat-data.tsx +83 -0
- package/data/code-data.tsx +1040 -0
- package/data/comboboxes-data.tsx +1843 -0
- package/data/command-data.tsx +1381 -0
- package/data/core-data.tsx +15953 -0
- package/data/crm-data.tsx +47 -0
- package/data/data.tsx +159 -0
- package/data/date-and-time-data.tsx +554 -0
- package/data/dependencies.tsx +7 -0
- package/data/ecommerce-data.tsx +1387 -0
- package/data/forms-data.tsx +7890 -0
- package/data/hooks-data.tsx +5487 -0
- package/data/index.ts +34 -0
- package/data/inputs-data.tsx +557 -0
- package/data/interactive-data.tsx +5394 -0
- package/data/lofi-data.tsx +18295 -0
- package/data/marketing-data.tsx +2546 -0
- package/data/media-data.tsx +1510 -0
- package/data/motion-data.tsx +5801 -0
- package/data/overlay-data.tsx +4136 -0
- package/data/pdf-data.tsx +124 -0
- package/data/pos-data.tsx +213 -0
- package/data/postcss.config.js +6 -0
- package/data/primitive-data.tsx +5170 -0
- package/data/prompt-data.tsx +1226 -0
- package/data/requiredLibs.ts +4 -0
- package/data/sandbox-data.tsx +1 -0
- package/data/sidebars-data.tsx +5421 -0
- package/data/stacks-data.tsx +32 -0
- package/data/table-data.tsx +706 -0
- package/data/tailwind.config.js +3830 -0
- package/data/tailwind.config.ngin.js +3830 -0
- package/data/tailwind.css +431 -0
- package/data/tools-data.tsx +6910 -0
- package/data/typography-data.tsx +2050 -0
- package/data/utils-data.tsx +6500 -0
- package/data/x-data.tsx +1171 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30245 -0
- package/dist/index.js.map +362 -0
- package/package.json +50 -0
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useId } from 'react'
|
|
4
|
+
import { withMask } from 'use-mask-input'
|
|
5
|
+
import { cva } from 'class-variance-authority'
|
|
6
|
+
import { CreditCard } from 'lucide-react'
|
|
7
|
+
import { cn } from '~/components/catalyst-ui'
|
|
8
|
+
import { Input, useValidateCreditCard } from '~/components/catalyst-ui'
|
|
9
|
+
import { Field, FieldLabel, FieldDescription, FieldError } from '~/components/catalyst-ui'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ InputMaskX Component ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
13
|
+
* ★ ━━━━ ☆ ━━━━ ━━━━ ☆ ━━━━ ★
|
|
14
|
+
* InputMaskX: A collection of input components with various masking patterns for structured data entry
|
|
15
|
+
*
|
|
16
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Basic Input Mask ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
17
|
+
* ```jsx
|
|
18
|
+
* // Default input
|
|
19
|
+
* <InputMaskX label="Default Input" placeholder="Enter text" />
|
|
20
|
+
*
|
|
21
|
+
* // Custom mask pattern
|
|
22
|
+
* <InputMaskX
|
|
23
|
+
* inputMask="WithMask"
|
|
24
|
+
* label="License Plate"
|
|
25
|
+
* mask="AA99 AAA"
|
|
26
|
+
* placeholder="AB12 CDE"
|
|
27
|
+
* />
|
|
28
|
+
*
|
|
29
|
+
* // Time input with mask
|
|
30
|
+
* <InputMaskX
|
|
31
|
+
* inputMask="Time"
|
|
32
|
+
* label="Time"
|
|
33
|
+
* placeholder="HH:MM:SS"
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Credit Card Inputs ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
38
|
+
* ```jsx
|
|
39
|
+
* // Card number with auto-detection
|
|
40
|
+
* <InputMaskX
|
|
41
|
+
* inputMask="CardNumber"
|
|
42
|
+
* label="Credit Card Number"
|
|
43
|
+
* placeholder="1234 5678 9012 3456"
|
|
44
|
+
* />
|
|
45
|
+
*
|
|
46
|
+
* // Expiry date
|
|
47
|
+
* <InputMaskX
|
|
48
|
+
* inputMask="ExpiryDate"
|
|
49
|
+
* label="Expiration Date"
|
|
50
|
+
* placeholder="MM/YY"
|
|
51
|
+
* />
|
|
52
|
+
*
|
|
53
|
+
* // CVC code
|
|
54
|
+
* <InputMaskX
|
|
55
|
+
* inputMask="CVCCode"
|
|
56
|
+
* label="CVC"
|
|
57
|
+
* placeholder="123"
|
|
58
|
+
* />
|
|
59
|
+
*
|
|
60
|
+
* // Complete card form
|
|
61
|
+
* <InputMaskX
|
|
62
|
+
* inputMask="CardDetails"
|
|
63
|
+
* label="Card Information"
|
|
64
|
+
* />
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Custom Mask Patterns ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
68
|
+
* ```jsx
|
|
69
|
+
* // Phone number
|
|
70
|
+
* <InputMaskX
|
|
71
|
+
* inputMask="WithMask"
|
|
72
|
+
* label="Phone Number"
|
|
73
|
+
* mask="(999) 999-9999"
|
|
74
|
+
* placeholder="(123) 456-7890"
|
|
75
|
+
* />
|
|
76
|
+
*
|
|
77
|
+
* // Social Security Number
|
|
78
|
+
* <InputMaskX
|
|
79
|
+
* inputMask="WithMask"
|
|
80
|
+
* label="SSN"
|
|
81
|
+
* mask="999-99-9999"
|
|
82
|
+
* placeholder="123-45-6789"
|
|
83
|
+
* />
|
|
84
|
+
*
|
|
85
|
+
* // Postal/ZIP Code
|
|
86
|
+
* <InputMaskX
|
|
87
|
+
* inputMask="WithMask"
|
|
88
|
+
* label="ZIP Code"
|
|
89
|
+
* mask="99999-9999"
|
|
90
|
+
* placeholder="12345-6789"
|
|
91
|
+
* />
|
|
92
|
+
*
|
|
93
|
+
* // Date
|
|
94
|
+
* <InputMaskX
|
|
95
|
+
* inputMask="WithMask"
|
|
96
|
+
* label="Date"
|
|
97
|
+
* mask="99/99/9999"
|
|
98
|
+
* placeholder="MM/DD/YYYY"
|
|
99
|
+
* />
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Custom Styling Variations ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
103
|
+
* ```jsx
|
|
104
|
+
* // Filled variant
|
|
105
|
+
* <InputMaskX
|
|
106
|
+
* inputMask="CardNumber"
|
|
107
|
+
* variant="filled"
|
|
108
|
+
* label="Card Number"
|
|
109
|
+
* />
|
|
110
|
+
*
|
|
111
|
+
* // Small size
|
|
112
|
+
* <InputMaskX
|
|
113
|
+
* inputMask="CVCCode"
|
|
114
|
+
* size="sm"
|
|
115
|
+
* label="CVC"
|
|
116
|
+
* />
|
|
117
|
+
*
|
|
118
|
+
* // Large size
|
|
119
|
+
* <InputMaskX
|
|
120
|
+
* inputMask="ExpiryDate"
|
|
121
|
+
* size="lg"
|
|
122
|
+
* label="Expiry"
|
|
123
|
+
* />
|
|
124
|
+
*
|
|
125
|
+
* // Error state
|
|
126
|
+
* <InputMaskX
|
|
127
|
+
* inputMask="CardNumber"
|
|
128
|
+
* state="error"
|
|
129
|
+
* label="Invalid Card"
|
|
130
|
+
* error="Please enter a valid card number"
|
|
131
|
+
* />
|
|
132
|
+
*
|
|
133
|
+
* // Success state
|
|
134
|
+
* <InputMaskX
|
|
135
|
+
* inputMask="CardNumber"
|
|
136
|
+
* state="success"
|
|
137
|
+
* label="Valid Card"
|
|
138
|
+
* />
|
|
139
|
+
*
|
|
140
|
+
* // Rounded variants
|
|
141
|
+
* <InputMaskX
|
|
142
|
+
* inputMask="WithMask"
|
|
143
|
+
* rounded="lg"
|
|
144
|
+
* label="Rounded Input"
|
|
145
|
+
* mask="AAA-999"
|
|
146
|
+
* />
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ With Icons and Visuals ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
150
|
+
* ```jsx
|
|
151
|
+
* // Card number with icon
|
|
152
|
+
* <InputMaskX
|
|
153
|
+
* inputMask="CardNumber"
|
|
154
|
+
* label="Card Number"
|
|
155
|
+
* // Shows card type icon (Visa, Mastercard, etc.) automatically
|
|
156
|
+
* />
|
|
157
|
+
*
|
|
158
|
+
* // Custom icon placement
|
|
159
|
+
* <div className="relative">
|
|
160
|
+
* <InputMaskX
|
|
161
|
+
* inputMask="WithMask"
|
|
162
|
+
* label="Phone"
|
|
163
|
+
* mask="(999) 999-9999"
|
|
164
|
+
* className="pl-10" // Space for icon
|
|
165
|
+
* />
|
|
166
|
+
* <Phone className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2" />
|
|
167
|
+
* </div>
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Complete Payment Form ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
171
|
+
* ```jsx
|
|
172
|
+
* function PaymentForm() {
|
|
173
|
+
* const [cardNumber, setCardNumber] = useState('')
|
|
174
|
+
* const [expiry, setExpiry] = useState('')
|
|
175
|
+
* const [cvc, setCVC] = useState('')
|
|
176
|
+
*
|
|
177
|
+
* return (
|
|
178
|
+
* <div className="space-y-4 max-w-md">
|
|
179
|
+
* <InputMaskX
|
|
180
|
+
* inputMask="CardNumber"
|
|
181
|
+
* label="Card Number"
|
|
182
|
+
* value={cardNumber}
|
|
183
|
+
* onChange={(e) => setCardNumber(e.target.value)}
|
|
184
|
+
* />
|
|
185
|
+
*
|
|
186
|
+
* <div className="grid grid-cols-2 gap-4">
|
|
187
|
+
* <InputMaskX
|
|
188
|
+
* inputMask="ExpiryDate"
|
|
189
|
+
* label="Expiry Date"
|
|
190
|
+
* value={expiry}
|
|
191
|
+
* onChange={(e) => setExpiry(e.target.value)}
|
|
192
|
+
* />
|
|
193
|
+
* <InputMaskX
|
|
194
|
+
* inputMask="CVCCode"
|
|
195
|
+
* label="CVC"
|
|
196
|
+
* value={cvc}
|
|
197
|
+
* onChange={(e) => setCVC(e.target.value)}
|
|
198
|
+
* />
|
|
199
|
+
* </div>
|
|
200
|
+
*
|
|
201
|
+
* <button className="w-full bg-blue-600 text-white py-2 rounded">
|
|
202
|
+
* Pay Now
|
|
203
|
+
* </button>
|
|
204
|
+
* </div>
|
|
205
|
+
* )
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Available Input Mask Types ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
210
|
+
* 1. **default** - Regular input without mask
|
|
211
|
+
* 2. **WithMask** - Custom mask pattern
|
|
212
|
+
* 3. **Time** - Time input (HH:MM:SS format)
|
|
213
|
+
* 4. **CardNumber** - Credit card number with validation
|
|
214
|
+
* 5. **ExpiryDate** - Card expiry date (MM/YY)
|
|
215
|
+
* 6. **CVCCode** - Card security code
|
|
216
|
+
* 7. **CardDetails** - Combined card form (number + expiry + CVC)
|
|
217
|
+
*
|
|
218
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Mask Pattern Syntax ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
219
|
+
* ```jsx
|
|
220
|
+
* // Common mask patterns
|
|
221
|
+
* <InputMaskX
|
|
222
|
+
* inputMask="WithMask"
|
|
223
|
+
* mask="999-999-9999" // Numbers only
|
|
224
|
+
* />
|
|
225
|
+
*
|
|
226
|
+
* <InputMaskX
|
|
227
|
+
* inputMask="WithMask"
|
|
228
|
+
* mask="AAA-999" // Letters then numbers
|
|
229
|
+
* />
|
|
230
|
+
*
|
|
231
|
+
* <InputMaskX
|
|
232
|
+
* inputMask="WithMask"
|
|
233
|
+
* mask="**** **** **** 9999" // Fixed characters with numbers
|
|
234
|
+
* />
|
|
235
|
+
*
|
|
236
|
+
* // Date/time patterns
|
|
237
|
+
* <InputMaskX
|
|
238
|
+
* inputMask="Time"
|
|
239
|
+
* // Automatically uses HH:MM:SS format
|
|
240
|
+
* />
|
|
241
|
+
*
|
|
242
|
+
* // Custom datetime
|
|
243
|
+
* <InputMaskX
|
|
244
|
+
* inputMask="WithMask"
|
|
245
|
+
* mask="datetime"
|
|
246
|
+
* // Additional props: inputFormat, outputFormat
|
|
247
|
+
* />
|
|
248
|
+
* ```
|
|
249
|
+
*
|
|
250
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Props Reference ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
251
|
+
* - **Common Props**:
|
|
252
|
+
* - `inputMask`: string - Type of mask to apply
|
|
253
|
+
* - `label`: string - Input field label
|
|
254
|
+
* - `description`: string - Helper text
|
|
255
|
+
* - `error`: string - Error message
|
|
256
|
+
* - `placeholder`: string - Placeholder text
|
|
257
|
+
* - `value`: string - Controlled value
|
|
258
|
+
* - `onChange`: (e: React.ChangeEvent) => void - Change handler
|
|
259
|
+
*
|
|
260
|
+
* - **WithMask Specific Props**:
|
|
261
|
+
* - `mask`: string - Mask pattern (e.g., "999-999-9999")
|
|
262
|
+
* - `showMaskOnHover`: boolean - Show mask on hover
|
|
263
|
+
*
|
|
264
|
+
* - **Time Specific Props**:
|
|
265
|
+
* - `inputFormat`: string - Input format (default: "HH:MM:ss")
|
|
266
|
+
* - `outputFormat`: string - Output format (default: "HH:MM:ss")
|
|
267
|
+
*
|
|
268
|
+
* - **Styling Props** (via inputMaskVariants):
|
|
269
|
+
* - `variant`: 'default' | 'filled' | 'ghost'
|
|
270
|
+
* - `size`: 'sm' | 'default' | 'lg'
|
|
271
|
+
* - `rounded`: 'sm' | 'default' | 'lg' | 'full' | 'none'
|
|
272
|
+
* - `state`: 'default' | 'error' | 'success'
|
|
273
|
+
* - `className`: string - Additional CSS classes
|
|
274
|
+
*
|
|
275
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Validation Features ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
276
|
+
* ```jsx
|
|
277
|
+
* // Card number validation
|
|
278
|
+
* <InputMaskX
|
|
279
|
+
* inputMask="CardNumber"
|
|
280
|
+
* label="Card Number"
|
|
281
|
+
* // Automatically validates: Luhn algorithm, card type detection
|
|
282
|
+
* />
|
|
283
|
+
*
|
|
284
|
+
* // Expiry date validation
|
|
285
|
+
* <InputMaskX
|
|
286
|
+
* inputMask="ExpiryDate"
|
|
287
|
+
* label="Expiry"
|
|
288
|
+
* // Validates: Not expired, valid month/year format
|
|
289
|
+
* />
|
|
290
|
+
*
|
|
291
|
+
* // Custom validation
|
|
292
|
+
* function CustomValidationExample() {
|
|
293
|
+
* const [value, setValue] = useState('')
|
|
294
|
+
* const [error, setError] = useState('')
|
|
295
|
+
*
|
|
296
|
+
* const validate = (input: string) => {
|
|
297
|
+
* if (!input.match(/^AB\d{2} CDE$/)) {
|
|
298
|
+
* setError('Invalid format')
|
|
299
|
+
* } else {
|
|
300
|
+
* setError('')
|
|
301
|
+
* }
|
|
302
|
+
* }
|
|
303
|
+
*
|
|
304
|
+
* return (
|
|
305
|
+
* <InputMaskX
|
|
306
|
+
* inputMask="WithMask"
|
|
307
|
+
* label="Custom Pattern"
|
|
308
|
+
* mask="AB99 CDE"
|
|
309
|
+
* value={value}
|
|
310
|
+
* onChange={(e) => {
|
|
311
|
+
* setValue(e.target.value)
|
|
312
|
+
* validate(e.target.value)
|
|
313
|
+
* }}
|
|
314
|
+
* error={error}
|
|
315
|
+
* />
|
|
316
|
+
* )
|
|
317
|
+
* }
|
|
318
|
+
* ```
|
|
319
|
+
*
|
|
320
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Accessibility Features ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
321
|
+
* - **Auto IDs**: Automatic ID generation for label association
|
|
322
|
+
* - **Screen Reader**: All labels and descriptions accessible
|
|
323
|
+
* - **Keyboard Navigation**: Full keyboard support
|
|
324
|
+
* - **Focus Management**: Clear focus indicators
|
|
325
|
+
* - **ARIA Attributes**: Proper role and state attributes
|
|
326
|
+
*
|
|
327
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Usage Examples ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
328
|
+
* ```jsx
|
|
329
|
+
* // Registration form
|
|
330
|
+
* function RegistrationForm() {
|
|
331
|
+
* return (
|
|
332
|
+
* <div className="space-y-4">
|
|
333
|
+
* <InputMaskX label="Full Name" placeholder="John Doe" />
|
|
334
|
+
* <InputMaskX
|
|
335
|
+
* inputMask="WithMask"
|
|
336
|
+
* label="Phone"
|
|
337
|
+
* mask="(999) 999-9999"
|
|
338
|
+
* placeholder="(123) 456-7890"
|
|
339
|
+
* />
|
|
340
|
+
* <InputMaskX
|
|
341
|
+
* inputMask="WithMask"
|
|
342
|
+
* label="SSN"
|
|
343
|
+
* mask="999-99-9999"
|
|
344
|
+
* placeholder="123-45-6789"
|
|
345
|
+
* />
|
|
346
|
+
* </div>
|
|
347
|
+
* )
|
|
348
|
+
* }
|
|
349
|
+
*
|
|
350
|
+
* // Booking system
|
|
351
|
+
* function BookingForm() {
|
|
352
|
+
* return (
|
|
353
|
+
* <div className="space-y-4">
|
|
354
|
+
* <InputMaskX
|
|
355
|
+
* inputMask="WithMask"
|
|
356
|
+
* label="Booking Reference"
|
|
357
|
+
* mask="AAA-9999"
|
|
358
|
+
* placeholder="ABC-1234"
|
|
359
|
+
* />
|
|
360
|
+
* <InputMaskX
|
|
361
|
+
* inputMask="Time"
|
|
362
|
+
* label="Check-in Time"
|
|
363
|
+
* placeholder="14:00:00"
|
|
364
|
+
* />
|
|
365
|
+
* <InputMaskX
|
|
366
|
+
* inputMask="CardDetails"
|
|
367
|
+
* label="Payment Information"
|
|
368
|
+
* />
|
|
369
|
+
* </div>
|
|
370
|
+
* )
|
|
371
|
+
* }
|
|
372
|
+
*
|
|
373
|
+
* // Settings form
|
|
374
|
+
* function SettingsForm() {
|
|
375
|
+
* return (
|
|
376
|
+
* <div className="space-y-4">
|
|
377
|
+
* <InputMaskX
|
|
378
|
+
* inputMask="WithMask"
|
|
379
|
+
* label="API Key"
|
|
380
|
+
* mask="****-****-****-****"
|
|
381
|
+
* placeholder="••••-••••-••••-••••"
|
|
382
|
+
* />
|
|
383
|
+
* <InputMaskX
|
|
384
|
+
* inputMask="Time"
|
|
385
|
+
* label="Schedule Time"
|
|
386
|
+
* placeholder="09:00:00"
|
|
387
|
+
* />
|
|
388
|
+
* <InputMaskX
|
|
389
|
+
* variant="filled"
|
|
390
|
+
* size="sm"
|
|
391
|
+
* label="Short Code"
|
|
392
|
+
* placeholder="ABC123"
|
|
393
|
+
* />
|
|
394
|
+
* </div>
|
|
395
|
+
* )
|
|
396
|
+
* }
|
|
397
|
+
* ```
|
|
398
|
+
*
|
|
399
|
+
* ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Integration with Form Libraries ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
|
|
400
|
+
* ```jsx
|
|
401
|
+
* // With React Hook Form
|
|
402
|
+
* import { useForm } from 'react-hook-form'
|
|
403
|
+
*
|
|
404
|
+
* function HookFormExample() {
|
|
405
|
+
* const { register, handleSubmit } = useForm()
|
|
406
|
+
*
|
|
407
|
+
* return (
|
|
408
|
+
* <form onSubmit={handleSubmit(data => console.log(data))}>
|
|
409
|
+
* <InputMaskX
|
|
410
|
+
* inputMask="CardNumber"
|
|
411
|
+
* label="Card Number"
|
|
412
|
+
* {...register('cardNumber', { required: true })}
|
|
413
|
+
* />
|
|
414
|
+
* <InputMaskX
|
|
415
|
+
* inputMask="ExpiryDate"
|
|
416
|
+
* label="Expiry"
|
|
417
|
+
* {...register('expiry', { required: true })}
|
|
418
|
+
* />
|
|
419
|
+
* <button type="submit">Submit</button>
|
|
420
|
+
* </form>
|
|
421
|
+
* )
|
|
422
|
+
* }
|
|
423
|
+
*
|
|
424
|
+
* // With Formik
|
|
425
|
+
* import { useField } from 'formik'
|
|
426
|
+
*
|
|
427
|
+
* function FormikField({ label, ...props }) {
|
|
428
|
+
* const [field, meta] = useField(props)
|
|
429
|
+
*
|
|
430
|
+
* return (
|
|
431
|
+
* <InputMaskX
|
|
432
|
+
* label={label}
|
|
433
|
+
* {...field}
|
|
434
|
+
* error={meta.touched && meta.error ? meta.error : ''}
|
|
435
|
+
* {...props}
|
|
436
|
+
* />
|
|
437
|
+
* )
|
|
438
|
+
* }
|
|
439
|
+
* ```
|
|
440
|
+
*
|
|
441
|
+
*/
|
|
442
|
+
|
|
443
|
+
// #region CVA VARIANTS
|
|
444
|
+
export const inputMaskVariants = cva(
|
|
445
|
+
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
446
|
+
{
|
|
447
|
+
variants: {
|
|
448
|
+
variant: {
|
|
449
|
+
default: '',
|
|
450
|
+
filled: 'bg-muted border-transparent shadow-none',
|
|
451
|
+
ghost: 'border-transparent shadow-none',
|
|
452
|
+
},
|
|
453
|
+
size: {
|
|
454
|
+
sm: 'h-8 text-xs',
|
|
455
|
+
default: 'h-10',
|
|
456
|
+
lg: 'h-12 text-base',
|
|
457
|
+
},
|
|
458
|
+
rounded: {
|
|
459
|
+
default: 'rounded-md',
|
|
460
|
+
sm: 'rounded-sm',
|
|
461
|
+
lg: 'rounded-lg',
|
|
462
|
+
full: 'rounded-full',
|
|
463
|
+
none: 'rounded-none',
|
|
464
|
+
},
|
|
465
|
+
state: {
|
|
466
|
+
default: '',
|
|
467
|
+
error: 'border-destructive focus-visible:ring-destructive',
|
|
468
|
+
success: 'border-green-500 focus-visible:ring-green-500',
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
defaultVariants: {
|
|
472
|
+
variant: 'default',
|
|
473
|
+
size: 'default',
|
|
474
|
+
rounded: 'default',
|
|
475
|
+
state: 'default',
|
|
476
|
+
},
|
|
477
|
+
}
|
|
478
|
+
)
|
|
479
|
+
// #endregion
|
|
480
|
+
|
|
481
|
+
// #region PARENT FUNCTION
|
|
482
|
+
export function InputMaskX({ inputMask = 'default', ...props }: any) {
|
|
483
|
+
switch (inputMask) {
|
|
484
|
+
case 'WithMask':
|
|
485
|
+
return <WithMask {...props} />
|
|
486
|
+
case 'Time':
|
|
487
|
+
return <Time {...props} />
|
|
488
|
+
case 'CardNumber':
|
|
489
|
+
return <CardNumber {...props} />
|
|
490
|
+
case 'ExpiryDate':
|
|
491
|
+
return <ExpiryDate {...props} />
|
|
492
|
+
case 'CVCCode':
|
|
493
|
+
return <CVCCode {...props} />
|
|
494
|
+
case 'CardDetails':
|
|
495
|
+
return <CardDetails {...props} />
|
|
496
|
+
default:
|
|
497
|
+
return <DefaultInputMask {...props} />
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// #endregion
|
|
501
|
+
|
|
502
|
+
// #region SUPPORTING FUNCTIONS
|
|
503
|
+
const DefaultInputMask = ({ label, description, error, variant, size, rounded, state, className, ...props }: any) => {
|
|
504
|
+
const id = useId()
|
|
505
|
+
return (
|
|
506
|
+
<Field className='w-full max-w-xs space-y-2'>
|
|
507
|
+
{label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
|
|
508
|
+
<Input
|
|
509
|
+
id={id}
|
|
510
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), className)}
|
|
511
|
+
{...props}
|
|
512
|
+
/>
|
|
513
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
514
|
+
{error && <FieldError>{error}</FieldError>}
|
|
515
|
+
</Field>
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const WithMask = ({ label = 'Input with mask', description, error, variant, size, rounded, state, className, mask = 'AA99 AAA', placeholder = 'AB12 CDE', ...props }: any) => {
|
|
520
|
+
const id = useId()
|
|
521
|
+
return (
|
|
522
|
+
<Field className='w-full max-w-xs space-y-2'>
|
|
523
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
524
|
+
<Input
|
|
525
|
+
id={id}
|
|
526
|
+
type='text'
|
|
527
|
+
placeholder={placeholder}
|
|
528
|
+
ref={withMask(mask, {
|
|
529
|
+
placeholder: '_',
|
|
530
|
+
showMaskOnHover: false
|
|
531
|
+
})}
|
|
532
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), className)}
|
|
533
|
+
{...props}
|
|
534
|
+
/>
|
|
535
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
536
|
+
{error && <FieldError>{error}</FieldError>}
|
|
537
|
+
<p className='text-muted-foreground text-xs'>
|
|
538
|
+
Built with{' '}
|
|
539
|
+
<a
|
|
540
|
+
className='hover:text-foreground underline'
|
|
541
|
+
href='https://github.com/eduardoborges/use-mask-input'
|
|
542
|
+
target='_blank'
|
|
543
|
+
rel='noopener noreferrer'
|
|
544
|
+
>
|
|
545
|
+
use-mask-input
|
|
546
|
+
</a>
|
|
547
|
+
</p>
|
|
548
|
+
</Field>
|
|
549
|
+
)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const Time = ({ label = 'Input time', description, error, variant, size, rounded, state, className, placeholder = '00:00:00', ...props }: any) => {
|
|
553
|
+
const id = useId()
|
|
554
|
+
return (
|
|
555
|
+
<Field className='w-full max-w-xs space-y-2'>
|
|
556
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
557
|
+
<Input
|
|
558
|
+
id={id}
|
|
559
|
+
type='text'
|
|
560
|
+
placeholder={placeholder}
|
|
561
|
+
ref={withMask('datetime', {
|
|
562
|
+
placeholder: '_',
|
|
563
|
+
inputFormat: 'HH:MM:ss',
|
|
564
|
+
outputFormat: 'HH:MM:ss',
|
|
565
|
+
showMaskOnHover: false
|
|
566
|
+
})}
|
|
567
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), className)}
|
|
568
|
+
{...props}
|
|
569
|
+
/>
|
|
570
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
571
|
+
{error && <FieldError>{error}</FieldError>}
|
|
572
|
+
<p className='text-muted-foreground text-xs'>
|
|
573
|
+
Built with{' '}
|
|
574
|
+
<a
|
|
575
|
+
className='hover:text-foreground underline'
|
|
576
|
+
href='https://github.com/eduardoborges/use-mask-input'
|
|
577
|
+
target='_blank'
|
|
578
|
+
rel='noopener noreferrer'
|
|
579
|
+
>
|
|
580
|
+
use-mask-input
|
|
581
|
+
</a>
|
|
582
|
+
</p>
|
|
583
|
+
</Field>
|
|
584
|
+
)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const CardNumber = ({ label = 'Card number', description, error, variant, size, rounded, state, className, ...props }: any) => {
|
|
588
|
+
const id = useId()
|
|
589
|
+
const { getCardNumberProps, getCardIcon } = useValidateCreditCard()
|
|
590
|
+
const CardIcon = getCardIcon()
|
|
591
|
+
|
|
592
|
+
return (
|
|
593
|
+
<Field className='w-full max-w-xs space-y-2'>
|
|
594
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
595
|
+
<div className='relative'>
|
|
596
|
+
<Input
|
|
597
|
+
{...getCardNumberProps()}
|
|
598
|
+
id={id}
|
|
599
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), 'peer pr-11', className)}
|
|
600
|
+
{...props}
|
|
601
|
+
/>
|
|
602
|
+
<div className='text-muted-foreground pointer-events-none absolute inset-y-0 right-0 flex items-center justify-center pr-3 peer-disabled:opacity-50'>
|
|
603
|
+
<CardIcon className='size-6' />
|
|
604
|
+
<span className='sr-only'>Card Provider</span>
|
|
605
|
+
</div>
|
|
606
|
+
</div>
|
|
607
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
608
|
+
{error && <FieldError>{error}</FieldError>}
|
|
609
|
+
<p className='text-muted-foreground text-xs'>
|
|
610
|
+
Built with custom validation
|
|
611
|
+
</p>
|
|
612
|
+
</Field>
|
|
613
|
+
)
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const ExpiryDate = ({ label = 'Expiry date', description, error, variant, size, rounded, state, className, placeholder = 'MM/YY', ...props }: any) => {
|
|
617
|
+
const id = useId()
|
|
618
|
+
return (
|
|
619
|
+
<Field className='w-full max-w-xs space-y-2'>
|
|
620
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
621
|
+
<Input
|
|
622
|
+
id={id}
|
|
623
|
+
type='text'
|
|
624
|
+
placeholder={placeholder}
|
|
625
|
+
ref={withMask('99/99', {
|
|
626
|
+
placeholder: '_',
|
|
627
|
+
showMaskOnHover: false
|
|
628
|
+
})}
|
|
629
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), className)}
|
|
630
|
+
{...props}
|
|
631
|
+
/>
|
|
632
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
633
|
+
{error && <FieldError>{error}</FieldError>}
|
|
634
|
+
</Field>
|
|
635
|
+
)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const CVCCode = ({ label = 'CVC code', description, error, variant, size, rounded, state, className, placeholder = '123', ...props }: any) => {
|
|
639
|
+
const id = useId()
|
|
640
|
+
return (
|
|
641
|
+
<Field className='w-full max-w-xs space-y-2'>
|
|
642
|
+
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
|
643
|
+
<Input
|
|
644
|
+
id={id}
|
|
645
|
+
type='text'
|
|
646
|
+
placeholder={placeholder}
|
|
647
|
+
ref={withMask('999', {
|
|
648
|
+
placeholder: '_',
|
|
649
|
+
showMaskOnHover: false
|
|
650
|
+
})}
|
|
651
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), className)}
|
|
652
|
+
{...props}
|
|
653
|
+
/>
|
|
654
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
655
|
+
{error && <FieldError>{error}</FieldError>}
|
|
656
|
+
</Field>
|
|
657
|
+
)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const CardDetails = ({ label = 'Card details', description, error, variant, size, rounded, state, className, ...props }: any) => {
|
|
661
|
+
const id = useId()
|
|
662
|
+
const { getCardNumberProps, getCardIcon } = useValidateCreditCard()
|
|
663
|
+
const CardIcon = getCardIcon()
|
|
664
|
+
|
|
665
|
+
return (
|
|
666
|
+
<Field className='w-full max-w-xs space-y-2'>
|
|
667
|
+
<FieldLabel>{label}</FieldLabel>
|
|
668
|
+
<div>
|
|
669
|
+
<div className='relative focus-within:z-1'>
|
|
670
|
+
<Input
|
|
671
|
+
{...getCardNumberProps()}
|
|
672
|
+
id={`number-${id}`}
|
|
673
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), 'peer rounded-b-none pr-9 shadow-none', className)}
|
|
674
|
+
{...props}
|
|
675
|
+
/>
|
|
676
|
+
<div className='text-muted-foreground pointer-events-none absolute inset-y-0 right-0 flex items-center justify-center pr-3 peer-disabled:opacity-50'>
|
|
677
|
+
<CardIcon className='size-6' />
|
|
678
|
+
<span className='sr-only'>Card Provider</span>
|
|
679
|
+
</div>
|
|
680
|
+
</div>
|
|
681
|
+
<div className='-mt-px flex'>
|
|
682
|
+
<div className='min-w-0 flex-1 focus-within:z-1'>
|
|
683
|
+
<Input
|
|
684
|
+
type='text'
|
|
685
|
+
placeholder='MM/YY'
|
|
686
|
+
ref={withMask('99/99', {
|
|
687
|
+
placeholder: '_',
|
|
688
|
+
showMaskOnHover: false
|
|
689
|
+
})}
|
|
690
|
+
id={`expiry-${id}`}
|
|
691
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), 'rounded-t-none rounded-r-none shadow-none')}
|
|
692
|
+
/>
|
|
693
|
+
</div>
|
|
694
|
+
<div className='-ms-px min-w-0 flex-1 focus-within:z-1'>
|
|
695
|
+
<Input
|
|
696
|
+
type='text'
|
|
697
|
+
placeholder='123'
|
|
698
|
+
ref={withMask('999', {
|
|
699
|
+
placeholder: '_',
|
|
700
|
+
showMaskOnHover: false
|
|
701
|
+
})}
|
|
702
|
+
id={`cvc-${id}`}
|
|
703
|
+
className={cn(inputMaskVariants({ variant, size, rounded, state }), 'rounded-t-none rounded-l-none shadow-none')}
|
|
704
|
+
/>
|
|
705
|
+
</div>
|
|
706
|
+
</div>
|
|
707
|
+
</div>
|
|
708
|
+
{description && <FieldDescription>{description}</FieldDescription>}
|
|
709
|
+
{error && <FieldError>{error}</FieldError>}
|
|
710
|
+
<p className='text-muted-foreground text-xs'>
|
|
711
|
+
Built with custom validation
|
|
712
|
+
</p>
|
|
713
|
+
</Field>
|
|
714
|
+
)
|
|
715
|
+
}
|
|
716
|
+
// #endregion
|
|
717
|
+
|
|
718
|
+
// #region DEMO
|
|
719
|
+
export function InputMaskXDemo() {
|
|
720
|
+
return (
|
|
721
|
+
<div className='space-y-12 p-6 max-w-3xl mx-auto'>
|
|
722
|
+
<div>
|
|
723
|
+
<h3 className='text-lg font-semibold mb-4'>Default</h3>
|
|
724
|
+
<InputMaskX label='Default Input' placeholder='Enter text' />
|
|
725
|
+
</div>
|
|
726
|
+
|
|
727
|
+
<div>
|
|
728
|
+
<h3 className='text-lg font-semibold mb-4'>With Mask</h3>
|
|
729
|
+
<InputMaskX inputMask='WithMask' />
|
|
730
|
+
</div>
|
|
731
|
+
|
|
732
|
+
<div>
|
|
733
|
+
<h3 className='text-lg font-semibold mb-4'>Time Input</h3>
|
|
734
|
+
<InputMaskX inputMask='Time' />
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<div>
|
|
738
|
+
<h3 className='text-lg font-semibold mb-4'>Card Number</h3>
|
|
739
|
+
<InputMaskX inputMask='CardNumber' />
|
|
740
|
+
</div>
|
|
741
|
+
|
|
742
|
+
<div>
|
|
743
|
+
<h3 className='text-lg font-semibold mb-4'>Expiry Date</h3>
|
|
744
|
+
<InputMaskX inputMask='ExpiryDate' />
|
|
745
|
+
</div>
|
|
746
|
+
|
|
747
|
+
<div>
|
|
748
|
+
<h3 className='text-lg font-semibold mb-4'>CVC Code</h3>
|
|
749
|
+
<InputMaskX inputMask='CVCCode' />
|
|
750
|
+
</div>
|
|
751
|
+
|
|
752
|
+
<div>
|
|
753
|
+
<h3 className='text-lg font-semibold mb-4'>Card Details (Combined)</h3>
|
|
754
|
+
<InputMaskX inputMask='CardDetails' />
|
|
755
|
+
</div>
|
|
756
|
+
</div>
|
|
757
|
+
)
|
|
758
|
+
}
|
|
759
|
+
// #endregion
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
export function InputMaskX({ inputMask = 'default', children, ...props }: any) {
|
|
770
|
+
switch (inputMask) {
|
|
771
|
+
case 'Closable':
|
|
772
|
+
break
|
|
773
|
+
default:
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const InputWithMaskDemo = () => {
|
|
779
|
+
const id = useId()
|
|
780
|
+
|
|
781
|
+
return (
|
|
782
|
+
<div className='w-full max-w-xs space-y-2'>
|
|
783
|
+
<Label htmlFor={id}>Input with mask</Label>
|
|
784
|
+
<Input
|
|
785
|
+
id={id}
|
|
786
|
+
type='text'
|
|
787
|
+
placeholder='AB12 CDE'
|
|
788
|
+
ref={withMask('AA99 AAA', {
|
|
789
|
+
placeholder: '_',
|
|
790
|
+
showMaskOnHover: false
|
|
791
|
+
})}
|
|
792
|
+
/>
|
|
793
|
+
<p className='text-muted-foreground text-xs'>
|
|
794
|
+
Built with{' '}
|
|
795
|
+
<a
|
|
796
|
+
className='hover:text-foreground underline'
|
|
797
|
+
href='https://github.com/eduardoborges/use-mask-input'
|
|
798
|
+
target='_blank'
|
|
799
|
+
rel='noopener noreferrer'
|
|
800
|
+
>
|
|
801
|
+
use-mask-input
|
|
802
|
+
</a>
|
|
803
|
+
</p>
|
|
804
|
+
</div>
|
|
805
|
+
)
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const InputTimeDemo = () => {
|
|
809
|
+
const id = useId()
|
|
810
|
+
|
|
811
|
+
return (
|
|
812
|
+
<div className='w-full max-w-xs space-y-2'>
|
|
813
|
+
<Label htmlFor={id}>Input time</Label>
|
|
814
|
+
<Input
|
|
815
|
+
id={id}
|
|
816
|
+
type='text'
|
|
817
|
+
placeholder='00:00:00'
|
|
818
|
+
ref={withMask('datetime', {
|
|
819
|
+
placeholder: '_',
|
|
820
|
+
inputFormat: 'HH:MM:ss',
|
|
821
|
+
outputFormat: 'HH:MM:ss',
|
|
822
|
+
showMaskOnHover: false
|
|
823
|
+
})}
|
|
824
|
+
/>
|
|
825
|
+
<p className='text-muted-foreground text-xs'>
|
|
826
|
+
Built with{' '}
|
|
827
|
+
<a
|
|
828
|
+
className='hover:text-foreground underline'
|
|
829
|
+
href='https://github.com/eduardoborges/use-mask-input'
|
|
830
|
+
target='_blank'
|
|
831
|
+
rel='noopener noreferrer'
|
|
832
|
+
>
|
|
833
|
+
use-mask-input
|
|
834
|
+
</a>
|
|
835
|
+
</p>
|
|
836
|
+
</div>
|
|
837
|
+
)
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const InputCardNumberDemo = () => {
|
|
841
|
+
const id = useId()
|
|
842
|
+
const { meta, getCardNumberProps, getCardImageProps } = usePaymentInputs()
|
|
843
|
+
|
|
844
|
+
return (
|
|
845
|
+
<div className='w-full max-w-xs space-y-2'>
|
|
846
|
+
<Label htmlFor={id}>Card number</Label>
|
|
847
|
+
<div className='relative'>
|
|
848
|
+
<Input {...getCardNumberProps()} id={id} className='peer pr-11' />
|
|
849
|
+
<div className='text-muted-foreground pointer-events-none absolute inset-y-0 right-0 flex items-center justify-center pr-3 peer-disabled:opacity-50'>
|
|
850
|
+
{meta.cardType ? (
|
|
851
|
+
<svg
|
|
852
|
+
className='w-6 overflow-hidden'
|
|
853
|
+
{...getCardImageProps({
|
|
854
|
+
images: images as unknown as CardImages
|
|
855
|
+
})}
|
|
856
|
+
/>
|
|
857
|
+
) : (
|
|
858
|
+
<CreditCardIcon className='size-4' />
|
|
859
|
+
)}
|
|
860
|
+
<span className='sr-only'>Card Provider</span>
|
|
861
|
+
</div>
|
|
862
|
+
</div>
|
|
863
|
+
<p className='text-muted-foreground text-xs'>
|
|
864
|
+
Built with{' '}
|
|
865
|
+
<a
|
|
866
|
+
className='hover:text-foreground underline'
|
|
867
|
+
href='https://github.com/medipass/react-payment-inputs'
|
|
868
|
+
target='_blank'
|
|
869
|
+
rel='noopener noreferrer'
|
|
870
|
+
>
|
|
871
|
+
React Payment Inputs
|
|
872
|
+
</a>
|
|
873
|
+
</p>
|
|
874
|
+
</div>
|
|
875
|
+
)
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const InputExpiryDateDemo = () => {
|
|
879
|
+
const id = useId()
|
|
880
|
+
const { getExpiryDateProps } = usePaymentInputs()
|
|
881
|
+
|
|
882
|
+
return (
|
|
883
|
+
<div className='w-full max-w-xs space-y-2'>
|
|
884
|
+
<Label htmlFor={id}>Expiry date</Label>
|
|
885
|
+
<Input {...getExpiryDateProps()} id={id} />
|
|
886
|
+
<p className='text-muted-foreground text-xs'>
|
|
887
|
+
Built with{' '}
|
|
888
|
+
<a
|
|
889
|
+
className='hover:text-foreground underline'
|
|
890
|
+
href='https://github.com/medipass/react-payment-inputs'
|
|
891
|
+
target='_blank'
|
|
892
|
+
rel='noopener noreferrer'
|
|
893
|
+
>
|
|
894
|
+
React Payment Inputs
|
|
895
|
+
</a>
|
|
896
|
+
</p>
|
|
897
|
+
</div>
|
|
898
|
+
)
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const InputCVCCodeDemo = () => {
|
|
902
|
+
const id = useId()
|
|
903
|
+
const { getCVCProps } = usePaymentInputs()
|
|
904
|
+
|
|
905
|
+
return (
|
|
906
|
+
<div className='w-full max-w-xs space-y-2'>
|
|
907
|
+
<Label htmlFor={id}>CVC code</Label>
|
|
908
|
+
<Input {...getCVCProps()} id={id} />
|
|
909
|
+
<p className='text-muted-foreground text-xs'>
|
|
910
|
+
Built with{' '}
|
|
911
|
+
<a
|
|
912
|
+
className='hover:text-foreground underline'
|
|
913
|
+
href='https://github.com/medipass/react-payment-inputs'
|
|
914
|
+
target='_blank'
|
|
915
|
+
rel='noopener noreferrer'
|
|
916
|
+
>
|
|
917
|
+
React Payment Inputs
|
|
918
|
+
</a>
|
|
919
|
+
</p>
|
|
920
|
+
</div>
|
|
921
|
+
)
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const InputCardDetailsDemo = () => {
|
|
925
|
+
const id = useId()
|
|
926
|
+
const { meta, getCardNumberProps, getExpiryDateProps, getCVCProps, getCardImageProps } = usePaymentInputs()
|
|
927
|
+
|
|
928
|
+
return (
|
|
929
|
+
<div className='w-full max-w-xs space-y-2'>
|
|
930
|
+
<Label>Card details</Label>
|
|
931
|
+
<div>
|
|
932
|
+
<div className='relative focus-within:z-1'>
|
|
933
|
+
<Input {...getCardNumberProps()} id={`number-${id}`} className='peer rounded-b-none pr-9 shadow-none' />
|
|
934
|
+
<div className='text-muted-foreground pointer-events-none absolute inset-y-0 right-0 flex items-center justify-center pr-3 peer-disabled:opacity-50'>
|
|
935
|
+
{meta.cardType ? (
|
|
936
|
+
<svg
|
|
937
|
+
className='w-6 overflow-hidden'
|
|
938
|
+
{...getCardImageProps({
|
|
939
|
+
images: images as unknown as CardImages
|
|
940
|
+
})}
|
|
941
|
+
/>
|
|
942
|
+
) : (
|
|
943
|
+
<CreditCardIcon className='size-4' />
|
|
944
|
+
)}
|
|
945
|
+
<span className='sr-only'>Card Provider</span>
|
|
946
|
+
</div>
|
|
947
|
+
</div>
|
|
948
|
+
<div className='-mt-px flex'>
|
|
949
|
+
<div className='min-w-0 flex-1 focus-within:z-1'>
|
|
950
|
+
<Input
|
|
951
|
+
{...getExpiryDateProps()}
|
|
952
|
+
id={`expiry-${id}`}
|
|
953
|
+
className='rounded-t-none rounded-r-none shadow-none'
|
|
954
|
+
/>
|
|
955
|
+
</div>
|
|
956
|
+
<div className='-ms-px min-w-0 flex-1 focus-within:z-1'>
|
|
957
|
+
<Input {...getCVCProps()} id={`cvc-${id}`} className='rounded-t-none rounded-l-none shadow-none' />
|
|
958
|
+
</div>
|
|
959
|
+
</div>
|
|
960
|
+
</div>
|
|
961
|
+
<p className='text-muted-foreground text-xs'>
|
|
962
|
+
Built with{' '}
|
|
963
|
+
<a
|
|
964
|
+
className='hover:text-foreground underline'
|
|
965
|
+
href='https://github.com/medipass/react-payment-inputs'
|
|
966
|
+
target='_blank'
|
|
967
|
+
rel='noopener noreferrer'
|
|
968
|
+
>
|
|
969
|
+
React Payment Inputs
|
|
970
|
+
</a>
|
|
971
|
+
</p>
|
|
972
|
+
</div>
|
|
973
|
+
)
|
|
974
|
+
} */
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
|