@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/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
- SneekOtpLogin: () => SneekOtpLogin,
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
- formatChannels: () => formatChannels,
34
- initialOtpState: () => initialOtpState,
35
- otpReducer: () => otpReducer,
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-otp.ts
40
+ // src/use-sneek-login.ts
41
41
  var import_react = require("react");
42
42
 
43
43
  // src/state.ts
44
- var initialOtpState = {
44
+ var initialLoginState = {
45
45
  step: "identify",
46
46
  status: "idle",
47
47
  identifier: "",
48
48
  code: "",
49
49
  requestId: "",
50
- channels: [],
51
- expiresInSeconds: 0,
50
+ channel: null,
51
+ maskedIdentifier: "",
52
+ expiresAt: "",
52
53
  error: null
53
54
  };
54
55
  var isBusy = (status) => status !== "idle";
55
- function otpReducer(state, action) {
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: "sending", error: null };
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.result.requestId,
71
- channels: action.result.channels ?? [],
72
- expiresInSeconds: action.result.expiresInSeconds ?? 0,
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
- channels: [],
90
- expiresInSeconds: 0,
91
+ channel: null,
92
+ maskedIdentifier: "",
93
+ expiresAt: "",
91
94
  error: null
92
95
  };
93
96
  case "reset":
94
- return { ...initialOtpState };
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 formatChannels(channels) {
105
- return channels.map((c) => CHANNEL_LABELS[c] ?? c).join(", ");
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-otp.ts
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 useSneekOtp(handlers) {
117
- const [state, dispatch] = (0, import_react.useReducer)(otpReducer, initialOtpState);
118
- const { requestOtp, verifyOtp, onSuccess, onError } = handlers;
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 result = await requestOtp(trimmed);
135
- dispatch({ type: "request_success", result });
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
- [requestOtp, onError]
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 verify = (0, import_react.useCallback)(async () => {
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 verifyOtp({ requestId: state.requestId, code });
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
- }, [verifyOtp, state.code, state.requestId, onSuccess, onError]);
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
- sendOtp,
168
- verify,
173
+ startVerification,
174
+ submitCode,
169
175
  back,
170
176
  resend,
171
- isSending: state.status === "sending",
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-otp-country-chip",
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-otp-styles";
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-otp {${light}
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-otp:not([data-theme="light"]) {${dark}
555
+ .sneek-login:not([data-theme="light"]) {${dark}
550
556
  }
551
557
  }
552
- .sneek-otp[data-theme="dark"] {${dark}
558
+ .sneek-login[data-theme="dark"] {${dark}
553
559
  }
554
- .sneek-otp[data-theme="light"] {${light}
560
+ .sneek-login[data-theme="light"] {${light}
555
561
  }
556
- .sneek-otp *, .sneek-otp *::before, .sneek-otp *::after { box-sizing: border-box; }
557
- .sneek-otp-input:focus-visible,
558
- .sneek-otp-button:focus-visible,
559
- .sneek-otp-link:focus-visible,
560
- .sneek-otp-country-chip:focus-visible,
561
- .sneek-otp-brand-link:focus-visible {
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-otp-input:focus {
571
+ .sneek-login-input:focus {
566
572
  border-color: var(--sneek-accent);
567
573
  }
568
- .sneek-otp-phone-field:focus-within {
574
+ .sneek-login-phone-field:focus-within {
569
575
  border-color: var(--sneek-accent);
570
576
  }
571
- .sneek-otp-button:not(:disabled):hover {
577
+ .sneek-login-button:not(:disabled):hover {
572
578
  filter: brightness(1.08);
573
579
  }
574
- .sneek-otp-link:not(:disabled):hover {
580
+ .sneek-login-link:not(:disabled):hover {
575
581
  text-decoration: underline;
576
582
  }
577
- .sneek-otp-country-chip:not(:disabled):hover {
583
+ .sneek-login-country-chip:not(:disabled):hover {
578
584
  filter: brightness(1.08);
579
585
  }
580
- .sneek-otp-brand-link {
586
+ .sneek-login-brand-link {
581
587
  box-shadow: 0 1px 6px var(--sneek-accent-shadow);
582
588
  }
583
- .sneek-otp-brand-link:hover {
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-otp * { transition-duration: 0.01ms !important; }
593
+ .sneek-login * { transition-duration: 0.01ms !important; }
588
594
  }
589
- @keyframes sneek-otp-caret {
595
+ @keyframes sneek-login-caret {
590
596
  0%, 45% { opacity: 1; }
591
597
  50%, 100% { opacity: 0; }
592
598
  }
593
- .sneek-otp-caret {
594
- animation: sneek-otp-caret 1s step-end infinite;
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 SneekOtpLogin(props) {
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. We will send you a code.",
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 otp = useSneekOtp(handlers);
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
- otp.setIdentifier(`+${next.dial}${nationalNumber}`);
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
- otp.setIdentifier(`+${effectiveCountry.dial}${digits}`);
692
+ login.setIdentifier(`+${effectiveCountry.dial}${digits}`);
687
693
  };
688
694
  const switchMode = (next) => {
689
695
  setMode(next);
690
696
  setNationalNumber("");
691
- otp.setIdentifier("");
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 otp.sendOtp();
711
+ void login.startVerification();
706
712
  };
707
713
  const onVerifySubmit = (event) => {
708
714
  event.preventDefault();
709
- void otp.verify();
715
+ void login.submitCode();
710
716
  };
711
717
  (0, import_react3.useEffect)(() => {
712
- if (otp.step === "verify") setResendIn(RESEND_SECONDS);
713
- }, [otp.step]);
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 (otp.step === "verify" && otp.code.length === CODE_LENGTH && !otp.isBusy) {
721
- void otp.verify();
726
+ if (login.step === "verify" && login.code.length === CODE_LENGTH && !login.isBusy) {
727
+ void login.submitCode();
722
728
  }
723
- }, [otp.code, otp.step]);
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: otp.isBusy ? "not-allowed" : "pointer",
752
- opacity: otp.isBusy ? 0.6 : 1,
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: otp.isBusy ? "not-allowed" : "pointer",
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-otp ${className}` : "sneek-otp",
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: otp.step === "verify" ? verifyTitle : title
811
+ children: login.step === "verify" ? verifyTitle : title
804
812
  }
805
813
  ),
806
- otp.step === "verify" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
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: otp.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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: otp.error
835
+ children: login.error
855
836
  }
856
- ) : null }),
857
- otp.step === "identify" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
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-otp-link",
850
+ className: "sneek-login-link",
870
851
  onClick: () => switchMode(mode === "phone" ? "email" : "phone"),
871
- disabled: otp.isBusy,
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-otp-phone-field",
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: otp.isBusy
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-otp-input",
918
+ className: "sneek-login-input",
938
919
  type: "email",
939
- value: otp.identifier,
940
- onChange: (e) => otp.setIdentifier(e.target.value),
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-otp-button",
954
- disabled: otp.isBusy,
934
+ className: "sneek-login-button",
935
+ disabled: login.isBusy,
955
936
  style: button,
956
- children: otp.isSending ? "Sending code\u2026" : "Send code"
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-otp-code-input",
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: otp.code,
983
- onChange: (e) => otp.setCode(
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-otp-code-cells",
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 = otp.code[i];
1010
- const isActive = codeFocused && i === otp.code.length;
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-otp-caret",
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-otp-button",
1049
- disabled: otp.isBusy || otp.code.length < CODE_LENGTH,
1029
+ className: "sneek-login-button",
1030
+ disabled: login.isBusy || login.code.length < CODE_LENGTH,
1050
1031
  style: {
1051
1032
  ...button,
1052
- opacity: otp.isBusy || otp.code.length < CODE_LENGTH ? 0.6 : 1
1033
+ opacity: login.isBusy || login.code.length < CODE_LENGTH ? 0.6 : 1
1053
1034
  },
1054
- children: otp.isVerifying ? "Verifying\u2026" : "Verify"
1035
+ children: login.isVerifying ? "Verifying\u2026" : "Verify"
1055
1036
  }
1056
1037
  ),
1057
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { textAlign: "center" }, children: resendIn > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1058
- "span",
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
- "Resend code in ",
1066
- resendIn,
1067
- "s"
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
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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-otp-brand-link",
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-otp";
1145
- const verifyUrl = options.verifyUrl ?? "/api/auth/verify-otp";
1143
+ const requestUrl = options.requestUrl ?? "/api/auth/request";
1144
+ const verifyUrl = options.verifyUrl ?? "/api/auth/verify";
1146
1145
  return {
1147
- requestOtp: (identifier) => postJson(requestUrl, { identifier }, options),
1148
- verifyOtp: (input) => postJson(verifyUrl, input, options)
1146
+ requestVerification: (identifier) => postJson(requestUrl, { identifier }, options),
1147
+ verify: (input) => postJson(verifyUrl, input, options)
1149
1148
  };
1150
1149
  }
1151
1150
  //# sourceMappingURL=index.js.map