@better-auth-ui/heroui 1.6.28 → 1.6.29
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/last-login-method/last-used-badge.d.ts +13 -0
- package/dist/components/auth/provider-button.d.ts +3 -1
- package/dist/components/auth/provider-buttons.d.ts +3 -1
- package/dist/index.js +310 -291
- package/dist/lib/auth/last-login-method-plugin.d.ts +12 -0
- package/dist/plugins.d.ts +2 -0
- package/dist/plugins.js +1003 -981
- package/dist/user-view-D9ial2fQ.js +150 -0
- package/package.json +1 -1
- package/src/components/auth/last-login-method/last-used-badge.tsx +40 -0
- package/src/components/auth/magic-link/magic-link.tsx +2 -2
- package/src/components/auth/provider-button.tsx +17 -3
- package/src/components/auth/provider-buttons.tsx +5 -1
- package/src/components/auth/sign-in.tsx +10 -3
- package/src/components/auth/sign-up.tsx +2 -2
- package/src/components/auth/username/sign-in-username.tsx +6 -3
- package/src/lib/auth/last-login-method-plugin.ts +12 -0
- package/src/plugins.ts +2 -0
- package/dist/user-view-CxSDufx_.js +0 -126
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { authMutationKeys as e, getEmailProviderLink as t, getProviderName as n } from "@better-auth-ui/core";
|
|
2
|
+
import { providerIcons as r, useAuth as i, useLastLoginMethod as a, useSession as o, useSignInSocial as s } from "@better-auth-ui/react";
|
|
3
|
+
import { ArrowUpRightFromSquare as c, Person as l } from "@gravity-ui/icons";
|
|
4
|
+
import { Avatar as u, Button as d, Chip as f, Link as p, Separator as m, Skeleton as h, Spinner as g, cn as _ } from "@heroui/react";
|
|
5
|
+
import { useMemo as v } from "react";
|
|
6
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
7
|
+
import { buttonVariants as x } from "@heroui/styles";
|
|
8
|
+
import { useIsMutating as S } from "@tanstack/react-query";
|
|
9
|
+
//#region src/components/auth/open-email-button.tsx
|
|
10
|
+
function C({ email: e, className: n }) {
|
|
11
|
+
let { localization: r } = i(), a = t(e);
|
|
12
|
+
return a ? /* @__PURE__ */ b(p, {
|
|
13
|
+
href: a.loginUrl,
|
|
14
|
+
target: "_blank",
|
|
15
|
+
rel: "noopener noreferrer",
|
|
16
|
+
className: _(x({ variant: "primary" }), "w-full gap-2", n),
|
|
17
|
+
children: [r.auth.openEmailProvider.replace("{{provider}}", a.companyProvider), /* @__PURE__ */ y(c, { className: "size-3" })]
|
|
18
|
+
}) : null;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/components/auth/field-separator.tsx
|
|
22
|
+
function w({ children: e }) {
|
|
23
|
+
return /* @__PURE__ */ b("div", {
|
|
24
|
+
className: "flex items-center gap-4",
|
|
25
|
+
children: [
|
|
26
|
+
/* @__PURE__ */ y(m, { className: "flex-1" }),
|
|
27
|
+
/* @__PURE__ */ y("p", {
|
|
28
|
+
className: "text-xs text-muted shrink-0",
|
|
29
|
+
children: e
|
|
30
|
+
}),
|
|
31
|
+
/* @__PURE__ */ y(m, { className: "flex-1" })
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/components/auth/last-login-method/last-used-badge.tsx
|
|
37
|
+
function T({ method: e, compact: t, floating: n }) {
|
|
38
|
+
let { method: r, localization: i } = a();
|
|
39
|
+
return !r || !(Array.isArray(e) ? e : [e]).includes(r) ? null : /* @__PURE__ */ y(f, {
|
|
40
|
+
className: _(n && "pointer-events-none absolute top-0 right-0 z-10 translate-x-1/4 -translate-y-1/2 shadow-sm"),
|
|
41
|
+
color: n ? "accent" : "default",
|
|
42
|
+
size: "sm",
|
|
43
|
+
variant: n ? "primary" : "secondary",
|
|
44
|
+
children: t || n ? i.lastUsedShort : i.lastUsed
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/components/auth/provider-button.tsx
|
|
49
|
+
function E({ provider: t, display: a = "full", view: o = "signIn", variant: c = "tertiary", className: l, ...u }) {
|
|
50
|
+
let { authClient: f, baseURL: p, localization: m, redirectTo: h } = i(), v = `${p}${h}`, { mutate: x, isPending: C } = s(f), w = r[t];
|
|
51
|
+
return /* @__PURE__ */ b(d, {
|
|
52
|
+
variant: c,
|
|
53
|
+
isPending: S({ mutationKey: e.signIn.all }) + S({ mutationKey: e.signUp.all }) > 0,
|
|
54
|
+
onPress: () => x({
|
|
55
|
+
provider: t,
|
|
56
|
+
callbackURL: v
|
|
57
|
+
}),
|
|
58
|
+
className: _("relative overflow-visible", l),
|
|
59
|
+
...u,
|
|
60
|
+
children: [
|
|
61
|
+
C ? /* @__PURE__ */ y(g, {
|
|
62
|
+
color: "current",
|
|
63
|
+
size: "sm"
|
|
64
|
+
}) : /* @__PURE__ */ y(w, {}),
|
|
65
|
+
a === "full" ? m.auth.continueWith.replace("{{provider}}", n(t)) : a === "name" ? n(t) : null,
|
|
66
|
+
a === "icon" && /* @__PURE__ */ y("span", {
|
|
67
|
+
className: "sr-only",
|
|
68
|
+
children: n(t)
|
|
69
|
+
}),
|
|
70
|
+
o !== "signUp" && /* @__PURE__ */ y(T, {
|
|
71
|
+
method: t,
|
|
72
|
+
floating: !0
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/components/auth/provider-buttons.tsx
|
|
79
|
+
function D({ socialLayout: e = "auto", view: t = "signIn" }) {
|
|
80
|
+
let { socialProviders: n } = i(), r = v(() => e === "auto" ? n?.length && n.length >= 4 ? "horizontal" : "vertical" : e, [e, n?.length]);
|
|
81
|
+
return /* @__PURE__ */ y("div", {
|
|
82
|
+
className: _("gap-3", r === "grid" && "grid grid-cols-2", r === "vertical" && "flex flex-col", r === "horizontal" && "flex flex-wrap"),
|
|
83
|
+
children: n?.map((e) => /* @__PURE__ */ y(E, {
|
|
84
|
+
provider: e,
|
|
85
|
+
view: t,
|
|
86
|
+
display: r === "vertical" ? "full" : r === "grid" ? "name" : "icon",
|
|
87
|
+
className: _("w-full", r === "horizontal" && "flex-1")
|
|
88
|
+
}, e))
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/components/auth/user/user-avatar.tsx
|
|
93
|
+
function O({ className: e, fallback: t, isPending: n, user: r, size: a = "sm", style: s, ...c }) {
|
|
94
|
+
let { authClient: d } = i(), { data: f, isPending: p } = o(d, { enabled: !r && !n });
|
|
95
|
+
if ((n || p) && !r) return /* @__PURE__ */ y(h, {
|
|
96
|
+
className: _("rounded-full", a === "sm" ? "size-8" : a === "md" ? "size-10" : "size-12", e),
|
|
97
|
+
style: s
|
|
98
|
+
});
|
|
99
|
+
let m = r ?? f?.user, g = (m?.username || m?.name || m?.email)?.slice(0, 2).toUpperCase();
|
|
100
|
+
return /* @__PURE__ */ b(u, {
|
|
101
|
+
size: a,
|
|
102
|
+
className: _("rounded-full", e),
|
|
103
|
+
style: s,
|
|
104
|
+
...c,
|
|
105
|
+
children: [/* @__PURE__ */ y(u.Image, {
|
|
106
|
+
alt: m?.displayUsername || m?.name || m?.email,
|
|
107
|
+
src: m?.image ?? void 0
|
|
108
|
+
}), /* @__PURE__ */ y(u.Fallback, {
|
|
109
|
+
className: _(a === "lg" ? "text-xl" : a === "md" ? "text-base" : "text-sm"),
|
|
110
|
+
delayMs: m?.image ? 600 : void 0,
|
|
111
|
+
children: t || g || /* @__PURE__ */ y(l, { className: "size-4" })
|
|
112
|
+
})]
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/components/auth/user/user-view.tsx
|
|
117
|
+
function k({ className: e, isPending: t, size: n = "md", hideSubtitle: r = !1, user: a, ...s }) {
|
|
118
|
+
let { authClient: c } = i(), { data: l, isPending: u } = o(c, { enabled: !a && !t }), d = a ?? l?.user;
|
|
119
|
+
return (t || u) && !a ? /* @__PURE__ */ b("div", {
|
|
120
|
+
className: _("flex items-center gap-2 min-w-0", e),
|
|
121
|
+
...s,
|
|
122
|
+
children: [/* @__PURE__ */ y(O, {
|
|
123
|
+
isPending: !0,
|
|
124
|
+
className: n === "sm" ? "size-5 [&>span]:text-xs" : void 0,
|
|
125
|
+
size: n === "lg" ? "md" : "sm"
|
|
126
|
+
}), /* @__PURE__ */ b("div", {
|
|
127
|
+
className: "flex flex-col gap-1 min-w-0",
|
|
128
|
+
children: [/* @__PURE__ */ y(h, { className: "h-3.5 w-24 rounded-lg" }), r ? null : /* @__PURE__ */ y(h, { className: "h-3 w-32 rounded-lg" })]
|
|
129
|
+
})]
|
|
130
|
+
}) : /* @__PURE__ */ b("div", {
|
|
131
|
+
className: _("flex items-center gap-2 min-w-0", e),
|
|
132
|
+
...s,
|
|
133
|
+
children: [/* @__PURE__ */ y(O, {
|
|
134
|
+
className: n === "sm" ? "size-5 [&>span]:text-xs" : void 0,
|
|
135
|
+
user: d,
|
|
136
|
+
size: n === "lg" ? "md" : "sm"
|
|
137
|
+
}), /* @__PURE__ */ b("div", {
|
|
138
|
+
className: "flex flex-col min-w-0",
|
|
139
|
+
children: [/* @__PURE__ */ y("p", {
|
|
140
|
+
className: "text-foreground text-sm font-medium leading-tight truncate",
|
|
141
|
+
children: d?.displayUsername || d?.name || d?.email
|
|
142
|
+
}), !r && (d?.displayUsername || d?.name) ? /* @__PURE__ */ y("p", {
|
|
143
|
+
className: "text-muted text-xs leading-tight truncate overflow-x-hidden",
|
|
144
|
+
children: d?.email
|
|
145
|
+
}) : null]
|
|
146
|
+
})]
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { T as a, E as i, O as n, w as o, D as r, C as s, k as t };
|
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useLastLoginMethod } from "@better-auth-ui/react"
|
|
2
|
+
import { Chip, cn } from "@heroui/react"
|
|
3
|
+
|
|
4
|
+
export type LastUsedBadgeProps = {
|
|
5
|
+
/** Login method IDs that should display the indicator. */
|
|
6
|
+
method: string | string[]
|
|
7
|
+
/** Use the shorter label in constrained layouts. */
|
|
8
|
+
compact?: boolean
|
|
9
|
+
/** Float the compact indicator over the top-right edge of its container. */
|
|
10
|
+
floating?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Displays an indicator when one of the supplied method IDs matches Better
|
|
15
|
+
* Auth's stored last login method.
|
|
16
|
+
*/
|
|
17
|
+
export function LastUsedBadge({
|
|
18
|
+
method,
|
|
19
|
+
compact,
|
|
20
|
+
floating
|
|
21
|
+
}: LastUsedBadgeProps) {
|
|
22
|
+
const { method: lastLoginMethod, localization } = useLastLoginMethod()
|
|
23
|
+
const methods = Array.isArray(method) ? method : [method]
|
|
24
|
+
|
|
25
|
+
if (!lastLoginMethod || !methods.includes(lastLoginMethod)) return null
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Chip
|
|
29
|
+
className={cn(
|
|
30
|
+
floating &&
|
|
31
|
+
"pointer-events-none absolute top-0 right-0 z-10 translate-x-1/4 -translate-y-1/2 shadow-sm"
|
|
32
|
+
)}
|
|
33
|
+
color={floating ? "accent" : "default"}
|
|
34
|
+
size="sm"
|
|
35
|
+
variant={floating ? "primary" : "secondary"}
|
|
36
|
+
>
|
|
37
|
+
{compact || floating ? localization.lastUsedShort : localization.lastUsed}
|
|
38
|
+
</Chip>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -104,7 +104,7 @@ export function MagicLink({
|
|
|
104
104
|
{socialPosition === "top" && (
|
|
105
105
|
<>
|
|
106
106
|
{!!socialProviders?.length && (
|
|
107
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
107
|
+
<ProviderButtons socialLayout={socialLayout} view="magicLink" />
|
|
108
108
|
)}
|
|
109
109
|
|
|
110
110
|
{showSeparator && (
|
|
@@ -158,7 +158,7 @@ export function MagicLink({
|
|
|
158
158
|
)}
|
|
159
159
|
|
|
160
160
|
{!!socialProviders?.length && (
|
|
161
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
161
|
+
<ProviderButtons socialLayout={socialLayout} view="magicLink" />
|
|
162
162
|
)}
|
|
163
163
|
</>
|
|
164
164
|
)}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type AuthView,
|
|
3
|
+
authMutationKeys,
|
|
4
|
+
getProviderName
|
|
5
|
+
} from "@better-auth-ui/core"
|
|
2
6
|
import { providerIcons, useAuth, useSignInSocial } from "@better-auth-ui/react"
|
|
3
|
-
import { Button, type ButtonProps, Spinner } from "@heroui/react"
|
|
7
|
+
import { Button, type ButtonProps, cn, Spinner } from "@heroui/react"
|
|
4
8
|
import { useIsMutating } from "@tanstack/react-query"
|
|
5
9
|
import type { SocialProvider } from "better-auth/social-providers"
|
|
10
|
+
import { LastUsedBadge } from "./last-login-method/last-used-badge"
|
|
6
11
|
|
|
7
12
|
export type ProviderButtonProps = {
|
|
8
13
|
provider: SocialProvider
|
|
9
14
|
display?: "full" | "name" | "icon"
|
|
15
|
+
view?: AuthView
|
|
10
16
|
} & Omit<ButtonProps, "children" | "onPress" | "isPending" | "isDisabled">
|
|
11
17
|
|
|
12
18
|
/**
|
|
@@ -18,7 +24,9 @@ export type ProviderButtonProps = {
|
|
|
18
24
|
export function ProviderButton({
|
|
19
25
|
provider,
|
|
20
26
|
display = "full",
|
|
27
|
+
view = "signIn",
|
|
21
28
|
variant = "tertiary",
|
|
29
|
+
className,
|
|
22
30
|
...props
|
|
23
31
|
}: ProviderButtonProps) {
|
|
24
32
|
const { authClient, baseURL, localization, redirectTo } = useAuth()
|
|
@@ -43,8 +51,8 @@ export function ProviderButton({
|
|
|
43
51
|
variant={variant}
|
|
44
52
|
isPending={isPending}
|
|
45
53
|
onPress={() => signInSocial({ provider, callbackURL })}
|
|
54
|
+
className={cn("relative overflow-visible", className)}
|
|
46
55
|
{...props}
|
|
47
|
-
aria-label={getProviderName(provider)}
|
|
48
56
|
>
|
|
49
57
|
{signInSocialPending ? (
|
|
50
58
|
<Spinner color="current" size="sm" />
|
|
@@ -60,6 +68,12 @@ export function ProviderButton({
|
|
|
60
68
|
: display === "name"
|
|
61
69
|
? getProviderName(provider)
|
|
62
70
|
: null}
|
|
71
|
+
|
|
72
|
+
{display === "icon" && (
|
|
73
|
+
<span className="sr-only">{getProviderName(provider)}</span>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
{view !== "signUp" && <LastUsedBadge method={provider} floating />}
|
|
63
77
|
</Button>
|
|
64
78
|
)
|
|
65
79
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { AuthView } from "@better-auth-ui/core"
|
|
1
2
|
import { useAuth } from "@better-auth-ui/react"
|
|
2
3
|
import { cn } from "@heroui/react"
|
|
3
4
|
import { useMemo } from "react"
|
|
4
5
|
import { ProviderButton } from "./provider-button"
|
|
5
6
|
export type ProviderButtonsProps = {
|
|
6
7
|
socialLayout?: SocialLayout
|
|
8
|
+
view?: AuthView
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
export type SocialLayout = "auto" | "horizontal" | "vertical" | "grid"
|
|
@@ -16,7 +18,8 @@ export type SocialLayout = "auto" | "horizontal" | "vertical" | "grid"
|
|
|
16
18
|
* @returns The JSX element that renders the configured social provider buttons.
|
|
17
19
|
*/
|
|
18
20
|
export function ProviderButtons({
|
|
19
|
-
socialLayout = "auto"
|
|
21
|
+
socialLayout = "auto",
|
|
22
|
+
view = "signIn"
|
|
20
23
|
}: ProviderButtonsProps) {
|
|
21
24
|
const { socialProviders } = useAuth()
|
|
22
25
|
|
|
@@ -45,6 +48,7 @@ export function ProviderButtons({
|
|
|
45
48
|
<ProviderButton
|
|
46
49
|
key={provider}
|
|
47
50
|
provider={provider}
|
|
51
|
+
view={view}
|
|
48
52
|
display={
|
|
49
53
|
resolvedSocialLayout === "vertical"
|
|
50
54
|
? "full"
|
|
@@ -19,6 +19,7 @@ import { useIsMutating } from "@tanstack/react-query"
|
|
|
19
19
|
import { type SyntheticEvent, useState } from "react"
|
|
20
20
|
|
|
21
21
|
import { FieldSeparator } from "./field-separator"
|
|
22
|
+
import { LastUsedBadge } from "./last-login-method/last-used-badge"
|
|
22
23
|
import { ProviderButtons, type SocialLayout } from "./provider-buttons"
|
|
23
24
|
|
|
24
25
|
export interface SignInProps {
|
|
@@ -118,7 +119,7 @@ export function SignIn({
|
|
|
118
119
|
{socialPosition === "top" && (
|
|
119
120
|
<>
|
|
120
121
|
{!!socialProviders?.length && (
|
|
121
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
122
|
+
<ProviderButtons socialLayout={socialLayout} view="signIn" />
|
|
122
123
|
)}
|
|
123
124
|
|
|
124
125
|
{showSeparator && (
|
|
@@ -206,10 +207,16 @@ export function SignIn({
|
|
|
206
207
|
{Captcha && <div className="flex justify-center">{Captcha}</div>}
|
|
207
208
|
|
|
208
209
|
<div className="flex flex-col gap-3">
|
|
209
|
-
<Button
|
|
210
|
+
<Button
|
|
211
|
+
type="submit"
|
|
212
|
+
className="relative w-full overflow-visible"
|
|
213
|
+
isPending={isPending}
|
|
214
|
+
>
|
|
210
215
|
{signInEmailPending && <Spinner color="current" size="sm" />}
|
|
211
216
|
|
|
212
217
|
{localization.auth.signIn}
|
|
218
|
+
|
|
219
|
+
<LastUsedBadge method="email" floating />
|
|
213
220
|
</Button>
|
|
214
221
|
|
|
215
222
|
{plugins.flatMap((plugin) =>
|
|
@@ -231,7 +238,7 @@ export function SignIn({
|
|
|
231
238
|
)}
|
|
232
239
|
|
|
233
240
|
{!!socialProviders?.length && (
|
|
234
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
241
|
+
<ProviderButtons socialLayout={socialLayout} view="signIn" />
|
|
235
242
|
)}
|
|
236
243
|
</>
|
|
237
244
|
)}
|
|
@@ -168,7 +168,7 @@ export function SignUp({
|
|
|
168
168
|
{socialPosition === "top" && (
|
|
169
169
|
<>
|
|
170
170
|
{!!socialProviders?.length && (
|
|
171
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
171
|
+
<ProviderButtons socialLayout={socialLayout} view="signUp" />
|
|
172
172
|
)}
|
|
173
173
|
|
|
174
174
|
{showSeparator && (
|
|
@@ -396,7 +396,7 @@ export function SignUp({
|
|
|
396
396
|
)}
|
|
397
397
|
|
|
398
398
|
{!!socialProviders?.length && (
|
|
399
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
399
|
+
<ProviderButtons socialLayout={socialLayout} view="signUp" />
|
|
400
400
|
)}
|
|
401
401
|
</>
|
|
402
402
|
)}
|
|
@@ -26,6 +26,7 @@ import { useIsMutating } from "@tanstack/react-query"
|
|
|
26
26
|
import { type SyntheticEvent, useState } from "react"
|
|
27
27
|
import { usernamePlugin } from "../../../lib/auth/username-plugin"
|
|
28
28
|
import { FieldSeparator } from "../field-separator"
|
|
29
|
+
import { LastUsedBadge } from "../last-login-method/last-used-badge"
|
|
29
30
|
import { ProviderButtons, type SocialLayout } from "../provider-buttons"
|
|
30
31
|
|
|
31
32
|
export interface SignInUsernameProps {
|
|
@@ -163,7 +164,7 @@ export function SignInUsername({
|
|
|
163
164
|
{socialPosition === "top" && (
|
|
164
165
|
<>
|
|
165
166
|
{!!socialProviders?.length && (
|
|
166
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
167
|
+
<ProviderButtons socialLayout={socialLayout} view="signIn" />
|
|
167
168
|
)}
|
|
168
169
|
|
|
169
170
|
{showSeparator && (
|
|
@@ -251,12 +252,14 @@ export function SignInUsername({
|
|
|
251
252
|
<div className="flex flex-col gap-3">
|
|
252
253
|
<Button
|
|
253
254
|
type="submit"
|
|
254
|
-
className="w-full"
|
|
255
|
+
className="relative w-full overflow-visible"
|
|
255
256
|
isPending={isSignInPending || isPending}
|
|
256
257
|
>
|
|
257
258
|
{isSignInPending && <Spinner color="current" size="sm" />}
|
|
258
259
|
|
|
259
260
|
{localization.auth.signIn}
|
|
261
|
+
|
|
262
|
+
<LastUsedBadge method={["email", "username"]} floating />
|
|
260
263
|
</Button>
|
|
261
264
|
|
|
262
265
|
{plugins.flatMap((plugin) =>
|
|
@@ -278,7 +281,7 @@ export function SignInUsername({
|
|
|
278
281
|
)}
|
|
279
282
|
|
|
280
283
|
{!!socialProviders?.length && (
|
|
281
|
-
<ProviderButtons socialLayout={socialLayout} />
|
|
284
|
+
<ProviderButtons socialLayout={socialLayout} view="signIn" />
|
|
282
285
|
)}
|
|
283
286
|
</>
|
|
284
287
|
)}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createAuthPlugin } from "@better-auth-ui/core"
|
|
2
|
+
import {
|
|
3
|
+
lastLoginMethodPlugin as coreLastLoginMethodPlugin,
|
|
4
|
+
type LastLoginMethodPluginOptions
|
|
5
|
+
} from "@better-auth-ui/core/plugins"
|
|
6
|
+
|
|
7
|
+
export const lastLoginMethodPlugin = createAuthPlugin(
|
|
8
|
+
coreLastLoginMethodPlugin.id,
|
|
9
|
+
(options: LastLoginMethodPluginOptions = {}) => ({
|
|
10
|
+
...coreLastLoginMethodPlugin(options)
|
|
11
|
+
})
|
|
12
|
+
)
|
package/src/plugins.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./components/auth/api-key/api-keys"
|
|
|
4
4
|
export * from "./components/auth/api-key/organization-api-keys"
|
|
5
5
|
export * from "./components/auth/delete-user/danger-zone"
|
|
6
6
|
export * from "./components/auth/delete-user/delete-account"
|
|
7
|
+
export * from "./components/auth/last-login-method/last-used-badge"
|
|
7
8
|
// Plugin-contributed components that may be used standalone
|
|
8
9
|
export * from "./components/auth/magic-link/magic-link"
|
|
9
10
|
export * from "./components/auth/magic-link/magic-link-sent"
|
|
@@ -42,6 +43,7 @@ export * from "./components/auth/username/username-field"
|
|
|
42
43
|
export * from "./lib/auth/api-key-plugin"
|
|
43
44
|
export * from "./lib/auth/auth-plugin"
|
|
44
45
|
export * from "./lib/auth/delete-user-plugin"
|
|
46
|
+
export * from "./lib/auth/last-login-method-plugin"
|
|
45
47
|
export * from "./lib/auth/magic-link-plugin"
|
|
46
48
|
export * from "./lib/auth/multi-session-plugin"
|
|
47
49
|
export * from "./lib/auth/organization-plugin"
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { authMutationKeys as e, getEmailProviderLink as t, getProviderName as n } from "@better-auth-ui/core";
|
|
2
|
-
import { providerIcons as r, useAuth as i, useSession as a, useSignInSocial as o } from "@better-auth-ui/react";
|
|
3
|
-
import { ArrowUpRightFromSquare as s, Person as c } from "@gravity-ui/icons";
|
|
4
|
-
import { Avatar as l, Button as u, Link as d, Separator as f, Skeleton as p, Spinner as m, cn as h } from "@heroui/react";
|
|
5
|
-
import { useMemo as g } from "react";
|
|
6
|
-
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
7
|
-
import { buttonVariants as y } from "@heroui/styles";
|
|
8
|
-
import { useIsMutating as b } from "@tanstack/react-query";
|
|
9
|
-
//#region src/components/auth/open-email-button.tsx
|
|
10
|
-
function x({ email: e, className: n }) {
|
|
11
|
-
let { localization: r } = i(), a = t(e);
|
|
12
|
-
return a ? /* @__PURE__ */ v(d, {
|
|
13
|
-
href: a.loginUrl,
|
|
14
|
-
target: "_blank",
|
|
15
|
-
rel: "noopener noreferrer",
|
|
16
|
-
className: h(y({ variant: "primary" }), "w-full gap-2", n),
|
|
17
|
-
children: [r.auth.openEmailProvider.replace("{{provider}}", a.companyProvider), /* @__PURE__ */ _(s, { className: "size-3" })]
|
|
18
|
-
}) : null;
|
|
19
|
-
}
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/components/auth/field-separator.tsx
|
|
22
|
-
function S({ children: e }) {
|
|
23
|
-
return /* @__PURE__ */ v("div", {
|
|
24
|
-
className: "flex items-center gap-4",
|
|
25
|
-
children: [
|
|
26
|
-
/* @__PURE__ */ _(f, { className: "flex-1" }),
|
|
27
|
-
/* @__PURE__ */ _("p", {
|
|
28
|
-
className: "text-xs text-muted shrink-0",
|
|
29
|
-
children: e
|
|
30
|
-
}),
|
|
31
|
-
/* @__PURE__ */ _(f, { className: "flex-1" })
|
|
32
|
-
]
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
//#endregion
|
|
36
|
-
//#region src/components/auth/provider-button.tsx
|
|
37
|
-
function C({ provider: t, display: a = "full", variant: s = "tertiary", ...c }) {
|
|
38
|
-
let { authClient: l, baseURL: d, localization: f, redirectTo: p } = i(), h = `${d}${p}`, { mutate: g, isPending: y } = o(l), x = r[t];
|
|
39
|
-
return /* @__PURE__ */ v(u, {
|
|
40
|
-
variant: s,
|
|
41
|
-
isPending: b({ mutationKey: e.signIn.all }) + b({ mutationKey: e.signUp.all }) > 0,
|
|
42
|
-
onPress: () => g({
|
|
43
|
-
provider: t,
|
|
44
|
-
callbackURL: h
|
|
45
|
-
}),
|
|
46
|
-
...c,
|
|
47
|
-
"aria-label": n(t),
|
|
48
|
-
children: [y ? /* @__PURE__ */ _(m, {
|
|
49
|
-
color: "current",
|
|
50
|
-
size: "sm"
|
|
51
|
-
}) : /* @__PURE__ */ _(x, {}), a === "full" ? f.auth.continueWith.replace("{{provider}}", n(t)) : a === "name" ? n(t) : null]
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
//#endregion
|
|
55
|
-
//#region src/components/auth/provider-buttons.tsx
|
|
56
|
-
function w({ socialLayout: e = "auto" }) {
|
|
57
|
-
let { socialProviders: t } = i(), n = g(() => e === "auto" ? t?.length && t.length >= 4 ? "horizontal" : "vertical" : e, [e, t?.length]);
|
|
58
|
-
return /* @__PURE__ */ _("div", {
|
|
59
|
-
className: h("gap-3", n === "grid" && "grid grid-cols-2", n === "vertical" && "flex flex-col", n === "horizontal" && "flex flex-wrap"),
|
|
60
|
-
children: t?.map((e) => /* @__PURE__ */ _(C, {
|
|
61
|
-
provider: e,
|
|
62
|
-
display: n === "vertical" ? "full" : n === "grid" ? "name" : "icon",
|
|
63
|
-
className: h("w-full", n === "horizontal" && "flex-1")
|
|
64
|
-
}, e))
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
//#endregion
|
|
68
|
-
//#region src/components/auth/user/user-avatar.tsx
|
|
69
|
-
function T({ className: e, fallback: t, isPending: n, user: r, size: o = "sm", style: s, ...u }) {
|
|
70
|
-
let { authClient: d } = i(), { data: f, isPending: m } = a(d, { enabled: !r && !n });
|
|
71
|
-
if ((n || m) && !r) return /* @__PURE__ */ _(p, {
|
|
72
|
-
className: h("rounded-full", o === "sm" ? "size-8" : o === "md" ? "size-10" : "size-12", e),
|
|
73
|
-
style: s
|
|
74
|
-
});
|
|
75
|
-
let g = r ?? f?.user, y = (g?.username || g?.name || g?.email)?.slice(0, 2).toUpperCase();
|
|
76
|
-
return /* @__PURE__ */ v(l, {
|
|
77
|
-
size: o,
|
|
78
|
-
className: h("rounded-full", e),
|
|
79
|
-
style: s,
|
|
80
|
-
...u,
|
|
81
|
-
children: [/* @__PURE__ */ _(l.Image, {
|
|
82
|
-
alt: g?.displayUsername || g?.name || g?.email,
|
|
83
|
-
src: g?.image ?? void 0
|
|
84
|
-
}), /* @__PURE__ */ _(l.Fallback, {
|
|
85
|
-
className: h(o === "lg" ? "text-xl" : o === "md" ? "text-base" : "text-sm"),
|
|
86
|
-
delayMs: g?.image ? 600 : void 0,
|
|
87
|
-
children: t || y || /* @__PURE__ */ _(c, { className: "size-4" })
|
|
88
|
-
})]
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
//#endregion
|
|
92
|
-
//#region src/components/auth/user/user-view.tsx
|
|
93
|
-
function E({ className: e, isPending: t, size: n = "md", hideSubtitle: r = !1, user: o, ...s }) {
|
|
94
|
-
let { authClient: c } = i(), { data: l, isPending: u } = a(c, { enabled: !o && !t }), d = o ?? l?.user;
|
|
95
|
-
return (t || u) && !o ? /* @__PURE__ */ v("div", {
|
|
96
|
-
className: h("flex items-center gap-2 min-w-0", e),
|
|
97
|
-
...s,
|
|
98
|
-
children: [/* @__PURE__ */ _(T, {
|
|
99
|
-
isPending: !0,
|
|
100
|
-
className: n === "sm" ? "size-5 [&>span]:text-xs" : void 0,
|
|
101
|
-
size: n === "lg" ? "md" : "sm"
|
|
102
|
-
}), /* @__PURE__ */ v("div", {
|
|
103
|
-
className: "flex flex-col gap-1 min-w-0",
|
|
104
|
-
children: [/* @__PURE__ */ _(p, { className: "h-3.5 w-24 rounded-lg" }), r ? null : /* @__PURE__ */ _(p, { className: "h-3 w-32 rounded-lg" })]
|
|
105
|
-
})]
|
|
106
|
-
}) : /* @__PURE__ */ v("div", {
|
|
107
|
-
className: h("flex items-center gap-2 min-w-0", e),
|
|
108
|
-
...s,
|
|
109
|
-
children: [/* @__PURE__ */ _(T, {
|
|
110
|
-
className: n === "sm" ? "size-5 [&>span]:text-xs" : void 0,
|
|
111
|
-
user: d,
|
|
112
|
-
size: n === "lg" ? "md" : "sm"
|
|
113
|
-
}), /* @__PURE__ */ v("div", {
|
|
114
|
-
className: "flex flex-col min-w-0",
|
|
115
|
-
children: [/* @__PURE__ */ _("p", {
|
|
116
|
-
className: "text-foreground text-sm font-medium leading-tight truncate",
|
|
117
|
-
children: d?.displayUsername || d?.name || d?.email
|
|
118
|
-
}), !r && (d?.displayUsername || d?.name) ? /* @__PURE__ */ _("p", {
|
|
119
|
-
className: "text-muted text-xs leading-tight truncate overflow-x-hidden",
|
|
120
|
-
children: d?.email
|
|
121
|
-
}) : null]
|
|
122
|
-
})]
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
//#endregion
|
|
126
|
-
export { S as a, C as i, T as n, x as o, w as r, E as t };
|