@reliability-design/web 1.0.3 → 1.0.6
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/dist/components/rel-accordion/README.md +132 -0
- package/dist/components/rel-accordion/rel-accordion.ts +43 -16
- package/dist/components/rel-alert/README.md +141 -0
- package/dist/components/rel-alert/rel-alert.ts +18 -7
- package/dist/components/rel-avatar/README.md +119 -0
- package/dist/components/rel-badge/README.md +86 -0
- package/dist/components/rel-badge/rel-badge.ts +6 -6
- package/dist/components/rel-breadcrumb/README.md +102 -0
- package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +12 -6
- package/dist/components/rel-button/README.md +130 -0
- package/dist/components/rel-calendar/README.md +109 -0
- package/dist/components/rel-calendar/index.ts +1 -0
- package/dist/components/rel-calendar/rel-calendar.ts +467 -0
- package/dist/components/rel-card/README.md +94 -0
- package/dist/components/rel-card/rel-card.ts +12 -3
- package/dist/components/rel-cardmedia/README.md +127 -0
- package/dist/components/rel-cardmedia/rel-cardmedia.ts +14 -4
- package/dist/components/rel-carousel/README.md +166 -0
- package/dist/components/rel-carousel/index.ts +1 -0
- package/dist/components/rel-carousel/rel-carousel.ts +469 -0
- package/dist/components/rel-chart/README.md +286 -0
- package/dist/components/rel-chart/index.ts +1 -0
- package/dist/components/rel-chart/rel-chart.ts +736 -0
- package/dist/components/rel-checkbox/README.md +77 -0
- package/dist/components/rel-checkbox/rel-checkbox.ts +18 -10
- package/dist/components/rel-chip/README.md +114 -0
- package/dist/components/rel-chip/rel-chip.ts +20 -4
- package/dist/components/rel-container/README.md +99 -0
- package/dist/components/rel-date-picker/README.md +253 -0
- package/dist/components/rel-date-picker/index.ts +1 -0
- package/dist/components/rel-date-picker/rel-date-picker.ts +916 -0
- package/dist/components/rel-divider/README.md +100 -0
- package/dist/components/rel-drawer/README.md +200 -0
- package/dist/components/rel-drawer/index.ts +1 -0
- package/dist/components/rel-drawer/rel-drawer.ts +621 -0
- package/dist/components/rel-kbd/README.md +151 -0
- package/dist/components/rel-kbd/rel-kbd.ts +118 -0
- package/dist/components/rel-layout/README.md +88 -0
- package/dist/components/rel-menu/README.md +151 -0
- package/dist/components/rel-menu/rel-menu.ts +18 -10
- package/dist/components/rel-menubar/README.md +329 -0
- package/dist/components/rel-menubar/index.ts +2 -0
- package/dist/components/rel-menubar/rel-menubar-menu.ts +16 -0
- package/dist/components/rel-menubar/rel-menubar.ts +576 -0
- package/dist/components/rel-nav-menu/README.md +289 -0
- package/dist/components/rel-nav-menu/index.ts +3 -0
- package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +143 -0
- package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +53 -0
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +331 -0
- package/dist/components/rel-navbar/README.md +126 -0
- package/dist/components/rel-navbar/rel-navbar.ts +91 -0
- package/dist/components/rel-otp/README.md +440 -0
- package/dist/components/rel-otp/index.ts +1 -0
- package/dist/components/rel-otp/rel-otp.ts +504 -0
- package/dist/components/rel-pagination/README.md +410 -0
- package/dist/components/rel-pagination/index.ts +1 -0
- package/dist/components/rel-pagination/rel-pagination.ts +376 -0
- package/dist/components/rel-popover/README.md +568 -0
- package/dist/components/rel-popover/index.ts +2 -0
- package/dist/components/rel-popover/rel-popover-content.ts +55 -0
- package/dist/components/rel-popover/rel-popover.ts +327 -0
- package/dist/components/rel-progress/README.md +110 -0
- package/dist/components/rel-progress/rel-progress.ts +32 -5
- package/dist/components/rel-quote-page/README.md +166 -0
- package/dist/components/rel-quote-page/rel-quote-page.ts +816 -0
- package/dist/components/rel-radio/README.md +120 -0
- package/dist/components/rel-radio/rel-radio.ts +13 -5
- package/dist/components/rel-resizable/README.md +140 -0
- package/dist/components/rel-resizable/index.ts +3 -0
- package/dist/components/rel-resizable/rel-resizable-group.ts +374 -0
- package/dist/components/rel-resizable/rel-resizable-handle.ts +149 -0
- package/dist/components/rel-resizable/rel-resizable-panel.ts +47 -0
- package/dist/components/rel-segmented-button/README.md +141 -0
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +18 -13
- package/dist/components/rel-select/README.md +107 -0
- package/dist/components/rel-select/rel-select.ts +14 -14
- package/dist/components/rel-sheet/README.md +224 -0
- package/dist/components/rel-sheet/rel-sheet.ts +683 -0
- package/dist/components/rel-sidebar/README.md +209 -0
- package/dist/components/rel-sidebar/rel-sidebar.ts +1158 -0
- package/dist/components/rel-slider/README.md +135 -0
- package/dist/components/rel-slider/rel-slider.ts +19 -7
- package/dist/components/rel-snackbar/README.md +134 -0
- package/dist/components/rel-snackbar/rel-snackbar.ts +67 -2
- package/dist/components/rel-stepper/README.md +160 -0
- package/dist/components/rel-stepper/rel-stepper.ts +6 -6
- package/dist/components/rel-switch/README.md +102 -0
- package/dist/components/rel-switch/rel-switch.ts +15 -6
- package/dist/components/rel-tab/README.md +163 -0
- package/dist/components/rel-tab/rel-tab.ts +36 -14
- package/dist/components/rel-table/README.md +178 -0
- package/dist/components/rel-table/rel-table-body.ts +16 -0
- package/dist/components/rel-table/rel-table-caption.ts +22 -0
- package/dist/components/rel-table/rel-table-cell.ts +49 -0
- package/dist/components/rel-table/rel-table-footer.ts +18 -0
- package/dist/components/rel-table/rel-table-head.ts +33 -0
- package/dist/components/rel-table/rel-table-header.ts +16 -0
- package/dist/components/rel-table/rel-table-row.ts +32 -0
- package/dist/components/rel-table/rel-table.ts +47 -0
- package/dist/components/rel-textfield/README.md +90 -0
- package/dist/components/rel-textfield/rel-textfield.ts +35 -19
- package/dist/components/rel-toast/README.md +148 -0
- package/dist/components/rel-toast/rel-toast.ts +32 -14
- package/dist/components/rel-tooltip/README.md +103 -0
- package/dist/components/rel-tooltip/rel-tooltip.ts +9 -9
- package/dist/index.d.mts +836 -1
- package/dist/index.d.ts +836 -1
- package/dist/index.js +10658 -1493
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10616 -1499
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +293 -183
- package/dist/{vars-6ZIUPIU4.css → vars-ITL2K3KO.css} +293 -183
- package/package.json +1 -1
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
# RelOtp Component
|
|
2
|
+
|
|
3
|
+
An accessible one-time password (OTP) input component for the Reliability Design System.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Auto-advance on input
|
|
8
|
+
- Smart backspace behavior (clear current, then move back)
|
|
9
|
+
- Arrow key navigation (Left/Right, Home/End)
|
|
10
|
+
- Paste support (distributes characters across cells)
|
|
11
|
+
- Mobile OTP autofill support (iOS/Android)
|
|
12
|
+
- Numeric or alphanumeric validation
|
|
13
|
+
- Optional masking (password-style)
|
|
14
|
+
- Visual grouping with separators
|
|
15
|
+
- Form integration via hidden input
|
|
16
|
+
- Token-driven styling
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<!-- Simple 6-digit numeric OTP -->
|
|
22
|
+
<rel-otp length="6" group-size="3"></rel-otp>
|
|
23
|
+
|
|
24
|
+
<!-- Alphanumeric with masking -->
|
|
25
|
+
<rel-otp
|
|
26
|
+
length="8"
|
|
27
|
+
type="alphanumeric"
|
|
28
|
+
mask="true"
|
|
29
|
+
group-size="4">
|
|
30
|
+
</rel-otp>
|
|
31
|
+
|
|
32
|
+
<!-- With form integration -->
|
|
33
|
+
<form>
|
|
34
|
+
<rel-otp
|
|
35
|
+
name="otp_code"
|
|
36
|
+
length="6"
|
|
37
|
+
required
|
|
38
|
+
aria-label="Enter verification code">
|
|
39
|
+
</rel-otp>
|
|
40
|
+
<button type="submit">Verify</button>
|
|
41
|
+
</form>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Properties
|
|
45
|
+
|
|
46
|
+
| Property | Type | Default | Description |
|
|
47
|
+
|----------|------|---------|-------------|
|
|
48
|
+
| `value` | `string` | `""` | The concatenated OTP value (reflects) |
|
|
49
|
+
| `length` | `number` | `6` | Total number of characters/digits |
|
|
50
|
+
| `group-size` | `number` | `3` | Visual grouping size (0 = no grouping) |
|
|
51
|
+
| `placeholder` | `string` | `""` | Placeholder for each cell |
|
|
52
|
+
| `type` | `"numeric" \| "alphanumeric"` | `"numeric"` | Input validation type |
|
|
53
|
+
| `mask` | `boolean` | `false` | Password-style masking |
|
|
54
|
+
| `disabled` | `boolean` | `false` | Disables all inputs |
|
|
55
|
+
| `readonly` | `boolean` | `false` | Makes inputs read-only |
|
|
56
|
+
| `required` | `boolean` | `false` | HTML5 required attribute |
|
|
57
|
+
| `name` | `string \| null` | `null` | Creates hidden input for forms |
|
|
58
|
+
| `autocomplete` | `string` | `"one-time-code"` | Autocomplete attribute (mobile OTP) |
|
|
59
|
+
| `inputmode` | `"numeric" \| "text" \| null` | `null` | Input mode (auto-detected from type) |
|
|
60
|
+
| `pattern` | `string \| null` | `null` | HTML5 pattern attribute |
|
|
61
|
+
| `aria-label` | `string \| null` | `null` | Label for the entire control |
|
|
62
|
+
| `invalid` | `boolean` | `false` | Shows error styling |
|
|
63
|
+
| `show-separator` | `boolean` | `true` | Show separator between groups |
|
|
64
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size variant |
|
|
65
|
+
|
|
66
|
+
## Events
|
|
67
|
+
|
|
68
|
+
| Event | Detail | Description |
|
|
69
|
+
|-------|--------|-------------|
|
|
70
|
+
| `rel-change` | `{ value: string, complete: boolean }` | Fires on any value change |
|
|
71
|
+
| `rel-complete` | `{ value: string }` | Fires when valid code is complete |
|
|
72
|
+
|
|
73
|
+
## Keyboard Behavior
|
|
74
|
+
|
|
75
|
+
| Key | Action |
|
|
76
|
+
|-----|--------|
|
|
77
|
+
| `0-9` / `A-Z` | Enter character (based on type), auto-advance |
|
|
78
|
+
| `Backspace` | Clear current cell, or move back and clear |
|
|
79
|
+
| `Delete` | Clear current cell, stay in place |
|
|
80
|
+
| `ArrowLeft` | Move to previous cell |
|
|
81
|
+
| `ArrowRight` | Move to next cell |
|
|
82
|
+
| `Home` | Jump to first cell |
|
|
83
|
+
| `End` | Jump to last cell |
|
|
84
|
+
|
|
85
|
+
## Paste Behavior
|
|
86
|
+
|
|
87
|
+
When you paste a value:
|
|
88
|
+
- Only valid characters are accepted (based on `type`)
|
|
89
|
+
- Characters distribute across cells starting from current position
|
|
90
|
+
- Overflow beyond `length` is truncated
|
|
91
|
+
- Focus moves to next empty cell (or last cell if full)
|
|
92
|
+
- Triggers `rel-change` and `rel-complete` events
|
|
93
|
+
|
|
94
|
+
Example: Pasting "123456" into a 6-digit OTP fills all cells.
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
### Basic Numeric OTP (6 digits)
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<rel-otp
|
|
102
|
+
id="otp-basic"
|
|
103
|
+
length="6"
|
|
104
|
+
group-size="3">
|
|
105
|
+
</rel-otp>
|
|
106
|
+
|
|
107
|
+
<script>
|
|
108
|
+
const otp = document.getElementById('otp-basic');
|
|
109
|
+
|
|
110
|
+
otp.addEventListener('rel-change', (e) => {
|
|
111
|
+
console.log('Value:', e.detail.value);
|
|
112
|
+
console.log('Complete:', e.detail.complete);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
otp.addEventListener('rel-complete', (e) => {
|
|
116
|
+
console.log('OTP Complete:', e.detail.value);
|
|
117
|
+
// Submit to server, etc.
|
|
118
|
+
});
|
|
119
|
+
</script>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Alphanumeric with Custom Grouping
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<rel-otp
|
|
126
|
+
length="8"
|
|
127
|
+
type="alphanumeric"
|
|
128
|
+
group-size="4"
|
|
129
|
+
aria-label="Enter 8-character code">
|
|
130
|
+
</rel-otp>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Masked OTP (Password Style)
|
|
134
|
+
|
|
135
|
+
```html
|
|
136
|
+
<rel-otp
|
|
137
|
+
length="6"
|
|
138
|
+
mask="true"
|
|
139
|
+
group-size="3"
|
|
140
|
+
aria-label="Enter PIN">
|
|
141
|
+
</rel-otp>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Form Integration
|
|
145
|
+
|
|
146
|
+
```html
|
|
147
|
+
<form id="verify-form">
|
|
148
|
+
<label>Enter verification code:</label>
|
|
149
|
+
<rel-otp
|
|
150
|
+
name="verification_code"
|
|
151
|
+
length="6"
|
|
152
|
+
required
|
|
153
|
+
invalid="false"
|
|
154
|
+
id="otp-form">
|
|
155
|
+
</rel-otp>
|
|
156
|
+
<button type="submit">Verify</button>
|
|
157
|
+
</form>
|
|
158
|
+
|
|
159
|
+
<script>
|
|
160
|
+
const form = document.getElementById('verify-form');
|
|
161
|
+
const otpInput = document.getElementById('otp-form');
|
|
162
|
+
|
|
163
|
+
form.addEventListener('submit', (e) => {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
const formData = new FormData(form);
|
|
166
|
+
console.log('OTP:', formData.get('verification_code'));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Show error on invalid attempt
|
|
170
|
+
function showError() {
|
|
171
|
+
otpInput.invalid = true;
|
|
172
|
+
}
|
|
173
|
+
</script>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Programmatic Control
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
const otp = document.querySelector('rel-otp');
|
|
180
|
+
|
|
181
|
+
// Set value programmatically
|
|
182
|
+
otp.value = "123456";
|
|
183
|
+
|
|
184
|
+
// Clear value
|
|
185
|
+
otp.value = "";
|
|
186
|
+
|
|
187
|
+
// Mark as invalid
|
|
188
|
+
otp.invalid = true;
|
|
189
|
+
|
|
190
|
+
// Disable
|
|
191
|
+
otp.disabled = true;
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Without Separators
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<rel-otp
|
|
198
|
+
length="6"
|
|
199
|
+
show-separator="false">
|
|
200
|
+
</rel-otp>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Different Sizes
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<!-- Small -->
|
|
207
|
+
<rel-otp size="sm" length="4"></rel-otp>
|
|
208
|
+
|
|
209
|
+
<!-- Medium (default) -->
|
|
210
|
+
<rel-otp size="md" length="6"></rel-otp>
|
|
211
|
+
|
|
212
|
+
<!-- Large -->
|
|
213
|
+
<rel-otp size="lg" length="6"></rel-otp>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Mobile OTP Autofill
|
|
217
|
+
|
|
218
|
+
The component supports native mobile OTP autofill on iOS and Android:
|
|
219
|
+
|
|
220
|
+
- Sets `autocomplete="one-time-code"` on the first input
|
|
221
|
+
- When the browser/OS injects the full code into the first cell, the component automatically distributes it across all cells
|
|
222
|
+
- Works with SMS-based verification codes
|
|
223
|
+
|
|
224
|
+
```html
|
|
225
|
+
<rel-otp
|
|
226
|
+
length="6"
|
|
227
|
+
autocomplete="one-time-code">
|
|
228
|
+
</rel-otp>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Accessibility
|
|
232
|
+
|
|
233
|
+
- **Role**: The container has `role="group"` with a descriptive `aria-label`
|
|
234
|
+
- **Cell Labels**: Each input has `aria-label` like "Digit 1 of 6"
|
|
235
|
+
- **Input Mode**: Sets appropriate `inputmode` for mobile keyboards
|
|
236
|
+
- `numeric` type → `inputmode="numeric"`
|
|
237
|
+
- `alphanumeric` type → `inputmode="text"`
|
|
238
|
+
- **Focus Management**: Auto-advances focus, keyboard navigation supported
|
|
239
|
+
- **States**: `disabled`, `readonly`, `required`, and `invalid` states properly reflected
|
|
240
|
+
|
|
241
|
+
```html
|
|
242
|
+
<rel-otp
|
|
243
|
+
aria-label="Enter your 6-digit verification code"
|
|
244
|
+
length="6">
|
|
245
|
+
</rel-otp>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Styling
|
|
249
|
+
|
|
250
|
+
The OTP component uses design system tokens for all styling. Customize via CSS parts:
|
|
251
|
+
|
|
252
|
+
```css
|
|
253
|
+
rel-otp::part(cell) {
|
|
254
|
+
border-color: var(--rel-colors-blue-primary);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
rel-otp::part(cell):focus {
|
|
258
|
+
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
rel-otp::part(separator) {
|
|
262
|
+
color: var(--rel-colors-blue-secondary);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Custom invalid styling */
|
|
266
|
+
rel-otp[invalid]::part(cell) {
|
|
267
|
+
border-color: var(--rel-colors-red-secondary);
|
|
268
|
+
animation: shake 0.3s;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@keyframes shake {
|
|
272
|
+
0%, 100% { transform: translateX(0); }
|
|
273
|
+
25% { transform: translateX(-4px); }
|
|
274
|
+
75% { transform: translateX(4px); }
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## CSS Parts
|
|
279
|
+
|
|
280
|
+
| Part | Description |
|
|
281
|
+
|------|-------------|
|
|
282
|
+
| `root` | Root container element |
|
|
283
|
+
| `cell` | Individual input cell |
|
|
284
|
+
| `separator` | Separator between groups |
|
|
285
|
+
| `hidden-input` | Hidden input for form submission |
|
|
286
|
+
|
|
287
|
+
## Framework Integration
|
|
288
|
+
|
|
289
|
+
### React
|
|
290
|
+
|
|
291
|
+
```jsx
|
|
292
|
+
import { RelOtp } from '@reliability-design/web';
|
|
293
|
+
|
|
294
|
+
function OtpForm() {
|
|
295
|
+
const [otp, setOtp] = useState('');
|
|
296
|
+
const [isInvalid, setIsInvalid] = useState(false);
|
|
297
|
+
|
|
298
|
+
const handleChange = (e) => {
|
|
299
|
+
setOtp(e.detail.value);
|
|
300
|
+
setIsInvalid(false);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const handleComplete = async (e) => {
|
|
304
|
+
const code = e.detail.value;
|
|
305
|
+
const valid = await verifyOtp(code);
|
|
306
|
+
setIsInvalid(!valid);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
return (
|
|
310
|
+
<RelOtp
|
|
311
|
+
length={6}
|
|
312
|
+
onRelChange={handleChange}
|
|
313
|
+
onRelComplete={handleComplete}
|
|
314
|
+
invalid={isInvalid}
|
|
315
|
+
/>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Angular
|
|
321
|
+
|
|
322
|
+
```html
|
|
323
|
+
<rel-otp
|
|
324
|
+
[length]="6"
|
|
325
|
+
[group-size]="3"
|
|
326
|
+
(rel-complete)="onOtpComplete($event)"
|
|
327
|
+
[invalid]="isInvalid">
|
|
328
|
+
</rel-otp>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
export class OtpComponent {
|
|
333
|
+
isInvalid = false;
|
|
334
|
+
|
|
335
|
+
async onOtpComplete(event: CustomEvent) {
|
|
336
|
+
const code = event.detail.value;
|
|
337
|
+
const valid = await this.verifyOtp(code);
|
|
338
|
+
this.isInvalid = !valid;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Vue
|
|
344
|
+
|
|
345
|
+
```vue
|
|
346
|
+
<template>
|
|
347
|
+
<rel-otp
|
|
348
|
+
:length="6"
|
|
349
|
+
:group-size="3"
|
|
350
|
+
:invalid="isInvalid"
|
|
351
|
+
@rel-complete="handleComplete"
|
|
352
|
+
/>
|
|
353
|
+
</template>
|
|
354
|
+
|
|
355
|
+
<script setup>
|
|
356
|
+
import { ref } from 'vue';
|
|
357
|
+
|
|
358
|
+
const isInvalid = ref(false);
|
|
359
|
+
|
|
360
|
+
async function handleComplete(event) {
|
|
361
|
+
const code = event.detail.value;
|
|
362
|
+
const valid = await verifyOtp(code);
|
|
363
|
+
isInvalid.value = !valid;
|
|
364
|
+
}
|
|
365
|
+
</script>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Validation
|
|
369
|
+
|
|
370
|
+
The component provides basic character validation (numeric or alphanumeric) but does not validate the actual OTP code. Implement your own verification logic:
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
const otp = document.querySelector('rel-otp');
|
|
374
|
+
|
|
375
|
+
otp.addEventListener('rel-complete', async (e) => {
|
|
376
|
+
const code = e.detail.value;
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
const response = await fetch('/api/verify-otp', {
|
|
380
|
+
method: 'POST',
|
|
381
|
+
body: JSON.stringify({ code }),
|
|
382
|
+
headers: { 'Content-Type': 'application/json' }
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
if (response.ok) {
|
|
386
|
+
// Success
|
|
387
|
+
console.log('OTP verified!');
|
|
388
|
+
} else {
|
|
389
|
+
// Invalid
|
|
390
|
+
otp.invalid = true;
|
|
391
|
+
otp.value = ''; // Clear for retry
|
|
392
|
+
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
console.error('Verification failed:', error);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Common Patterns
|
|
400
|
+
|
|
401
|
+
### Resend Timer
|
|
402
|
+
|
|
403
|
+
```html
|
|
404
|
+
<rel-otp id="otp" length="6"></rel-otp>
|
|
405
|
+
<button id="resend" disabled>Resend code in <span id="timer">60</span>s</button>
|
|
406
|
+
|
|
407
|
+
<script>
|
|
408
|
+
let timeLeft = 60;
|
|
409
|
+
const timer = document.getElementById('timer');
|
|
410
|
+
const resendBtn = document.getElementById('resend');
|
|
411
|
+
|
|
412
|
+
const countdown = setInterval(() => {
|
|
413
|
+
timeLeft--;
|
|
414
|
+
timer.textContent = timeLeft;
|
|
415
|
+
|
|
416
|
+
if (timeLeft <= 0) {
|
|
417
|
+
clearInterval(countdown);
|
|
418
|
+
resendBtn.disabled = false;
|
|
419
|
+
resendBtn.textContent = 'Resend code';
|
|
420
|
+
}
|
|
421
|
+
}, 1000);
|
|
422
|
+
</script>
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Auto-Submit on Complete
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
const otp = document.querySelector('rel-otp');
|
|
429
|
+
|
|
430
|
+
otp.addEventListener('rel-complete', async (e) => {
|
|
431
|
+
// Show loading state
|
|
432
|
+
otp.disabled = true;
|
|
433
|
+
|
|
434
|
+
// Auto-submit
|
|
435
|
+
await submitOtp(e.detail.value);
|
|
436
|
+
|
|
437
|
+
// Re-enable if failed
|
|
438
|
+
otp.disabled = false;
|
|
439
|
+
});
|
|
440
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./rel-otp";
|