@crossmint/client-sdk-react-ui 1.8.0 → 1.9.2

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.
Files changed (40) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.d.cts +6 -4
  3. package/dist/index.d.ts +6 -4
  4. package/dist/index.js +1 -1
  5. package/package.json +10 -6
  6. package/src/components/auth/AuthForm.tsx +50 -0
  7. package/src/components/auth/AuthFormBackButton.tsx +26 -0
  8. package/src/components/auth/AuthFormDialog.tsx +33 -0
  9. package/src/components/auth/EmbeddedAuthForm.tsx +5 -0
  10. package/src/components/auth/methods/email/EmailAuthFlow.tsx +19 -0
  11. package/src/components/auth/methods/email/EmailOTPInput.tsx +123 -0
  12. package/src/components/auth/methods/email/EmailSignIn.tsx +113 -0
  13. package/src/components/auth/methods/farcaster/FarcasterSignIn.tsx +170 -0
  14. package/src/components/auth/methods/google/GoogleSignIn.tsx +62 -0
  15. package/src/components/common/Dialog.tsx +141 -0
  16. package/src/components/common/Divider.tsx +25 -0
  17. package/src/components/common/InputOTP.tsx +89 -0
  18. package/src/components/common/PoweredByCrossmint.tsx +4 -9
  19. package/src/components/common/Spinner.tsx +22 -0
  20. package/src/components/dynamic-xyz/DynamicContextProviderWrapper.tsx +12 -2
  21. package/src/components/embed/v3/EmbeddedCheckoutV3IFrame.tsx +6 -1
  22. package/src/components/embed/v3/crypto/CryptoWalletConnectionHandler.tsx +11 -3
  23. package/src/components/index.ts +2 -1
  24. package/src/hooks/useAuthSignIn.ts +117 -0
  25. package/src/hooks/useOAuthWindowListener.ts +87 -0
  26. package/src/icons/alert.tsx +19 -0
  27. package/src/icons/discord.tsx +18 -0
  28. package/src/icons/emailOTP.tsx +147 -0
  29. package/src/icons/farcaster.tsx +26 -0
  30. package/src/icons/google.tsx +30 -0
  31. package/src/icons/leftArrow.tsx +20 -0
  32. package/src/icons/poweredByLeaf.tsx +2 -2
  33. package/src/providers/CrossmintAuthProvider.tsx +24 -25
  34. package/src/providers/CrossmintWalletProvider.tsx +3 -3
  35. package/src/providers/auth/AuthFormProvider.test.tsx +105 -0
  36. package/src/providers/auth/AuthFormProvider.tsx +116 -0
  37. package/src/providers/auth/FarcasterProvider.tsx +12 -0
  38. package/src/twind.config.ts +101 -1
  39. package/src/types/auth.ts +4 -0
  40. package/src/components/auth/AuthModal.tsx +0 -207
