@better-auth-ui/heroui 1.6.7 → 1.6.9
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/additional-field.d.ts +2 -2
- package/dist/components/auth/additional-field.js +133 -266
- package/dist/components/auth/api-key/api-key-skeleton.js +4 -20
- package/dist/components/auth/api-key/api-key.d.ts +6 -2
- package/dist/components/auth/api-key/api-key.js +16 -54
- package/dist/components/auth/api-key/api-keys-empty.d.ts +3 -1
- package/dist/components/auth/api-key/api-keys-empty.js +8 -34
- package/dist/components/auth/api-key/api-keys.d.ts +10 -2
- package/dist/components/auth/api-key/api-keys.js +21 -40
- package/dist/components/auth/api-key/create-api-key-dialog.d.ts +3 -1
- package/dist/components/auth/api-key/create-api-key-dialog.js +43 -73
- package/dist/components/auth/api-key/delete-api-key-dialog.d.ts +4 -2
- package/dist/components/auth/api-key/delete-api-key-dialog.js +16 -50
- package/dist/components/auth/api-key/new-api-key-dialog.js +23 -59
- package/dist/components/auth/api-key/organization-api-keys.d.ts +14 -0
- package/dist/components/auth/api-key/organization-api-keys.js +22 -0
- package/dist/components/auth/auth-provider.d.ts +1 -1
- package/dist/components/auth/auth-provider.js +10 -16
- package/dist/components/auth/auth.d.ts +3 -3
- package/dist/components/auth/auth.js +86 -61
- package/dist/components/auth/delete-user/danger-zone.d.ts +3 -3
- package/dist/components/auth/delete-user/danger-zone.js +11 -17
- package/dist/components/auth/delete-user/delete-account.d.ts +9 -0
- package/dist/components/auth/delete-user/delete-account.js +50 -0
- package/dist/components/auth/email/email-changed-email.d.ts +2 -2
- package/dist/components/auth/email/email-changed-email.js +22 -29
- package/dist/components/auth/email/email-verification-email.d.ts +2 -2
- package/dist/components/auth/email/email-verification-email.js +22 -29
- package/dist/components/auth/email/magic-link-email.d.ts +2 -2
- package/dist/components/auth/email/magic-link-email.js +22 -29
- package/dist/components/auth/email/new-device-email.d.ts +2 -2
- package/dist/components/auth/email/new-device-email.js +22 -29
- package/dist/components/auth/email/organization-invitation-email.d.ts +3 -0
- package/dist/components/auth/email/organization-invitation-email.js +23 -0
- package/dist/components/auth/email/otp-email.d.ts +2 -2
- package/dist/components/auth/email/otp-email.js +21 -28
- package/dist/components/auth/email/password-changed-email.d.ts +2 -2
- package/dist/components/auth/email/password-changed-email.js +22 -29
- package/dist/components/auth/email/reset-password-email.d.ts +2 -2
- package/dist/components/auth/email/reset-password-email.js +22 -29
- package/dist/components/auth/error-toaster.js +32 -16
- package/dist/components/auth/field-separator.d.ts +1 -1
- package/dist/components/auth/field-separator.js +4 -17
- package/dist/components/auth/forgot-password.d.ts +1 -1
- package/dist/components/auth/forgot-password.js +36 -88
- package/dist/components/auth/magic-link/magic-link-button.d.ts +1 -1
- package/dist/components/auth/magic-link/magic-link-button.js +33 -19
- package/dist/components/auth/magic-link/magic-link.d.ts +2 -2
- package/dist/components/auth/magic-link/magic-link.js +39 -86
- package/dist/components/auth/multi-session/manage-account.d.ts +1 -1
- package/dist/components/auth/multi-session/manage-account.js +32 -48
- package/dist/components/auth/multi-session/manage-accounts.d.ts +1 -1
- package/dist/components/auth/multi-session/manage-accounts.js +32 -33
- package/dist/components/auth/multi-session/switch-account-submenu-content.d.ts +3 -1
- package/dist/components/auth/multi-session/switch-account-submenu-content.js +24 -27
- package/dist/components/auth/multi-session/switch-account-submenu-item.d.ts +3 -2
- package/dist/components/auth/multi-session/switch-account-submenu-item.js +16 -19
- package/dist/components/auth/multi-session/switch-account-submenu.d.ts +2 -1
- package/dist/components/auth/multi-session/switch-account-submenu.js +23 -23
- package/dist/components/auth/organization/change-organization-logo.d.ts +4 -0
- package/dist/components/auth/organization/change-organization-logo.js +69 -0
- package/dist/components/auth/organization/create-organization-dialog.d.ts +13 -0
- package/dist/components/auth/organization/create-organization-dialog.js +37 -0
- package/dist/components/auth/organization/delete-organization-dialog.d.ts +7 -0
- package/dist/components/auth/organization/delete-organization-dialog.js +25 -0
- package/dist/components/auth/organization/delete-organization-skeleton.d.ts +4 -0
- package/dist/components/auth/organization/delete-organization-skeleton.js +8 -0
- package/dist/components/auth/organization/delete-organization.d.ts +5 -0
- package/dist/components/auth/organization/delete-organization.js +27 -0
- package/dist/components/auth/organization/invite-member-dialog.d.ts +13 -0
- package/dist/components/auth/organization/invite-member-dialog.js +47 -0
- package/dist/components/auth/organization/leave-organization-dialog.d.ts +7 -0
- package/dist/components/auth/organization/leave-organization-dialog.js +21 -0
- package/dist/components/auth/organization/leave-organization.d.ts +4 -0
- package/dist/components/auth/organization/leave-organization.js +16 -0
- package/dist/components/auth/organization/organization-danger-zone.d.ts +16 -0
- package/dist/components/auth/organization/organization-danger-zone.js +23 -0
- package/dist/components/auth/organization/organization-invitation-row-skeleton.d.ts +5 -0
- package/dist/components/auth/organization/organization-invitation-row-skeleton.js +9 -0
- package/dist/components/auth/organization/organization-invitation-row.d.ts +5 -0
- package/dist/components/auth/organization/organization-invitation-row.js +31 -0
- package/dist/components/auth/organization/organization-invitations-empty.d.ts +8 -0
- package/dist/components/auth/organization/organization-invitations-empty.js +13 -0
- package/dist/components/auth/organization/organization-invitations.d.ts +9 -0
- package/dist/components/auth/organization/organization-invitations.js +66 -0
- package/dist/components/auth/organization/organization-logo.d.ts +15 -0
- package/dist/components/auth/organization/organization-logo.js +14 -0
- package/dist/components/auth/organization/organization-member-row-skeleton.d.ts +4 -0
- package/dist/components/auth/organization/organization-member-row-skeleton.js +9 -0
- package/dist/components/auth/organization/organization-member-row.d.ts +9 -0
- package/dist/components/auth/organization/organization-member-row.js +34 -0
- package/dist/components/auth/organization/organization-members.d.ts +9 -0
- package/dist/components/auth/organization/organization-members.js +67 -0
- package/dist/components/auth/organization/organization-people.d.ts +10 -0
- package/dist/components/auth/organization/organization-people.js +11 -0
- package/dist/components/auth/organization/organization-profile.d.ts +9 -0
- package/dist/components/auth/organization/organization-profile.js +34 -0
- package/dist/components/auth/organization/organization-row.d.ts +8 -0
- package/dist/components/auth/organization/organization-row.js +31 -0
- package/dist/components/auth/organization/organization-settings.d.ts +12 -0
- package/dist/components/auth/organization/organization-settings.js +13 -0
- package/dist/components/auth/organization/organization-switcher.d.ts +20 -0
- package/dist/components/auth/organization/organization-switcher.js +48 -0
- package/dist/components/auth/organization/organization-view-skeleton.d.ts +6 -0
- package/dist/components/auth/organization/organization-view-skeleton.js +9 -0
- package/dist/components/auth/organization/organization-view.d.ts +15 -0
- package/dist/components/auth/organization/organization-view.js +31 -0
- package/dist/components/auth/organization/organization.d.ts +17 -0
- package/dist/components/auth/organization/organization.js +60 -0
- package/dist/components/auth/organization/organizations-empty.d.ts +4 -0
- package/dist/components/auth/organization/organizations-empty.js +9 -0
- package/dist/components/auth/organization/organizations-settings.d.ts +17 -0
- package/dist/components/auth/organization/organizations-settings.js +17 -0
- package/dist/components/auth/organization/organizations.d.ts +10 -0
- package/dist/components/auth/organization/organizations.js +21 -0
- package/dist/components/auth/organization/remove-member-dialog.d.ts +9 -0
- package/dist/components/auth/organization/remove-member-dialog.js +20 -0
- package/dist/components/auth/organization/slug-field.d.ts +19 -0
- package/dist/components/auth/organization/slug-field.js +38 -0
- package/dist/components/auth/organization/user-invitation-row-skeleton.d.ts +4 -0
- package/dist/components/auth/organization/user-invitation-row-skeleton.js +8 -0
- package/dist/components/auth/organization/user-invitation-row.d.ts +10 -0
- package/dist/components/auth/organization/user-invitation-row.js +22 -0
- package/dist/components/auth/organization/user-invitations-empty.d.ts +4 -0
- package/dist/components/auth/organization/user-invitations-empty.js +12 -0
- package/dist/components/auth/organization/user-invitations.d.ts +10 -0
- package/dist/components/auth/organization/user-invitations.js +18 -0
- package/dist/components/auth/passkey/add-passkey-dialog.d.ts +5 -0
- package/dist/components/auth/passkey/add-passkey-dialog.js +22 -0
- package/dist/components/auth/passkey/delete-passkey-dialog.d.ts +11 -0
- package/dist/components/auth/passkey/delete-passkey-dialog.js +14 -0
- package/dist/components/auth/passkey/passkey-button.d.ts +1 -1
- package/dist/components/auth/passkey/passkey-button.js +24 -23
- package/dist/components/auth/passkey/passkey-skeleton.d.ts +1 -0
- package/dist/components/auth/passkey/passkey-skeleton.js +5 -0
- package/dist/components/auth/passkey/passkey.d.ts +2 -5
- package/dist/components/auth/passkey/passkey.js +16 -44
- package/dist/components/auth/passkey/passkeys-empty.d.ts +4 -0
- package/dist/components/auth/passkey/passkeys-empty.js +9 -0
- package/dist/components/auth/passkey/passkeys.d.ts +1 -1
- package/dist/components/auth/passkey/passkeys.js +15 -105
- package/dist/components/auth/provider-button.d.ts +2 -2
- package/dist/components/auth/provider-button.js +28 -24
- package/dist/components/auth/provider-buttons.js +28 -18
- package/dist/components/auth/reset-password.d.ts +1 -1
- package/dist/components/auth/reset-password.js +56 -146
- package/dist/components/auth/settings/account/account-settings.d.ts +2 -2
- package/dist/components/auth/settings/account/account-settings.js +21 -19
- package/dist/components/auth/settings/account/change-avatar.js +53 -88
- package/dist/components/auth/settings/account/change-email.d.ts +1 -1
- package/dist/components/auth/settings/account/change-email.js +27 -53
- package/dist/components/auth/settings/account/user-profile.d.ts +1 -1
- package/dist/components/auth/settings/account/user-profile.js +64 -85
- package/dist/components/auth/settings/security/active-session.d.ts +1 -1
- package/dist/components/auth/settings/security/active-session.js +50 -57
- package/dist/components/auth/settings/security/active-sessions.d.ts +1 -1
- package/dist/components/auth/settings/security/active-sessions.js +21 -32
- package/dist/components/auth/settings/security/change-password.d.ts +1 -1
- package/dist/components/auth/settings/security/change-password.js +76 -186
- package/dist/components/auth/settings/security/linked-account.d.ts +1 -1
- package/dist/components/auth/settings/security/linked-account.js +37 -55
- package/dist/components/auth/settings/security/linked-accounts.d.ts +1 -1
- package/dist/components/auth/settings/security/linked-accounts.js +34 -43
- package/dist/components/auth/settings/security/security-settings.d.ts +2 -2
- package/dist/components/auth/settings/security/security-settings.js +19 -21
- package/dist/components/auth/settings/settings.d.ts +4 -4
- package/dist/components/auth/settings/settings.js +52 -44
- package/dist/components/auth/sign-in.d.ts +2 -2
- package/dist/components/auth/sign-in.js +63 -132
- package/dist/components/auth/sign-out.js +32 -26
- package/dist/components/auth/sign-up.d.ts +2 -2
- package/dist/components/auth/sign-up.js +97 -211
- package/dist/components/auth/theme/appearance.d.ts +1 -1
- package/dist/components/auth/theme/appearance.js +23 -71
- package/dist/components/auth/theme/theme-toggle-item.js +56 -61
- package/dist/components/auth/user/user-avatar.d.ts +4 -4
- package/dist/components/auth/user/user-avatar.js +31 -28
- package/dist/components/auth/user/user-button.d.ts +6 -7
- package/dist/components/auth/user/user-button.js +38 -72
- package/dist/components/auth/user/user-view.d.ts +13 -7
- package/dist/components/auth/user/user-view.js +26 -36
- package/dist/components/auth/username/sign-in-username.d.ts +2 -2
- package/dist/components/auth/username/sign-in-username.js +87 -147
- package/dist/components/auth/username/username-field.d.ts +1 -1
- package/dist/components/auth/username/username-field.js +44 -52
- package/dist/email.d.ts +8 -7
- package/dist/email.js +8 -8
- package/dist/index.d.ts +21 -21
- package/dist/index.js +21 -22
- package/dist/lib/auth/api-key-plugin.d.ts +7 -4
- package/dist/lib/auth/api-key-plugin.js +12 -10
- package/dist/lib/auth/auth-plugin.d.ts +10 -6
- package/dist/lib/auth/auth-plugin.js +1 -0
- package/dist/lib/auth/delete-user-plugin.d.ts +6 -6
- package/dist/lib/auth/delete-user-plugin.js +6 -9
- package/dist/lib/auth/magic-link-plugin.d.ts +5 -5
- package/dist/lib/auth/magic-link-plugin.js +17 -12
- package/dist/lib/auth/multi-session-plugin.d.ts +5 -5
- package/dist/lib/auth/multi-session-plugin.js +8 -11
- package/dist/lib/auth/organization-plugin.d.ts +37 -0
- package/dist/lib/auth/organization-plugin.js +19 -0
- package/dist/lib/auth/passkey-plugin.d.ts +9 -6
- package/dist/lib/auth/passkey-plugin.js +8 -11
- package/dist/lib/auth/theme-plugin.d.ts +6 -6
- package/dist/lib/auth/theme-plugin.js +25 -22
- package/dist/lib/auth/username-plugin.d.ts +11 -11
- package/dist/lib/auth/username-plugin.js +18 -17
- package/dist/plugins.d.ts +46 -22
- package/dist/plugins.js +47 -22
- package/package.json +25 -19
- package/src/components/auth/api-key/api-key.tsx +25 -16
- package/src/components/auth/api-key/api-keys-empty.tsx +8 -4
- package/src/components/auth/api-key/api-keys.tsx +52 -14
- package/src/components/auth/api-key/create-api-key-dialog.tsx +17 -4
- package/src/components/auth/api-key/delete-api-key-dialog.tsx +10 -2
- package/src/components/auth/api-key/organization-api-keys.tsx +55 -0
- package/src/components/auth/delete-user/danger-zone.tsx +3 -3
- package/src/components/auth/delete-user/{delete-user.tsx → delete-account.tsx} +9 -9
- package/src/components/auth/email/organization-invitation-email.tsx +38 -0
- package/src/components/auth/error-toaster.tsx +28 -10
- package/src/components/auth/forgot-password.tsx +10 -2
- package/src/components/auth/multi-session/manage-accounts.tsx +1 -1
- package/src/components/auth/multi-session/switch-account-submenu-content.tsx +7 -2
- package/src/components/auth/multi-session/switch-account-submenu-item.tsx +4 -2
- package/src/components/auth/multi-session/switch-account-submenu.tsx +8 -4
- package/src/components/auth/organization/change-organization-logo.tsx +171 -0
- package/src/components/auth/organization/create-organization-dialog.tsx +133 -0
- package/src/components/auth/organization/delete-organization-dialog.tsx +97 -0
- package/src/components/auth/organization/delete-organization-skeleton.tsx +17 -0
- package/src/components/auth/organization/delete-organization.tsx +77 -0
- package/src/components/auth/organization/invite-member-dialog.tsx +177 -0
- package/src/components/auth/organization/leave-organization-dialog.tsx +95 -0
- package/src/components/auth/organization/leave-organization.tsx +59 -0
- package/src/components/auth/organization/organization-danger-zone.tsx +68 -0
- package/src/components/auth/organization/organization-invitation-row-skeleton.tsx +29 -0
- package/src/components/auth/organization/organization-invitation-row.tsx +97 -0
- package/src/components/auth/organization/organization-invitations-empty.tsx +39 -0
- package/src/components/auth/organization/organization-invitations.tsx +351 -0
- package/src/components/auth/organization/organization-logo.tsx +64 -0
- package/src/components/auth/organization/organization-member-row-skeleton.tsx +24 -0
- package/src/components/auth/organization/organization-member-row.tsx +157 -0
- package/src/components/auth/organization/organization-members.tsx +284 -0
- package/src/components/auth/organization/organization-people.tsx +26 -0
- package/src/components/auth/organization/organization-profile.tsx +141 -0
- package/src/components/auth/organization/organization-row.tsx +66 -0
- package/src/components/auth/organization/organization-settings.tsx +38 -0
- package/src/components/auth/organization/organization-switcher.tsx +233 -0
- package/src/components/auth/organization/organization-view-skeleton.tsx +36 -0
- package/src/components/auth/organization/organization-view.tsx +109 -0
- package/src/components/auth/organization/organization.tsx +162 -0
- package/src/components/auth/organization/organizations-empty.tsx +35 -0
- package/src/components/auth/organization/organizations-settings.tsx +36 -0
- package/src/components/auth/organization/organizations.tsx +80 -0
- package/src/components/auth/organization/remove-member-dialog.tsx +95 -0
- package/src/components/auth/organization/slug-field.tsx +111 -0
- package/src/components/auth/organization/user-invitation-row-skeleton.tsx +17 -0
- package/src/components/auth/organization/user-invitation-row.tsx +90 -0
- package/src/components/auth/organization/user-invitations-empty.tsx +30 -0
- package/src/components/auth/organization/user-invitations.tsx +59 -0
- package/src/components/auth/passkey/add-passkey-dialog.tsx +109 -0
- package/src/components/auth/passkey/delete-passkey-dialog.tsx +95 -0
- package/src/components/auth/passkey/passkey-skeleton.tsx +16 -0
- package/src/components/auth/passkey/passkey.tsx +23 -22
- package/src/components/auth/passkey/passkeys-empty.tsx +35 -0
- package/src/components/auth/passkey/passkeys.tsx +33 -154
- package/src/components/auth/settings/account/change-email.tsx +1 -1
- package/src/components/auth/settings/account/user-profile.tsx +5 -5
- package/src/components/auth/settings/security/active-sessions.tsx +6 -4
- package/src/components/auth/settings/security/linked-accounts.tsx +2 -2
- package/src/components/auth/settings/settings.tsx +77 -14
- package/src/components/auth/user/user-avatar.tsx +4 -1
- package/src/components/auth/user/user-button.tsx +14 -11
- package/src/components/auth/user/user-view.tsx +32 -12
- package/src/email.ts +1 -0
- package/src/lib/auth/api-key-plugin.ts +10 -4
- package/src/lib/auth/auth-plugin.ts +6 -1
- package/src/lib/auth/organization-plugin.tsx +32 -0
- package/src/plugins.ts +25 -1
- package/dist/components/auth/delete-user/delete-user.d.ts +0 -9
- package/dist/components/auth/delete-user/delete-user.js +0 -95
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Skeleton } from "@heroui/react"
|
|
2
|
+
|
|
3
|
+
export function PasskeySkeleton() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="flex items-center justify-between">
|
|
6
|
+
<div className="flex items-center gap-3">
|
|
7
|
+
<Skeleton className="size-10 rounded-xl" />
|
|
8
|
+
|
|
9
|
+
<div className="flex flex-col gap-1">
|
|
10
|
+
<Skeleton className="h-4 w-28 rounded-lg" />
|
|
11
|
+
<Skeleton className="h-3 w-32 rounded-lg" />
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -1,29 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type PasskeyAuthClient,
|
|
3
|
-
useAuth,
|
|
4
|
-
useAuthPlugin,
|
|
5
|
-
useDeletePasskey
|
|
6
|
-
} from "@better-auth-ui/react"
|
|
1
|
+
import { useAuth, useAuthPlugin } from "@better-auth-ui/react"
|
|
7
2
|
import { Fingerprint, Xmark } from "@gravity-ui/icons"
|
|
8
|
-
import { Button
|
|
3
|
+
import { Button } from "@heroui/react"
|
|
4
|
+
import { useState } from "react"
|
|
9
5
|
|
|
10
6
|
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
7
|
+
import {
|
|
8
|
+
DeletePasskeyDialog,
|
|
9
|
+
type ListedPasskey
|
|
10
|
+
} from "./delete-passkey-dialog"
|
|
11
11
|
|
|
12
12
|
export type PasskeyProps = {
|
|
13
|
-
passkey:
|
|
14
|
-
id: string
|
|
15
|
-
name?: string | null
|
|
16
|
-
createdAt: Date
|
|
17
|
-
}
|
|
13
|
+
passkey: ListedPasskey
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
export function Passkey({ passkey }: PasskeyProps) {
|
|
21
|
-
const {
|
|
17
|
+
const { localization } = useAuth()
|
|
22
18
|
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
23
|
-
|
|
24
|
-
const { mutate: deletePasskey, isPending } = useDeletePasskey(
|
|
25
|
-
authClient as PasskeyAuthClient
|
|
26
|
-
)
|
|
19
|
+
const [deleteOpen, setDeleteOpen] = useState(false)
|
|
27
20
|
|
|
28
21
|
const passkeyName = passkey.name || passkeyLocalization.passkey
|
|
29
22
|
|
|
@@ -33,8 +26,10 @@ export function Passkey({ passkey }: PasskeyProps) {
|
|
|
33
26
|
<Fingerprint className="size-4.5" />
|
|
34
27
|
</div>
|
|
35
28
|
|
|
36
|
-
<div className="flex
|
|
37
|
-
<span className="text-sm font-medium leading-tight">
|
|
29
|
+
<div className="flex min-w-0 flex-col">
|
|
30
|
+
<span className="truncate text-sm font-medium leading-tight">
|
|
31
|
+
{passkeyName}
|
|
32
|
+
</span>
|
|
38
33
|
|
|
39
34
|
<span className="text-xs text-muted">
|
|
40
35
|
{new Date(passkey.createdAt).toLocaleString(undefined, {
|
|
@@ -48,16 +43,22 @@ export function Passkey({ passkey }: PasskeyProps) {
|
|
|
48
43
|
className="ml-auto shrink-0"
|
|
49
44
|
variant="outline"
|
|
50
45
|
size="sm"
|
|
51
|
-
|
|
52
|
-
onPress={() => deletePasskey({ id: passkey.id })}
|
|
46
|
+
onPress={() => setDeleteOpen(true)}
|
|
53
47
|
aria-label={passkeyLocalization.deletePasskey.replace(
|
|
54
48
|
"{{name}}",
|
|
55
49
|
passkeyName
|
|
56
50
|
)}
|
|
57
51
|
>
|
|
58
|
-
|
|
52
|
+
<Xmark />
|
|
53
|
+
|
|
59
54
|
{localization.settings.delete}
|
|
60
55
|
</Button>
|
|
56
|
+
|
|
57
|
+
<DeletePasskeyDialog
|
|
58
|
+
isOpen={deleteOpen}
|
|
59
|
+
onOpenChange={setDeleteOpen}
|
|
60
|
+
passkey={passkey}
|
|
61
|
+
/>
|
|
61
62
|
</div>
|
|
62
63
|
)
|
|
63
64
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useAuthPlugin } from "@better-auth-ui/react"
|
|
2
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
3
|
+
import { Button } from "@heroui/react"
|
|
4
|
+
|
|
5
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
6
|
+
|
|
7
|
+
export type PasskeysEmptyProps = {
|
|
8
|
+
onAddPress: () => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function PasskeysEmpty({ onAddPress }: PasskeysEmptyProps) {
|
|
12
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="flex flex-col items-center justify-center gap-4">
|
|
16
|
+
<div className="flex size-10 items-center justify-center rounded-xl bg-surface-secondary">
|
|
17
|
+
<Fingerprint className="size-4.5" />
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div className="flex flex-col items-center justify-center gap-1 text-center">
|
|
21
|
+
<p className="text-sm font-semibold">
|
|
22
|
+
{passkeyLocalization.noPasskeys}
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
<p className="text-muted text-xs">
|
|
26
|
+
{passkeyLocalization.passkeysDescription}
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<Button size="sm" onPress={onAddPress}>
|
|
31
|
+
{passkeyLocalization.addPasskey}
|
|
32
|
+
</Button>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type PasskeyAuthClient,
|
|
3
|
-
useAddPasskey,
|
|
4
3
|
useAuth,
|
|
5
4
|
useAuthPlugin,
|
|
6
5
|
useListPasskeys
|
|
7
6
|
} from "@better-auth-ui/react"
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
AlertDialog,
|
|
11
|
-
Button,
|
|
12
|
-
Card,
|
|
13
|
-
type CardProps,
|
|
14
|
-
cn,
|
|
15
|
-
FieldError,
|
|
16
|
-
Form,
|
|
17
|
-
Input,
|
|
18
|
-
Label,
|
|
19
|
-
Skeleton,
|
|
20
|
-
Spinner,
|
|
21
|
-
TextField
|
|
22
|
-
} from "@heroui/react"
|
|
23
|
-
import { type SyntheticEvent, useState } from "react"
|
|
7
|
+
import { Button, Card, type CardProps, cn } from "@heroui/react"
|
|
8
|
+
import { useState } from "react"
|
|
24
9
|
|
|
25
10
|
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
11
|
+
import { AddPasskeyDialog } from "./add-passkey-dialog"
|
|
26
12
|
import { Passkey } from "./passkey"
|
|
13
|
+
import { PasskeySkeleton } from "./passkey-skeleton"
|
|
14
|
+
import { PasskeysEmpty } from "./passkeys-empty"
|
|
27
15
|
|
|
28
16
|
export type PasskeysProps = {
|
|
29
17
|
className?: string
|
|
@@ -35,162 +23,53 @@ export function Passkeys({
|
|
|
35
23
|
variant,
|
|
36
24
|
...props
|
|
37
25
|
}: PasskeysProps & Omit<CardProps, "children">) {
|
|
38
|
-
const { authClient
|
|
26
|
+
const { authClient } = useAuth()
|
|
39
27
|
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
40
28
|
|
|
41
29
|
const { data: passkeys, isPending } = useListPasskeys(
|
|
42
30
|
authClient as PasskeyAuthClient
|
|
43
31
|
)
|
|
44
|
-
const { mutate: addPasskey, isPending: isAdding } = useAddPasskey(
|
|
45
|
-
authClient as PasskeyAuthClient
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
const [nameOpen, setNameOpen] = useState(false)
|
|
49
|
-
const [name, setName] = useState("")
|
|
50
32
|
|
|
51
|
-
const
|
|
52
|
-
setNameOpen(open)
|
|
53
|
-
setName("")
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const handleSubmit = async (e: SyntheticEvent<HTMLFormElement>) => {
|
|
57
|
-
e.preventDefault()
|
|
58
|
-
|
|
59
|
-
addPasskey(
|
|
60
|
-
{ name: name.trim() || undefined },
|
|
61
|
-
{
|
|
62
|
-
onSuccess: () => {
|
|
63
|
-
setNameOpen(false)
|
|
64
|
-
setName("")
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
}
|
|
33
|
+
const [addOpen, setAddOpen] = useState(false)
|
|
69
34
|
|
|
70
35
|
return (
|
|
71
|
-
<div>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
</p>
|
|
87
|
-
</div>
|
|
88
|
-
|
|
89
|
-
<AlertDialog>
|
|
90
|
-
<Button
|
|
91
|
-
className="shrink-0"
|
|
92
|
-
size="sm"
|
|
93
|
-
isDisabled={isPending}
|
|
94
|
-
onPress={() => setNameOpen(true)}
|
|
95
|
-
>
|
|
96
|
-
{passkeyLocalization.addPasskey}
|
|
97
|
-
</Button>
|
|
98
|
-
|
|
99
|
-
<AlertDialog.Backdrop
|
|
100
|
-
isOpen={nameOpen}
|
|
101
|
-
onOpenChange={handleDialogOpenChange}
|
|
102
|
-
>
|
|
103
|
-
<AlertDialog.Container>
|
|
104
|
-
<AlertDialog.Dialog>
|
|
105
|
-
<Form onSubmit={handleSubmit}>
|
|
106
|
-
<AlertDialog.CloseTrigger />
|
|
107
|
-
|
|
108
|
-
<AlertDialog.Header>
|
|
109
|
-
<AlertDialog.Icon status="default">
|
|
110
|
-
<Fingerprint />
|
|
111
|
-
</AlertDialog.Icon>
|
|
112
|
-
|
|
113
|
-
<AlertDialog.Heading>
|
|
114
|
-
{passkeyLocalization.addPasskey}
|
|
115
|
-
</AlertDialog.Heading>
|
|
116
|
-
</AlertDialog.Header>
|
|
117
|
-
|
|
118
|
-
<AlertDialog.Body className="overflow-visible">
|
|
119
|
-
<p className="text-muted text-sm">
|
|
120
|
-
{passkeyLocalization.passkeysInstructions}
|
|
121
|
-
</p>
|
|
122
|
-
|
|
123
|
-
<TextField
|
|
124
|
-
className="mt-4"
|
|
125
|
-
name="passkey-name"
|
|
126
|
-
isDisabled={isAdding}
|
|
127
|
-
value={name}
|
|
128
|
-
onChange={setName}
|
|
129
|
-
>
|
|
130
|
-
<Label>{passkeyLocalization.passkey}</Label>
|
|
131
|
-
|
|
132
|
-
<Input
|
|
133
|
-
autoFocus
|
|
134
|
-
placeholder={localization.settings.optional}
|
|
135
|
-
variant="secondary"
|
|
136
|
-
/>
|
|
137
|
-
|
|
138
|
-
<FieldError />
|
|
139
|
-
</TextField>
|
|
140
|
-
</AlertDialog.Body>
|
|
141
|
-
|
|
142
|
-
<AlertDialog.Footer>
|
|
143
|
-
<Button
|
|
144
|
-
slot="close"
|
|
145
|
-
variant="tertiary"
|
|
146
|
-
isDisabled={isAdding}
|
|
147
|
-
>
|
|
148
|
-
{localization.settings.cancel}
|
|
149
|
-
</Button>
|
|
150
|
-
|
|
151
|
-
<Button type="submit" isPending={isAdding}>
|
|
152
|
-
{isAdding && <Spinner color="current" size="sm" />}
|
|
153
|
-
|
|
154
|
-
{passkeyLocalization.addPasskey}
|
|
155
|
-
</Button>
|
|
156
|
-
</AlertDialog.Footer>
|
|
157
|
-
</Form>
|
|
158
|
-
</AlertDialog.Dialog>
|
|
159
|
-
</AlertDialog.Container>
|
|
160
|
-
</AlertDialog.Backdrop>
|
|
161
|
-
</AlertDialog>
|
|
162
|
-
</div>
|
|
36
|
+
<div className={cn("flex flex-col gap-3", className)}>
|
|
37
|
+
<div className="flex items-end justify-between gap-3">
|
|
38
|
+
<h2 className="text-sm font-semibold truncate">
|
|
39
|
+
{passkeyLocalization.passkeys}
|
|
40
|
+
</h2>
|
|
41
|
+
|
|
42
|
+
<Button
|
|
43
|
+
className="shrink-0"
|
|
44
|
+
size="sm"
|
|
45
|
+
isDisabled={isPending}
|
|
46
|
+
onPress={() => setAddOpen(true)}
|
|
47
|
+
>
|
|
48
|
+
{passkeyLocalization.addPasskey}
|
|
49
|
+
</Button>
|
|
50
|
+
</div>
|
|
163
51
|
|
|
52
|
+
<Card variant={variant} {...props}>
|
|
53
|
+
<Card.Content>
|
|
164
54
|
{isPending ? (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
</>
|
|
55
|
+
<PasskeySkeleton />
|
|
56
|
+
) : !passkeys?.length ? (
|
|
57
|
+
<PasskeysEmpty onAddPress={() => setAddOpen(true)} />
|
|
169
58
|
) : (
|
|
170
|
-
passkeys
|
|
59
|
+
passkeys.map((passkey, index) => (
|
|
171
60
|
<div key={passkey.id}>
|
|
172
|
-
|
|
61
|
+
{index > 0 && (
|
|
62
|
+
<div className="border-b border-dashed -mx-4 my-4" />
|
|
63
|
+
)}
|
|
64
|
+
|
|
173
65
|
<Passkey passkey={passkey} />
|
|
174
66
|
</div>
|
|
175
67
|
))
|
|
176
68
|
)}
|
|
177
69
|
</Card.Content>
|
|
178
70
|
</Card>
|
|
179
|
-
</div>
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
71
|
|
|
183
|
-
|
|
184
|
-
return (
|
|
185
|
-
<div className="flex items-center justify-between">
|
|
186
|
-
<div className="flex items-center gap-3">
|
|
187
|
-
<Skeleton className="size-10 rounded-xl" />
|
|
188
|
-
|
|
189
|
-
<div className="flex flex-col gap-1">
|
|
190
|
-
<Skeleton className="h-4 w-24 rounded-lg" />
|
|
191
|
-
<Skeleton className="h-3 w-20 rounded-lg" />
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
72
|
+
<AddPasskeyDialog isOpen={addOpen} onOpenChange={setAddOpen} />
|
|
194
73
|
</div>
|
|
195
74
|
)
|
|
196
75
|
}
|
|
@@ -64,7 +64,7 @@ export function ChangeEmail({
|
|
|
64
64
|
<Fieldset className="w-full gap-4">
|
|
65
65
|
<Fieldset.Group>
|
|
66
66
|
<TextField
|
|
67
|
-
key={session?.user.email}
|
|
67
|
+
key={`${session?.user.id}-${session?.user.email}-email`}
|
|
68
68
|
name="email"
|
|
69
69
|
type="email"
|
|
70
70
|
defaultValue={session?.user.email}
|
|
@@ -81,7 +81,7 @@ export function UserProfile({
|
|
|
81
81
|
return (
|
|
82
82
|
<div>
|
|
83
83
|
<h2 className={cn("text-sm font-semibold mb-3")}>
|
|
84
|
-
{localization.settings.
|
|
84
|
+
{localization.settings.userProfile}
|
|
85
85
|
</h2>
|
|
86
86
|
|
|
87
87
|
<Card className={cn("p-4 gap-4", className)} variant={variant} {...props}>
|
|
@@ -90,7 +90,7 @@ export function UserProfile({
|
|
|
90
90
|
<ChangeAvatar />
|
|
91
91
|
|
|
92
92
|
<TextField
|
|
93
|
-
key={session?.user?.name}
|
|
93
|
+
key={`${session?.user?.id}-${session?.user?.name}-name`}
|
|
94
94
|
name="name"
|
|
95
95
|
defaultValue={session?.user.name}
|
|
96
96
|
isDisabled={isPending || !session}
|
|
@@ -131,9 +131,9 @@ export function UserProfile({
|
|
|
131
131
|
field.name
|
|
132
132
|
]
|
|
133
133
|
|
|
134
|
-
// Re-mount when the session value
|
|
135
|
-
// uncontrolled `defaultValue` reflects the latest data.
|
|
136
|
-
const key = `${field.name}
|
|
134
|
+
// Re-mount when the user or the session value changes so the
|
|
135
|
+
// field's uncontrolled `defaultValue` reflects the latest data.
|
|
136
|
+
const key = `${session.user.id}-${field.name}-${
|
|
137
137
|
value instanceof Date
|
|
138
138
|
? value.toISOString()
|
|
139
139
|
: String(value ?? "")
|
|
@@ -25,9 +25,11 @@ export function ActiveSessions({
|
|
|
25
25
|
|
|
26
26
|
const { data: sessions, isPending } = useListSessions(authClient)
|
|
27
27
|
|
|
28
|
-
const activeSessions =
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const activeSessions =
|
|
29
|
+
sessions &&
|
|
30
|
+
[...sessions].sort((a, b) =>
|
|
31
|
+
a.id === session?.session.id ? -1 : b.id === session?.session.id ? 1 : 0
|
|
32
|
+
)
|
|
31
33
|
|
|
32
34
|
return (
|
|
33
35
|
<div>
|
|
@@ -43,7 +45,7 @@ export function ActiveSessions({
|
|
|
43
45
|
activeSessions?.map((activeSession, index) => (
|
|
44
46
|
<div key={activeSession.id}>
|
|
45
47
|
{index > 0 && (
|
|
46
|
-
<div className="border-b border-dashed -mx-4
|
|
48
|
+
<div className="border-b border-dashed -mx-4 my-4" />
|
|
47
49
|
)}
|
|
48
50
|
|
|
49
51
|
<ActiveSession activeSession={activeSession} />
|
|
@@ -54,7 +54,7 @@ export function LinkedAccounts({
|
|
|
54
54
|
? socialProviders?.map((provider, index) => (
|
|
55
55
|
<div key={provider}>
|
|
56
56
|
{index > 0 && (
|
|
57
|
-
<div className="border-b border-dashed -mx-4
|
|
57
|
+
<div className="border-b border-dashed -mx-4 my-4" />
|
|
58
58
|
)}
|
|
59
59
|
<AccountRowSkeleton />
|
|
60
60
|
</div>
|
|
@@ -62,7 +62,7 @@ export function LinkedAccounts({
|
|
|
62
62
|
: allRows.map((row, index) => (
|
|
63
63
|
<div key={row.key}>
|
|
64
64
|
{index > 0 && (
|
|
65
|
-
<div className="border-b border-dashed -mx-4
|
|
65
|
+
<div className="border-b border-dashed -mx-4 my-4" />
|
|
66
66
|
)}
|
|
67
67
|
|
|
68
68
|
<LinkedAccount
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { SettingsView } from "@better-auth-ui/core"
|
|
2
2
|
import { useAuth, useAuthenticate } from "@better-auth-ui/react"
|
|
3
|
+
import { Person, Shield } from "@gravity-ui/icons"
|
|
3
4
|
import { type CardProps, cn, Tabs } from "@heroui/react"
|
|
4
5
|
import { type ComponentProps, useMemo } from "react"
|
|
5
|
-
|
|
6
6
|
import { AccountSettings } from "./account/account-settings"
|
|
7
7
|
import { SecuritySettings } from "./security/security-settings"
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ export type SettingsProps = {
|
|
|
25
25
|
* @param variant - Card variant forwarded to each settings section card
|
|
26
26
|
* @returns A DOM tree containing the responsive settings tabs and the currently selected settings panel
|
|
27
27
|
*
|
|
28
|
-
* @throws Error if neither `view` nor `path` is provided
|
|
28
|
+
* @throws Error if neither `view` nor `path` is provided, or if `path` does not match any settings view
|
|
29
29
|
*/
|
|
30
30
|
export function Settings({
|
|
31
31
|
className,
|
|
@@ -35,22 +35,39 @@ export function Settings({
|
|
|
35
35
|
view,
|
|
36
36
|
...props
|
|
37
37
|
}: SettingsProps & ComponentProps<"div">) {
|
|
38
|
-
const { authClient, basePaths, localization, viewPaths } = useAuth()
|
|
39
|
-
useAuthenticate(authClient)
|
|
40
|
-
|
|
41
38
|
if (!view && !path) {
|
|
42
39
|
throw new Error("[Better Auth UI] Either `view` or `path` must be provided")
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
const { authClient, basePaths, localization, viewPaths, plugins } = useAuth()
|
|
43
|
+
useAuthenticate(authClient)
|
|
44
|
+
|
|
45
|
+
// Built-ins first, then plugin segments
|
|
46
|
+
const currentView = useMemo(() => {
|
|
47
|
+
if (view) return view
|
|
48
|
+
if (!path) return undefined
|
|
52
49
|
|
|
53
|
-
|
|
50
|
+
const match = [
|
|
51
|
+
viewPaths.settings,
|
|
52
|
+
...plugins.map((plugin) => plugin.viewPaths?.settings)
|
|
53
|
+
]
|
|
54
|
+
.flatMap((source) => Object.entries(source ?? {}))
|
|
55
|
+
.find(([, segment]) => segment === path)
|
|
56
|
+
|
|
57
|
+
return match?.[0] as SettingsView | undefined
|
|
58
|
+
}, [view, path, viewPaths.settings, plugins])
|
|
59
|
+
|
|
60
|
+
if (!currentView) {
|
|
61
|
+
const validPaths = [
|
|
62
|
+
viewPaths.settings,
|
|
63
|
+
...plugins.map((plugin) => plugin.viewPaths?.settings)
|
|
64
|
+
]
|
|
65
|
+
.flatMap((source) => Object.values(source ?? {}))
|
|
66
|
+
.join(", ")
|
|
67
|
+
throw new Error(
|
|
68
|
+
`[Better Auth UI] Unknown settings path "${path}". Valid paths are: ${validPaths}`
|
|
69
|
+
)
|
|
70
|
+
}
|
|
54
71
|
|
|
55
72
|
return (
|
|
56
73
|
<Tabs
|
|
@@ -62,12 +79,18 @@ export function Settings({
|
|
|
62
79
|
<Tabs.ListContainer>
|
|
63
80
|
<Tabs.List
|
|
64
81
|
aria-label={localization.settings.settings}
|
|
65
|
-
className="overflow-auto
|
|
82
|
+
className="max-w-fit overflow-x-auto [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
|
|
66
83
|
>
|
|
67
84
|
<Tabs.Tab
|
|
68
85
|
id="account"
|
|
69
86
|
href={`${basePaths.settings}/${viewPaths.settings.account}`}
|
|
87
|
+
className="gap-2"
|
|
88
|
+
onPress={(e) =>
|
|
89
|
+
e.target.scrollIntoView({ behavior: "smooth", inline: "center" })
|
|
90
|
+
}
|
|
70
91
|
>
|
|
92
|
+
<Person className="text-muted" />
|
|
93
|
+
|
|
71
94
|
{localization.settings.account}
|
|
72
95
|
|
|
73
96
|
<Tabs.Indicator />
|
|
@@ -76,11 +99,39 @@ export function Settings({
|
|
|
76
99
|
<Tabs.Tab
|
|
77
100
|
id="security"
|
|
78
101
|
href={`${basePaths.settings}/${viewPaths.settings.security}`}
|
|
102
|
+
className="gap-2"
|
|
103
|
+
onPress={(e) =>
|
|
104
|
+
e.target.scrollIntoView({ behavior: "smooth", inline: "center" })
|
|
105
|
+
}
|
|
79
106
|
>
|
|
107
|
+
<Shield className="text-muted" />
|
|
108
|
+
|
|
80
109
|
{localization.settings.security}
|
|
81
110
|
|
|
82
111
|
<Tabs.Indicator />
|
|
83
112
|
</Tabs.Tab>
|
|
113
|
+
|
|
114
|
+
{plugins.flatMap(
|
|
115
|
+
(plugin) =>
|
|
116
|
+
plugin.settingsTabs?.map((settingsTab, index) => (
|
|
117
|
+
<Tabs.Tab
|
|
118
|
+
key={`${plugin.id}-${index.toString()}`}
|
|
119
|
+
id={settingsTab.view}
|
|
120
|
+
href={`${basePaths.settings}/${plugin.viewPaths?.settings?.[settingsTab.view]}`}
|
|
121
|
+
className="gap-2"
|
|
122
|
+
onPress={(e) =>
|
|
123
|
+
e.target.scrollIntoView({
|
|
124
|
+
behavior: "smooth",
|
|
125
|
+
inline: "center"
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
>
|
|
129
|
+
{settingsTab.label}
|
|
130
|
+
|
|
131
|
+
<Tabs.Indicator />
|
|
132
|
+
</Tabs.Tab>
|
|
133
|
+
)) ?? []
|
|
134
|
+
)}
|
|
84
135
|
</Tabs.List>
|
|
85
136
|
</Tabs.ListContainer>
|
|
86
137
|
|
|
@@ -91,6 +142,18 @@ export function Settings({
|
|
|
91
142
|
<Tabs.Panel id="security" className="px-0">
|
|
92
143
|
<SecuritySettings variant={variant} />
|
|
93
144
|
</Tabs.Panel>
|
|
145
|
+
|
|
146
|
+
{plugins.flatMap((plugin) =>
|
|
147
|
+
plugin.settingsTabs?.map((settingsTab, index) => (
|
|
148
|
+
<Tabs.Panel
|
|
149
|
+
key={`${plugin.id}-${index.toString()}`}
|
|
150
|
+
id={settingsTab.view}
|
|
151
|
+
className="px-0"
|
|
152
|
+
>
|
|
153
|
+
<settingsTab.component variant={variant} />
|
|
154
|
+
</Tabs.Panel>
|
|
155
|
+
))
|
|
156
|
+
)}
|
|
94
157
|
</Tabs>
|
|
95
158
|
)
|
|
96
159
|
}
|
|
@@ -13,7 +13,10 @@ export type UserAvatarProps = {
|
|
|
13
13
|
fallback?: ReactNode
|
|
14
14
|
isPending?: boolean
|
|
15
15
|
/** @remarks `User` */
|
|
16
|
-
user?: User & {
|
|
16
|
+
user?: Partial<User> & {
|
|
17
|
+
username?: string | null
|
|
18
|
+
displayUsername?: string | null
|
|
19
|
+
}
|
|
17
20
|
size?: AvatarProps["size"]
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -46,16 +46,15 @@ export type UserButtonLink = {
|
|
|
46
46
|
export type UserButtonProps = {
|
|
47
47
|
className?: string
|
|
48
48
|
size?: "default" | "icon"
|
|
49
|
-
/**
|
|
50
|
-
* The placement of the element with respect to its anchor element.
|
|
51
|
-
* @default "bottom"
|
|
52
|
-
*/
|
|
49
|
+
/** The placement of the element with respect to its anchor element. */
|
|
53
50
|
placement?: DropdownPopoverProps["placement"]
|
|
54
51
|
variant?: ButtonProps["variant"]
|
|
55
52
|
/** Additional menu entries rendered above the built-in items. */
|
|
56
53
|
links?: (UserButtonLink | ReactElement)[]
|
|
57
54
|
/** Hide the built-in "Settings" link. Useful when replacing it via `links`. */
|
|
58
55
|
hideSettings?: boolean
|
|
56
|
+
/** When true, the subtitle line (email when name/username is shown) is hidden. */
|
|
57
|
+
hideSubtitle?: boolean
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
function renderUserLink(
|
|
@@ -91,11 +90,12 @@ function renderUserLink(
|
|
|
91
90
|
*/
|
|
92
91
|
export function UserButton({
|
|
93
92
|
className,
|
|
94
|
-
placement
|
|
93
|
+
placement,
|
|
95
94
|
size = "default",
|
|
96
95
|
variant = "ghost",
|
|
97
96
|
links,
|
|
98
|
-
hideSettings
|
|
97
|
+
hideSettings,
|
|
98
|
+
hideSubtitle
|
|
99
99
|
}: UserButtonProps) {
|
|
100
100
|
const { authClient, basePaths, viewPaths, localization, plugins } = useAuth()
|
|
101
101
|
|
|
@@ -104,7 +104,10 @@ export function UserButton({
|
|
|
104
104
|
const userMenuItems = plugins.flatMap(
|
|
105
105
|
(plugin) =>
|
|
106
106
|
plugin.userMenuItems?.map((Item, index) => (
|
|
107
|
-
<Item
|
|
107
|
+
<Item
|
|
108
|
+
key={`${plugin.id}-${index.toString()}`}
|
|
109
|
+
hideSubtitle={hideSubtitle}
|
|
110
|
+
/>
|
|
108
111
|
)) ?? []
|
|
109
112
|
)
|
|
110
113
|
|
|
@@ -127,12 +130,12 @@ export function UserButton({
|
|
|
127
130
|
<Button
|
|
128
131
|
variant={variant}
|
|
129
132
|
className={cn(
|
|
130
|
-
"h-auto font-normal justify-start px-
|
|
133
|
+
"h-auto font-normal justify-start px-2 py-2 text-left",
|
|
131
134
|
className
|
|
132
135
|
)}
|
|
133
136
|
>
|
|
134
137
|
{session || sessionPending ? (
|
|
135
|
-
<UserView isPending={sessionPending} />
|
|
138
|
+
<UserView isPending={sessionPending} hideSubtitle={hideSubtitle} />
|
|
136
139
|
) : (
|
|
137
140
|
<>
|
|
138
141
|
<UserAvatar />
|
|
@@ -141,7 +144,7 @@ export function UserButton({
|
|
|
141
144
|
</>
|
|
142
145
|
)}
|
|
143
146
|
|
|
144
|
-
<ChevronsExpandVertical className="ml-auto size-3
|
|
147
|
+
<ChevronsExpandVertical className="ml-auto size-3 text-muted" />
|
|
145
148
|
</Button>
|
|
146
149
|
)}
|
|
147
150
|
|
|
@@ -151,7 +154,7 @@ export function UserButton({
|
|
|
151
154
|
>
|
|
152
155
|
{session && (
|
|
153
156
|
<div className="px-3 pt-3 pb-1">
|
|
154
|
-
<UserView />
|
|
157
|
+
<UserView hideSubtitle={hideSubtitle} />
|
|
155
158
|
</div>
|
|
156
159
|
)}
|
|
157
160
|
|