@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
@@ -1,207 +0,0 @@
1
- import { Dialog, Transition } from "@headlessui/react";
2
- import { type CSSProperties, Fragment, useEffect, useRef, useState } from "react";
3
- import { z } from "zod";
4
-
5
- import { IFrameWindow } from "@crossmint/client-sdk-window";
6
- import type { UIConfig } from "@crossmint/common-sdk-base";
7
- import { CrossmintInternalEvents } from "@crossmint/client-sdk-base";
8
- import type { AuthMaterialWithUser } from "@crossmint/common-sdk-auth";
9
- import type { LoginMethod } from "@/providers";
10
-
11
- import X from "../../icons/x";
12
-
13
- const authMaterialSchema = z.object({
14
- oneTimeSecret: z.string(),
15
- });
16
-
17
- const incomingModalIframeEvents = {
18
- authMaterialFromAuthFrame: authMaterialSchema,
19
- };
20
-
21
- type IncomingModalIframeEventsType = {
22
- authMaterialFromAuthFrame: typeof incomingModalIframeEvents.authMaterialFromAuthFrame;
23
- };
24
-
25
- type AuthModalProps = {
26
- setModalOpen: (open: boolean) => void;
27
- apiKey: string;
28
- fetchAuthMaterial: (refreshToken: string) => Promise<AuthMaterialWithUser>;
29
- baseUrl: string;
30
- appearance?: UIConfig;
31
- loginMethods?: LoginMethod[];
32
- };
33
-
34
- export default function AuthModal({
35
- setModalOpen,
36
- apiKey,
37
- fetchAuthMaterial,
38
- baseUrl,
39
- appearance,
40
- loginMethods,
41
- }: AuthModalProps) {
42
- let iframeSrc = `${baseUrl}sdk/2024-09-26/auth/frame?apiKey=${apiKey}`;
43
- if (appearance != null) {
44
- // The appearance object is serialized into a query parameter
45
- iframeSrc += `&uiConfig=${encodeURIComponent(JSON.stringify(appearance))}`;
46
- }
47
-
48
- if (loginMethods != null) {
49
- iframeSrc += `&loginMethods=${loginMethods.join(",")}`;
50
- }
51
-
52
- const iframeRef = useRef<HTMLIFrameElement | null>(null);
53
- const iframeWindowRef = useRef<IFrameWindow<IncomingModalIframeEventsType, Record<string, never>> | null>(null);
54
-
55
- const [iframeChildrenHeight, setIframeChildrenHeight] = useState(0);
56
- const iframePaddingTopPX = 48;
57
- const iframePaddingBottomPX = 32;
58
- const paddingOffset = iframePaddingTopPX + iframePaddingBottomPX;
59
- // Farcaster needs more height to render its QR code container.
60
- const iFrameRenderMinHeightPX = loginMethods?.includes("farcaster") ? 500 : 300;
61
-
62
- const setupIframeWindowListener = () => {
63
- if (iframeWindowRef.current == null) {
64
- return;
65
- }
66
-
67
- iframeWindowRef.current.on("authMaterialFromAuthFrame", (data) => {
68
- fetchAuthMaterial(data.oneTimeSecret);
69
- iframeWindowRef.current?.off("authMaterialFromAuthFrame");
70
- setModalOpen(false);
71
- });
72
- };
73
-
74
- const handleIframeLoaded = async () => {
75
- if (iframeRef.current == null) {
76
- // The iframe should be load, here we should log on DD if possible
77
- console.error("Something wrong happened, please try again");
78
- return;
79
- }
80
-
81
- if (iframeWindowRef.current == null) {
82
- const initIframe = await IFrameWindow.init(iframeRef.current, {
83
- incomingEvents: incomingModalIframeEvents,
84
- outgoingEvents: {},
85
- });
86
-
87
- iframeWindowRef.current = initIframe;
88
- setupIframeWindowListener();
89
- }
90
- };
91
-
92
- useEffect(() => {
93
- function _onEvent(event: MessageEvent) {
94
- if (event.data.type === CrossmintInternalEvents.UI_HEIGHT_CHANGED) {
95
- setIframeChildrenHeight(event.data.payload.height);
96
- }
97
- }
98
- window.addEventListener("message", _onEvent);
99
- return () => {
100
- window.removeEventListener("message", _onEvent);
101
- };
102
- }, []);
103
-
104
- return (
105
- <Transition.Root show as={Fragment}>
106
- <Dialog as="div" style={styles.dialog} onClose={() => setModalOpen(false)}>
107
- <Transition.Child
108
- as={Fragment}
109
- enter="ease-out duration-400"
110
- enterFrom="opacity-0"
111
- enterTo="opacity-100"
112
- leave="ease-in duration-400"
113
- leaveFrom="opacity-100"
114
- leaveTo="opacity-0"
115
- >
116
- <div style={styles.transitionBegin} />
117
- </Transition.Child>
118
- <Transition.Child
119
- as={Fragment}
120
- enter="ease-out duration-400"
121
- enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
122
- enterTo="opacity-100 translate-y-0 sm:scale-100"
123
- leave="ease-in duration-400"
124
- leaveFrom="opacity-100 translate-y-0 sm:scale-100"
125
- leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
126
- >
127
- <div style={styles.transitionEnd} onClick={(e) => e.stopPropagation()}>
128
- <div style={{ position: "relative", width: "100%" }}>
129
- <button
130
- type="button"
131
- aria-label="Close"
132
- style={{
133
- width: "1.5rem",
134
- position: "absolute",
135
- right: "1.5rem",
136
- top: "1.5rem",
137
- cursor: "pointer",
138
- color: appearance?.colors?.border ?? "#909ca3",
139
- outlineOffset: "4px",
140
- borderRadius: "100%",
141
- }}
142
- onClick={() => setModalOpen(false)}
143
- >
144
- <X />
145
- </button>
146
- </div>
147
- <iframe
148
- ref={iframeRef}
149
- src={iframeSrc}
150
- onLoad={handleIframeLoaded}
151
- title="Authentication Modal"
152
- style={{
153
- width: "100%",
154
- minHeight: iFrameRenderMinHeightPX,
155
- border: "1px solid",
156
- borderColor: appearance?.colors?.border ?? "#D0D5DD",
157
- borderRadius: appearance?.borderRadius ?? "1rem",
158
- backgroundColor: appearance?.colors?.background ?? "white",
159
- height: iframeChildrenHeight + paddingOffset,
160
- paddingTop: iframePaddingTopPX,
161
- paddingBottom: iframePaddingBottomPX,
162
- }}
163
- />
164
- </div>
165
- </Transition.Child>
166
- </Dialog>
167
- </Transition.Root>
168
- );
169
- }
170
-
171
- const styles: { [key: string]: CSSProperties } = {
172
- dialog: {
173
- display: "flex",
174
- justifyContent: "center",
175
- alignItems: "center",
176
- overflowY: "auto",
177
- position: "fixed",
178
- top: 0,
179
- right: 0,
180
- bottom: 0,
181
- left: 0,
182
- zIndex: 20,
183
- },
184
- transitionBegin: {
185
- background: "rgba(139, 151, 151, 0.2)",
186
- backdropFilter: "blur(2px)",
187
- position: "fixed",
188
- top: 0,
189
- right: 0,
190
- bottom: 0,
191
- left: 0,
192
- transitionProperty: "opacity",
193
- transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
194
- transitionDuration: "300ms",
195
- zIndex: -10,
196
- },
197
- transitionEnd: {
198
- display: "flex",
199
- flexDirection: "column",
200
- alignItems: "center",
201
- width: "100%",
202
- maxWidth: "448px",
203
- borderRadius: "0.75rem",
204
- boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
205
- zIndex: 30,
206
- },
207
- };