@@ -0,0 +1,30 @@
1
+ export function GoogleIcon({ className }: { className?: string }) {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ x="0px"
6
+ y="0px"
7
+ width="100"
8
+ height="100"
9
+ viewBox="0 0 48 48"
10
+ className={className}
11
+ >
12
+ <path
13
+ fill="#fbc02d"
14
+ d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12 s5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24s8.955,20,20,20 s20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
15
+ ></path>
16
+ <path
17
+ fill="#e53935"
18
+ d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039 l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
19
+ ></path>
20
+ <path
21
+ fill="#4caf50"
22
+ d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36 c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
23
+ ></path>
24
+ <path
25
+ fill="#1565c0"
26
+ d="M43.611,20.083L43.595,20L42,20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571 c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
27
+ ></path>
28
+ </svg>
29
+ );
30
+ }
@@ -0,0 +1,20 @@
1
+ export function LeftArrowIcon({ className, style }: { className?: string; style?: React.CSSProperties }) {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ strokeWidth="2"
11
+ strokeLinecap="round"
12
+ strokeLinejoin="round"
13
+ className={className}
14
+ style={style}
15
+ >
16
+ <path d="m12 19-7-7 7-7" />
17
+ <path d="M19 12H5" />
18
+ </svg>
19
+ );
20
+ }
@@ -7,8 +7,8 @@ export const PoweredByLeaf = ({ color, size = "16" }: { color: string; size?: st
7
7
  maskUnits="userSpaceOnUse"
8
8
  x="0"
9
9
  y="0"
10
- width={size}
11
- height={size}
10
+ width="16"
11
+ height="16"
12
12
  >
13
13
  <mask id="path-1-inside-1_794_7000" fill="white">
14
14
  <path
@@ -1,5 +1,4 @@
1
1
  import { type ReactNode, createContext, useEffect, useState } from "react";
2
- import { createPortal } from "react-dom";
3
2
 
4
3
  import { CrossmintAuthService } from "@crossmint/client-sdk-auth";
5
4
  import type { EVMSmartWalletChain } from "@crossmint/client-sdk-smart-wallet";
@@ -11,16 +10,17 @@ import {
11
10
  type SDKExternalUser,
12
11
  } from "@crossmint/common-sdk-auth";
13
12
 
14
- import AuthModal from "../components/auth/AuthModal";
13
+ import AuthFormDialog from "../components/auth/AuthFormDialog";
15
14
  import { useCrossmint, useRefreshToken, useWallet } from "../hooks";
16
15
  import { CrossmintWalletProvider } from "./CrossmintWalletProvider";
17
16
  import { deleteCookie, getCookie, setCookie } from "@/utils/authCookies";
17
+ import { AuthFormProvider } from "./auth/AuthFormProvider";
18
18
 
19
19
  export type CrossmintAuthWalletConfig = {
20
20
  defaultChain: EVMSmartWalletChain;
21
21
  createOnLogin: "all-users" | "off";
22
22
  type: "evm-smart-wallet";
23
- showWalletModals?: boolean;
23
+ showPasskeyHelpers?: boolean;
24
24
  };
25
25
 
26
26
  export type LoginMethod = "email" | "google" | "farcaster";
@@ -69,7 +69,7 @@ export function CrossmintAuthProvider({
69
69
  );
70
70
  const crossmintAuthService = new CrossmintAuthService(crossmint.apiKey);
71
71
  const crossmintBaseUrl = validateApiKeyAndGetCrossmintBaseUrl(crossmint.apiKey);
72
- const [modalOpen, setModalOpen] = useState(false);
72
+ const [dialogOpen, setDialogOpen] = useState(false);
73
73
 
74
74
  const setAuthMaterial = (authMaterial: AuthMaterialWithUser) => {
75
75
  setCookie(SESSION_PREFIX, authMaterial.jwt);
@@ -101,7 +101,7 @@ export function CrossmintAuthProvider({
101
101
  return;
102
102
  }
103
103
 
104
- setModalOpen(false);
104
+ setDialogOpen(false);
105
105
  }, [crossmint.jwt]);
106
106
 
107
107
  const login = () => {
@@ -110,14 +110,14 @@ export function CrossmintAuthProvider({
110
110
  return;
111
111
  }
112
112
 
113
- setModalOpen(true);
113
+ setDialogOpen(true);
114
114
  };
115
115
 
116
116
  const getAuthStatus = (): AuthStatus => {
117
117
  if (crossmint.jwt != null) {
118
118
  return "logged-in";
119
119
  }
120
- if (modalOpen) {
120
+ if (dialogOpen) {
121
121
  return "in-progress";
122
122
  }
123
123
  return "logged-out";
@@ -153,26 +153,25 @@ export function CrossmintAuthProvider({
153
153
  >
154
154
  <CrossmintWalletProvider
155
155
  defaultChain={embeddedWallets.defaultChain}
156
- showWalletModals={embeddedWallets.showWalletModals}
156
+ showPasskeyHelpers={embeddedWallets.showPasskeyHelpers}
157
157
  appearance={appearance}
158
158
  >
159
- <WalletManager embeddedWallets={embeddedWallets} accessToken={crossmint.jwt}>
160
- {children}
161
- </WalletManager>
162
- {modalOpen
163
- ? createPortal(
164
- <AuthModal
165
- baseUrl={crossmintBaseUrl}
166
- setModalOpen={setModalOpen}
167
- fetchAuthMaterial={fetchAuthMaterial}
168
- apiKey={crossmint.apiKey}
169
- appearance={appearance}
170
- loginMethods={loginMethods}
171
- />,
172
-
173
- document.body
174
- )
175
- : null}
159
+ <AuthFormProvider
160
+ initialState={{
161
+ apiKey: crossmint.apiKey,
162
+ baseUrl: crossmintBaseUrl,
163
+ fetchAuthMaterial,
164
+ appearance,
165
+ setDialogOpen,
166
+ loginMethods,
167
+ }}
168
+ >
169
+ <WalletManager embeddedWallets={embeddedWallets} accessToken={crossmint.jwt}>
170
+ {children}
171
+ </WalletManager>
172
+
173
+ <AuthFormDialog open={dialogOpen} />
174
+ </AuthFormProvider>
176
175
  </CrossmintWalletProvider>
177
176
  </AuthContext.Provider>
178
177
  );
@@ -57,12 +57,12 @@ export type WalletConfig = WalletParams & { type: "evm-smart-wallet" };
57
57
  export function CrossmintWalletProvider({
58
58
  children,
59
59
  defaultChain,
60
- showWalletModals = true, // enabled by default
60
+ showPasskeyHelpers = true, // enabled by default
61
61
  appearance,
62
62
  }: {
63
63
  children: ReactNode;
64
64
  defaultChain: EVMSmartWalletChain;
65
- showWalletModals?: boolean;
65
+ showPasskeyHelpers?: boolean;
66
66
  appearance?: UIConfig;
67
67
  }) {
68
68
  const { crossmint } = useCrossmint("CrossmintWalletProvider must be used within CrossmintProvider");
@@ -99,7 +99,7 @@ export function CrossmintWalletProvider({
99
99
  };
100
100
 
101
101
  const enhanceConfigWithPasskeyPrompts = (config: WalletConfig) => {
102
- if (showWalletModals && (config.signer as PasskeySigner).type === "PASSKEY") {
102
+ if (showPasskeyHelpers && (config.signer as PasskeySigner).type === "PASSKEY") {
103
103
  return {
104
104
  ...config,
105
105
  signer: {
@@ -0,0 +1,105 @@
1
+ import { render, fireEvent, waitFor } from "@testing-library/react";
2
+ import { AuthFormProvider, useAuthForm } from "./AuthFormProvider";
3
+ import { describe, expect, test, vi } from "vitest";
4
+ import type { LoginMethod } from "..";
5
+
6
+ // Mock component to test the AuthFormProvider
7
+ function TestComponent() {
8
+ const { step, apiKey, baseUrl, loginMethods, setStep, setDialogOpen, oauthUrlMap, isLoadingOauthUrlMap } =
9
+ useAuthForm();
10
+
11
+ return (
12
+ <div>
13
+ <div data-testid="step">{step}</div>
14
+ <div data-testid="api-key">{apiKey}</div>
15
+ <div data-testid="base-url">{baseUrl}</div>
16
+ <div data-testid="login-methods">{JSON.stringify(loginMethods)}</div>
17
+ <button onClick={() => setStep("otp")} data-testid="set-step">
18
+ Set Step to OTP
19
+ </button>
20
+ <button onClick={() => setDialogOpen(true)} data-testid="set-dialog-open">
21
+ Open Dialog
22
+ </button>
23
+ <div data-testid="oauth-url">{JSON.stringify(oauthUrlMap)}</div>
24
+ <div data-testid="is-loading-oauth-url">{isLoadingOauthUrlMap.toString()}</div>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ describe("AuthFormProvider", () => {
30
+ const mockFetchAuthMaterial = vi.fn().mockResolvedValue({});
31
+ const mockInitialState = {
32
+ apiKey: "test-api-key",
33
+ baseUrl: "https://api.example.com",
34
+ fetchAuthMaterial: mockFetchAuthMaterial,
35
+ loginMethods: ["email", "google"] as LoginMethod[],
36
+ setDialogOpen: vi.fn(),
37
+ };
38
+
39
+ beforeEach(() => {
40
+ vi.resetAllMocks();
41
+ global.fetch = vi.fn().mockResolvedValue({
42
+ ok: true,
43
+ json: () => Promise.resolve({ oauthUrl: "https://oauth.example.com" }),
44
+ });
45
+ });
46
+
47
+ test("provides initial context values and fetches OAuth URLs", async () => {
48
+ const { getByTestId } = render(
49
+ <AuthFormProvider initialState={mockInitialState}>
50
+ <TestComponent />
51
+ </AuthFormProvider>
52
+ );
53
+
54
+ expect(getByTestId("step").textContent).toBe("initial");
55
+ expect(getByTestId("api-key").textContent).toBe("test-api-key");
56
+ expect(getByTestId("base-url").textContent).toBe("https://api.example.com");
57
+ expect(getByTestId("login-methods").textContent).toBe('["email","google"]');
58
+
59
+ await waitFor(() => {
60
+ expect(getByTestId("oauth-url").textContent).toBe('{"google":"https://oauth.example.com"}');
61
+ expect(getByTestId("is-loading-oauth-url").textContent).toBe("false");
62
+ });
63
+ });
64
+
65
+ test("updates step", () => {
66
+ const { getByTestId } = render(
67
+ <AuthFormProvider initialState={mockInitialState}>
68
+ <TestComponent />
69
+ </AuthFormProvider>
70
+ );
71
+
72
+ fireEvent.click(getByTestId("set-step"));
73
+ expect(getByTestId("step").textContent).toBe("otp");
74
+ });
75
+
76
+ test("calls setDialogOpen", () => {
77
+ const { getByTestId } = render(
78
+ <AuthFormProvider initialState={mockInitialState}>
79
+ <TestComponent />
80
+ </AuthFormProvider>
81
+ );
82
+
83
+ fireEvent.click(getByTestId("set-dialog-open"));
84
+ expect(mockInitialState.setDialogOpen).toHaveBeenCalledWith(true);
85
+ });
86
+
87
+ test("handles OAuth URL fetch error", async () => {
88
+ global.fetch = vi.fn().mockRejectedValue(new Error("Fetch failed"));
89
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
90
+
91
+ const { getByTestId } = render(
92
+ <AuthFormProvider initialState={mockInitialState}>
93
+ <TestComponent />
94
+ </AuthFormProvider>
95
+ );
96
+
97
+ await waitFor(() => {
98
+ expect(getByTestId("oauth-url").textContent).toBe('{"google":""}');
99
+ expect(getByTestId("is-loading-oauth-url").textContent).toBe("false");
100
+ });
101
+
102
+ expect(consoleSpy).toHaveBeenCalled();
103
+ consoleSpy.mockRestore();
104
+ });
105
+ });
@@ -0,0 +1,116 @@
1
+ import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
2
+ import type { AuthMaterialWithUser, OAuthProvider } from "@crossmint/common-sdk-auth";
3
+ import type { UIConfig } from "@crossmint/common-sdk-base";
4
+ import type { LoginMethod } from "../CrossmintAuthProvider";
5
+
6
+ type AuthStep = "initial" | "walletMethod" | "otp" | "qrCode";
7
+ type OAuthUrlMap = Record<OAuthProvider, string>;
8
+ const initialOAuthUrlMap: OAuthUrlMap = {
9
+ google: "",
10
+ // Farcaster is not included here as it uses a different authentication method
11
+ };
12
+ interface AuthFormContextType {
13
+ step: AuthStep;
14
+ apiKey: string;
15
+ baseUrl: string;
16
+ fetchAuthMaterial: (refreshToken: string) => Promise<AuthMaterialWithUser>;
17
+ appearance?: UIConfig;
18
+ loginMethods: LoginMethod[];
19
+ oauthUrlMap: OAuthUrlMap;
20
+ isLoadingOauthUrlMap: boolean;
21
+ setStep: (step: AuthStep) => void;
22
+ setDialogOpen: (open: boolean) => void;
23
+ }
24
+
25
+ type ContextInitialStateProps = {
26
+ apiKey: string;
27
+ baseUrl: string;
28
+ fetchAuthMaterial: (refreshToken: string) => Promise<AuthMaterialWithUser>;
29
+ appearance?: UIConfig;
30
+ loginMethods: LoginMethod[];
31
+ setDialogOpen?: (open: boolean) => void;
32
+ };
33
+
34
+ const AuthFormContext = createContext<AuthFormContextType | undefined>(undefined);
35
+
36
+ export const useAuthForm = () => {
37
+ const context = useContext(AuthFormContext);
38
+ if (!context) {
39
+ throw new Error("useAuthForm must be used within an AuthFormProvider");
40
+ }
41
+ return context;
42
+ };
43
+
44
+ export const AuthFormProvider = ({
45
+ children,
46
+ initialState,
47
+ }: { children: ReactNode; initialState: ContextInitialStateProps }) => {
48
+ const [step, setStep] = useState<AuthStep>("initial");
49
+ const [oauthUrlMap, setOauthUrlMap] = useState<OAuthUrlMap>(initialOAuthUrlMap);
50
+ const [isLoadingOauthUrlMap, setIsLoadingOauthUrlMap] = useState(true);
51
+
52
+ const { loginMethods, apiKey, baseUrl } = initialState;
53
+
54
+ useEffect(() => {
55
+ const preFetchAndSetOauthUrl = async () => {
56
+ setIsLoadingOauthUrlMap(true);
57
+ try {
58
+ const oauthProviders = loginMethods.filter(
59
+ (method): method is OAuthProvider => method in initialOAuthUrlMap
60
+ );
61
+
62
+ const oauthPromiseList = oauthProviders.map(async (provider) => {
63
+ const url = await getOAuthUrl(provider, { apiKey, baseUrl });
64
+ return { [provider]: url };
65
+ });
66
+
67
+ const oauthUrlMap = Object.assign({}, ...(await Promise.all(oauthPromiseList)));
68
+ setOauthUrlMap(oauthUrlMap);
69
+ } catch (error) {
70
+ console.error("Error fetching OAuth URLs:", error);
71
+ } finally {
72
+ setIsLoadingOauthUrlMap(false);
73
+ }
74
+ };
75
+ preFetchAndSetOauthUrl();
76
+ }, [loginMethods, apiKey, baseUrl]);
77
+
78
+ const value: AuthFormContextType = {
79
+ step,
80
+ apiKey,
81
+ baseUrl,
82
+ fetchAuthMaterial: initialState.fetchAuthMaterial,
83
+ appearance: initialState.appearance,
84
+ loginMethods,
85
+ oauthUrlMap,
86
+ isLoadingOauthUrlMap,
87
+ setDialogOpen: initialState.setDialogOpen ?? (() => {}),
88
+ setStep,
89
+ };
90
+
91
+ return <AuthFormContext.Provider value={value}>{children}</AuthFormContext.Provider>;
92
+ };
93
+
94
+ async function getOAuthUrl(provider: OAuthProvider, options: { baseUrl: string; apiKey: string }) {
95
+ try {
96
+ const queryParams = new URLSearchParams({ apiKey: options.apiKey });
97
+ const response = await fetch(
98
+ `${options.baseUrl}api/2024-09-26/session/sdk/auth/social/${provider}/start?${queryParams}`,
99
+ {
100
+ headers: {
101
+ "x-api-key": options.apiKey,
102
+ },
103
+ }
104
+ );
105
+
106
+ if (!response.ok) {
107
+ throw new Error("Failed to get OAuth URL. Please try again or contact support.");
108
+ }
109
+
110
+ const data = (await response.json()) as { oauthUrl: string };
111
+ return data.oauthUrl;
112
+ } catch (error) {
113
+ console.error(`Error fetching OAuth URL for ${provider}:`, error);
114
+ throw new Error(`Failed to get OAuth URL for ${provider}. Please try again or contact support.`);
115
+ }
116
+ }
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from "react";
2
+ import { AuthKitProvider } from "@farcaster/auth-kit";
3
+
4
+ export function FarcasterProvider({ baseUrl, children }: { baseUrl: string; children: ReactNode }) {
5
+ const config = {
6
+ rpcUrl: "https://mainnet.optimism.io",
7
+ domain: new URL(baseUrl).hostname.replace(/^www\./, ""),
8
+ siweUri: `${baseUrl}api/2024-09-26/session/sdk/auth/authenticate`,
9
+ };
10
+
11
+ return <AuthKitProvider config={config}>{children}</AuthKitProvider>;
12
+ }
@@ -2,6 +2,106 @@ import { defineConfig } from "@twind/core";
2
2
  import presetTailwind from "@twind/preset-tailwind";
3
3
 
4
4
  export default defineConfig({
5
- presets: [presetTailwind(/* options */)],
5
+ presets: [presetTailwind()],
6
+ theme: {
7
+ screens: {
8
+ xs: "480px",
9
+ },
10
+ extend: {
11
+ colors: {
12
+ // Crossmint colors (cm- prefix)
13
+ "cm-text-primary": "#00150D",
14
+ "cm-text-secondary": "#67797F",
15
+ "cm-background-primary": "#FFFFFF",
16
+ "cm-muted-primary": "#F0F2F4",
17
+ "cm-border": "#D9D9D9",
18
+ "cm-ring": "#1A73E8",
19
+ },
20
+ keyframes: {
21
+ "caret-blink": {
22
+ "0%,70%,100%": { opacity: "1" },
23
+ "20%,50%": { opacity: "0" },
24
+ },
25
+ "fade-in": {
26
+ from: { opacity: "0" },
27
+ to: { opacity: "1" },
28
+ },
29
+ "fade-out": {
30
+ from: { opacity: "1" },
31
+ to: { opacity: "0" },
32
+ },
33
+ "slide-in-from-top": {
34
+ "0%": { transform: "translateY(-100%)" },
35
+ "100%": { transform: "translateY(0)" },
36
+ },
37
+ "slide-out-to-top": {
38
+ "0%": { transform: "translateY(0)" },
39
+ "100%": { transform: "translateY(-100%)" },
40
+ },
41
+ "slide-in-from-bottom": {
42
+ "0%": { transform: "translateY(100%)" },
43
+ "100%": { transform: "translateY(0)" },
44
+ },
45
+ "slide-out-to-bottom": {
46
+ "0%": { transform: "translateY(0)" },
47
+ "100%": { transform: "translateY(100%)" },
48
+ },
49
+ "slide-in-from-left": {
50
+ "0%": { transform: "translateX(-100%)" },
51
+ "100%": { transform: "translateX(0)" },
52
+ },
53
+ "slide-out-to-left": {
54
+ "0%": { transform: "translateX(0)" },
55
+ "100%": { transform: "translateX(-100%)" },
56
+ },
57
+ "slide-in-from-right": {
58
+ "0%": { transform: "translateX(100%)" },
59
+ "100%": { transform: "translateX(0)" },
60
+ },
61
+ "slide-out-to-right": {
62
+ "0%": { transform: "translateX(0)" },
63
+ "100%": { transform: "translateX(100%)" },
64
+ },
65
+ "zoom-in-95": {
66
+ "0%": { opacity: "0", transform: "scale(0.95) translate(-50%, -50%)" },
67
+ "100%": { opacity: "1", transform: "scale(1) translate(-50%, -50%)" },
68
+ },
69
+ "zoom-out-95": {
70
+ "0%": { opacity: "1", transform: "scale(1) translate(-50%, -50%)" },
71
+ "100%": { opacity: "0", transform: "scale(0.95) translate(-50%, -50%)" },
72
+ },
73
+ },
74
+ animation: {
75
+ "caret-blink": "caret-blink 1.25s ease-out infinite",
76
+ "fade-in": "fade-in 150ms cubic-bezier(0.16, 1, 0.3, 1)",
77
+ "fade-out": "fade-out 150ms cubic-bezier(0.16, 1, 0.3, 1)",
78
+ "slide-in-from-top": "slide-in-from-top 150ms cubic-bezier(0.16, 1, 0.3, 1)",
79
+ "slide-out-to-top": "slide-out-to-top 150ms cubic-bezier(0.16, 1, 0.3, 1)",
80
+ "slide-in-from-bottom": "slide-in-from-bottom 150ms cubic-bezier(0.16, 1, 0.3, 1)",
81
+ "slide-out-to-bottom": "slide-out-to-bottom 150ms cubic-bezier(0.16, 1, 0.3, 1)",
82
+ "slide-in-from-left": "slide-in-from-left 150ms cubic-bezier(0.16, 1, 0.3, 1)",
83
+ "slide-out-to-left": "slide-out-to-left 150ms cubic-bezier(0.16, 1, 0.3, 1)",
84
+ "slide-in-from-right": "slide-in-from-right 150ms cubic-bezier(0.16, 1, 0.3, 1)",
85
+ "slide-out-to-right": "slide-out-to-right 150ms cubic-bezier(0.16, 1, 0.3, 1)",
86
+ "zoom-in-95": "zoom-in-95 150ms cubic-bezier(0.16, 1, 0.3, 1)",
87
+ "zoom-out-95": "zoom-out-95 150ms cubic-bezier(0.16, 1, 0.3, 1)",
88
+ },
89
+ durations: {
90
+ "300": "300ms",
91
+ "500": "500ms",
92
+ },
93
+ },
94
+ },
95
+ rules: [
96
+ [
97
+ "responsive-border-radius-auth-dialog",
98
+ {
99
+ "@media (max-width: 479px)": {
100
+ "border-bottom-left-radius": "0 !important",
101
+ "border-bottom-right-radius": "0 !important",
102
+ },
103
+ },
104
+ ],
105
+ ],
6
106
  /* config */
7
107
  });
@@ -0,0 +1,4 @@
1
+ export type OtpEmailPayload = {
2
+ email: string;
3
+ emailId: string;
4
+ };