@better-auth-ui/heroui 1.6.9 → 1.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/dist/components/auth/additional-field.d.ts +2 -2
  2. package/dist/components/auth/api-key/api-key.d.ts +1 -1
  3. package/dist/components/auth/api-key/api-keys.d.ts +1 -1
  4. package/dist/components/auth/api-key/delete-api-key-dialog.d.ts +1 -1
  5. package/dist/components/auth/api-key/organization-api-keys.d.ts +1 -1
  6. package/dist/components/auth/auth-provider.d.ts +1 -1
  7. package/dist/components/auth/auth.d.ts +3 -3
  8. package/dist/components/auth/delete-user/danger-zone.d.ts +2 -2
  9. package/dist/components/auth/delete-user/delete-account.d.ts +1 -1
  10. package/dist/components/auth/email/email-changed-email.d.ts +2 -2
  11. package/dist/components/auth/email/email-verification-email.d.ts +2 -2
  12. package/dist/components/auth/email/magic-link-email.d.ts +2 -2
  13. package/dist/components/auth/email/new-device-email.d.ts +2 -2
  14. package/dist/components/auth/email/organization-invitation-email.d.ts +2 -2
  15. package/dist/components/auth/email/otp-email.d.ts +2 -2
  16. package/dist/components/auth/email/password-changed-email.d.ts +2 -2
  17. package/dist/components/auth/email/reset-password-email.d.ts +2 -2
  18. package/dist/components/auth/field-separator.d.ts +1 -1
  19. package/dist/components/auth/forgot-password.d.ts +1 -1
  20. package/dist/components/auth/magic-link/magic-link-button.d.ts +1 -1
  21. package/dist/components/auth/magic-link/magic-link.d.ts +2 -2
  22. package/dist/components/auth/multi-session/manage-account.d.ts +1 -1
  23. package/dist/components/auth/multi-session/manage-accounts.d.ts +1 -1
  24. package/dist/components/auth/multi-session/switch-account-submenu-item.d.ts +1 -1
  25. package/dist/components/auth/organization/delete-organization-dialog.d.ts +1 -1
  26. package/dist/components/auth/organization/leave-organization-dialog.d.ts +1 -1
  27. package/dist/components/auth/organization/organization-danger-zone.d.ts +2 -2
  28. package/dist/components/auth/organization/organization-invitation-row.d.ts +1 -1
  29. package/dist/components/auth/organization/organization-invitations.d.ts +1 -1
  30. package/dist/components/auth/organization/organization-logo.d.ts +3 -3
  31. package/dist/components/auth/organization/organization-member-row.d.ts +1 -1
  32. package/dist/components/auth/organization/organization-members.d.ts +1 -1
  33. package/dist/components/auth/organization/organization-people.d.ts +1 -1
  34. package/dist/components/auth/organization/organization-profile.d.ts +1 -1
  35. package/dist/components/auth/organization/organization-row.d.ts +1 -1
  36. package/dist/components/auth/organization/organization-settings.d.ts +2 -2
  37. package/dist/components/auth/organization/organization-switcher.d.ts +3 -3
  38. package/dist/components/auth/organization/organization-view-skeleton.d.ts +2 -2
  39. package/dist/components/auth/organization/organization-view.d.ts +3 -3
  40. package/dist/components/auth/organization/organization.d.ts +3 -3
  41. package/dist/components/auth/organization/organizations-settings.d.ts +2 -2
  42. package/dist/components/auth/organization/organizations.d.ts +1 -1
  43. package/dist/components/auth/organization/remove-member-dialog.d.ts +1 -1
  44. package/dist/components/auth/organization/slug-field.d.ts +1 -1
  45. package/dist/components/auth/organization/user-invitation-row.d.ts +1 -1
  46. package/dist/components/auth/organization/user-invitations.d.ts +1 -1
  47. package/dist/components/auth/passkey/passkey-button.d.ts +1 -1
  48. package/dist/components/auth/passkey/passkey.d.ts +1 -1
  49. package/dist/components/auth/passkey/passkeys.d.ts +1 -1
  50. package/dist/components/auth/provider-button.d.ts +2 -2
  51. package/dist/components/auth/reset-password.d.ts +1 -1
  52. package/dist/components/auth/settings/account/account-settings.d.ts +2 -2
  53. package/dist/components/auth/settings/account/change-email.d.ts +1 -1
  54. package/dist/components/auth/settings/account/user-profile.d.ts +1 -1
  55. package/dist/components/auth/settings/security/active-session.d.ts +1 -1
  56. package/dist/components/auth/settings/security/active-sessions.d.ts +1 -1
  57. package/dist/components/auth/settings/security/change-password.d.ts +1 -1
  58. package/dist/components/auth/settings/security/linked-account.d.ts +1 -1
  59. package/dist/components/auth/settings/security/linked-accounts.d.ts +1 -1
  60. package/dist/components/auth/settings/security/security-settings.d.ts +2 -2
  61. package/dist/components/auth/settings/settings.d.ts +3 -3
  62. package/dist/components/auth/sign-in.d.ts +2 -2
  63. package/dist/components/auth/sign-up.d.ts +2 -2
  64. package/dist/components/auth/theme/appearance.d.ts +1 -1
  65. package/dist/components/auth/user/user-avatar.d.ts +3 -3
  66. package/dist/components/auth/user/user-button.d.ts +2 -2
  67. package/dist/components/auth/user/user-view.d.ts +3 -3
  68. package/dist/components/auth/username/sign-in-username.d.ts +2 -2
  69. package/dist/components/auth/username/username-field.d.ts +1 -1
  70. package/dist/email.d.ts +8 -8
  71. package/dist/email.js +211 -8
  72. package/dist/index.d.ts +21 -21
  73. package/dist/index.js +3393 -21
  74. package/dist/lib/auth/api-key-plugin.d.ts +3 -3
  75. package/dist/lib/auth/auth-plugin.d.ts +5 -5
  76. package/dist/lib/auth/delete-user-plugin.d.ts +2 -2
  77. package/dist/lib/auth/magic-link-plugin.d.ts +3 -3
  78. package/dist/lib/auth/multi-session-plugin.d.ts +3 -3
  79. package/dist/lib/auth/organization-plugin.d.ts +2 -2
  80. package/dist/lib/auth/passkey-plugin.d.ts +3 -3
  81. package/dist/lib/auth/theme-plugin.d.ts +3 -3
  82. package/dist/lib/auth/username-plugin.d.ts +3 -3
  83. package/dist/plugins.d.ts +46 -46
  84. package/dist/plugins.js +2824 -47
  85. package/dist/user-view--47tVtJ3.js +113 -0
  86. package/package.json +3 -5
  87. package/dist/components/auth/additional-field.js +0 -137
  88. package/dist/components/auth/api-key/api-key-skeleton.js +0 -5
  89. package/dist/components/auth/api-key/api-key.js +0 -17
  90. package/dist/components/auth/api-key/api-keys-empty.js +0 -9
  91. package/dist/components/auth/api-key/api-keys.js +0 -22
  92. package/dist/components/auth/api-key/create-api-key-dialog.js +0 -44
  93. package/dist/components/auth/api-key/delete-api-key-dialog.js +0 -17
  94. package/dist/components/auth/api-key/new-api-key-dialog.js +0 -24
  95. package/dist/components/auth/api-key/organization-api-keys.js +0 -22
  96. package/dist/components/auth/auth-provider.js +0 -11
  97. package/dist/components/auth/auth.js +0 -88
  98. package/dist/components/auth/delete-user/danger-zone.js +0 -12
  99. package/dist/components/auth/delete-user/delete-account.js +0 -50
  100. package/dist/components/auth/email/email-changed-email.js +0 -23
  101. package/dist/components/auth/email/email-verification-email.js +0 -23
  102. package/dist/components/auth/email/magic-link-email.js +0 -23
  103. package/dist/components/auth/email/new-device-email.js +0 -23
  104. package/dist/components/auth/email/organization-invitation-email.js +0 -23
  105. package/dist/components/auth/email/otp-email.js +0 -22
  106. package/dist/components/auth/email/password-changed-email.js +0 -23
  107. package/dist/components/auth/email/reset-password-email.js +0 -23
  108. package/dist/components/auth/error-toaster.js +0 -33
  109. package/dist/components/auth/field-separator.js +0 -5
  110. package/dist/components/auth/forgot-password.js +0 -37
  111. package/dist/components/auth/magic-link/magic-link-button.js +0 -34
  112. package/dist/components/auth/magic-link/magic-link.js +0 -40
  113. package/dist/components/auth/multi-session/manage-account.js +0 -33
  114. package/dist/components/auth/multi-session/manage-accounts.js +0 -33
  115. package/dist/components/auth/multi-session/switch-account-submenu-content.js +0 -25
  116. package/dist/components/auth/multi-session/switch-account-submenu-item.js +0 -17
  117. package/dist/components/auth/multi-session/switch-account-submenu.js +0 -24
  118. package/dist/components/auth/organization/change-organization-logo.js +0 -69
  119. package/dist/components/auth/organization/create-organization-dialog.js +0 -37
  120. package/dist/components/auth/organization/delete-organization-dialog.js +0 -25
  121. package/dist/components/auth/organization/delete-organization-skeleton.js +0 -8
  122. package/dist/components/auth/organization/delete-organization.js +0 -27
  123. package/dist/components/auth/organization/invite-member-dialog.js +0 -47
  124. package/dist/components/auth/organization/leave-organization-dialog.js +0 -21
  125. package/dist/components/auth/organization/leave-organization.js +0 -16
  126. package/dist/components/auth/organization/organization-danger-zone.js +0 -23
  127. package/dist/components/auth/organization/organization-invitation-row-skeleton.js +0 -9
  128. package/dist/components/auth/organization/organization-invitation-row.js +0 -31
  129. package/dist/components/auth/organization/organization-invitations-empty.js +0 -13
  130. package/dist/components/auth/organization/organization-invitations.js +0 -66
  131. package/dist/components/auth/organization/organization-logo.js +0 -14
  132. package/dist/components/auth/organization/organization-member-row-skeleton.js +0 -9
  133. package/dist/components/auth/organization/organization-member-row.js +0 -34
  134. package/dist/components/auth/organization/organization-members.js +0 -67
  135. package/dist/components/auth/organization/organization-people.js +0 -11
  136. package/dist/components/auth/organization/organization-profile.js +0 -34
  137. package/dist/components/auth/organization/organization-row.js +0 -31
  138. package/dist/components/auth/organization/organization-settings.js +0 -13
  139. package/dist/components/auth/organization/organization-switcher.js +0 -48
  140. package/dist/components/auth/organization/organization-view-skeleton.js +0 -9
  141. package/dist/components/auth/organization/organization-view.js +0 -31
  142. package/dist/components/auth/organization/organization.js +0 -60
  143. package/dist/components/auth/organization/organizations-empty.js +0 -9
  144. package/dist/components/auth/organization/organizations-settings.js +0 -17
  145. package/dist/components/auth/organization/organizations.js +0 -21
  146. package/dist/components/auth/organization/remove-member-dialog.js +0 -20
  147. package/dist/components/auth/organization/slug-field.js +0 -38
  148. package/dist/components/auth/organization/user-invitation-row-skeleton.js +0 -8
  149. package/dist/components/auth/organization/user-invitation-row.js +0 -22
  150. package/dist/components/auth/organization/user-invitations-empty.js +0 -12
  151. package/dist/components/auth/organization/user-invitations.js +0 -18
  152. package/dist/components/auth/passkey/add-passkey-dialog.js +0 -22
  153. package/dist/components/auth/passkey/delete-passkey-dialog.js +0 -14
  154. package/dist/components/auth/passkey/passkey-button.js +0 -25
  155. package/dist/components/auth/passkey/passkey-skeleton.js +0 -5
  156. package/dist/components/auth/passkey/passkey.js +0 -17
  157. package/dist/components/auth/passkey/passkeys-empty.js +0 -9
  158. package/dist/components/auth/passkey/passkeys.js +0 -16
  159. package/dist/components/auth/provider-button.js +0 -29
  160. package/dist/components/auth/provider-buttons.js +0 -29
  161. package/dist/components/auth/reset-password.js +0 -57
  162. package/dist/components/auth/settings/account/account-settings.js +0 -22
  163. package/dist/components/auth/settings/account/change-avatar.js +0 -54
  164. package/dist/components/auth/settings/account/change-email.js +0 -28
  165. package/dist/components/auth/settings/account/user-profile.js +0 -65
  166. package/dist/components/auth/settings/security/active-session.js +0 -52
  167. package/dist/components/auth/settings/security/active-sessions.js +0 -23
  168. package/dist/components/auth/settings/security/change-password.js +0 -79
  169. package/dist/components/auth/settings/security/linked-account.js +0 -38
  170. package/dist/components/auth/settings/security/linked-accounts.js +0 -36
  171. package/dist/components/auth/settings/security/security-settings.js +0 -20
  172. package/dist/components/auth/settings/settings.js +0 -53
  173. package/dist/components/auth/sign-in.js +0 -64
  174. package/dist/components/auth/sign-out.js +0 -33
  175. package/dist/components/auth/sign-up.js +0 -98
  176. package/dist/components/auth/theme/appearance.js +0 -24
  177. package/dist/components/auth/theme/theme-toggle-item.js +0 -57
  178. package/dist/components/auth/user/user-avatar.js +0 -32
  179. package/dist/components/auth/user/user-button.js +0 -40
  180. package/dist/components/auth/user/user-view.js +0 -27
  181. package/dist/components/auth/username/sign-in-username.js +0 -88
  182. package/dist/components/auth/username/username-field.js +0 -45
  183. package/dist/lib/auth/api-key-plugin.js +0 -12
  184. package/dist/lib/auth/auth-plugin.js +0 -1
  185. package/dist/lib/auth/delete-user-plugin.js +0 -7
  186. package/dist/lib/auth/magic-link-plugin.js +0 -18
  187. package/dist/lib/auth/multi-session-plugin.js +0 -9
  188. package/dist/lib/auth/organization-plugin.js +0 -19
  189. package/dist/lib/auth/passkey-plugin.js +0 -9
  190. package/dist/lib/auth/theme-plugin.js +0 -26
  191. package/dist/lib/auth/username-plugin.js +0 -19
