@erikey/react 0.4.35 → 0.5.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/styles.css +144 -1
- package/dist/styles.css.map +1 -1
- package/dist/ui/index.mjs +109 -47
- package/dist/ui/index.mjs.map +1 -1
- package/dist/ui/style.css +28 -0
- package/dist/ui/style.css.map +1 -1
- package/package.json +1 -1
- package/src/ui/components/auth/auth-flow.tsx +45 -8
- package/src/ui/components/auth/auth-form.tsx +6 -0
- package/src/ui/components/auth/auth-view.tsx +6 -0
- package/src/ui/components/auth/forms/email-verification-form.tsx +12 -8
- package/src/ui/components/auth/forms/forgot-password-form.tsx +11 -4
- package/src/ui/components/auth/forms/reset-password-form.tsx +18 -8
- package/src/ui/components/auth/forms/sign-in-form.tsx +16 -9
- package/src/ui/components/auth/forms/sign-up-form.tsx +28 -14
- package/src/ui/lib/auth-ui-provider.tsx +5 -0
- package/src/ui/style.css +32 -1
package/dist/ui/style.css
CHANGED
|
@@ -5,6 +5,34 @@
|
|
|
5
5
|
--team-3: oklch(0.398 0.07 227.392);
|
|
6
6
|
--team-4: oklch(0.828 0.189 84.429);
|
|
7
7
|
--team-5: oklch(0.769 0.188 70.08);
|
|
8
|
+
--card: 0 0% 100%;
|
|
9
|
+
--card-foreground: 240 10% 3.9%;
|
|
10
|
+
--background: 0 0% 100%;
|
|
11
|
+
--foreground: 240 10% 3.9%;
|
|
12
|
+
--muted: 240 4.8% 95.9%;
|
|
13
|
+
--muted-foreground: 240 3.8% 46.1%;
|
|
14
|
+
--border: 240 5.9% 90%;
|
|
15
|
+
--input: 240 5.9% 90%;
|
|
16
|
+
--ring: 240 5.9% 10%;
|
|
17
|
+
--primary: 240 5.9% 10%;
|
|
18
|
+
--primary-foreground: 0 0% 98%;
|
|
19
|
+
--destructive: 0 84.2% 60.2%;
|
|
20
|
+
--destructive-foreground: 0 0% 98%;
|
|
21
|
+
}
|
|
22
|
+
.dark {
|
|
23
|
+
--card: 240 10% 3.9%;
|
|
24
|
+
--card-foreground: 0 0% 98%;
|
|
25
|
+
--background: 240 10% 3.9%;
|
|
26
|
+
--foreground: 0 0% 98%;
|
|
27
|
+
--muted: 240 3.7% 15.9%;
|
|
28
|
+
--muted-foreground: 240 5% 64.9%;
|
|
29
|
+
--border: 240 3.7% 15.9%;
|
|
30
|
+
--input: 240 3.7% 15.9%;
|
|
31
|
+
--ring: 240 4.9% 83.9%;
|
|
32
|
+
--primary: 0 0% 98%;
|
|
33
|
+
--primary-foreground: 240 5.9% 10%;
|
|
34
|
+
--destructive: 0 62.8% 30.6%;
|
|
35
|
+
--destructive-foreground: 0 0% 98%;
|
|
8
36
|
}
|
|
9
37
|
@keyframes enter {
|
|
10
38
|
from {
|
package/dist/ui/style.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ui/style.css"],"sourcesContent":["/*
|
|
1
|
+
{"version":3,"sources":["../../src/ui/style.css"],"sourcesContent":["/* Default theme variables - users can override these in their own CSS */\n:root {\n\t--team-1: oklch(0.646 0.222 41.116);\n\t--team-2: oklch(0.6 0.118 184.704);\n\t--team-3: oklch(0.398 0.07 227.392);\n\t--team-4: oklch(0.828 0.189 84.429);\n\t--team-5: oklch(0.769 0.188 70.08);\n\n\t/* Card and UI component default colors */\n\t--card: 0 0% 100%;\n\t--card-foreground: 240 10% 3.9%;\n\t--background: 0 0% 100%;\n\t--foreground: 240 10% 3.9%;\n\t--muted: 240 4.8% 95.9%;\n\t--muted-foreground: 240 3.8% 46.1%;\n\t--border: 240 5.9% 90%;\n\t--input: 240 5.9% 90%;\n\t--ring: 240 5.9% 10%;\n\t--primary: 240 5.9% 10%;\n\t--primary-foreground: 0 0% 98%;\n\t--destructive: 0 84.2% 60.2%;\n\t--destructive-foreground: 0 0% 98%;\n}\n\n.dark {\n\t--card: 240 10% 3.9%;\n\t--card-foreground: 0 0% 98%;\n\t--background: 240 10% 3.9%;\n\t--foreground: 0 0% 98%;\n\t--muted: 240 3.7% 15.9%;\n\t--muted-foreground: 240 5% 64.9%;\n\t--border: 240 3.7% 15.9%;\n\t--input: 240 3.7% 15.9%;\n\t--ring: 240 4.9% 83.9%;\n\t--primary: 0 0% 98%;\n\t--primary-foreground: 240 5.9% 10%;\n\t--destructive: 0 62.8% 30.6%;\n\t--destructive-foreground: 0 0% 98%;\n}\n\n/*\n * Animation CSS for SDK components\n *\n * These styles are bundled with the SDK to ensure they work regardless of\n * consumer's Tailwind configuration. Uses @layer for cascade control so\n * consumers can override with their own Tailwind utilities if needed.\n *\n * This solves the problem where consumer's Tailwind purges animation classes\n * because they only exist in the SDK's minified bundle.\n */\n\n/* Keyframes for enter/exit animations (from tailwindcss-animate) */\n@keyframes enter {\n\tfrom {\n\t\topacity: var(--tw-enter-opacity, 1);\n\t\ttransform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0)\n\t\t\tscale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1))\n\t\t\trotate(var(--tw-enter-rotate, 0));\n\t}\n}\n\n@keyframes exit {\n\tto {\n\t\topacity: var(--tw-exit-opacity, 1);\n\t\ttransform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0)\n\t\t\tscale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1))\n\t\t\trotate(var(--tw-exit-rotate, 0));\n\t}\n}\n\n/* Animation base classes */\n.animate-in {\n\tanimation-name: enter;\n\tanimation-duration: 150ms;\n\t--tw-enter-opacity: initial;\n\t--tw-enter-scale: initial;\n\t--tw-enter-rotate: initial;\n\t--tw-enter-translate-x: initial;\n\t--tw-enter-translate-y: initial;\n}\n\n.animate-out {\n\tanimation-name: exit;\n\tanimation-duration: 150ms;\n\t--tw-exit-opacity: initial;\n\t--tw-exit-scale: initial;\n\t--tw-exit-rotate: initial;\n\t--tw-exit-translate-x: initial;\n\t--tw-exit-translate-y: initial;\n}\n\n/* Opacity variants */\n.fade-in-0 { --tw-enter-opacity: 0; }\n.fade-out-0 { --tw-exit-opacity: 0; }\n\n/* Scale variants */\n.zoom-in-95 { --tw-enter-scale: 0.95; }\n.zoom-out-95 { --tw-exit-scale: 0.95; }\n\n/* Slide variants */\n.slide-in-from-top-2 { --tw-enter-translate-y: -0.5rem; }\n.slide-in-from-bottom-2 { --tw-enter-translate-y: 0.5rem; }\n.slide-in-from-left-2 { --tw-enter-translate-x: -0.5rem; }\n.slide-in-from-right-2 { --tw-enter-translate-x: 0.5rem; }\n\n/* Data attribute variants for Radix UI components */\n[data-state=\"open\"].animate-in {\n\tanimation-name: enter;\n\tanimation-duration: 150ms;\n}\n\n[data-state=\"closed\"].animate-out {\n\tanimation-name: exit;\n\tanimation-duration: 150ms;\n}\n\n[data-state=\"open\"].fade-in-0,\n.data-\\[state\\=open\\]\\:fade-in-0[data-state=\"open\"] {\n\t--tw-enter-opacity: 0;\n}\n\n[data-state=\"closed\"].fade-out-0,\n.data-\\[state\\=closed\\]\\:fade-out-0[data-state=\"closed\"] {\n\t--tw-exit-opacity: 0;\n}\n\n[data-state=\"open\"].zoom-in-95,\n.data-\\[state\\=open\\]\\:zoom-in-95[data-state=\"open\"] {\n\t--tw-enter-scale: 0.95;\n}\n\n[data-state=\"closed\"].zoom-out-95,\n.data-\\[state\\=closed\\]\\:zoom-out-95[data-state=\"closed\"] {\n\t--tw-exit-scale: 0.95;\n}\n\n/* Slide variants with data-side for dropdown positioning */\n[data-side=\"bottom\"].slide-in-from-top-2,\n.data-\\[side\\=bottom\\]\\:slide-in-from-top-2[data-side=\"bottom\"] {\n\t--tw-enter-translate-y: -0.5rem;\n}\n\n[data-side=\"top\"].slide-in-from-bottom-2,\n.data-\\[side\\=top\\]\\:slide-in-from-bottom-2[data-side=\"top\"] {\n\t--tw-enter-translate-y: 0.5rem;\n}\n\n[data-side=\"left\"].slide-in-from-right-2,\n.data-\\[side\\=left\\]\\:slide-in-from-right-2[data-side=\"left\"] {\n\t--tw-enter-translate-x: 0.5rem;\n}\n\n[data-side=\"right\"].slide-in-from-left-2,\n.data-\\[side\\=right\\]\\:slide-in-from-left-2[data-side=\"right\"] {\n\t--tw-enter-translate-x: -0.5rem;\n}\n\n/* Tailwind's data attribute variant syntax support */\n.data-\\[state\\=open\\]\\:animate-in[data-state=\"open\"] {\n\tanimation-name: enter;\n\tanimation-duration: 150ms;\n\t--tw-enter-opacity: initial;\n\t--tw-enter-scale: initial;\n\t--tw-enter-rotate: initial;\n\t--tw-enter-translate-x: initial;\n\t--tw-enter-translate-y: initial;\n}\n\n.data-\\[state\\=closed\\]\\:animate-out[data-state=\"closed\"] {\n\tanimation-name: exit;\n\tanimation-duration: 150ms;\n\t--tw-exit-opacity: initial;\n\t--tw-exit-scale: initial;\n\t--tw-exit-rotate: initial;\n\t--tw-exit-translate-x: initial;\n\t--tw-exit-translate-y: initial;\n}\n"],"mappings":";AACA;AACC,YAAU,MAAM,MAAM,MAAM;AAC5B,YAAU,MAAM,IAAI,MAAM;AAC1B,YAAU,MAAM,MAAM,KAAK;AAC3B,YAAU,MAAM,MAAM,MAAM;AAC5B,YAAU,MAAM,MAAM,MAAM;AAG5B,UAAQ,EAAE,GAAG;AACb,qBAAmB,IAAI,IAAI;AAC3B,gBAAc,EAAE,GAAG;AACnB,gBAAc,IAAI,IAAI;AACtB,WAAS,IAAI,KAAK;AAClB,sBAAoB,IAAI,KAAK;AAC7B,YAAU,IAAI,KAAK;AACnB,WAAS,IAAI,KAAK;AAClB,UAAQ,IAAI,KAAK;AACjB,aAAW,IAAI,KAAK;AACpB,wBAAsB,EAAE,GAAG;AAC3B,iBAAe,EAAE,MAAM;AACvB,4BAA0B,EAAE,GAAG;AAChC;AAEA,CAAC;AACA,UAAQ,IAAI,IAAI;AAChB,qBAAmB,EAAE,GAAG;AACxB,gBAAc,IAAI,IAAI;AACtB,gBAAc,EAAE,GAAG;AACnB,WAAS,IAAI,KAAK;AAClB,sBAAoB,IAAI,GAAG;AAC3B,YAAU,IAAI,KAAK;AACnB,WAAS,IAAI,KAAK;AAClB,UAAQ,IAAI,KAAK;AACjB,aAAW,EAAE,GAAG;AAChB,wBAAsB,IAAI,KAAK;AAC/B,iBAAe,EAAE,MAAM;AACvB,4BAA0B,EAAE,GAAG;AAChC;AAcA,WAAW;AACV;AACC,aAAS,IAAI,kBAAkB,EAAE;AACjC,eAAW,YAAY,IAAI,sBAAsB,EAAE,EAAE,EAAE,IAAI,sBAAsB,EAAE,EAAE,EAAE,GACtF,QAAQ,IAAI,gBAAgB,EAAE,EAAE,EAAE,IAAI,gBAAgB,EAAE,EAAE,EAAE,IAAI,gBAAgB,EAAE,IAClF,OAAO,IAAI,iBAAiB,EAAE;AAChC;AACD;AAEA,WAAW;AACV;AACC,aAAS,IAAI,iBAAiB,EAAE;AAChC,eAAW,YAAY,IAAI,qBAAqB,EAAE,EAAE,EAAE,IAAI,qBAAqB,EAAE,EAAE,EAAE,GACpF,QAAQ,IAAI,eAAe,EAAE,EAAE,EAAE,IAAI,eAAe,EAAE,EAAE,EAAE,IAAI,eAAe,EAAE,IAC/E,OAAO,IAAI,gBAAgB,EAAE;AAC/B;AACD;AAGA,CAAC;AACA,kBAAgB;AAChB,sBAAoB;AACpB,sBAAoB;AACpB,oBAAkB;AAClB,qBAAmB;AACnB,0BAAwB;AACxB,0BAAwB;AACzB;AAEA,CAAC;AACA,kBAAgB;AAChB,sBAAoB;AACpB,qBAAmB;AACnB,mBAAiB;AACjB,oBAAkB;AAClB,yBAAuB;AACvB,yBAAuB;AACxB;AAGA,CAAC;AAAY,sBAAoB;AAAG;AACpC,CAAC;AAAa,qBAAmB;AAAG;AAGpC,CAAC;AAAa,oBAAkB;AAAM;AACtC,CAAC;AAAc,mBAAiB;AAAM;AAGtC,CAAC;AAAsB,0BAAwB;AAAS;AACxD,CAAC;AAAyB,0BAAwB;AAAQ;AAC1D,CAAC;AAAuB,0BAAwB;AAAS;AACzD,CAAC;AAAwB,0BAAwB;AAAQ;AAGzD,CAAC,gBAAkB,CAnClB;AAoCA,kBAAgB;AAChB,sBAAoB;AACrB;AAEA,CAAC,kBAAoB,CA9BpB;AA+BA,kBAAgB;AAChB,sBAAoB;AACrB;AAEA,CAAC,gBAAkB,CAxBlB;AAyBD,CAAC,+BAA+B,CAAC;AAChC,sBAAoB;AACrB;AAEA,CAAC,kBAAoB,CA5BpB;AA6BD,CAAC,kCAAkC,CAAC;AACnC,qBAAmB;AACpB;AAEA,CAAC,gBAAkB,CA9BlB;AA+BD,CAAC,gCAAgC,CAAC;AACjC,oBAAkB;AACnB;AAEA,CAAC,kBAAoB,CAlCpB;AAmCD,CAAC,mCAAmC,CAAC;AACpC,mBAAiB;AAClB;AAGA,CAAC,iBAAmB,CArCnB;AAsCD,CAAC,0CAA0C,CAAC;AAC3C,0BAAwB;AACzB;AAEA,CAAC,cAAgB,CAzChB;AA0CD,CAAC,0CAA0C,CAAC;AAC3C,0BAAwB;AACzB;AAEA,CAAC,eAAiB,CA5CjB;AA6CD,CAAC,0CAA0C,CAAC;AAC3C,0BAAwB;AACzB;AAEA,CAAC,gBAAkB,CAlDlB;AAmDD,CAAC,0CAA0C,CAAC;AAC3C,0BAAwB;AACzB;AAGA,CAAC,gCAAgC,CAAC;AACjC,kBAAgB;AAChB,sBAAoB;AACpB,sBAAoB;AACpB,oBAAkB;AAClB,qBAAmB;AACnB,0BAAwB;AACxB,0BAAwB;AACzB;AAEA,CAAC,mCAAmC,CAAC;AACpC,kBAAgB;AAChB,sBAAoB;AACpB,qBAAmB;AACnB,mBAAiB;AACjB,oBAAkB;AAClB,yBAAuB;AACvB,yBAAuB;AACxB;","names":[]}
|
package/package.json
CHANGED
|
@@ -40,7 +40,8 @@ export interface AuthFlowProps
|
|
|
40
40
|
initialView?: AuthFlowView
|
|
41
41
|
/**
|
|
42
42
|
* URL to redirect to after successful authentication.
|
|
43
|
-
*
|
|
43
|
+
* Used by OAuth callbacks and underlying auth forms.
|
|
44
|
+
* In "internal" mode, session state change triggers re-render automatically.
|
|
44
45
|
*/
|
|
45
46
|
redirectTo?: string
|
|
46
47
|
/**
|
|
@@ -290,21 +291,55 @@ export function AuthFlow({
|
|
|
290
291
|
)
|
|
291
292
|
|
|
292
293
|
// Combined event handler that wraps user's onEvent
|
|
294
|
+
// In internal mode, this handles ALL view transitions centrally
|
|
293
295
|
const handleAuthEvent = useCallback(
|
|
294
296
|
(event: AuthFlowEvent) => {
|
|
295
297
|
// Forward to user's handler
|
|
296
298
|
onEvent?.(event)
|
|
297
299
|
|
|
298
|
-
// In internal mode, handle
|
|
299
|
-
if (mode === "internal"
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
// In internal mode, handle view transitions based on events
|
|
301
|
+
if (mode === "internal") {
|
|
302
|
+
switch (event.type) {
|
|
303
|
+
// Sign-up flow
|
|
304
|
+
case "SIGN_UP_REQUIRES_VERIFICATION":
|
|
305
|
+
setCurrentView("EMAIL_VERIFICATION")
|
|
306
|
+
setVerifyEmail(event.email)
|
|
307
|
+
break
|
|
308
|
+
|
|
309
|
+
// Sign-in flow
|
|
310
|
+
case "SIGN_IN_REQUIRES_2FA":
|
|
311
|
+
setCurrentView("TWO_FACTOR")
|
|
312
|
+
break
|
|
313
|
+
case "SIGN_IN_REQUIRES_VERIFICATION":
|
|
314
|
+
setCurrentView("EMAIL_VERIFICATION")
|
|
315
|
+
setVerifyEmail(event.email)
|
|
316
|
+
break
|
|
317
|
+
|
|
318
|
+
// Verification success without token → sign in
|
|
319
|
+
case "VERIFICATION_SUCCESS":
|
|
320
|
+
if (!event.session?.token) {
|
|
321
|
+
setCurrentView("SIGN_IN")
|
|
322
|
+
}
|
|
323
|
+
// If token present, let session state handle UI
|
|
324
|
+
break
|
|
325
|
+
|
|
326
|
+
// Password reset flow
|
|
327
|
+
case "PASSWORD_RESET_SUCCESS":
|
|
328
|
+
setCurrentView("SIGN_IN")
|
|
329
|
+
break
|
|
330
|
+
|
|
331
|
+
// View toggles (footer links) - handled by InternalLink
|
|
332
|
+
case "VIEW_CHANGE":
|
|
333
|
+
setCurrentView(event.view)
|
|
334
|
+
if (event.email) setVerifyEmail(event.email)
|
|
335
|
+
break
|
|
336
|
+
|
|
337
|
+
// AUTH_SUCCESS - let React re-render via useSession
|
|
338
|
+
// No action needed - session state triggers re-render
|
|
304
339
|
}
|
|
305
340
|
}
|
|
306
341
|
},
|
|
307
|
-
[onEvent, mode
|
|
342
|
+
[onEvent, mode]
|
|
308
343
|
)
|
|
309
344
|
|
|
310
345
|
// Get the view key for AuthView component
|
|
@@ -321,6 +356,7 @@ export function AuthFlow({
|
|
|
321
356
|
basePath={basePath}
|
|
322
357
|
navigate={handleNavigate}
|
|
323
358
|
onAuthEvent={handleAuthEvent}
|
|
359
|
+
authFlowMode={mode}
|
|
324
360
|
redirectTo={redirectTo}
|
|
325
361
|
Link={InternalLink}
|
|
326
362
|
{...providerProps}
|
|
@@ -330,6 +366,7 @@ export function AuthFlow({
|
|
|
330
366
|
classNames={classNames}
|
|
331
367
|
cardHeader={cardHeader}
|
|
332
368
|
cardFooter={cardFooter}
|
|
369
|
+
email={verifyEmail}
|
|
333
370
|
socialLayout={socialLayout}
|
|
334
371
|
otpSeparators={otpSeparators}
|
|
335
372
|
redirectTo={redirectTo}
|
|
@@ -41,6 +41,10 @@ export interface AuthFormProps {
|
|
|
41
41
|
className?: string
|
|
42
42
|
classNames?: AuthFormClassNames
|
|
43
43
|
callbackURL?: string
|
|
44
|
+
/**
|
|
45
|
+
* Email for verification flows (passed in internal mode)
|
|
46
|
+
*/
|
|
47
|
+
email?: string
|
|
44
48
|
isSubmitting?: boolean
|
|
45
49
|
localization?: Partial<AuthLocalization>
|
|
46
50
|
pathname?: string
|
|
@@ -71,6 +75,7 @@ export function AuthForm({
|
|
|
71
75
|
className,
|
|
72
76
|
classNames,
|
|
73
77
|
callbackURL,
|
|
78
|
+
email,
|
|
74
79
|
isSubmitting,
|
|
75
80
|
localization,
|
|
76
81
|
pathname,
|
|
@@ -261,6 +266,7 @@ export function AuthForm({
|
|
|
261
266
|
className={className}
|
|
262
267
|
classNames={classNames}
|
|
263
268
|
callbackURL={callbackURL}
|
|
269
|
+
email={email}
|
|
264
270
|
localization={localization}
|
|
265
271
|
otpSeparators={otpSeparators}
|
|
266
272
|
redirectTo={redirectTo}
|
|
@@ -47,6 +47,10 @@ export interface AuthViewProps {
|
|
|
47
47
|
callbackURL?: string
|
|
48
48
|
cardHeader?: ReactNode
|
|
49
49
|
cardFooter?: ReactNode
|
|
50
|
+
/**
|
|
51
|
+
* Email for verification flows (passed in internal mode)
|
|
52
|
+
*/
|
|
53
|
+
email?: string
|
|
50
54
|
localization?: AuthLocalization
|
|
51
55
|
path?: string
|
|
52
56
|
pathname?: string
|
|
@@ -62,6 +66,7 @@ export function AuthView({
|
|
|
62
66
|
callbackURL,
|
|
63
67
|
cardHeader,
|
|
64
68
|
cardFooter,
|
|
69
|
+
email,
|
|
65
70
|
localization,
|
|
66
71
|
path: pathProp,
|
|
67
72
|
pathname,
|
|
@@ -173,6 +178,7 @@ export function AuthView({
|
|
|
173
178
|
<AuthForm
|
|
174
179
|
classNames={classNames?.form}
|
|
175
180
|
callbackURL={callbackURL}
|
|
181
|
+
email={email}
|
|
176
182
|
isSubmitting={isSubmitting}
|
|
177
183
|
localization={localization}
|
|
178
184
|
otpSeparators={otpSeparators}
|
|
@@ -63,7 +63,8 @@ export function EmailVerificationForm({
|
|
|
63
63
|
basePath,
|
|
64
64
|
viewPaths,
|
|
65
65
|
emailVerification,
|
|
66
|
-
onAuthEvent
|
|
66
|
+
onAuthEvent,
|
|
67
|
+
authFlowMode
|
|
67
68
|
} = useContext(AuthUIContext)
|
|
68
69
|
|
|
69
70
|
localization = { ...contextLocalization, ...localization }
|
|
@@ -159,13 +160,16 @@ export function EmailVerificationForm({
|
|
|
159
160
|
// No token - verification succeeded but no session
|
|
160
161
|
const session = { user }
|
|
161
162
|
onAuthEvent?.({ type: "VERIFICATION_SUCCESS", user, session })
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
163
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
164
|
+
if (authFlowMode !== "internal") {
|
|
165
|
+
navigate(
|
|
166
|
+
`${basePath}/${viewPaths.SIGN_IN}${window.location.search}`
|
|
167
|
+
)
|
|
168
|
+
toast({
|
|
169
|
+
variant: "success",
|
|
170
|
+
message: localization.EMAIL_VERIFICATION_SUCCESS!
|
|
171
|
+
})
|
|
172
|
+
}
|
|
169
173
|
}
|
|
170
174
|
} catch (error) {
|
|
171
175
|
const errorMessage = getLocalizedError({
|
|
@@ -52,7 +52,9 @@ export function ForgotPasswordForm({
|
|
|
52
52
|
navigate,
|
|
53
53
|
toast,
|
|
54
54
|
viewPaths,
|
|
55
|
-
localizeErrors
|
|
55
|
+
localizeErrors,
|
|
56
|
+
onAuthEvent,
|
|
57
|
+
authFlowMode
|
|
56
58
|
} = useContext(AuthUIContext)
|
|
57
59
|
|
|
58
60
|
localization = { ...contextLocalization, ...localization }
|
|
@@ -94,14 +96,19 @@ export function ForgotPasswordForm({
|
|
|
94
96
|
fetchOptions
|
|
95
97
|
})
|
|
96
98
|
|
|
99
|
+
onAuthEvent?.({ type: "FORGOT_PASSWORD_EMAIL_SENT", email })
|
|
100
|
+
|
|
97
101
|
toast({
|
|
98
102
|
variant: "success",
|
|
99
103
|
message: localization.FORGOT_PASSWORD_EMAIL
|
|
100
104
|
})
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
107
|
+
if (authFlowMode !== "internal") {
|
|
108
|
+
navigate(
|
|
109
|
+
`${basePath}/${viewPaths.SIGN_IN}${window.location.search}`
|
|
110
|
+
)
|
|
111
|
+
}
|
|
105
112
|
} catch (error) {
|
|
106
113
|
toast({
|
|
107
114
|
variant: "error",
|
|
@@ -45,7 +45,9 @@ export function ResetPasswordForm({
|
|
|
45
45
|
viewPaths,
|
|
46
46
|
navigate,
|
|
47
47
|
toast,
|
|
48
|
-
localizeErrors
|
|
48
|
+
localizeErrors,
|
|
49
|
+
onAuthEvent,
|
|
50
|
+
authFlowMode
|
|
49
51
|
} = useContext(AuthUIContext)
|
|
50
52
|
|
|
51
53
|
const confirmPasswordEnabled = credentials?.confirmPassword
|
|
@@ -99,12 +101,15 @@ export function ResetPasswordForm({
|
|
|
99
101
|
const token = searchParams.get("token")
|
|
100
102
|
|
|
101
103
|
if (!token || token === "INVALID_TOKEN") {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
105
|
+
if (authFlowMode !== "internal") {
|
|
106
|
+
navigate(
|
|
107
|
+
`${basePath}/${viewPaths.SIGN_IN}${window.location.search}`
|
|
108
|
+
)
|
|
109
|
+
}
|
|
105
110
|
toast({ variant: "error", message: localization.INVALID_TOKEN })
|
|
106
111
|
}
|
|
107
|
-
}, [basePath, navigate, toast, viewPaths, localization])
|
|
112
|
+
}, [basePath, navigate, toast, viewPaths, localization, authFlowMode])
|
|
108
113
|
|
|
109
114
|
async function resetPassword({ newPassword }: z.infer<typeof formSchema>) {
|
|
110
115
|
try {
|
|
@@ -117,14 +122,19 @@ export function ResetPasswordForm({
|
|
|
117
122
|
fetchOptions: { throw: true }
|
|
118
123
|
})
|
|
119
124
|
|
|
125
|
+
onAuthEvent?.({ type: "PASSWORD_RESET_SUCCESS" })
|
|
126
|
+
|
|
120
127
|
toast({
|
|
121
128
|
variant: "success",
|
|
122
129
|
message: localization.RESET_PASSWORD_SUCCESS
|
|
123
130
|
})
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
133
|
+
if (authFlowMode !== "internal") {
|
|
134
|
+
navigate(
|
|
135
|
+
`${basePath}/${viewPaths.SIGN_IN}${window.location.search}`
|
|
136
|
+
)
|
|
137
|
+
}
|
|
128
138
|
} catch (error) {
|
|
129
139
|
toast({
|
|
130
140
|
variant: "error",
|
|
@@ -69,7 +69,8 @@ export function SignInForm({
|
|
|
69
69
|
Link,
|
|
70
70
|
localizeErrors,
|
|
71
71
|
emailVerification,
|
|
72
|
-
onAuthEvent
|
|
72
|
+
onAuthEvent,
|
|
73
|
+
authFlowMode
|
|
73
74
|
} = useContext(AuthUIContext)
|
|
74
75
|
|
|
75
76
|
const rememberMeEnabled = credentials?.rememberMe
|
|
@@ -150,9 +151,12 @@ export function SignInForm({
|
|
|
150
151
|
|
|
151
152
|
if (response.twoFactorRedirect) {
|
|
152
153
|
onAuthEvent?.({ type: "SIGN_IN_REQUIRES_2FA", email })
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
155
|
+
if (authFlowMode !== "internal") {
|
|
156
|
+
navigate(
|
|
157
|
+
`${basePath}/${viewPaths.TWO_FACTOR}${window.location.search}`
|
|
158
|
+
)
|
|
159
|
+
}
|
|
156
160
|
} else {
|
|
157
161
|
// Build user and session for events
|
|
158
162
|
const user = response.user as {
|
|
@@ -202,11 +206,14 @@ export function SignInForm({
|
|
|
202
206
|
|
|
203
207
|
if (errorCode === "EMAIL_NOT_VERIFIED") {
|
|
204
208
|
onAuthEvent?.({ type: "SIGN_IN_REQUIRES_VERIFICATION", email })
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
210
|
+
if (authFlowMode !== "internal") {
|
|
211
|
+
navigate(
|
|
212
|
+
`${basePath}/${
|
|
213
|
+
viewPaths.EMAIL_VERIFICATION
|
|
214
|
+
}?email=${encodeURIComponent(email)}`
|
|
215
|
+
)
|
|
216
|
+
}
|
|
210
217
|
}
|
|
211
218
|
}
|
|
212
219
|
}
|
|
@@ -93,7 +93,8 @@ export function SignUpForm({
|
|
|
93
93
|
avatar,
|
|
94
94
|
localizeErrors,
|
|
95
95
|
emailVerification,
|
|
96
|
-
onAuthEvent
|
|
96
|
+
onAuthEvent,
|
|
97
|
+
authFlowMode
|
|
97
98
|
} = useContext(AuthUIContext)
|
|
98
99
|
|
|
99
100
|
const confirmPasswordEnabled = credentials?.confirmPassword
|
|
@@ -415,9 +416,16 @@ export function SignUpForm({
|
|
|
415
416
|
|
|
416
417
|
// Check for 2FA redirect first (same pattern as sign-in form)
|
|
417
418
|
if (response.twoFactorRedirect) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
419
|
+
onAuthEvent?.({
|
|
420
|
+
type: "SIGN_IN_REQUIRES_2FA",
|
|
421
|
+
email: email as string
|
|
422
|
+
})
|
|
423
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
424
|
+
if (authFlowMode !== "internal") {
|
|
425
|
+
navigate(
|
|
426
|
+
`${basePath}/${viewPaths.TWO_FACTOR}${window.location.search}`
|
|
427
|
+
)
|
|
428
|
+
}
|
|
421
429
|
} else if (response.token && user) {
|
|
422
430
|
// Token present = verified or no verification required
|
|
423
431
|
const session = {
|
|
@@ -433,22 +441,28 @@ export function SignUpForm({
|
|
|
433
441
|
type: "SIGN_UP_REQUIRES_VERIFICATION",
|
|
434
442
|
email: email as string
|
|
435
443
|
})
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
444
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
445
|
+
if (authFlowMode !== "internal") {
|
|
446
|
+
navigate(
|
|
447
|
+
`${basePath}/${viewPaths.EMAIL_VERIFICATION}?email=${encodeURIComponent(email as string)}`
|
|
448
|
+
)
|
|
449
|
+
}
|
|
439
450
|
} else {
|
|
440
451
|
// Fallback: redirect to sign-in (e.g., link-based verification sent)
|
|
441
452
|
onAuthEvent?.({
|
|
442
453
|
type: "SIGN_UP_REQUIRES_VERIFICATION",
|
|
443
454
|
email: email as string
|
|
444
455
|
})
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
456
|
+
// In internal mode, AuthFlow handles view transitions via events
|
|
457
|
+
if (authFlowMode !== "internal") {
|
|
458
|
+
navigate(
|
|
459
|
+
`${basePath}/${viewPaths.SIGN_IN}${window.location.search}`
|
|
460
|
+
)
|
|
461
|
+
toast({
|
|
462
|
+
variant: "success",
|
|
463
|
+
message: localization.SIGN_UP_EMAIL!
|
|
464
|
+
})
|
|
465
|
+
}
|
|
452
466
|
}
|
|
453
467
|
} catch (error) {
|
|
454
468
|
const errorMessage = getLocalizedError({
|
|
@@ -225,6 +225,11 @@ export type AuthUIContextType = {
|
|
|
225
225
|
* Called with typed events during sign-in, sign-up, verification, etc.
|
|
226
226
|
*/
|
|
227
227
|
onAuthEvent?: AuthFlowEventHandler
|
|
228
|
+
/**
|
|
229
|
+
* AuthFlow mode - when 'internal', forms skip navigation and let AuthFlow handle view transitions.
|
|
230
|
+
* undefined = standalone form usage (normal navigation)
|
|
231
|
+
*/
|
|
232
|
+
authFlowMode?: "internal" | "route"
|
|
228
233
|
/**
|
|
229
234
|
* Replace the current URL
|
|
230
235
|
* @default navigate
|
package/src/ui/style.css
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* Default theme variables - users can override these in their own CSS */
|
|
2
2
|
:root {
|
|
3
3
|
--team-1: oklch(0.646 0.222 41.116);
|
|
4
4
|
--team-2: oklch(0.6 0.118 184.704);
|
|
5
5
|
--team-3: oklch(0.398 0.07 227.392);
|
|
6
6
|
--team-4: oklch(0.828 0.189 84.429);
|
|
7
7
|
--team-5: oklch(0.769 0.188 70.08);
|
|
8
|
+
|
|
9
|
+
/* Card and UI component default colors */
|
|
10
|
+
--card: 0 0% 100%;
|
|
11
|
+
--card-foreground: 240 10% 3.9%;
|
|
12
|
+
--background: 0 0% 100%;
|
|
13
|
+
--foreground: 240 10% 3.9%;
|
|
14
|
+
--muted: 240 4.8% 95.9%;
|
|
15
|
+
--muted-foreground: 240 3.8% 46.1%;
|
|
16
|
+
--border: 240 5.9% 90%;
|
|
17
|
+
--input: 240 5.9% 90%;
|
|
18
|
+
--ring: 240 5.9% 10%;
|
|
19
|
+
--primary: 240 5.9% 10%;
|
|
20
|
+
--primary-foreground: 0 0% 98%;
|
|
21
|
+
--destructive: 0 84.2% 60.2%;
|
|
22
|
+
--destructive-foreground: 0 0% 98%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.dark {
|
|
26
|
+
--card: 240 10% 3.9%;
|
|
27
|
+
--card-foreground: 0 0% 98%;
|
|
28
|
+
--background: 240 10% 3.9%;
|
|
29
|
+
--foreground: 0 0% 98%;
|
|
30
|
+
--muted: 240 3.7% 15.9%;
|
|
31
|
+
--muted-foreground: 240 5% 64.9%;
|
|
32
|
+
--border: 240 3.7% 15.9%;
|
|
33
|
+
--input: 240 3.7% 15.9%;
|
|
34
|
+
--ring: 240 4.9% 83.9%;
|
|
35
|
+
--primary: 0 0% 98%;
|
|
36
|
+
--primary-foreground: 240 5.9% 10%;
|
|
37
|
+
--destructive: 0 62.8% 30.6%;
|
|
38
|
+
--destructive-foreground: 0 0% 98%;
|
|
8
39
|
}
|
|
9
40
|
|
|
10
41
|
/*
|