@sneekin/ui 0.4.2 → 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 +43 -19
- package/dist/component.d.ts +5 -5
- package/dist/country-picker.d.ts +1 -1
- package/dist/fetch-handlers.d.ts +10 -9
- package/dist/index.d.ts +6 -6
- package/dist/index.js +147 -148
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +147 -148
- package/dist/index.mjs.map +1 -1
- package/dist/state.d.ts +34 -21
- package/dist/theme.d.ts +2 -2
- package/dist/use-sneek-login.d.ts +44 -0
- package/package.json +1 -1
- package/dist/use-sneek-otp.d.ts +0 -40
package/dist/index.js
CHANGED
|
@@ -24,35 +24,36 @@ __export(index_exports, {
|
|
|
24
24
|
DEFAULT_COUNTRY_ISO2: () => DEFAULT_COUNTRY_ISO2,
|
|
25
25
|
SNEEK_ACCENT_DARK: () => SNEEK_ACCENT_DARK,
|
|
26
26
|
SNEEK_ACCENT_LIGHT: () => SNEEK_ACCENT_LIGHT,
|
|
27
|
-
|
|
27
|
+
SneekLogin: () => SneekLogin,
|
|
28
|
+
channelLabel: () => channelLabel,
|
|
28
29
|
countryByDialPrefix: () => countryByDialPrefix,
|
|
29
30
|
countryByIso2: () => countryByIso2,
|
|
30
31
|
createFetchHandlers: () => createFetchHandlers,
|
|
31
32
|
detectCountryFromLocale: () => detectCountryFromLocale,
|
|
32
33
|
flagEmoji: () => flagEmoji,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
useSneekOtp: () => useSneekOtp
|
|
34
|
+
initialLoginState: () => initialLoginState,
|
|
35
|
+
loginReducer: () => loginReducer,
|
|
36
|
+
useSneekLogin: () => useSneekLogin
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(index_exports);
|
|
39
39
|
|
|
40
|
-
// src/use-sneek-
|
|
40
|
+
// src/use-sneek-login.ts
|
|
41
41
|
var import_react = require("react");
|
|
42
42
|
|
|
43
43
|
// src/state.ts
|
|
44
|
-
var
|
|
44
|
+
var initialLoginState = {
|
|
45
45
|
step: "identify",
|
|
46
46
|
status: "idle",
|
|
47
47
|
identifier: "",
|
|
48
48
|
code: "",
|
|
49
49
|
requestId: "",
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
channel: null,
|
|
51
|
+
maskedIdentifier: "",
|
|
52
|
+
expiresAt: "",
|
|
52
53
|
error: null
|
|
53
54
|
};
|
|
54
55
|
var isBusy = (status) => status !== "idle";
|
|
55
|
-
function
|
|
56
|
+
function loginReducer(state, action) {
|
|
56
57
|
switch (action.type) {
|
|
57
58
|
case "set_identifier":
|
|
58
59
|
return { ...state, identifier: action.value, error: null };
|
|
@@ -60,16 +61,17 @@ function otpReducer(state, action) {
|
|
|
60
61
|
return { ...state, code: action.value, error: null };
|
|
61
62
|
case "request_start":
|
|
62
63
|
if (isBusy(state.status)) return state;
|
|
63
|
-
return { ...state, status: "
|
|
64
|
+
return { ...state, status: "requesting", error: null };
|
|
64
65
|
case "request_success":
|
|
65
66
|
return {
|
|
66
67
|
...state,
|
|
67
68
|
step: "verify",
|
|
68
69
|
status: "idle",
|
|
69
70
|
code: "",
|
|
70
|
-
requestId: action.
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
requestId: action.request.requestId,
|
|
72
|
+
channel: action.request.channel ?? null,
|
|
73
|
+
maskedIdentifier: action.request.maskedIdentifier ?? "",
|
|
74
|
+
expiresAt: action.request.expiresAt ?? "",
|
|
73
75
|
error: null
|
|
74
76
|
};
|
|
75
77
|
case "request_error":
|
|
@@ -86,12 +88,13 @@ function otpReducer(state, action) {
|
|
|
86
88
|
status: "idle",
|
|
87
89
|
code: "",
|
|
88
90
|
requestId: "",
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
channel: null,
|
|
92
|
+
maskedIdentifier: "",
|
|
93
|
+
expiresAt: "",
|
|
91
94
|
error: null
|
|
92
95
|
};
|
|
93
96
|
case "reset":
|
|
94
|
-
return { ...
|
|
97
|
+
return { ...initialLoginState };
|
|
95
98
|
default:
|
|
96
99
|
return state;
|
|
97
100
|
}
|
|
@@ -101,8 +104,8 @@ var CHANNEL_LABELS = {
|
|
|
101
104
|
whatsapp: "WhatsApp",
|
|
102
105
|
email: "email"
|
|
103
106
|
};
|
|
104
|
-
function
|
|
105
|
-
return
|
|
107
|
+
function channelLabel(channel) {
|
|
108
|
+
return CHANNEL_LABELS[channel] ?? channel;
|
|
106
109
|
}
|
|
107
110
|
function toMessage(error, fallback) {
|
|
108
111
|
if (error instanceof Error && error.message) return error.message;
|
|
@@ -110,12 +113,12 @@ function toMessage(error, fallback) {
|
|
|
110
113
|
return fallback;
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
// src/use-sneek-
|
|
116
|
+
// src/use-sneek-login.ts
|
|
114
117
|
var DEFAULT_REQUEST_ERROR = "Could not send the code. Please try again.";
|
|
115
118
|
var DEFAULT_VERIFY_ERROR = "That code was not correct. Please try again.";
|
|
116
|
-
function
|
|
117
|
-
const [state, dispatch] = (0, import_react.useReducer)(
|
|
118
|
-
const {
|
|
119
|
+
function useSneekLogin(handlers) {
|
|
120
|
+
const [state, dispatch] = (0, import_react.useReducer)(loginReducer, initialLoginState);
|
|
121
|
+
const { requestVerification, verify, onSuccess, onError } = handlers;
|
|
119
122
|
const setIdentifier = (0, import_react.useCallback)((value) => {
|
|
120
123
|
dispatch({ type: "set_identifier", value });
|
|
121
124
|
}, []);
|
|
@@ -131,18 +134,21 @@ function useSneekOtp(handlers) {
|
|
|
131
134
|
}
|
|
132
135
|
dispatch({ type: "request_start" });
|
|
133
136
|
try {
|
|
134
|
-
const
|
|
135
|
-
dispatch({ type: "request_success",
|
|
137
|
+
const request = await requestVerification(trimmed);
|
|
138
|
+
dispatch({ type: "request_success", request });
|
|
136
139
|
} catch (error) {
|
|
137
140
|
dispatch({ type: "request_error", message: toMessage(error, DEFAULT_REQUEST_ERROR) });
|
|
138
141
|
onError?.(error);
|
|
139
142
|
}
|
|
140
143
|
},
|
|
141
|
-
[
|
|
144
|
+
[requestVerification, onError]
|
|
145
|
+
);
|
|
146
|
+
const startVerification = (0, import_react.useCallback)(
|
|
147
|
+
() => runRequest(state.identifier),
|
|
148
|
+
[runRequest, state.identifier]
|
|
142
149
|
);
|
|
143
|
-
const sendOtp = (0, import_react.useCallback)(() => runRequest(state.identifier), [runRequest, state.identifier]);
|
|
144
150
|
const resend = (0, import_react.useCallback)(() => runRequest(state.identifier), [runRequest, state.identifier]);
|
|
145
|
-
const
|
|
151
|
+
const submitCode = (0, import_react.useCallback)(async () => {
|
|
146
152
|
const code = state.code.trim();
|
|
147
153
|
if (!code) {
|
|
148
154
|
dispatch({ type: "verify_error", message: "Enter the code you received." });
|
|
@@ -150,13 +156,13 @@ function useSneekOtp(handlers) {
|
|
|
150
156
|
}
|
|
151
157
|
dispatch({ type: "verify_start" });
|
|
152
158
|
try {
|
|
153
|
-
const result = await
|
|
159
|
+
const result = await verify({ requestId: state.requestId, code });
|
|
154
160
|
onSuccess?.(result);
|
|
155
161
|
} catch (error) {
|
|
156
162
|
dispatch({ type: "verify_error", message: toMessage(error, DEFAULT_VERIFY_ERROR) });
|
|
157
163
|
onError?.(error);
|
|
158
164
|
}
|
|
159
|
-
}, [
|
|
165
|
+
}, [verify, state.code, state.requestId, onSuccess, onError]);
|
|
160
166
|
const back = (0, import_react.useCallback)(() => {
|
|
161
167
|
dispatch({ type: "back_to_identify" });
|
|
162
168
|
}, []);
|
|
@@ -164,11 +170,11 @@ function useSneekOtp(handlers) {
|
|
|
164
170
|
...state,
|
|
165
171
|
setIdentifier,
|
|
166
172
|
setCode,
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
startVerification,
|
|
174
|
+
submitCode,
|
|
169
175
|
back,
|
|
170
176
|
resend,
|
|
171
|
-
|
|
177
|
+
isRequesting: state.status === "requesting",
|
|
172
178
|
isVerifying: state.status === "verifying",
|
|
173
179
|
isBusy: state.status !== "idle"
|
|
174
180
|
};
|
|
@@ -371,7 +377,7 @@ function CountryPicker({ country, onSelect, disabled }) {
|
|
|
371
377
|
"button",
|
|
372
378
|
{
|
|
373
379
|
type: "button",
|
|
374
|
-
className: "sneek-
|
|
380
|
+
className: "sneek-login-country-chip",
|
|
375
381
|
onClick: () => setOpen((o) => !o),
|
|
376
382
|
disabled,
|
|
377
383
|
"aria-haspopup": "listbox",
|
|
@@ -510,7 +516,7 @@ function CountryPicker({ country, onSelect, disabled }) {
|
|
|
510
516
|
}
|
|
511
517
|
|
|
512
518
|
// src/theme.ts
|
|
513
|
-
var SNEEK_STYLE_ID = "sneek-
|
|
519
|
+
var SNEEK_STYLE_ID = "sneek-login-styles";
|
|
514
520
|
var SNEEK_ACCENT_DARK = "#f86513";
|
|
515
521
|
var SNEEK_ACCENT_LIGHT = "#b83f06";
|
|
516
522
|
function buildStyles(accent) {
|
|
@@ -542,56 +548,56 @@ function buildStyles(accent) {
|
|
|
542
548
|
--sneek-danger: #d43a3c;
|
|
543
549
|
--sneek-shadow: none;`;
|
|
544
550
|
return `
|
|
545
|
-
.sneek-
|
|
551
|
+
.sneek-login {${light}
|
|
546
552
|
--sneek-ease: cubic-bezier(0, 0, 0.2, 1);
|
|
547
553
|
}
|
|
548
554
|
@media (prefers-color-scheme: dark) {
|
|
549
|
-
.sneek-
|
|
555
|
+
.sneek-login:not([data-theme="light"]) {${dark}
|
|
550
556
|
}
|
|
551
557
|
}
|
|
552
|
-
.sneek-
|
|
558
|
+
.sneek-login[data-theme="dark"] {${dark}
|
|
553
559
|
}
|
|
554
|
-
.sneek-
|
|
560
|
+
.sneek-login[data-theme="light"] {${light}
|
|
555
561
|
}
|
|
556
|
-
.sneek-
|
|
557
|
-
.sneek-
|
|
558
|
-
.sneek-
|
|
559
|
-
.sneek-
|
|
560
|
-
.sneek-
|
|
561
|
-
.sneek-
|
|
562
|
+
.sneek-login *, .sneek-login *::before, .sneek-login *::after { box-sizing: border-box; }
|
|
563
|
+
.sneek-login-input:focus-visible,
|
|
564
|
+
.sneek-login-button:focus-visible,
|
|
565
|
+
.sneek-login-link:focus-visible,
|
|
566
|
+
.sneek-login-country-chip:focus-visible,
|
|
567
|
+
.sneek-login-brand-link:focus-visible {
|
|
562
568
|
outline: 2px solid var(--sneek-accent);
|
|
563
569
|
outline-offset: 2px;
|
|
564
570
|
}
|
|
565
|
-
.sneek-
|
|
571
|
+
.sneek-login-input:focus {
|
|
566
572
|
border-color: var(--sneek-accent);
|
|
567
573
|
}
|
|
568
|
-
.sneek-
|
|
574
|
+
.sneek-login-phone-field:focus-within {
|
|
569
575
|
border-color: var(--sneek-accent);
|
|
570
576
|
}
|
|
571
|
-
.sneek-
|
|
577
|
+
.sneek-login-button:not(:disabled):hover {
|
|
572
578
|
filter: brightness(1.08);
|
|
573
579
|
}
|
|
574
|
-
.sneek-
|
|
580
|
+
.sneek-login-link:not(:disabled):hover {
|
|
575
581
|
text-decoration: underline;
|
|
576
582
|
}
|
|
577
|
-
.sneek-
|
|
583
|
+
.sneek-login-country-chip:not(:disabled):hover {
|
|
578
584
|
filter: brightness(1.08);
|
|
579
585
|
}
|
|
580
|
-
.sneek-
|
|
586
|
+
.sneek-login-brand-link {
|
|
581
587
|
box-shadow: 0 1px 6px var(--sneek-accent-shadow);
|
|
582
588
|
}
|
|
583
|
-
.sneek-
|
|
589
|
+
.sneek-login-brand-link:hover {
|
|
584
590
|
box-shadow: 0 2px 10px var(--sneek-accent-shadow);
|
|
585
591
|
}
|
|
586
592
|
@media (prefers-reduced-motion: reduce) {
|
|
587
|
-
.sneek-
|
|
593
|
+
.sneek-login * { transition-duration: 0.01ms !important; }
|
|
588
594
|
}
|
|
589
|
-
@keyframes sneek-
|
|
595
|
+
@keyframes sneek-login-caret {
|
|
590
596
|
0%, 45% { opacity: 1; }
|
|
591
597
|
50%, 100% { opacity: 0; }
|
|
592
598
|
}
|
|
593
|
-
.sneek-
|
|
594
|
-
animation: sneek-
|
|
599
|
+
.sneek-login-caret {
|
|
600
|
+
animation: sneek-login-caret 1s step-end infinite;
|
|
595
601
|
}
|
|
596
602
|
`;
|
|
597
603
|
}
|
|
@@ -635,11 +641,11 @@ function SneekMarkBox() {
|
|
|
635
641
|
}
|
|
636
642
|
);
|
|
637
643
|
}
|
|
638
|
-
function
|
|
644
|
+
function SneekLogin(props) {
|
|
639
645
|
const {
|
|
640
646
|
title = "Sign in",
|
|
641
647
|
verifyTitle = "Enter the code",
|
|
642
|
-
subtitle = "Enter your email or phone
|
|
648
|
+
subtitle = "Enter your email or phone to continue.",
|
|
643
649
|
identifierMode = "both",
|
|
644
650
|
defaultCountry,
|
|
645
651
|
identifierLabel,
|
|
@@ -652,7 +658,7 @@ function SneekOtpLogin(props) {
|
|
|
652
658
|
className,
|
|
653
659
|
...handlers
|
|
654
660
|
} = props;
|
|
655
|
-
const
|
|
661
|
+
const login = useSneekLogin(handlers);
|
|
656
662
|
const uid = (0, import_react3.useId)();
|
|
657
663
|
const [resendIn, setResendIn] = (0, import_react3.useState)(0);
|
|
658
664
|
const [codeFocused, setCodeFocused] = (0, import_react3.useState)(false);
|
|
@@ -668,7 +674,7 @@ function SneekOtpLogin(props) {
|
|
|
668
674
|
const statusId = `${uid}-status`;
|
|
669
675
|
const onCountrySelect = (next) => {
|
|
670
676
|
setCountry(next);
|
|
671
|
-
|
|
677
|
+
login.setIdentifier(`+${next.dial}${nationalNumber}`);
|
|
672
678
|
};
|
|
673
679
|
const onNationalNumberChange = (raw) => {
|
|
674
680
|
let digits = raw.replace(/\D/g, "").slice(0, MAX_NATIONAL_DIGITS + 3);
|
|
@@ -683,12 +689,12 @@ function SneekOtpLogin(props) {
|
|
|
683
689
|
digits = digits.slice(0, MAX_NATIONAL_DIGITS);
|
|
684
690
|
if (effectiveCountry !== country) setCountry(effectiveCountry);
|
|
685
691
|
setNationalNumber(digits);
|
|
686
|
-
|
|
692
|
+
login.setIdentifier(`+${effectiveCountry.dial}${digits}`);
|
|
687
693
|
};
|
|
688
694
|
const switchMode = (next) => {
|
|
689
695
|
setMode(next);
|
|
690
696
|
setNationalNumber("");
|
|
691
|
-
|
|
697
|
+
login.setIdentifier("");
|
|
692
698
|
};
|
|
693
699
|
(0, import_react3.useEffect)(() => {
|
|
694
700
|
if (typeof document === "undefined") return;
|
|
@@ -702,25 +708,27 @@ function SneekOtpLogin(props) {
|
|
|
702
708
|
}, [accentColor]);
|
|
703
709
|
const onIdentifySubmit = (event) => {
|
|
704
710
|
event.preventDefault();
|
|
705
|
-
void
|
|
711
|
+
void login.startVerification();
|
|
706
712
|
};
|
|
707
713
|
const onVerifySubmit = (event) => {
|
|
708
714
|
event.preventDefault();
|
|
709
|
-
void
|
|
715
|
+
void login.submitCode();
|
|
710
716
|
};
|
|
711
717
|
(0, import_react3.useEffect)(() => {
|
|
712
|
-
if (
|
|
713
|
-
}, [
|
|
718
|
+
if (login.step === "verify") setResendIn(RESEND_SECONDS);
|
|
719
|
+
}, [login.step]);
|
|
714
720
|
(0, import_react3.useEffect)(() => {
|
|
715
721
|
if (resendIn <= 0) return;
|
|
716
722
|
const timer = setTimeout(() => setResendIn((n) => n - 1), 1e3);
|
|
717
723
|
return () => clearTimeout(timer);
|
|
718
724
|
}, [resendIn]);
|
|
719
725
|
(0, import_react3.useEffect)(() => {
|
|
720
|
-
if (
|
|
721
|
-
void
|
|
726
|
+
if (login.step === "verify" && login.code.length === CODE_LENGTH && !login.isBusy) {
|
|
727
|
+
void login.submitCode();
|
|
722
728
|
}
|
|
723
|
-
}, [
|
|
729
|
+
}, [login.code, login.step]);
|
|
730
|
+
const destination = login.maskedIdentifier || login.identifier;
|
|
731
|
+
const deliveryNotice = login.channel ? `Sent via ${channelLabel(login.channel)} to ${destination}` : `Sent to ${destination}`;
|
|
724
732
|
const label = {
|
|
725
733
|
display: "flex",
|
|
726
734
|
flexDirection: "column",
|
|
@@ -748,8 +756,8 @@ function SneekOtpLogin(props) {
|
|
|
748
756
|
color: "var(--sneek-accent-contrast)",
|
|
749
757
|
fontSize: 15,
|
|
750
758
|
fontWeight: 500,
|
|
751
|
-
cursor:
|
|
752
|
-
opacity:
|
|
759
|
+
cursor: login.isBusy ? "not-allowed" : "pointer",
|
|
760
|
+
opacity: login.isBusy ? 0.6 : 1,
|
|
753
761
|
width: "100%",
|
|
754
762
|
transition: "filter 200ms var(--sneek-ease), opacity 200ms var(--sneek-ease)"
|
|
755
763
|
};
|
|
@@ -757,7 +765,7 @@ function SneekOtpLogin(props) {
|
|
|
757
765
|
border: "none",
|
|
758
766
|
background: "transparent",
|
|
759
767
|
color: "var(--sneek-accent)",
|
|
760
|
-
cursor:
|
|
768
|
+
cursor: login.isBusy ? "not-allowed" : "pointer",
|
|
761
769
|
font: "inherit",
|
|
762
770
|
fontSize: 14,
|
|
763
771
|
padding: 0
|
|
@@ -773,7 +781,7 @@ function SneekOtpLogin(props) {
|
|
|
773
781
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
774
782
|
"div",
|
|
775
783
|
{
|
|
776
|
-
className: className ? `sneek-
|
|
784
|
+
className: className ? `sneek-login ${className}` : "sneek-login",
|
|
777
785
|
"data-theme": theme === "auto" ? void 0 : theme,
|
|
778
786
|
style: {
|
|
779
787
|
maxWidth: 400,
|
|
@@ -800,37 +808,10 @@ function SneekOtpLogin(props) {
|
|
|
800
808
|
letterSpacing: "-0.01em",
|
|
801
809
|
margin: "16px 0 0"
|
|
802
810
|
},
|
|
803
|
-
children:
|
|
811
|
+
children: login.step === "verify" ? verifyTitle : title
|
|
804
812
|
}
|
|
805
813
|
),
|
|
806
|
-
|
|
807
|
-
"p",
|
|
808
|
-
{
|
|
809
|
-
style: {
|
|
810
|
-
fontSize: 14,
|
|
811
|
-
lineHeight: "20px",
|
|
812
|
-
color: "var(--sneek-text-muted)",
|
|
813
|
-
margin: "8px 0 0"
|
|
814
|
-
},
|
|
815
|
-
children: [
|
|
816
|
-
"Sent to",
|
|
817
|
-
" ",
|
|
818
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "var(--sneek-text)" }, children: otp.identifier }),
|
|
819
|
-
" \xB7 ",
|
|
820
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
821
|
-
"button",
|
|
822
|
-
{
|
|
823
|
-
type: "button",
|
|
824
|
-
className: "sneek-otp-link",
|
|
825
|
-
onClick: otp.back,
|
|
826
|
-
disabled: otp.isBusy,
|
|
827
|
-
style: { ...link, fontSize: 14 },
|
|
828
|
-
children: "Change"
|
|
829
|
-
}
|
|
830
|
-
)
|
|
831
|
-
]
|
|
832
|
-
}
|
|
833
|
-
) : subtitle ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
814
|
+
login.step === "identify" && subtitle ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
834
815
|
"p",
|
|
835
816
|
{
|
|
836
817
|
style: {
|
|
@@ -842,7 +823,7 @@ function SneekOtpLogin(props) {
|
|
|
842
823
|
children: subtitle
|
|
843
824
|
}
|
|
844
825
|
) : null,
|
|
845
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { role: "status", "aria-live": "polite", id: statusId, style: { marginTop: 32 }, children:
|
|
826
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { role: "status", "aria-live": "polite", id: statusId, style: { marginTop: 32 }, children: login.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
846
827
|
"div",
|
|
847
828
|
{
|
|
848
829
|
role: "alert",
|
|
@@ -851,10 +832,10 @@ function SneekOtpLogin(props) {
|
|
|
851
832
|
color: "var(--sneek-danger)",
|
|
852
833
|
borderColor: "var(--sneek-danger)"
|
|
853
834
|
},
|
|
854
|
-
children:
|
|
835
|
+
children: login.error
|
|
855
836
|
}
|
|
856
|
-
) : null }),
|
|
857
|
-
|
|
837
|
+
) : login.step === "verify" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: notice, children: deliveryNotice }) : null }),
|
|
838
|
+
login.step === "identify" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
858
839
|
"form",
|
|
859
840
|
{
|
|
860
841
|
onSubmit: onIdentifySubmit,
|
|
@@ -866,9 +847,9 @@ function SneekOtpLogin(props) {
|
|
|
866
847
|
"button",
|
|
867
848
|
{
|
|
868
849
|
type: "button",
|
|
869
|
-
className: "sneek-
|
|
850
|
+
className: "sneek-login-link",
|
|
870
851
|
onClick: () => switchMode(mode === "phone" ? "email" : "phone"),
|
|
871
|
-
disabled:
|
|
852
|
+
disabled: login.isBusy,
|
|
872
853
|
style: { ...link, fontSize: 13 },
|
|
873
854
|
children: mode === "phone" ? "Use email instead" : "Use phone instead"
|
|
874
855
|
}
|
|
@@ -877,7 +858,7 @@ function SneekOtpLogin(props) {
|
|
|
877
858
|
mode === "phone" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
878
859
|
"div",
|
|
879
860
|
{
|
|
880
|
-
className: "sneek-
|
|
861
|
+
className: "sneek-login-phone-field",
|
|
881
862
|
style: {
|
|
882
863
|
display: "flex",
|
|
883
864
|
alignItems: "stretch",
|
|
@@ -892,7 +873,7 @@ function SneekOtpLogin(props) {
|
|
|
892
873
|
{
|
|
893
874
|
country,
|
|
894
875
|
onSelect: onCountrySelect,
|
|
895
|
-
disabled:
|
|
876
|
+
disabled: login.isBusy
|
|
896
877
|
}
|
|
897
878
|
),
|
|
898
879
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -934,10 +915,10 @@ function SneekOtpLogin(props) {
|
|
|
934
915
|
"input",
|
|
935
916
|
{
|
|
936
917
|
id: identifierId,
|
|
937
|
-
className: "sneek-
|
|
918
|
+
className: "sneek-login-input",
|
|
938
919
|
type: "email",
|
|
939
|
-
value:
|
|
940
|
-
onChange: (e) =>
|
|
920
|
+
value: login.identifier,
|
|
921
|
+
onChange: (e) => login.setIdentifier(e.target.value),
|
|
941
922
|
placeholder: identifierPlaceholder ?? "you@company.com",
|
|
942
923
|
autoComplete: "email",
|
|
943
924
|
autoFocus: true,
|
|
@@ -950,10 +931,10 @@ function SneekOtpLogin(props) {
|
|
|
950
931
|
"button",
|
|
951
932
|
{
|
|
952
933
|
type: "submit",
|
|
953
|
-
className: "sneek-
|
|
954
|
-
disabled:
|
|
934
|
+
className: "sneek-login-button",
|
|
935
|
+
disabled: login.isBusy,
|
|
955
936
|
style: button,
|
|
956
|
-
children:
|
|
937
|
+
children: login.isRequesting ? "Working\u2026" : "Continue"
|
|
957
938
|
}
|
|
958
939
|
)
|
|
959
940
|
]
|
|
@@ -973,14 +954,14 @@ function SneekOtpLogin(props) {
|
|
|
973
954
|
"input",
|
|
974
955
|
{
|
|
975
956
|
id: codeId,
|
|
976
|
-
className: "sneek-
|
|
957
|
+
className: "sneek-login-code-input",
|
|
977
958
|
type: "text",
|
|
978
959
|
inputMode: "numeric",
|
|
979
960
|
pattern: "[0-9]*",
|
|
980
961
|
autoComplete: "one-time-code",
|
|
981
962
|
maxLength: CODE_LENGTH,
|
|
982
|
-
value:
|
|
983
|
-
onChange: (e) =>
|
|
963
|
+
value: login.code,
|
|
964
|
+
onChange: (e) => login.setCode(
|
|
984
965
|
e.target.value.replace(/\D/g, "").slice(0, CODE_LENGTH)
|
|
985
966
|
),
|
|
986
967
|
onFocus: () => setCodeFocused(true),
|
|
@@ -1002,12 +983,12 @@ function SneekOtpLogin(props) {
|
|
|
1002
983
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1003
984
|
"div",
|
|
1004
985
|
{
|
|
1005
|
-
className: "sneek-
|
|
986
|
+
className: "sneek-login-code-cells",
|
|
1006
987
|
"aria-hidden": "true",
|
|
1007
988
|
style: { display: "flex", gap: 10 },
|
|
1008
989
|
children: Array.from({ length: CODE_LENGTH }).map((_, i) => {
|
|
1009
|
-
const digit =
|
|
1010
|
-
const isActive = codeFocused && i ===
|
|
990
|
+
const digit = login.code[i];
|
|
991
|
+
const isActive = codeFocused && i === login.code.length;
|
|
1011
992
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1012
993
|
"div",
|
|
1013
994
|
{
|
|
@@ -1026,7 +1007,7 @@ function SneekOtpLogin(props) {
|
|
|
1026
1007
|
children: digit ?? (isActive ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1027
1008
|
"span",
|
|
1028
1009
|
{
|
|
1029
|
-
className: "sneek-
|
|
1010
|
+
className: "sneek-login-caret",
|
|
1030
1011
|
style: {
|
|
1031
1012
|
width: 2,
|
|
1032
1013
|
height: 22,
|
|
@@ -1045,39 +1026,57 @@ function SneekOtpLogin(props) {
|
|
|
1045
1026
|
"button",
|
|
1046
1027
|
{
|
|
1047
1028
|
type: "submit",
|
|
1048
|
-
className: "sneek-
|
|
1049
|
-
disabled:
|
|
1029
|
+
className: "sneek-login-button",
|
|
1030
|
+
disabled: login.isBusy || login.code.length < CODE_LENGTH,
|
|
1050
1031
|
style: {
|
|
1051
1032
|
...button,
|
|
1052
|
-
opacity:
|
|
1033
|
+
opacity: login.isBusy || login.code.length < CODE_LENGTH ? 0.6 : 1
|
|
1053
1034
|
},
|
|
1054
|
-
children:
|
|
1035
|
+
children: login.isVerifying ? "Verifying\u2026" : "Verify"
|
|
1055
1036
|
}
|
|
1056
1037
|
),
|
|
1057
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
1058
|
-
"
|
|
1038
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1039
|
+
"div",
|
|
1059
1040
|
{
|
|
1060
1041
|
style: {
|
|
1042
|
+
display: "flex",
|
|
1043
|
+
justifyContent: "center",
|
|
1044
|
+
alignItems: "center",
|
|
1045
|
+
gap: 10,
|
|
1061
1046
|
fontSize: 14,
|
|
1062
1047
|
color: "var(--sneek-text-muted)"
|
|
1063
1048
|
},
|
|
1064
1049
|
children: [
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1050
|
+
resendIn > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
1051
|
+
"Send again in ",
|
|
1052
|
+
resendIn,
|
|
1053
|
+
"s"
|
|
1054
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1055
|
+
"button",
|
|
1056
|
+
{
|
|
1057
|
+
type: "button",
|
|
1058
|
+
className: "sneek-login-link",
|
|
1059
|
+
onClick: () => void login.resend(),
|
|
1060
|
+
disabled: login.isBusy,
|
|
1061
|
+
style: link,
|
|
1062
|
+
children: "Send again"
|
|
1063
|
+
}
|
|
1064
|
+
),
|
|
1065
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1067
|
+
"button",
|
|
1068
|
+
{
|
|
1069
|
+
type: "button",
|
|
1070
|
+
className: "sneek-login-link",
|
|
1071
|
+
onClick: login.back,
|
|
1072
|
+
disabled: login.isBusy,
|
|
1073
|
+
style: link,
|
|
1074
|
+
children: "Use a different email or phone"
|
|
1075
|
+
}
|
|
1076
|
+
)
|
|
1068
1077
|
]
|
|
1069
1078
|
}
|
|
1070
|
-
)
|
|
1071
|
-
"button",
|
|
1072
|
-
{
|
|
1073
|
-
type: "button",
|
|
1074
|
-
className: "sneek-otp-link",
|
|
1075
|
-
onClick: () => void otp.resend(),
|
|
1076
|
-
disabled: otp.isBusy,
|
|
1077
|
-
style: link,
|
|
1078
|
-
children: "Resend code"
|
|
1079
|
-
}
|
|
1080
|
-
) })
|
|
1079
|
+
)
|
|
1081
1080
|
]
|
|
1082
1081
|
}
|
|
1083
1082
|
),
|
|
@@ -1100,7 +1099,7 @@ function SneekOtpLogin(props) {
|
|
|
1100
1099
|
href: "https://sneek.in",
|
|
1101
1100
|
target: "_blank",
|
|
1102
1101
|
rel: "noopener noreferrer",
|
|
1103
|
-
className: "sneek-
|
|
1102
|
+
className: "sneek-login-brand-link",
|
|
1104
1103
|
style: {
|
|
1105
1104
|
display: "inline-block",
|
|
1106
1105
|
padding: "0 5px",
|
|
@@ -1141,11 +1140,11 @@ async function postJson(url, body, options) {
|
|
|
1141
1140
|
return await response.json();
|
|
1142
1141
|
}
|
|
1143
1142
|
function createFetchHandlers(options = {}) {
|
|
1144
|
-
const requestUrl = options.requestUrl ?? "/api/auth/request
|
|
1145
|
-
const verifyUrl = options.verifyUrl ?? "/api/auth/verify
|
|
1143
|
+
const requestUrl = options.requestUrl ?? "/api/auth/request";
|
|
1144
|
+
const verifyUrl = options.verifyUrl ?? "/api/auth/verify";
|
|
1146
1145
|
return {
|
|
1147
|
-
|
|
1148
|
-
|
|
1146
|
+
requestVerification: (identifier) => postJson(requestUrl, { identifier }, options),
|
|
1147
|
+
verify: (input) => postJson(verifyUrl, input, options)
|
|
1149
1148
|
};
|
|
1150
1149
|
}
|
|
1151
1150
|
//# sourceMappingURL=index.js.map
|