@@ -1,24 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ThemePreviewDark, ThemePreviewLight, ThemePreviewSystem, useAuthPlugin } from "@better-auth-ui/react";
3
- import { Display, Moon, Sun } from "@gravity-ui/icons";
4
- import { Card, cn, Label, Radio, RadioGroup, useIsHydrated } from "@heroui/react";
5
- import { themePlugin } from "../../../lib/auth/theme-plugin";
6
- /**
7
- * Renders a theme selector card with visual theme previews.
8
- *
9
- * Displays a card containing radio buttons for selecting between system, light,
10
- * and dark themes. Each option shows a visual preview of the theme. Theme options
11
- * are conditionally shown based on the `themes` array provided via the theme plugin.
12
- */
13
- export function Appearance({ className, variant, ...props }) {
14
- const { useTheme, localization } = useAuthPlugin(themePlugin);
15
- const { theme, setTheme, themes = [] } = useTheme();
16
- const hydrated = useIsHydrated();
17
- return (_jsxs("div", { children: [_jsx("h2", { className: cn("text-sm font-semibold mb-3"), children: localization.appearance }), _jsx(Card, { className: cn("p-4 gap-4", className), variant: variant, ...props, children: _jsxs(Card.Content, { children: [_jsx(Label, { children: localization.theme }), _jsx(RadioGroup, { variant: variant === "transparent" ? "secondary" : "primary", value: hydrated ? theme : "", onChange: setTheme, isDisabled: !hydrated || !theme, children: _jsxs("div", { className: "grid gap-3 grid-cols-2 sm:grid-cols-3", children: [themes.includes("system") && (_jsx(Radio, { value: "system", className: cn("mt-0 px-4 pb-4 pt-3 rounded-xl", "border border-transparent data-[selected=true]:border-accent data-[selected=true]:bg-accent/10", "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10", variant === "transparent"
18
- ? "bg-surface"
19
- : "bg-surface-secondary"), children: _jsxs(Radio.Content, { className: "gap-2", children: [_jsxs("div", { className: "flex gap-2 justify-between", children: [_jsxs(Label, { className: "flex gap-2 items-center", children: [_jsx(Display, { className: "text-muted" }), localization.system] }), _jsx(Radio.Control, { children: _jsx(Radio.Indicator, {}) })] }), _jsx(ThemePreviewSystem, { className: "w-full" })] }) })), themes.includes("light") && (_jsx(Radio, { value: "light", className: cn("mt-0 px-4 pb-4 pt-3 rounded-xl", "border border-transparent data-[selected=true]:border-accent data-[selected=true]:bg-accent/10", "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10", variant === "transparent"
20
- ? "bg-surface"
21
- : "bg-surface-secondary"), children: _jsxs(Radio.Content, { className: "gap-2", children: [_jsxs("div", { className: "flex gap-2 justify-between", children: [_jsxs(Label, { className: "flex gap-2 items-center", children: [_jsx(Sun, { className: "text-muted" }), localization.light] }), _jsx(Radio.Control, { children: _jsx(Radio.Indicator, {}) })] }), _jsx(ThemePreviewLight, { className: "w-full" })] }) })), themes.includes("dark") && (_jsx(Radio, { value: "dark", className: cn("mt-0 px-4 pb-4 pt-3 rounded-xl", "border border-transparent data-[selected=true]:border-accent data-[selected=true]:bg-accent/10", "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10", variant === "transparent"
22
- ? "bg-surface"
23
- : "bg-surface-secondary"), children: _jsxs(Radio.Content, { className: "gap-2", children: [_jsxs("div", { className: "flex gap-2 justify-between", children: [_jsxs(Label, { className: "flex gap-2 items-center", children: [_jsx(Moon, { className: "text-muted" }), localization.dark] }), _jsx(Radio.Control, { children: _jsx(Radio.Indicator, {}) })] }), _jsx(ThemePreviewDark, { className: "w-full" })] }) }))] }) })] }) })] }));
24
- }
@@ -1,57 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useAuthPlugin } from "@better-auth-ui/react";
3
- import { Display, Moon, Palette, Sun } from "@gravity-ui/icons";
4
- import { Dropdown, Label, Tabs } from "@heroui/react";
5
- import { themePlugin } from "../../../lib/auth/theme-plugin";
6
- const ACTIVE_TAB_SELECTOR = '[role="tab"][aria-selected="true"]';
7
- /**
8
- * Theme toggle dropdown item used inside `UserButton`. Callers are responsible
9
- * for ensuring theming is configured before rendering this component.
10
- */
11
- export function ThemeToggleItem() {
12
- const { useTheme, localization } = useAuthPlugin(themePlugin);
13
- const { theme, setTheme, themes = [] } = useTheme();
14
- // The Tabs aren't part of the menu's keyboard-navigation list, so arrow-key
15
- // nav can't reach them on its own. When the wrapper menu item receives focus
16
- // we delegate focus to the active Tab so the user can switch themes with the
17
- // Left/Right arrows. React's synthetic `onFocus` bubbles (it's wired to
18
- // `focusin`), so we must guard against child-originated events: without the
19
- // `target === currentTarget` check, an arrow-key focus move between tabs
20
- // bubbles up here and the still-stale `aria-selected="true"` lookup yanks
21
- // focus back to the previously selected tab.
22
- const focusActiveTab = (event) => {
23
- if (event.target !== event.currentTarget)
24
- return;
25
- const activeTab = event.currentTarget.querySelector(ACTIVE_TAB_SELECTOR);
26
- activeTab?.focus({ preventScroll: true });
27
- };
28
- // Up/Down on a Tab escapes back to the previous/next sibling menu item so
29
- // users can keep navigating the menu with the arrow keys after they've
30
- // entered the Tabs.
31
- const handleTabsKeyDown = (event) => {
32
- if (event.key !== "ArrowUp" && event.key !== "ArrowDown")
33
- return;
34
- const target = event.target;
35
- if (target.getAttribute("role") !== "tab")
36
- return;
37
- const wrapper = target.closest('[role="menuitem"]');
38
- const menu = wrapper?.closest('[role="menu"]');
39
- if (!wrapper || !menu)
40
- return;
41
- const items = Array.from(menu.querySelectorAll('[role="menuitem"]:not([data-disabled])'));
42
- const currentIndex = items.indexOf(wrapper);
43
- if (currentIndex === -1)
44
- return;
45
- const nextIndex = event.key === "ArrowDown" ? currentIndex + 1 : currentIndex - 1;
46
- const next = items[nextIndex];
47
- if (!next)
48
- return;
49
- event.preventDefault();
50
- next.focus();
51
- };
52
- return (_jsxs(Dropdown.Item, { className: "py-1 pe-2", onFocus: focusActiveTab,
53
- // Without `shouldCloseOnSelect={false}` the menu auto-closes after the
54
- // user changes themes, because clicking on a Tab bubbles up as a
55
- // menu-item activation.
56
- shouldCloseOnSelect: false, children: [_jsx(Palette, { className: "text-muted" }), _jsx(Label, { children: localization.theme }), _jsx(Tabs, { className: "ml-auto", selectedKey: theme, onSelectionChange: (key) => setTheme(key), children: _jsx(Tabs.ListContainer, { onKeyDown: handleTabsKeyDown, children: _jsxs(Tabs.List, { "aria-label": localization.theme, className: "*:h-5 *:w-5 *:p-0", children: [themes.includes("system") && (_jsxs(Tabs.Tab, { id: "system", "aria-label": localization.system, children: [_jsx(Display, { className: "size-3" }), _jsx(Tabs.Indicator, {})] })), themes.includes("light") && (_jsxs(Tabs.Tab, { id: "light", "aria-label": localization.light, children: [_jsx(Sun, { className: "size-3" }), _jsx(Tabs.Indicator, {})] })), themes.includes("dark") && (_jsxs(Tabs.Tab, { id: "dark", "aria-label": localization.dark, children: [_jsx(Moon, { className: "size-3" }), _jsx(Tabs.Indicator, {})] }))] }) }) })] }));
57
- }
@@ -1,32 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useAuth, useSession } from "@better-auth-ui/react";
3
- import { Person } from "@gravity-ui/icons";
4
- import { Avatar, cn, Skeleton } from "@heroui/react";
5
- /**
6
- * Render a user avatar that shows the user's image, initials, or a custom fallback; displays a circular skeleton while the session (or explicit pending flag) is pending.
7
- *
8
- * @param className - Additional CSS classes applied to the outer avatar container
9
- * @param fallback - Custom fallback content to render when no image or initials are available
10
- * @param isPending - When true, force rendering of the loading skeleton (unless an explicit `user` prop is provided)
11
- * @param user - Optional user object to display; when omitted the current session user is used if available
12
- * @param size - Visual size of the avatar; one of `"sm"`, `"md"`, or `"lg"` (default: `"sm"`)
13
- * @returns A React element that displays the user's avatar image, initials, or the provided fallback; renders a circular skeleton while loading
14
- */
15
- export function UserAvatar({ className, fallback, isPending, user, size = "sm", style, ...props }) {
16
- const { authClient } = useAuth();
17
- const { data: session, isPending: sessionPending } = useSession(authClient, {
18
- enabled: !user && !isPending
19
- });
20
- if ((isPending || sessionPending) && !user) {
21
- return (_jsx(Skeleton, { className: cn("rounded-full", size === "sm" ? "size-8" : size === "md" ? "size-10" : "size-12", className), style: style }));
22
- }
23
- const resolvedUser = user ?? session?.user;
24
- const initials = (resolvedUser?.username ||
25
- resolvedUser?.name ||
26
- resolvedUser?.email)
27
- ?.slice(0, 2)
28
- .toUpperCase();
29
- return (_jsxs(Avatar, { size: size, className: cn("rounded-full", className), style: style, ...props, children: [_jsx(Avatar.Image, { alt: resolvedUser?.displayUsername ||
30
- resolvedUser?.name ||
31
- resolvedUser?.email, src: resolvedUser?.image ?? undefined }), _jsx(Avatar.Fallback, { className: cn(size === "lg" ? "text-xl" : size === "md" ? "text-base" : "text-sm"), delayMs: resolvedUser?.image ? 600 : undefined, children: fallback || initials || _jsx(Person, { className: "size-4" }) })] }));
32
- }
@@ -1,40 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useAuth, useSession } from "@better-auth-ui/react";
3
- import { ArrowRightFromSquare, ArrowRightToSquare, ChevronsExpandVertical, Gear, PersonPlus } from "@gravity-ui/icons";
4
- import { Button, cn, Dropdown, Label } from "@heroui/react";
5
- import { isValidElement } from "react";
6
- import { UserAvatar } from "./user-avatar";
7
- import { UserView } from "./user-view";
8
- function renderUserLink(link, fallbackKey) {
9
- if (isValidElement(link))
10
- return link;
11
- const { label, href, icon, variant } = link;
12
- return (_jsxs(Dropdown.Item, { href: href, variant: variant, textValue: typeof label === "string" ? label : undefined, children: [icon, _jsx(Label, { children: label })] }, fallbackKey));
13
- }
14
- /**
15
- * Render a user account dropdown button that shows account actions.
16
- *
17
- * @param className - Additional CSS classes applied to the trigger element
18
- * @param placement - Dropdown popover placement (e.g., "bottom", "top-start", "bottom-end")
19
- * @param size - "icon" renders an avatar-only trigger; "default" renders a button with label and chevron
20
- * @param variant - Button visual variant passed to the underlying Button component
21
- * @param links - Additional menu entries rendered above the built-in items
22
- * @param hideSettings - Hide the built-in "Settings" link
23
- * @returns The user button and its dropdown menu as a JSX element
24
- */
25
- export function UserButton({ className, placement, size = "default", variant = "ghost", links, hideSettings, hideSubtitle }) {
26
- const { authClient, basePaths, viewPaths, localization, plugins } = useAuth();
27
- const { data: session, isPending: sessionPending } = useSession(authClient);
28
- const userMenuItems = plugins.flatMap((plugin) => plugin.userMenuItems?.map((Item, index) => (_jsx(Item, { hideSubtitle: hideSubtitle }, `${plugin.id}-${index.toString()}`))) ?? []);
29
- const userLinks = links?.flatMap((link, index) => {
30
- if (!isValidElement(link)) {
31
- const visibility = link.visibility ?? "always";
32
- if (visibility === "authenticated" && !session)
33
- return [];
34
- if (visibility === "unauthenticated" && session)
35
- return [];
36
- }
37
- return [renderUserLink(link, `user-button-link-${index.toString()}`)];
38
- });
39
- return (_jsxs(Dropdown, { children: [size === "icon" ? (_jsx(Dropdown.Trigger, { className: cn("rounded-full", className), children: _jsx(UserAvatar, {}) })) : (_jsxs(Button, { variant: variant, className: cn("h-auto font-normal justify-start px-2 py-2 text-left", className), children: [session || sessionPending ? (_jsx(UserView, { isPending: sessionPending, hideSubtitle: hideSubtitle })) : (_jsxs(_Fragment, { children: [_jsx(UserAvatar, {}), _jsx("p", { className: "text-sm font-medium", children: localization.auth.account })] })), _jsx(ChevronsExpandVertical, { className: "ml-auto size-3 text-muted" })] })), _jsxs(Dropdown.Popover, { placement: placement, className: "min-w-40 md:min-w-56 max-w-[48svw]", children: [session && (_jsx("div", { className: "px-3 pt-3 pb-1", children: _jsx(UserView, { hideSubtitle: hideSubtitle }) })), _jsx(Dropdown.Menu, { children: session ? (_jsxs(_Fragment, { children: [userLinks, !hideSettings && (_jsxs(Dropdown.Item, { textValue: localization.settings.settings, href: `${basePaths.settings}/${viewPaths.settings.account}`, children: [_jsx(Gear, { className: "text-muted" }), _jsx(Label, { children: localization.settings.settings })] })), userMenuItems, _jsxs(Dropdown.Item, { textValue: localization.auth.signOut, href: `${basePaths.auth}/${viewPaths.auth.signOut}`, variant: "danger", children: [_jsx(ArrowRightFromSquare, { className: "text-danger" }), _jsx(Label, { children: localization.auth.signOut })] })] })) : (_jsxs(_Fragment, { children: [userLinks, _jsxs(Dropdown.Item, { textValue: localization.auth.signIn, href: `${basePaths.auth}/${viewPaths.auth.signIn}`, children: [_jsx(ArrowRightToSquare, { className: "text-muted" }), _jsx(Label, { children: localization.auth.signIn })] }), _jsxs(Dropdown.Item, { textValue: localization.auth.signUp, href: `${basePaths.auth}/${viewPaths.auth.signUp}`, children: [_jsx(PersonPlus, { className: "text-muted" }), _jsx(Label, { children: localization.auth.signUp })] }), userMenuItems] })) })] })] }));
40
- }
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useAuth, useSession } from "@better-auth-ui/react";
3
- import { cn, Skeleton } from "@heroui/react";
4
- import { UserAvatar } from "./user-avatar";
5
- /**
6
- * Render a compact user item with an avatar, a primary label (display username, name, or email), and an optional subtitle (email).
7
- *
8
- * @param isPending - If true and no `user` prop is provided, renders a loading skeleton instead of user details
9
- * @param size - Avatar size variant; defaults to `"md"`
10
- * @param hideSubtitle - When true, omits the muted subtitle row under the primary label
11
- * @param user - Optional user to display; when omitted the current session user is used if available
12
- * @returns A React element containing the user's avatar and text labels
13
- */
14
- export function UserView({ className, isPending, size = "md", hideSubtitle = false, user, ...props }) {
15
- const { authClient } = useAuth();
16
- const { data: session, isPending: sessionPending } = useSession(authClient, {
17
- enabled: !user && !isPending
18
- });
19
- const resolvedUser = user ?? session?.user;
20
- if ((isPending || sessionPending) && !user) {
21
- return (_jsxs("div", { className: cn("flex items-center gap-2 min-w-0", className), ...props, children: [_jsx(UserAvatar, { isPending: true, className: size === "sm" ? "size-5 [&>span]:text-xs" : undefined, size: size === "lg" ? "md" : "sm" }), _jsxs("div", { className: "flex flex-col gap-1 min-w-0", children: [_jsx(Skeleton, { className: "h-3.5 w-24 rounded-lg" }), !hideSubtitle ? _jsx(Skeleton, { className: "h-3 w-32 rounded-lg" }) : null] })] }));
22
- }
23
- return (_jsxs("div", { className: cn("flex items-center gap-2 min-w-0", className), ...props, children: [_jsx(UserAvatar, { className: size === "sm" ? "size-5 [&>span]:text-xs" : undefined, user: resolvedUser, size: size === "lg" ? "md" : "sm" }), _jsxs("div", { className: "flex flex-col min-w-0", children: [_jsx("p", { className: "text-foreground text-sm font-medium leading-tight truncate", children: resolvedUser?.displayUsername ||
24
- resolvedUser?.name ||
25
- resolvedUser?.email }), !hideSubtitle &&
26
- (resolvedUser?.displayUsername || resolvedUser?.name) ? (_jsx("p", { className: "text-muted text-xs leading-tight truncate overflow-x-hidden", children: resolvedUser?.email })) : null] })] }));
27
- }
@@ -1,88 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { authMutationKeys } from "@better-auth-ui/core";
3
- import { useAuth, useAuthPlugin, useFetchOptions, useSendVerificationEmail, useSignInEmail, useSignInUsername } from "@better-auth-ui/react";
4
- import { Button, Card, Checkbox, cn, Description, FieldError, Form, Input, Label, Link, Spinner, TextField, toast } from "@heroui/react";
5
- import { useIsMutating } from "@tanstack/react-query";
6
- import { useState } from "react";
7
- import { usernamePlugin } from "../../../lib/auth/username-plugin";
8
- import { FieldSeparator } from "../field-separator";
9
- import { ProviderButtons } from "../provider-buttons";
10
- /**
11
- * Render the username-based sign-in UI.
12
- *
13
- * @returns The sign-in JSX element containing username/password fields, optional magic-link button, and social provider buttons.
14
- */
15
- export function SignInUsername({ className, socialLayout, socialPosition = "bottom", variant, ...props }) {
16
- const { authClient, basePaths, baseURL, emailAndPassword, localization, plugins, redirectTo, socialProviders, viewPaths, navigate } = useAuth();
17
- const { fetchOptions, resetFetchOptions } = useFetchOptions();
18
- const { localization: usernameLocalization } = useAuthPlugin(usernamePlugin);
19
- const [password, setPassword] = useState("");
20
- const { mutate: sendVerificationEmail } = useSendVerificationEmail(authClient, {
21
- onSuccess: () => toast.success(localization.auth.verificationEmailSent)
22
- });
23
- function isEmail(value) {
24
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
25
- }
26
- const { mutate: signInEmail, isPending: isSignInEmailPending } = useSignInEmail(authClient, {
27
- onError: (error, { email }) => {
28
- setPassword("");
29
- if (error.error?.code === "EMAIL_NOT_VERIFIED") {
30
- toast.danger(error.error?.message || error.message, {
31
- actionProps: {
32
- children: localization.auth.resend,
33
- onClick: () => sendVerificationEmail({
34
- email,
35
- callbackURL: `${baseURL}${redirectTo}`
36
- })
37
- }
38
- });
39
- }
40
- else {
41
- toast.danger(error.error?.message || error.message);
42
- }
43
- resetFetchOptions();
44
- },
45
- onSuccess: () => navigate({ to: redirectTo })
46
- });
47
- const { mutate: signInUsername, isPending: isSignInUsernamePending } = useSignInUsername(authClient, {
48
- onError: (error) => {
49
- setPassword("");
50
- toast.danger(error.error?.message || error.message);
51
- resetFetchOptions();
52
- },
53
- onSuccess: () => navigate({ to: redirectTo })
54
- });
55
- const handleSubmit = (e) => {
56
- e.preventDefault();
57
- const formData = new FormData(e.currentTarget);
58
- const email = formData.get("email");
59
- const rememberMe = formData.get("rememberMe") === "on";
60
- if (isEmail(email)) {
61
- signInEmail({
62
- email,
63
- password,
64
- ...(emailAndPassword?.rememberMe ? { rememberMe } : {}),
65
- fetchOptions
66
- });
67
- }
68
- else {
69
- signInUsername({
70
- username: email,
71
- password,
72
- ...(emailAndPassword?.rememberMe ? { rememberMe } : {}),
73
- fetchOptions
74
- });
75
- }
76
- };
77
- const signInMutating = useIsMutating({
78
- mutationKey: authMutationKeys.signIn.all
79
- });
80
- const signUpMutating = useIsMutating({
81
- mutationKey: authMutationKeys.signUp.all
82
- });
83
- const isPending = signInMutating + signUpMutating > 0;
84
- const isSignInPending = isSignInEmailPending || isSignInUsernamePending;
85
- const Captcha = plugins.find((plugin) => plugin.captchaComponent)?.captchaComponent;
86
- const showSeparator = emailAndPassword?.enabled && !!socialProviders?.length;
87
- return (_jsxs(Card, { className: cn("w-full max-w-sm gap-4 md:p-6", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl font-semibold mb-1", children: localization.auth.signIn }) }), _jsxs(Card.Content, { className: "gap-4", children: [socialPosition === "top" && (_jsxs(_Fragment, { children: [!!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout })), showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or }))] })), emailAndPassword?.enabled && (_jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { name: "email", type: "text", autoComplete: "username email", isDisabled: isPending, children: [_jsx(Label, { children: usernameLocalization.username }), _jsx(Input, { placeholder: usernameLocalization.usernameOrEmailPlaceholder, variant: variant === "transparent" ? "primary" : "secondary", required: true }), _jsx(FieldError, {})] }), _jsxs(TextField, { minLength: emailAndPassword?.minPasswordLength, maxLength: emailAndPassword?.maxPasswordLength, name: "password", type: "password", autoComplete: "current-password", isDisabled: isPending, value: password, onChange: setPassword, children: [_jsx(Label, { children: localization.auth.password }), _jsx(Input, { placeholder: localization.auth.passwordPlaceholder, variant: variant === "transparent" ? "primary" : "secondary", required: true }), _jsx(FieldError, {})] }), emailAndPassword?.rememberMe && (_jsxs(Checkbox, { name: "rememberMe", isDisabled: isPending, variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(Checkbox.Control, { children: _jsx(Checkbox.Indicator, {}) }), _jsx(Checkbox.Content, { children: _jsx(Label, { children: localization.auth.rememberMe }) })] })), Captcha && _jsx("div", { className: "flex justify-center", children: Captcha }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs(Button, { type: "submit", className: "w-full", isPending: isSignInPending || isPending, children: [isSignInPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.signIn] }), plugins.flatMap((plugin) => plugin.authButtons?.map((AuthButton, index) => (_jsx(AuthButton, { view: "signIn" }, `${plugin.id}-${index.toString()}`))))] })] })), socialPosition === "bottom" && (_jsxs(_Fragment, { children: [showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or })), !!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout }))] }))] }), _jsxs(Card.Footer, { className: "flex-col gap-3", children: [emailAndPassword?.forgotPassword && (_jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.forgotPassword}`, className: "no-underline hover:underline", children: localization.auth.forgotPasswordLink })), emailAndPassword?.enabled && (_jsxs(Description, { className: "text-sm", children: [localization.auth.needToCreateAnAccount, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signUp}`, className: "text-accent no-underline hover:underline decoration-accent-hover", children: localization.auth.signUp })] }))] })] }));
88
- }
@@ -1,45 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useAuth, useAuthPlugin, useIsUsernameAvailable } from "@better-auth-ui/react";
3
- import { Check, Xmark } from "@gravity-ui/icons";
4
- import { FieldError, InputGroup, Label, Spinner, TextField } from "@heroui/react";
5
- import { useDebouncer } from "@tanstack/react-pacer";
6
- import { useState } from "react";
7
- import { usernamePlugin } from "../../../lib/auth/username-plugin";
8
- /**
9
- * Renderer for the `username` additional field. Owns availability checking,
10
- * length limits, and visual indicators. `FieldError` automatically surfaces
11
- * native validation messages. Availability feedback is shown via the icon and
12
- * aria-label without affecting the field's invalid state.
13
- */
14
- export function UsernameField({ name, field, isPending, variant }) {
15
- const { authClient } = useAuth();
16
- const { localization, minUsernameLength, maxUsernameLength, isUsernameAvailable: checkAvailability } = useAuthPlugin(usernamePlugin);
17
- const currentUsername = String(field.defaultValue ?? "");
18
- const [value, setValue] = useState(currentUsername);
19
- const { mutate: requestAvailability, data: availability, error: availabilityError, reset: resetAvailability } = useIsUsernameAvailable(authClient, {
20
- // Bypass global error toast
21
- onError: () => { }
22
- });
23
- const debouncer = useDebouncer((next) => {
24
- const trimmed = next.trim();
25
- // Skip blank input and the user's own current username (profile view).
26
- if (!trimmed || trimmed === currentUsername) {
27
- resetAvailability();
28
- return;
29
- }
30
- requestAvailability({ username: trimmed });
31
- }, { wait: 500 });
32
- function handleChange(next) {
33
- setValue(next);
34
- resetAvailability();
35
- if (checkAvailability) {
36
- debouncer.maybeExecute(next);
37
- }
38
- }
39
- const isCheckingAvailability = !!checkAvailability && !!value.trim() && value.trim() !== currentUsername;
40
- return (_jsxs(TextField, { name: name, type: "text", autoComplete: "username", minLength: minUsernameLength, maxLength: maxUsernameLength, isDisabled: isPending, isReadOnly: field.readOnly, value: value, onChange: handleChange, children: [_jsx(Label, { children: field.label }), _jsxs(InputGroup, { variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(InputGroup.Input, { placeholder: field.placeholder, required: field.required }), isCheckingAvailability && (_jsx(InputGroup.Suffix, { "aria-label": availability?.available
41
- ? localization.usernameAvailable
42
- : availability?.available === false
43
- ? localization.usernameTaken
44
- : undefined, className: "px-2", children: availability?.available ? (_jsx(Check, { className: "text-success" })) : availabilityError || availability?.available === false ? (_jsx(Xmark, { className: "text-danger" })) : (_jsx(Spinner, { size: "sm", color: "current" })) }))] }), _jsx(FieldError, {})] }));
45
- }
@@ -1,12 +0,0 @@
1
- import { createAuthPlugin } from "@better-auth-ui/core";
2
- import { apiKeyPlugin as coreApiKeyPlugin } from "@better-auth-ui/core/plugins";
3
- import { ApiKeys } from "../../components/auth/api-key/api-keys";
4
- import { OrganizationApiKeys } from "../../components/auth/api-key/organization-api-keys";
5
- export const apiKeyPlugin = createAuthPlugin(coreApiKeyPlugin.id, (options = {}) => {
6
- const core = coreApiKeyPlugin(options);
7
- return {
8
- ...core,
9
- securityCards: [ApiKeys],
10
- ...(core.organization ? { organizationCards: [OrganizationApiKeys] } : {})
11
- };
12
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,7 +0,0 @@
1
- import { createAuthPlugin } from "@better-auth-ui/core";
2
- import { deleteUserPlugin as coreDeleteUserPlugin } from "@better-auth-ui/core/plugins";
3
- import { DangerZone } from "../../components/auth/delete-user/danger-zone";
4
- export const deleteUserPlugin = createAuthPlugin(coreDeleteUserPlugin.id, (options = {}) => ({
5
- ...coreDeleteUserPlugin(options),
6
- securityCards: [DangerZone]
7
- }));
@@ -1,18 +0,0 @@
1
- import { createAuthPlugin } from "@better-auth-ui/core";
2
- import { magicLinkPlugin as coreMagicLinkPlugin } from "@better-auth-ui/core/plugins";
3
- import { MagicLink } from "../../components/auth/magic-link/magic-link";
4
- import { MagicLinkButton } from "../../components/auth/magic-link/magic-link-button";
5
- export const magicLinkPlugin = createAuthPlugin(coreMagicLinkPlugin.id, (options = {}) => ({
6
- ...coreMagicLinkPlugin(options),
7
- authButtons: [MagicLinkButton],
8
- views: {
9
- auth: { magicLink: MagicLink }
10
- },
11
- // Conditional, not an override: when `emailAndPassword.enabled === false`
12
- // the `<Auth>` router renders this at `/auth/sign-in` instead of the
13
- // disabled password form. With password auth on, the built-in `SignIn`
14
- // still wins.
15
- fallbackViews: {
16
- auth: { signIn: MagicLink }
17
- }
18
- }));
@@ -1,9 +0,0 @@
1
- import { createAuthPlugin } from "@better-auth-ui/core";
2
- import { multiSessionPlugin as coreMultiSessionPlugin } from "@better-auth-ui/core/plugins";
3
- import { ManageAccounts } from "../../components/auth/multi-session/manage-accounts";
4
- import { SwitchAccountSubmenu } from "../../components/auth/multi-session/switch-account-submenu";
5
- export const multiSessionPlugin = createAuthPlugin(coreMultiSessionPlugin.id, (options = {}) => ({
6
- ...coreMultiSessionPlugin(options),
7
- accountCards: [ManageAccounts],
8
- userMenuItems: [SwitchAccountSubmenu]
9
- }));
@@ -1,19 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { createAuthPlugin } from "@better-auth-ui/core";
3
- import { organizationPlugin as coreOrganizationPlugin } from "@better-auth-ui/core/plugins";
4
- import { Briefcase } from "@gravity-ui/icons";
5
- import { OrganizationsSettings } from "../../components/auth/organization/organizations-settings";
6
- export const organizationPlugin = createAuthPlugin(coreOrganizationPlugin.id, (options = {}) => {
7
- const coreOptions = coreOrganizationPlugin(options);
8
- return {
9
- ...coreOptions,
10
- localization: coreOptions.localization,
11
- settingsTabs: [
12
- {
13
- view: "organizations",
14
- label: (_jsxs(_Fragment, { children: [_jsx(Briefcase, { className: "text-muted" }), coreOptions.localization.organizations] })),
15
- component: OrganizationsSettings
16
- }
17
- ]
18
- };
19
- });
@@ -1,9 +0,0 @@
1
- import { createAuthPlugin } from "@better-auth-ui/core";
2
- import { passkeyPlugin as corePasskeyPlugin } from "@better-auth-ui/core/plugins";
3
- import { PasskeyButton } from "../../components/auth/passkey/passkey-button";
4
- import { Passkeys } from "../../components/auth/passkey/passkeys";
5
- export const passkeyPlugin = createAuthPlugin(corePasskeyPlugin.id, (options = {}) => ({
6
- ...corePasskeyPlugin(options),
7
- authButtons: [PasskeyButton],
8
- securityCards: [Passkeys]
9
- }));
@@ -1,26 +0,0 @@
1
- import { createAuthPlugin } from "@better-auth-ui/core";
2
- import { themePlugin as coreThemePlugin } from "@better-auth-ui/core/plugins";
3
- import { Appearance } from "../../components/auth/theme/appearance";
4
- import { ThemeToggleItem } from "../../components/auth/theme/theme-toggle-item";
5
- export const themePlugin = createAuthPlugin(coreThemePlugin.id, ({ useTheme, ...rest }) => {
6
- // No-op `setTheme` baseline keeps core's required option satisfied on the
7
- // hook branch (where the consumer doesn't pass a setter); on the static
8
- // branch the spread overrides it with the consumer's real setter.
9
- const base = coreThemePlugin({ setTheme: () => { }, ...rest });
10
- return {
11
- ...base,
12
- // Slot components always call `plugin.useTheme()` — invoking the hook
13
- // inside their render keeps it in scope of any `<ThemeProvider>` the
14
- // consumer mounts. On the static branch the factory re-runs on every
15
- // parent render, so the synthesized closure stays in sync with the
16
- // consumer's `theme` state.
17
- useTheme: useTheme ??
18
- (() => ({
19
- theme: base.theme,
20
- setTheme: base.setTheme,
21
- themes: base.themes
22
- })),
23
- userMenuItems: [ThemeToggleItem],
24
- accountCards: [Appearance]
25
- };
26
- });
@@ -1,19 +0,0 @@
1
- import { createAuthPlugin } from "@better-auth-ui/core";
2
- import { usernamePlugin as coreUsernamePlugin } from "@better-auth-ui/core/plugins";
3
- import { SignInUsername } from "../../components/auth/username/sign-in-username";
4
- import { UsernameField } from "../../components/auth/username/username-field";
5
- export const usernamePlugin = createAuthPlugin(coreUsernamePlugin.id, (options = {}) => {
6
- const core = coreUsernamePlugin(options);
7
- return {
8
- ...core,
9
- additionalFields: core.additionalFields?.map((field) => field.name === "username"
10
- ? {
11
- ...field,
12
- render: UsernameField
13
- }
14
- : field),
15
- views: {
16
- auth: { signIn: SignInUsername }
17
- }
18
- };
19
- });