@sneekin/ui 0.2.2 → 0.3.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 +24 -2
- package/dist/component.d.ts +14 -2
- package/dist/countries.d.ts +26 -0
- package/dist/country-picker.d.ts +12 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +526 -52
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +531 -52
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -136,12 +136,349 @@ function useSneekOtp(handlers) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// src/component.tsx
|
|
139
|
+
import {
|
|
140
|
+
useEffect as useEffect2,
|
|
141
|
+
useId as useId2,
|
|
142
|
+
useState as useState2
|
|
143
|
+
} from "react";
|
|
144
|
+
|
|
145
|
+
// src/country-picker.tsx
|
|
139
146
|
import {
|
|
140
147
|
useEffect,
|
|
141
148
|
useId,
|
|
149
|
+
useRef,
|
|
142
150
|
useState
|
|
143
151
|
} from "react";
|
|
144
152
|
|
|
153
|
+
// src/countries.ts
|
|
154
|
+
function flagFromIso2(iso2) {
|
|
155
|
+
const A = 127462;
|
|
156
|
+
const codePoints = [...iso2.toUpperCase()].map((c) => A + (c.charCodeAt(0) - 65));
|
|
157
|
+
return String.fromCodePoint(...codePoints);
|
|
158
|
+
}
|
|
159
|
+
var RAW = [
|
|
160
|
+
["US", "United States", "1"],
|
|
161
|
+
["CA", "Canada", "1"],
|
|
162
|
+
["GB", "United Kingdom", "44"],
|
|
163
|
+
["IN", "India", "91"],
|
|
164
|
+
["AU", "Australia", "61"],
|
|
165
|
+
["DE", "Germany", "49"],
|
|
166
|
+
["FR", "France", "33"],
|
|
167
|
+
["IT", "Italy", "39"],
|
|
168
|
+
["ES", "Spain", "34"],
|
|
169
|
+
["PT", "Portugal", "351"],
|
|
170
|
+
["NL", "Netherlands", "31"],
|
|
171
|
+
["BE", "Belgium", "32"],
|
|
172
|
+
["CH", "Switzerland", "41"],
|
|
173
|
+
["AT", "Austria", "43"],
|
|
174
|
+
["SE", "Sweden", "46"],
|
|
175
|
+
["NO", "Norway", "47"],
|
|
176
|
+
["DK", "Denmark", "45"],
|
|
177
|
+
["FI", "Finland", "358"],
|
|
178
|
+
["IE", "Ireland", "353"],
|
|
179
|
+
["IS", "Iceland", "354"],
|
|
180
|
+
["PL", "Poland", "48"],
|
|
181
|
+
["CZ", "Czechia", "420"],
|
|
182
|
+
["SK", "Slovakia", "421"],
|
|
183
|
+
["HU", "Hungary", "36"],
|
|
184
|
+
["RO", "Romania", "40"],
|
|
185
|
+
["BG", "Bulgaria", "359"],
|
|
186
|
+
["GR", "Greece", "30"],
|
|
187
|
+
["TR", "Turkiye", "90"],
|
|
188
|
+
["RU", "Russia", "7"],
|
|
189
|
+
["UA", "Ukraine", "380"],
|
|
190
|
+
["BY", "Belarus", "375"],
|
|
191
|
+
["LT", "Lithuania", "370"],
|
|
192
|
+
["LV", "Latvia", "371"],
|
|
193
|
+
["EE", "Estonia", "372"],
|
|
194
|
+
["HR", "Croatia", "385"],
|
|
195
|
+
["RS", "Serbia", "381"],
|
|
196
|
+
["SI", "Slovenia", "386"],
|
|
197
|
+
["CY", "Cyprus", "357"],
|
|
198
|
+
["MT", "Malta", "356"],
|
|
199
|
+
["LU", "Luxembourg", "352"],
|
|
200
|
+
["CN", "China", "86"],
|
|
201
|
+
["JP", "Japan", "81"],
|
|
202
|
+
["KR", "South Korea", "82"],
|
|
203
|
+
["HK", "Hong Kong", "852"],
|
|
204
|
+
["TW", "Taiwan", "886"],
|
|
205
|
+
["MO", "Macau", "853"],
|
|
206
|
+
["SG", "Singapore", "65"],
|
|
207
|
+
["MY", "Malaysia", "60"],
|
|
208
|
+
["ID", "Indonesia", "62"],
|
|
209
|
+
["TH", "Thailand", "66"],
|
|
210
|
+
["VN", "Vietnam", "84"],
|
|
211
|
+
["PH", "Philippines", "63"],
|
|
212
|
+
["PK", "Pakistan", "92"],
|
|
213
|
+
["BD", "Bangladesh", "880"],
|
|
214
|
+
["LK", "Sri Lanka", "94"],
|
|
215
|
+
["NP", "Nepal", "977"],
|
|
216
|
+
["MM", "Myanmar", "95"],
|
|
217
|
+
["KH", "Cambodia", "855"],
|
|
218
|
+
["LA", "Laos", "856"],
|
|
219
|
+
["MN", "Mongolia", "976"],
|
|
220
|
+
["KZ", "Kazakhstan", "7"],
|
|
221
|
+
["UZ", "Uzbekistan", "998"],
|
|
222
|
+
["AE", "United Arab Emirates", "971"],
|
|
223
|
+
["SA", "Saudi Arabia", "966"],
|
|
224
|
+
["QA", "Qatar", "974"],
|
|
225
|
+
["KW", "Kuwait", "965"],
|
|
226
|
+
["BH", "Bahrain", "973"],
|
|
227
|
+
["OM", "Oman", "968"],
|
|
228
|
+
["JO", "Jordan", "962"],
|
|
229
|
+
["LB", "Lebanon", "961"],
|
|
230
|
+
["IL", "Israel", "972"],
|
|
231
|
+
["IQ", "Iraq", "964"],
|
|
232
|
+
["IR", "Iran", "98"],
|
|
233
|
+
["EG", "Egypt", "20"],
|
|
234
|
+
["ZA", "South Africa", "27"],
|
|
235
|
+
["NG", "Nigeria", "234"],
|
|
236
|
+
["KE", "Kenya", "254"],
|
|
237
|
+
["GH", "Ghana", "233"],
|
|
238
|
+
["ET", "Ethiopia", "251"],
|
|
239
|
+
["TZ", "Tanzania", "255"],
|
|
240
|
+
["UG", "Uganda", "256"],
|
|
241
|
+
["MA", "Morocco", "212"],
|
|
242
|
+
["DZ", "Algeria", "213"],
|
|
243
|
+
["TN", "Tunisia", "216"],
|
|
244
|
+
["BR", "Brazil", "55"],
|
|
245
|
+
["MX", "Mexico", "52"],
|
|
246
|
+
["AR", "Argentina", "54"],
|
|
247
|
+
["CL", "Chile", "56"],
|
|
248
|
+
["CO", "Colombia", "57"],
|
|
249
|
+
["PE", "Peru", "51"],
|
|
250
|
+
["VE", "Venezuela", "58"],
|
|
251
|
+
["EC", "Ecuador", "593"],
|
|
252
|
+
["UY", "Uruguay", "598"],
|
|
253
|
+
["PY", "Paraguay", "595"],
|
|
254
|
+
["BO", "Bolivia", "591"],
|
|
255
|
+
["CR", "Costa Rica", "506"],
|
|
256
|
+
["PA", "Panama", "507"],
|
|
257
|
+
["DO", "Dominican Republic", "1"],
|
|
258
|
+
["GT", "Guatemala", "502"],
|
|
259
|
+
["NZ", "New Zealand", "64"],
|
|
260
|
+
["FJ", "Fiji", "679"]
|
|
261
|
+
];
|
|
262
|
+
var COUNTRIES = RAW.map(([iso2, name, dial]) => ({
|
|
263
|
+
iso2,
|
|
264
|
+
name,
|
|
265
|
+
dial
|
|
266
|
+
}));
|
|
267
|
+
var DEFAULT_COUNTRY_ISO2 = "IN";
|
|
268
|
+
function flagEmoji(iso2) {
|
|
269
|
+
return flagFromIso2(iso2);
|
|
270
|
+
}
|
|
271
|
+
function countryByIso2(iso2) {
|
|
272
|
+
return COUNTRIES.find((c) => c.iso2 === iso2);
|
|
273
|
+
}
|
|
274
|
+
function countryByDialPrefix(digits) {
|
|
275
|
+
for (const len of [3, 2, 1]) {
|
|
276
|
+
const prefix = digits.slice(0, len);
|
|
277
|
+
if (prefix.length < len) continue;
|
|
278
|
+
const match = COUNTRIES.find((c) => c.dial === prefix);
|
|
279
|
+
if (match) return match;
|
|
280
|
+
}
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
function detectCountryFromLocale() {
|
|
284
|
+
if (typeof navigator === "undefined") return void 0;
|
|
285
|
+
const locales = navigator.languages ?? [navigator.language];
|
|
286
|
+
for (const locale of locales) {
|
|
287
|
+
const match = /-([A-Z]{2})$/.exec(locale ?? "");
|
|
288
|
+
if (match) {
|
|
289
|
+
const found = countryByIso2(match[1]);
|
|
290
|
+
if (found) return found;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return void 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/country-picker.tsx
|
|
297
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
298
|
+
function ChevronDown() {
|
|
299
|
+
return /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
300
|
+
"path",
|
|
301
|
+
{
|
|
302
|
+
d: "M2 3.5 5 6.5 8 3.5",
|
|
303
|
+
stroke: "currentColor",
|
|
304
|
+
strokeWidth: "1.4",
|
|
305
|
+
strokeLinecap: "round",
|
|
306
|
+
strokeLinejoin: "round"
|
|
307
|
+
}
|
|
308
|
+
) });
|
|
309
|
+
}
|
|
310
|
+
function CountryPicker({ country, onSelect, disabled }) {
|
|
311
|
+
const [open, setOpen] = useState(false);
|
|
312
|
+
const [query, setQuery] = useState("");
|
|
313
|
+
const rootRef = useRef(null);
|
|
314
|
+
const searchRef = useRef(null);
|
|
315
|
+
const uid = useId();
|
|
316
|
+
useEffect(() => {
|
|
317
|
+
if (!open) return;
|
|
318
|
+
searchRef.current?.focus();
|
|
319
|
+
const onDocPointerDown = (event) => {
|
|
320
|
+
if (rootRef.current && !rootRef.current.contains(event.target)) {
|
|
321
|
+
setOpen(false);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
const onKeyDown = (event) => {
|
|
325
|
+
if (event.key === "Escape") setOpen(false);
|
|
326
|
+
};
|
|
327
|
+
document.addEventListener("mousedown", onDocPointerDown);
|
|
328
|
+
document.addEventListener("keydown", onKeyDown);
|
|
329
|
+
return () => {
|
|
330
|
+
document.removeEventListener("mousedown", onDocPointerDown);
|
|
331
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
332
|
+
};
|
|
333
|
+
}, [open]);
|
|
334
|
+
const q = query.trim().toLowerCase();
|
|
335
|
+
const filtered = q ? COUNTRIES.filter(
|
|
336
|
+
(c) => c.name.toLowerCase().includes(q) || c.dial.includes(q.replace(/^\+/, ""))
|
|
337
|
+
) : COUNTRIES;
|
|
338
|
+
const listId = `${uid}-country-list`;
|
|
339
|
+
return /* @__PURE__ */ jsxs("div", { ref: rootRef, style: { position: "relative", flexShrink: 0 }, children: [
|
|
340
|
+
/* @__PURE__ */ jsxs(
|
|
341
|
+
"button",
|
|
342
|
+
{
|
|
343
|
+
type: "button",
|
|
344
|
+
className: "sneek-otp-country-chip",
|
|
345
|
+
onClick: () => setOpen((o) => !o),
|
|
346
|
+
disabled,
|
|
347
|
+
"aria-haspopup": "listbox",
|
|
348
|
+
"aria-expanded": open,
|
|
349
|
+
style: {
|
|
350
|
+
display: "flex",
|
|
351
|
+
alignItems: "center",
|
|
352
|
+
gap: 6,
|
|
353
|
+
height: "100%",
|
|
354
|
+
padding: "0 10px 0 14px",
|
|
355
|
+
border: "none",
|
|
356
|
+
borderRadius: "12px 0 0 12px",
|
|
357
|
+
background: "transparent",
|
|
358
|
+
color: "var(--sneek-text)",
|
|
359
|
+
fontSize: 15,
|
|
360
|
+
cursor: disabled ? "not-allowed" : "pointer"
|
|
361
|
+
},
|
|
362
|
+
children: [
|
|
363
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 18, lineHeight: 1 }, children: flagEmoji(country.iso2) }),
|
|
364
|
+
/* @__PURE__ */ jsxs("span", { style: { color: "var(--sneek-text-muted)" }, children: [
|
|
365
|
+
"+",
|
|
366
|
+
country.dial
|
|
367
|
+
] }),
|
|
368
|
+
/* @__PURE__ */ jsx("span", { style: { color: "var(--sneek-text-muted)" }, children: /* @__PURE__ */ jsx(ChevronDown, {}) })
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
),
|
|
372
|
+
open ? /* @__PURE__ */ jsxs(
|
|
373
|
+
"div",
|
|
374
|
+
{
|
|
375
|
+
role: "presentation",
|
|
376
|
+
style: {
|
|
377
|
+
position: "absolute",
|
|
378
|
+
top: "calc(100% + 6px)",
|
|
379
|
+
left: 0,
|
|
380
|
+
zIndex: 10,
|
|
381
|
+
width: 260,
|
|
382
|
+
maxWidth: "80vw",
|
|
383
|
+
border: "1px solid var(--sneek-border)",
|
|
384
|
+
borderRadius: 12,
|
|
385
|
+
background: "var(--sneek-surface)",
|
|
386
|
+
boxShadow: "var(--sneek-shadow), 0 8px 24px rgb(0 0 0 / 0.12)",
|
|
387
|
+
overflow: "hidden"
|
|
388
|
+
},
|
|
389
|
+
children: [
|
|
390
|
+
/* @__PURE__ */ jsx(
|
|
391
|
+
"input",
|
|
392
|
+
{
|
|
393
|
+
ref: searchRef,
|
|
394
|
+
value: query,
|
|
395
|
+
onChange: (e) => setQuery(e.target.value),
|
|
396
|
+
onKeyDown: (e) => {
|
|
397
|
+
if (e.key === "Enter" && filtered[0]) {
|
|
398
|
+
onSelect(filtered[0]);
|
|
399
|
+
setOpen(false);
|
|
400
|
+
setQuery("");
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
placeholder: "Search country or code",
|
|
404
|
+
"aria-controls": listId,
|
|
405
|
+
role: "combobox",
|
|
406
|
+
"aria-expanded": open,
|
|
407
|
+
style: {
|
|
408
|
+
width: "100%",
|
|
409
|
+
padding: "10px 12px",
|
|
410
|
+
border: "none",
|
|
411
|
+
borderBottom: "1px solid var(--sneek-border)",
|
|
412
|
+
background: "transparent",
|
|
413
|
+
color: "var(--sneek-text)",
|
|
414
|
+
fontSize: 14,
|
|
415
|
+
outline: "none"
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
),
|
|
419
|
+
/* @__PURE__ */ jsx(
|
|
420
|
+
"div",
|
|
421
|
+
{
|
|
422
|
+
id: listId,
|
|
423
|
+
role: "listbox",
|
|
424
|
+
style: { maxHeight: 240, overflowY: "auto", padding: 6 },
|
|
425
|
+
children: filtered.length === 0 ? /* @__PURE__ */ jsx(
|
|
426
|
+
"p",
|
|
427
|
+
{
|
|
428
|
+
style: {
|
|
429
|
+
padding: "8px 10px",
|
|
430
|
+
fontSize: 13,
|
|
431
|
+
color: "var(--sneek-text-muted)"
|
|
432
|
+
},
|
|
433
|
+
children: "No match."
|
|
434
|
+
}
|
|
435
|
+
) : filtered.map((c) => {
|
|
436
|
+
const optionStyle = {
|
|
437
|
+
display: "flex",
|
|
438
|
+
width: "100%",
|
|
439
|
+
alignItems: "center",
|
|
440
|
+
gap: 10,
|
|
441
|
+
padding: "8px 10px",
|
|
442
|
+
border: "none",
|
|
443
|
+
borderRadius: 8,
|
|
444
|
+
background: c.iso2 === country.iso2 ? "var(--sneek-surface-2)" : "transparent",
|
|
445
|
+
color: "var(--sneek-text)",
|
|
446
|
+
fontSize: 14,
|
|
447
|
+
textAlign: "left",
|
|
448
|
+
cursor: "pointer"
|
|
449
|
+
};
|
|
450
|
+
return /* @__PURE__ */ jsxs(
|
|
451
|
+
"button",
|
|
452
|
+
{
|
|
453
|
+
type: "button",
|
|
454
|
+
role: "option",
|
|
455
|
+
"aria-selected": c.iso2 === country.iso2,
|
|
456
|
+
onClick: () => {
|
|
457
|
+
onSelect(c);
|
|
458
|
+
setOpen(false);
|
|
459
|
+
setQuery("");
|
|
460
|
+
},
|
|
461
|
+
style: optionStyle,
|
|
462
|
+
children: [
|
|
463
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 17, lineHeight: 1 }, children: flagEmoji(c.iso2) }),
|
|
464
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: c.name }),
|
|
465
|
+
/* @__PURE__ */ jsxs("span", { style: { color: "var(--sneek-text-muted)" }, children: [
|
|
466
|
+
"+",
|
|
467
|
+
c.dial
|
|
468
|
+
] })
|
|
469
|
+
]
|
|
470
|
+
},
|
|
471
|
+
c.iso2
|
|
472
|
+
);
|
|
473
|
+
})
|
|
474
|
+
}
|
|
475
|
+
)
|
|
476
|
+
]
|
|
477
|
+
}
|
|
478
|
+
) : null
|
|
479
|
+
] });
|
|
480
|
+
}
|
|
481
|
+
|
|
145
482
|
// src/theme.ts
|
|
146
483
|
var SNEEK_STYLE_ID = "sneek-otp-styles";
|
|
147
484
|
var SNEEK_ACCENT_DARK = "#f86513";
|
|
@@ -187,19 +524,30 @@ function buildStyles(accent) {
|
|
|
187
524
|
.sneek-otp *, .sneek-otp *::before, .sneek-otp *::after { box-sizing: border-box; }
|
|
188
525
|
.sneek-otp-input:focus-visible,
|
|
189
526
|
.sneek-otp-button:focus-visible,
|
|
190
|
-
.sneek-otp-link:focus-visible
|
|
527
|
+
.sneek-otp-link:focus-visible,
|
|
528
|
+
.sneek-otp-country-chip:focus-visible,
|
|
529
|
+
.sneek-otp-brand-link:focus-visible {
|
|
191
530
|
outline: 2px solid var(--sneek-accent);
|
|
192
531
|
outline-offset: 2px;
|
|
193
532
|
}
|
|
194
533
|
.sneek-otp-input:focus {
|
|
195
534
|
border-color: var(--sneek-accent);
|
|
196
535
|
}
|
|
536
|
+
.sneek-otp-phone-field:focus-within {
|
|
537
|
+
border-color: var(--sneek-accent);
|
|
538
|
+
}
|
|
197
539
|
.sneek-otp-button:not(:disabled):hover {
|
|
198
540
|
filter: brightness(1.08);
|
|
199
541
|
}
|
|
200
542
|
.sneek-otp-link:not(:disabled):hover {
|
|
201
543
|
text-decoration: underline;
|
|
202
544
|
}
|
|
545
|
+
.sneek-otp-country-chip:not(:disabled):hover {
|
|
546
|
+
filter: brightness(1.08);
|
|
547
|
+
}
|
|
548
|
+
.sneek-otp-brand-link:hover {
|
|
549
|
+
color: var(--sneek-accent);
|
|
550
|
+
}
|
|
203
551
|
@media (prefers-reduced-motion: reduce) {
|
|
204
552
|
.sneek-otp * { transition-duration: 0.01ms !important; }
|
|
205
553
|
}
|
|
@@ -214,11 +562,12 @@ function buildStyles(accent) {
|
|
|
214
562
|
}
|
|
215
563
|
|
|
216
564
|
// src/component.tsx
|
|
217
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
565
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
218
566
|
var CODE_LENGTH = 6;
|
|
219
567
|
var RESEND_SECONDS = 30;
|
|
568
|
+
var MAX_NATIONAL_DIGITS = 12;
|
|
220
569
|
function SneekMark() {
|
|
221
|
-
return /* @__PURE__ */
|
|
570
|
+
return /* @__PURE__ */ jsxs2(
|
|
222
571
|
"svg",
|
|
223
572
|
{
|
|
224
573
|
width: "36",
|
|
@@ -228,8 +577,8 @@ function SneekMark() {
|
|
|
228
577
|
role: "img",
|
|
229
578
|
"aria-label": "Sneek",
|
|
230
579
|
children: [
|
|
231
|
-
/* @__PURE__ */
|
|
232
|
-
/* @__PURE__ */
|
|
580
|
+
/* @__PURE__ */ jsx2("path", { d: "M21.3 1 29.3 16H13.3L21.3 1Z", fill: "var(--sneek-accent)" }),
|
|
581
|
+
/* @__PURE__ */ jsx2("path", { d: "M2.7 17.5H18.7L10.7 31 2.7 17.5Z", fill: "var(--sneek-accent)" })
|
|
233
582
|
]
|
|
234
583
|
}
|
|
235
584
|
);
|
|
@@ -239,8 +588,10 @@ function SneekOtpLogin(props) {
|
|
|
239
588
|
title = "Sign in",
|
|
240
589
|
verifyTitle = "Enter the code",
|
|
241
590
|
subtitle = "Enter your email or phone. We will send you a code.",
|
|
242
|
-
|
|
243
|
-
|
|
591
|
+
identifierMode = "both",
|
|
592
|
+
defaultCountry,
|
|
593
|
+
identifierLabel,
|
|
594
|
+
identifierPlaceholder,
|
|
244
595
|
theme = "auto",
|
|
245
596
|
accentColor,
|
|
246
597
|
logo,
|
|
@@ -250,13 +601,49 @@ function SneekOtpLogin(props) {
|
|
|
250
601
|
...handlers
|
|
251
602
|
} = props;
|
|
252
603
|
const otp = useSneekOtp(handlers);
|
|
253
|
-
const uid =
|
|
254
|
-
const [resendIn, setResendIn] =
|
|
255
|
-
const [codeFocused, setCodeFocused] =
|
|
604
|
+
const uid = useId2();
|
|
605
|
+
const [resendIn, setResendIn] = useState2(0);
|
|
606
|
+
const [codeFocused, setCodeFocused] = useState2(false);
|
|
607
|
+
const [mode, setMode] = useState2(
|
|
608
|
+
identifierMode === "email" ? "email" : "phone"
|
|
609
|
+
);
|
|
610
|
+
const [country, setCountry] = useState2(
|
|
611
|
+
() => defaultCountry && countryByIso2(defaultCountry) || countryByIso2(DEFAULT_COUNTRY_ISO2)
|
|
612
|
+
);
|
|
613
|
+
const [nationalNumber, setNationalNumber] = useState2("");
|
|
256
614
|
const identifierId = `${uid}-identifier`;
|
|
257
615
|
const codeId = `${uid}-code`;
|
|
258
616
|
const statusId = `${uid}-status`;
|
|
259
|
-
|
|
617
|
+
useEffect2(() => {
|
|
618
|
+
if (defaultCountry) return;
|
|
619
|
+
const guess = detectCountryFromLocale();
|
|
620
|
+
if (guess) setCountry(guess);
|
|
621
|
+
}, []);
|
|
622
|
+
const onCountrySelect = (next) => {
|
|
623
|
+
setCountry(next);
|
|
624
|
+
otp.setIdentifier(`+${next.dial}${nationalNumber}`);
|
|
625
|
+
};
|
|
626
|
+
const onNationalNumberChange = (raw) => {
|
|
627
|
+
let digits = raw.replace(/\D/g, "").slice(0, MAX_NATIONAL_DIGITS + 3);
|
|
628
|
+
let effectiveCountry = country;
|
|
629
|
+
if (raw.trim().startsWith("+")) {
|
|
630
|
+
const guess = countryByDialPrefix(digits);
|
|
631
|
+
if (guess && digits.startsWith(guess.dial)) {
|
|
632
|
+
effectiveCountry = guess;
|
|
633
|
+
digits = digits.slice(guess.dial.length);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
digits = digits.slice(0, MAX_NATIONAL_DIGITS);
|
|
637
|
+
if (effectiveCountry !== country) setCountry(effectiveCountry);
|
|
638
|
+
setNationalNumber(digits);
|
|
639
|
+
otp.setIdentifier(`+${effectiveCountry.dial}${digits}`);
|
|
640
|
+
};
|
|
641
|
+
const switchMode = (next) => {
|
|
642
|
+
setMode(next);
|
|
643
|
+
setNationalNumber("");
|
|
644
|
+
otp.setIdentifier("");
|
|
645
|
+
};
|
|
646
|
+
useEffect2(() => {
|
|
260
647
|
if (typeof document === "undefined") return;
|
|
261
648
|
let el = document.getElementById(SNEEK_STYLE_ID);
|
|
262
649
|
if (!el) {
|
|
@@ -274,15 +661,15 @@ function SneekOtpLogin(props) {
|
|
|
274
661
|
event.preventDefault();
|
|
275
662
|
void otp.verify();
|
|
276
663
|
};
|
|
277
|
-
|
|
664
|
+
useEffect2(() => {
|
|
278
665
|
if (otp.step === "verify") setResendIn(RESEND_SECONDS);
|
|
279
666
|
}, [otp.step]);
|
|
280
|
-
|
|
667
|
+
useEffect2(() => {
|
|
281
668
|
if (resendIn <= 0) return;
|
|
282
669
|
const timer = setTimeout(() => setResendIn((n) => n - 1), 1e3);
|
|
283
670
|
return () => clearTimeout(timer);
|
|
284
671
|
}, [resendIn]);
|
|
285
|
-
|
|
672
|
+
useEffect2(() => {
|
|
286
673
|
if (otp.step === "verify" && otp.code.length === CODE_LENGTH && !otp.isBusy) {
|
|
287
674
|
void otp.verify();
|
|
288
675
|
}
|
|
@@ -336,7 +723,7 @@ function SneekOtpLogin(props) {
|
|
|
336
723
|
fontSize: 14,
|
|
337
724
|
color: "var(--sneek-text-muted)"
|
|
338
725
|
};
|
|
339
|
-
return /* @__PURE__ */
|
|
726
|
+
return /* @__PURE__ */ jsxs2(
|
|
340
727
|
"div",
|
|
341
728
|
{
|
|
342
729
|
className: className ? `sneek-otp ${className}` : "sneek-otp",
|
|
@@ -355,8 +742,8 @@ function SneekOtpLogin(props) {
|
|
|
355
742
|
...style
|
|
356
743
|
},
|
|
357
744
|
children: [
|
|
358
|
-
logo === null ? null : logo ?? /* @__PURE__ */
|
|
359
|
-
/* @__PURE__ */
|
|
745
|
+
logo === null ? null : logo ?? /* @__PURE__ */ jsx2(SneekMark, {}),
|
|
746
|
+
/* @__PURE__ */ jsx2(
|
|
360
747
|
"h1",
|
|
361
748
|
{
|
|
362
749
|
style: {
|
|
@@ -369,7 +756,7 @@ function SneekOtpLogin(props) {
|
|
|
369
756
|
children: otp.step === "verify" ? verifyTitle : title
|
|
370
757
|
}
|
|
371
758
|
),
|
|
372
|
-
otp.step === "verify" ? /* @__PURE__ */
|
|
759
|
+
otp.step === "verify" ? /* @__PURE__ */ jsxs2(
|
|
373
760
|
"p",
|
|
374
761
|
{
|
|
375
762
|
style: {
|
|
@@ -381,9 +768,9 @@ function SneekOtpLogin(props) {
|
|
|
381
768
|
children: [
|
|
382
769
|
"Sent to",
|
|
383
770
|
" ",
|
|
384
|
-
/* @__PURE__ */
|
|
771
|
+
/* @__PURE__ */ jsx2("span", { style: { color: "var(--sneek-text)" }, children: otp.identifier }),
|
|
385
772
|
" \xB7 ",
|
|
386
|
-
/* @__PURE__ */
|
|
773
|
+
/* @__PURE__ */ jsx2(
|
|
387
774
|
"button",
|
|
388
775
|
{
|
|
389
776
|
type: "button",
|
|
@@ -396,7 +783,7 @@ function SneekOtpLogin(props) {
|
|
|
396
783
|
)
|
|
397
784
|
]
|
|
398
785
|
}
|
|
399
|
-
) : subtitle ? /* @__PURE__ */
|
|
786
|
+
) : subtitle ? /* @__PURE__ */ jsx2(
|
|
400
787
|
"p",
|
|
401
788
|
{
|
|
402
789
|
style: {
|
|
@@ -408,7 +795,7 @@ function SneekOtpLogin(props) {
|
|
|
408
795
|
children: subtitle
|
|
409
796
|
}
|
|
410
797
|
) : null,
|
|
411
|
-
/* @__PURE__ */
|
|
798
|
+
/* @__PURE__ */ jsx2("div", { role: "status", "aria-live": "polite", id: statusId, style: { marginTop: 32 }, children: otp.error ? /* @__PURE__ */ jsx2(
|
|
412
799
|
"div",
|
|
413
800
|
{
|
|
414
801
|
role: "alert",
|
|
@@ -420,32 +807,99 @@ function SneekOtpLogin(props) {
|
|
|
420
807
|
children: otp.error
|
|
421
808
|
}
|
|
422
809
|
) : null }),
|
|
423
|
-
otp.step === "identify" ? /* @__PURE__ */
|
|
810
|
+
otp.step === "identify" ? /* @__PURE__ */ jsxs2(
|
|
424
811
|
"form",
|
|
425
812
|
{
|
|
426
813
|
onSubmit: onIdentifySubmit,
|
|
427
814
|
style: { display: "flex", flexDirection: "column", gap: 16, marginTop: 16 },
|
|
428
815
|
children: [
|
|
429
|
-
/* @__PURE__ */
|
|
430
|
-
identifierLabel,
|
|
431
|
-
/* @__PURE__ */
|
|
432
|
-
"
|
|
816
|
+
/* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "baseline", justifyContent: "space-between" }, children: [
|
|
817
|
+
/* @__PURE__ */ jsx2("label", { style: { ...label, gap: 0 }, htmlFor: identifierId, children: identifierLabel ?? (mode === "phone" ? "Mobile number" : "Email address") }),
|
|
818
|
+
identifierMode === "both" ? /* @__PURE__ */ jsx2(
|
|
819
|
+
"button",
|
|
433
820
|
{
|
|
434
|
-
|
|
435
|
-
className: "sneek-otp-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
autoComplete: "username",
|
|
441
|
-
autoFocus: true,
|
|
442
|
-
required: true,
|
|
443
|
-
"aria-describedby": statusId,
|
|
444
|
-
style: input
|
|
821
|
+
type: "button",
|
|
822
|
+
className: "sneek-otp-link",
|
|
823
|
+
onClick: () => switchMode(mode === "phone" ? "email" : "phone"),
|
|
824
|
+
disabled: otp.isBusy,
|
|
825
|
+
style: { ...link, fontSize: 13 },
|
|
826
|
+
children: mode === "phone" ? "Use email instead" : "Use phone instead"
|
|
445
827
|
}
|
|
446
|
-
)
|
|
828
|
+
) : null
|
|
447
829
|
] }),
|
|
448
|
-
/* @__PURE__ */
|
|
830
|
+
mode === "phone" ? /* @__PURE__ */ jsxs2(
|
|
831
|
+
"div",
|
|
832
|
+
{
|
|
833
|
+
className: "sneek-otp-phone-field",
|
|
834
|
+
style: {
|
|
835
|
+
display: "flex",
|
|
836
|
+
alignItems: "stretch",
|
|
837
|
+
border: "1px solid var(--sneek-border)",
|
|
838
|
+
borderRadius: 12,
|
|
839
|
+
background: "var(--sneek-surface-2)",
|
|
840
|
+
transition: "border-color 200ms var(--sneek-ease)"
|
|
841
|
+
},
|
|
842
|
+
children: [
|
|
843
|
+
/* @__PURE__ */ jsx2(
|
|
844
|
+
CountryPicker,
|
|
845
|
+
{
|
|
846
|
+
country,
|
|
847
|
+
onSelect: onCountrySelect,
|
|
848
|
+
disabled: otp.isBusy
|
|
849
|
+
}
|
|
850
|
+
),
|
|
851
|
+
/* @__PURE__ */ jsx2(
|
|
852
|
+
"span",
|
|
853
|
+
{
|
|
854
|
+
"aria-hidden": "true",
|
|
855
|
+
style: {
|
|
856
|
+
width: 1,
|
|
857
|
+
margin: "9px 0",
|
|
858
|
+
background: "var(--sneek-border)"
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
),
|
|
862
|
+
/* @__PURE__ */ jsx2(
|
|
863
|
+
"input",
|
|
864
|
+
{
|
|
865
|
+
id: identifierId,
|
|
866
|
+
type: "tel",
|
|
867
|
+
inputMode: "tel",
|
|
868
|
+
autoComplete: "tel-national",
|
|
869
|
+
value: nationalNumber,
|
|
870
|
+
onChange: (e) => onNationalNumberChange(e.target.value),
|
|
871
|
+
placeholder: "Mobile number",
|
|
872
|
+
autoFocus: true,
|
|
873
|
+
required: true,
|
|
874
|
+
"aria-describedby": statusId,
|
|
875
|
+
style: {
|
|
876
|
+
...input,
|
|
877
|
+
flex: 1,
|
|
878
|
+
border: "none",
|
|
879
|
+
background: "transparent",
|
|
880
|
+
borderRadius: 0
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
)
|
|
884
|
+
]
|
|
885
|
+
}
|
|
886
|
+
) : /* @__PURE__ */ jsx2(
|
|
887
|
+
"input",
|
|
888
|
+
{
|
|
889
|
+
id: identifierId,
|
|
890
|
+
className: "sneek-otp-input",
|
|
891
|
+
type: "email",
|
|
892
|
+
value: otp.identifier,
|
|
893
|
+
onChange: (e) => otp.setIdentifier(e.target.value),
|
|
894
|
+
placeholder: identifierPlaceholder ?? "you@company.com",
|
|
895
|
+
autoComplete: "email",
|
|
896
|
+
autoFocus: true,
|
|
897
|
+
required: true,
|
|
898
|
+
"aria-describedby": statusId,
|
|
899
|
+
style: input
|
|
900
|
+
}
|
|
901
|
+
),
|
|
902
|
+
/* @__PURE__ */ jsx2(
|
|
449
903
|
"button",
|
|
450
904
|
{
|
|
451
905
|
type: "submit",
|
|
@@ -457,18 +911,18 @@ function SneekOtpLogin(props) {
|
|
|
457
911
|
)
|
|
458
912
|
]
|
|
459
913
|
}
|
|
460
|
-
) : /* @__PURE__ */
|
|
914
|
+
) : /* @__PURE__ */ jsxs2(
|
|
461
915
|
"form",
|
|
462
916
|
{
|
|
463
917
|
onSubmit: onVerifySubmit,
|
|
464
918
|
style: { display: "flex", flexDirection: "column", gap: 16, marginTop: 16 },
|
|
465
919
|
children: [
|
|
466
|
-
/* @__PURE__ */
|
|
920
|
+
/* @__PURE__ */ jsxs2("label", { style: { ...label, marginBottom: -8 }, htmlFor: codeId, children: [
|
|
467
921
|
CODE_LENGTH,
|
|
468
922
|
"-digit code"
|
|
469
923
|
] }),
|
|
470
|
-
/* @__PURE__ */
|
|
471
|
-
/* @__PURE__ */
|
|
924
|
+
/* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
|
|
925
|
+
/* @__PURE__ */ jsx2(
|
|
472
926
|
"input",
|
|
473
927
|
{
|
|
474
928
|
id: codeId,
|
|
@@ -498,7 +952,7 @@ function SneekOtpLogin(props) {
|
|
|
498
952
|
}
|
|
499
953
|
}
|
|
500
954
|
),
|
|
501
|
-
/* @__PURE__ */
|
|
955
|
+
/* @__PURE__ */ jsx2(
|
|
502
956
|
"div",
|
|
503
957
|
{
|
|
504
958
|
className: "sneek-otp-code-cells",
|
|
@@ -507,7 +961,7 @@ function SneekOtpLogin(props) {
|
|
|
507
961
|
children: Array.from({ length: CODE_LENGTH }).map((_, i) => {
|
|
508
962
|
const digit = otp.code[i];
|
|
509
963
|
const isActive = codeFocused && i === otp.code.length;
|
|
510
|
-
return /* @__PURE__ */
|
|
964
|
+
return /* @__PURE__ */ jsx2(
|
|
511
965
|
"div",
|
|
512
966
|
{
|
|
513
967
|
"data-active": isActive,
|
|
@@ -522,7 +976,7 @@ function SneekOtpLogin(props) {
|
|
|
522
976
|
borderBottom: `2px solid ${isActive ? "var(--sneek-accent)" : "var(--sneek-border)"}`,
|
|
523
977
|
transition: "border-color 200ms var(--sneek-ease)"
|
|
524
978
|
},
|
|
525
|
-
children: digit ?? (isActive ? /* @__PURE__ */
|
|
979
|
+
children: digit ?? (isActive ? /* @__PURE__ */ jsx2(
|
|
526
980
|
"span",
|
|
527
981
|
{
|
|
528
982
|
className: "sneek-otp-caret",
|
|
@@ -540,7 +994,7 @@ function SneekOtpLogin(props) {
|
|
|
540
994
|
}
|
|
541
995
|
)
|
|
542
996
|
] }),
|
|
543
|
-
/* @__PURE__ */
|
|
997
|
+
/* @__PURE__ */ jsx2(
|
|
544
998
|
"button",
|
|
545
999
|
{
|
|
546
1000
|
type: "submit",
|
|
@@ -553,7 +1007,7 @@ function SneekOtpLogin(props) {
|
|
|
553
1007
|
children: otp.isVerifying ? "Verifying\u2026" : "Verify"
|
|
554
1008
|
}
|
|
555
1009
|
),
|
|
556
|
-
/* @__PURE__ */
|
|
1010
|
+
/* @__PURE__ */ jsx2("div", { style: { textAlign: "center" }, children: resendIn > 0 ? /* @__PURE__ */ jsxs2(
|
|
557
1011
|
"span",
|
|
558
1012
|
{
|
|
559
1013
|
style: {
|
|
@@ -566,7 +1020,7 @@ function SneekOtpLogin(props) {
|
|
|
566
1020
|
"s"
|
|
567
1021
|
]
|
|
568
1022
|
}
|
|
569
|
-
) : /* @__PURE__ */
|
|
1023
|
+
) : /* @__PURE__ */ jsx2(
|
|
570
1024
|
"button",
|
|
571
1025
|
{
|
|
572
1026
|
type: "button",
|
|
@@ -580,7 +1034,7 @@ function SneekOtpLogin(props) {
|
|
|
580
1034
|
]
|
|
581
1035
|
}
|
|
582
1036
|
),
|
|
583
|
-
showBranding ? /* @__PURE__ */
|
|
1037
|
+
showBranding ? /* @__PURE__ */ jsxs2(
|
|
584
1038
|
"p",
|
|
585
1039
|
{
|
|
586
1040
|
style: {
|
|
@@ -590,7 +1044,26 @@ function SneekOtpLogin(props) {
|
|
|
590
1044
|
lineHeight: "16px",
|
|
591
1045
|
color: "var(--sneek-text-muted)"
|
|
592
1046
|
},
|
|
593
|
-
children:
|
|
1047
|
+
children: [
|
|
1048
|
+
"Secured by",
|
|
1049
|
+
" ",
|
|
1050
|
+
/* @__PURE__ */ jsx2(
|
|
1051
|
+
"a",
|
|
1052
|
+
{
|
|
1053
|
+
href: "https://sneek.in",
|
|
1054
|
+
target: "_blank",
|
|
1055
|
+
rel: "noopener noreferrer",
|
|
1056
|
+
className: "sneek-otp-brand-link",
|
|
1057
|
+
style: {
|
|
1058
|
+
color: "inherit",
|
|
1059
|
+
textDecoration: "underline",
|
|
1060
|
+
textUnderlineOffset: 2,
|
|
1061
|
+
transition: "color 200ms var(--sneek-ease)"
|
|
1062
|
+
},
|
|
1063
|
+
children: "Sneek"
|
|
1064
|
+
}
|
|
1065
|
+
)
|
|
1066
|
+
]
|
|
594
1067
|
}
|
|
595
1068
|
) : null
|
|
596
1069
|
]
|
|
@@ -626,10 +1099,16 @@ function createFetchHandlers(options = {}) {
|
|
|
626
1099
|
};
|
|
627
1100
|
}
|
|
628
1101
|
export {
|
|
1102
|
+
COUNTRIES,
|
|
1103
|
+
DEFAULT_COUNTRY_ISO2,
|
|
629
1104
|
SNEEK_ACCENT_DARK,
|
|
630
1105
|
SNEEK_ACCENT_LIGHT,
|
|
631
1106
|
SneekOtpLogin,
|
|
1107
|
+
countryByDialPrefix,
|
|
1108
|
+
countryByIso2,
|
|
632
1109
|
createFetchHandlers,
|
|
1110
|
+
detectCountryFromLocale,
|
|
1111
|
+
flagEmoji,
|
|
633
1112
|
formatChannels,
|
|
634
1113
|
initialOtpState,
|
|
635
1114
|
otpReducer,
|