better-auth-ui-svelte 0.12.3 → 0.12.5
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/components/auth/forms/email-otp-form.svelte +8 -6
- package/dist/components/auth/forms/forgot-password-form.svelte +7 -5
- package/dist/components/auth/forms/magic-link-form.svelte +3 -2
- package/dist/components/auth/forms/recover-account-form.svelte +5 -5
- package/dist/components/auth/forms/reset-password-form.svelte +6 -4
- package/dist/components/auth/forms/sign-in-form.svelte +5 -5
- package/dist/components/auth/forms/sign-up-form.svelte +4 -4
- package/dist/components/auth/forms/two-factor-form.svelte +5 -5
- package/dist/components/auth/magic-link-sent.svelte +4 -3
- package/dist/components/auth/passkey-button.svelte +13 -3
- package/dist/components/auth/provider-button.svelte +17 -8
- package/dist/components/auth/verify-email.svelte +5 -4
- package/dist/components/organization/organization-switcher.svelte +5 -5
- package/package.json +1 -1
|
@@ -45,9 +45,8 @@
|
|
|
45
45
|
let verifiedEmail = $state<string | undefined>(undefined);
|
|
46
46
|
|
|
47
47
|
// Transition for OTP verification success
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
});
|
|
48
|
+
const transition = useOnSuccessTransition({ redirectTo });
|
|
49
|
+
const { onSuccess } = transition;
|
|
51
50
|
|
|
52
51
|
// Reactive validation schemas
|
|
53
52
|
const emailFormSchema = $derived(
|
|
@@ -106,10 +105,13 @@
|
|
|
106
105
|
}
|
|
107
106
|
}));
|
|
108
107
|
|
|
108
|
+
const emailFormIsSubmitting = emailForm.useStore((s) => s.isSubmitting);
|
|
109
|
+
const otpFormIsSubmitting = otpForm.useStore((s) => s.isSubmitting);
|
|
110
|
+
|
|
109
111
|
// Computed submitting states
|
|
110
|
-
const emailFormSubmitting = $derived(isSubmittingProp ||
|
|
112
|
+
const emailFormSubmitting = $derived(isSubmittingProp || emailFormIsSubmitting.current);
|
|
111
113
|
const otpFormSubmitting = $derived(
|
|
112
|
-
isSubmittingProp ||
|
|
114
|
+
isSubmittingProp || otpFormIsSubmitting.current || transition.isPending
|
|
113
115
|
);
|
|
114
116
|
|
|
115
117
|
// Handle email form submission with error handling
|
|
@@ -137,7 +139,7 @@
|
|
|
137
139
|
// Update parent isSubmitting state
|
|
138
140
|
$effect(() => {
|
|
139
141
|
setIsSubmitting?.(
|
|
140
|
-
|
|
142
|
+
emailFormIsSubmitting.current || otpFormIsSubmitting.current || transition.isPending
|
|
141
143
|
);
|
|
142
144
|
});
|
|
143
145
|
|
|
@@ -103,10 +103,12 @@
|
|
|
103
103
|
}
|
|
104
104
|
}));
|
|
105
105
|
|
|
106
|
+
const formIsSubmitting = form.useStore((s) => s.isSubmitting);
|
|
107
|
+
|
|
106
108
|
// Sync isSubmitting state
|
|
107
109
|
$effect(() => {
|
|
108
|
-
isSubmitting =
|
|
109
|
-
setIsSubmitting?.(
|
|
110
|
+
isSubmitting = formIsSubmitting.current;
|
|
111
|
+
setIsSubmitting?.(formIsSubmitting.current);
|
|
110
112
|
});
|
|
111
113
|
</script>
|
|
112
114
|
|
|
@@ -130,7 +132,7 @@
|
|
|
130
132
|
value={field.state.value}
|
|
131
133
|
oninput={(e) => field.handleChange(e.currentTarget.value)}
|
|
132
134
|
onblur={field.handleBlur}
|
|
133
|
-
disabled={
|
|
135
|
+
disabled={formIsSubmitting.current}
|
|
134
136
|
class={classNames?.input}
|
|
135
137
|
/>
|
|
136
138
|
|
|
@@ -147,10 +149,10 @@
|
|
|
147
149
|
|
|
148
150
|
<Button
|
|
149
151
|
type="submit"
|
|
150
|
-
disabled={
|
|
152
|
+
disabled={formIsSubmitting.current}
|
|
151
153
|
class={cn('w-full', classNames?.button, classNames?.primaryButton)}
|
|
152
154
|
>
|
|
153
|
-
{#if
|
|
155
|
+
{#if formIsSubmitting.current}
|
|
154
156
|
<Loader2 class="animate-spin" />
|
|
155
157
|
{:else}
|
|
156
158
|
{localization.FORGOT_PASSWORD_ACTION}
|
|
@@ -129,11 +129,12 @@
|
|
|
129
129
|
}
|
|
130
130
|
}));
|
|
131
131
|
|
|
132
|
-
const
|
|
132
|
+
const formIsSubmitting = form.useStore((s) => s.isSubmitting);
|
|
133
|
+
const isSubmitting = $derived(isSubmittingProp || formIsSubmitting.current);
|
|
133
134
|
|
|
134
135
|
// Update parent isSubmitting state
|
|
135
136
|
$effect(() => {
|
|
136
|
-
setIsSubmitting?.(
|
|
137
|
+
setIsSubmitting?.(formIsSubmitting.current);
|
|
137
138
|
});
|
|
138
139
|
</script>
|
|
139
140
|
|
|
@@ -41,9 +41,8 @@
|
|
|
41
41
|
// Merge localization from context and props
|
|
42
42
|
const localization = { ...contextLocalization, ...localizationProp };
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
});
|
|
44
|
+
const transition = useOnSuccessTransition({ redirectTo });
|
|
45
|
+
const { onSuccess } = transition;
|
|
47
46
|
|
|
48
47
|
const schema = z.object({
|
|
49
48
|
code: z.string().min(1, { message: localization.BACKUP_CODE_REQUIRED })
|
|
@@ -71,11 +70,12 @@
|
|
|
71
70
|
}));
|
|
72
71
|
|
|
73
72
|
// Compute final isSubmitting state
|
|
74
|
-
const
|
|
73
|
+
const formIsSubmitting = form.useStore((s) => s.isSubmitting);
|
|
74
|
+
const isSubmitting = $derived(isSubmittingProp ?? (formIsSubmitting.current || transition.isPending));
|
|
75
75
|
|
|
76
76
|
// Sync isSubmitting state
|
|
77
77
|
$effect(() => {
|
|
78
|
-
setIsSubmitting?.(
|
|
78
|
+
setIsSubmitting?.(formIsSubmitting.current || transition.isPending);
|
|
79
79
|
});
|
|
80
80
|
</script>
|
|
81
81
|
|
|
@@ -125,6 +125,8 @@
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}));
|
|
128
|
+
|
|
129
|
+
const formIsSubmitting = form.useStore((s) => s.isSubmitting);
|
|
128
130
|
</script>
|
|
129
131
|
|
|
130
132
|
<form
|
|
@@ -147,7 +149,7 @@
|
|
|
147
149
|
value={field.state.value}
|
|
148
150
|
oninput={(e) => field.handleChange(e.currentTarget.value)}
|
|
149
151
|
onblur={field.handleBlur}
|
|
150
|
-
disabled={
|
|
152
|
+
disabled={formIsSubmitting.current}
|
|
151
153
|
class={classNames?.input}
|
|
152
154
|
/>
|
|
153
155
|
{#if field.state.meta.errors.length > 0}
|
|
@@ -183,7 +185,7 @@
|
|
|
183
185
|
value={field.state.value}
|
|
184
186
|
oninput={(e) => field.handleChange(e.currentTarget.value)}
|
|
185
187
|
onblur={field.handleBlur}
|
|
186
|
-
disabled={
|
|
188
|
+
disabled={formIsSubmitting.current}
|
|
187
189
|
class={classNames?.input}
|
|
188
190
|
/>
|
|
189
191
|
{#if field.state.meta.errors.length > 0}
|
|
@@ -198,10 +200,10 @@
|
|
|
198
200
|
|
|
199
201
|
<Button
|
|
200
202
|
type="submit"
|
|
201
|
-
disabled={
|
|
203
|
+
disabled={formIsSubmitting.current}
|
|
202
204
|
class={cn('w-full', classNames?.button, classNames?.primaryButton)}
|
|
203
205
|
>
|
|
204
|
-
{#if
|
|
206
|
+
{#if formIsSubmitting.current}
|
|
205
207
|
<Loader2 class="animate-spin" />
|
|
206
208
|
{:else}
|
|
207
209
|
{loc.RESET_PASSWORD_ACTION}
|
|
@@ -79,9 +79,8 @@
|
|
|
79
79
|
captchaHook.captchaRef = captchaRef;
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
});
|
|
82
|
+
const transition = useOnSuccessTransition({ redirectTo });
|
|
83
|
+
const { onSuccess } = transition;
|
|
85
84
|
|
|
86
85
|
// Form schema
|
|
87
86
|
const formSchema = $derived(
|
|
@@ -162,11 +161,12 @@
|
|
|
162
161
|
}
|
|
163
162
|
}));
|
|
164
163
|
|
|
165
|
-
const
|
|
164
|
+
const formIsSubmitting = form.useStore((s) => s.isSubmitting);
|
|
165
|
+
const isSubmitting = $derived(isSubmittingProp || formIsSubmitting.current || transition.isPending);
|
|
166
166
|
|
|
167
167
|
// Update parent isSubmitting state
|
|
168
168
|
$effect(() => {
|
|
169
|
-
setIsSubmitting?.(
|
|
169
|
+
setIsSubmitting?.(formIsSubmitting.current || transition.isPending);
|
|
170
170
|
});
|
|
171
171
|
</script>
|
|
172
172
|
|
|
@@ -98,9 +98,8 @@
|
|
|
98
98
|
});
|
|
99
99
|
|
|
100
100
|
// Success transition for navigation
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
});
|
|
101
|
+
const transition = useOnSuccessTransition({ redirectTo });
|
|
102
|
+
const { onSuccess } = transition;
|
|
104
103
|
|
|
105
104
|
// Helper functions
|
|
106
105
|
function getRedirectTo() {
|
|
@@ -391,7 +390,8 @@
|
|
|
391
390
|
}
|
|
392
391
|
|
|
393
392
|
// Combine isSubmitting states
|
|
394
|
-
const
|
|
393
|
+
const formIsSubmitting = form.useStore((s) => s.isSubmitting);
|
|
394
|
+
const isSubmitting = $derived(formIsSubmitting.current || transition.isPending);
|
|
395
395
|
|
|
396
396
|
// Sync external isSubmitting
|
|
397
397
|
$effect(() => {
|
|
@@ -56,9 +56,8 @@
|
|
|
56
56
|
|
|
57
57
|
const localization = $derived({ ...contextLocalization, ...localizationProp });
|
|
58
58
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
});
|
|
59
|
+
const transition = useOnSuccessTransition({ redirectTo });
|
|
60
|
+
const { onSuccess } = transition;
|
|
62
61
|
|
|
63
62
|
// Get session data to check if 2FA is being enabled
|
|
64
63
|
const sessionQuery = hooks?.useSession?.();
|
|
@@ -128,12 +127,13 @@
|
|
|
128
127
|
}
|
|
129
128
|
}));
|
|
130
129
|
|
|
131
|
-
const
|
|
130
|
+
const formIsSubmitting = form.useStore((s) => s.isSubmitting);
|
|
131
|
+
const isSubmitting = $derived(isSubmittingProp || formIsSubmitting.current || transition.isPending);
|
|
132
132
|
|
|
133
133
|
// Update parent isSubmitting state
|
|
134
134
|
$effect(() => {
|
|
135
135
|
if (setIsSubmitting) {
|
|
136
|
-
setIsSubmitting(
|
|
136
|
+
setIsSubmitting(formIsSubmitting.current || transition.isPending);
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
139
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as Card from '../ui/card/index.js';
|
|
6
6
|
import { Button } from '../ui/button/index.js';
|
|
7
7
|
import type { AuthLocalization } from '../../localization/auth-localization.js';
|
|
8
|
+
import Loader2 from '@lucide/svelte/icons/loader-2';
|
|
8
9
|
import MailOpen from '@lucide/svelte/icons/mail-open';
|
|
9
10
|
|
|
10
11
|
interface Props {
|
|
@@ -186,10 +187,10 @@
|
|
|
186
187
|
onclick={handleResendMagicLink}
|
|
187
188
|
class="w-full"
|
|
188
189
|
>
|
|
189
|
-
{#if
|
|
190
|
+
{#if isResending}
|
|
191
|
+
<Loader2 class="animate-spin" />
|
|
192
|
+
{:else if countdown > 0}
|
|
190
193
|
{localization.RESEND_MAGIC_LINK} ({countdown}s)
|
|
191
|
-
{:else if isResending}
|
|
192
|
-
{localization.RESEND_MAGIC_LINK}...
|
|
193
194
|
{:else}
|
|
194
195
|
{localization.RESEND_MAGIC_LINK}
|
|
195
196
|
{/if}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Fingerprint from '@lucide/svelte/icons/fingerprint';
|
|
3
|
+
import Loader2 from '@lucide/svelte/icons/loader-2';
|
|
3
4
|
import {
|
|
4
5
|
getAuthClient,
|
|
5
6
|
getAuthUIConfig,
|
|
@@ -31,8 +32,11 @@
|
|
|
31
32
|
|
|
32
33
|
const { onSuccess } = useOnSuccessTransition({ redirectTo });
|
|
33
34
|
|
|
35
|
+
let isPending = $state(false);
|
|
36
|
+
|
|
34
37
|
async function signInPasskey() {
|
|
35
38
|
setIsSubmitting?.(true);
|
|
39
|
+
isPending = true;
|
|
36
40
|
|
|
37
41
|
try {
|
|
38
42
|
const response = await authClient.signIn.passkey({
|
|
@@ -48,6 +52,7 @@
|
|
|
48
52
|
);
|
|
49
53
|
|
|
50
54
|
setIsSubmitting?.(false);
|
|
55
|
+
isPending = false;
|
|
51
56
|
} else {
|
|
52
57
|
await onSuccess();
|
|
53
58
|
}
|
|
@@ -55,6 +60,7 @@
|
|
|
55
60
|
config.toast.error(getLocalizedError({ error, localization: loc }));
|
|
56
61
|
|
|
57
62
|
setIsSubmitting?.(false);
|
|
63
|
+
isPending = false;
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
</script>
|
|
@@ -67,7 +73,11 @@
|
|
|
67
73
|
variant="secondary"
|
|
68
74
|
onclick={signInPasskey}
|
|
69
75
|
>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{
|
|
76
|
+
{#if isPending}
|
|
77
|
+
<Loader2 class="animate-spin" />
|
|
78
|
+
{:else}
|
|
79
|
+
<Fingerprint class={classNames?.form?.icon} />
|
|
80
|
+
{loc.SIGN_IN_WITH}
|
|
81
|
+
{loc.PASSKEY}
|
|
82
|
+
{/if}
|
|
73
83
|
</Button>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import Loader2 from '@lucide/svelte/icons/loader-2';
|
|
2
3
|
import { Button } from '../ui/button/index.js';
|
|
3
4
|
import { getAuthClient, getAuthUIConfig } from '../../context/auth-ui-config.svelte';
|
|
4
5
|
import { cn } from '../../utils/ui.js';
|
|
@@ -37,6 +38,8 @@
|
|
|
37
38
|
const authClient = getAuthClient();
|
|
38
39
|
const config = getAuthUIConfig();
|
|
39
40
|
|
|
41
|
+
let isPending = $state(false);
|
|
42
|
+
|
|
40
43
|
function getRedirectTo() {
|
|
41
44
|
return redirectToProp || getSearchParam('redirectTo') || config.redirectTo;
|
|
42
45
|
}
|
|
@@ -53,6 +56,7 @@
|
|
|
53
56
|
|
|
54
57
|
async function doSignInSocial() {
|
|
55
58
|
setIsSubmitting(true);
|
|
59
|
+
isPending = true;
|
|
56
60
|
|
|
57
61
|
try {
|
|
58
62
|
if (other) {
|
|
@@ -94,6 +98,7 @@
|
|
|
94
98
|
config.toast.error(getLocalizedError({ error, localization }));
|
|
95
99
|
|
|
96
100
|
setIsSubmitting(false);
|
|
101
|
+
isPending = false;
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
</script>
|
|
@@ -110,14 +115,18 @@
|
|
|
110
115
|
variant="outline"
|
|
111
116
|
onclick={doSignInSocial}
|
|
112
117
|
>
|
|
113
|
-
{#if
|
|
114
|
-
<
|
|
115
|
-
{
|
|
118
|
+
{#if isPending}
|
|
119
|
+
<Loader2 class="animate-spin" />
|
|
120
|
+
{:else}
|
|
121
|
+
{#if provider.icon}
|
|
122
|
+
<provider.icon class={classNames?.form?.icon} />
|
|
123
|
+
{/if}
|
|
116
124
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
{#if socialLayout === 'grid'}
|
|
126
|
+
{provider.name}
|
|
127
|
+
{/if}
|
|
128
|
+
{#if socialLayout === 'vertical'}
|
|
129
|
+
{localization.SIGN_IN_WITH} {provider.name}
|
|
130
|
+
{/if}
|
|
122
131
|
{/if}
|
|
123
132
|
</Button>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as Card from '../ui/card/index.js';
|
|
6
6
|
import { Button } from '../ui/button/index.js';
|
|
7
7
|
import type { AuthLocalization } from '../../localization/auth-localization.js';
|
|
8
|
+
import Loader2 from '@lucide/svelte/icons/loader-2';
|
|
8
9
|
import MailOpen from '@lucide/svelte/icons/mail-open';
|
|
9
10
|
import CheckCircle from '@lucide/svelte/icons/check-circle';
|
|
10
11
|
|
|
@@ -274,7 +275,7 @@
|
|
|
274
275
|
|
|
275
276
|
<Button onclick={handleVerifyEmail} disabled={isVerifying} class="w-full">
|
|
276
277
|
{#if isVerifying}
|
|
277
|
-
|
|
278
|
+
<Loader2 class="animate-spin" />
|
|
278
279
|
{:else}
|
|
279
280
|
{localization.VERIFY_EMAIL_BUTTON || 'Verify Email'}
|
|
280
281
|
{/if}
|
|
@@ -299,10 +300,10 @@
|
|
|
299
300
|
onclick={handleResendEmail}
|
|
300
301
|
class="w-full"
|
|
301
302
|
>
|
|
302
|
-
{#if
|
|
303
|
+
{#if isResending}
|
|
304
|
+
<Loader2 class="animate-spin" />
|
|
305
|
+
{:else if countdown > 0}
|
|
303
306
|
{localization.RESEND_VERIFICATION_EMAIL} ({countdown}s)
|
|
304
|
-
{:else if isResending}
|
|
305
|
-
{localization.RESEND_VERIFICATION_EMAIL}...
|
|
306
307
|
{:else}
|
|
307
308
|
{localization.RESEND_VERIFICATION_EMAIL}
|
|
308
309
|
{/if}
|
|
@@ -157,11 +157,11 @@
|
|
|
157
157
|
const organizationRefetching = $derived(currentOrgResult.isRefetching);
|
|
158
158
|
const organizationRefetch = currentOrgResult.refetch;
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
// Whether there are any non-active orgs or personal account rendered above additionalOrganizations
|
|
161
|
+
const hasItemsAboveAdditional = $derived(
|
|
162
|
+
(!hidePersonal && !!activeOrganization) ||
|
|
163
|
+
(organizations ?? []).some((o) => o.id !== activeOrganization?.id)
|
|
164
|
+
);
|
|
165
165
|
|
|
166
166
|
// Smarter pending logic: Only show loading if we're truly waiting for data
|
|
167
167
|
// If we have organizations list, we can show the UI even if active org is still loading
|