@better-auth-ui/heroui 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/components/account/account.d.ts +18 -0
  2. package/dist/components/account/account.js +24 -0
  3. package/dist/components/account/settings/account-settings.d.ts +5 -0
  4. package/dist/components/account/settings/account-settings.js +6 -0
  5. package/dist/components/account/settings/settings.d.ts +5 -0
  6. package/dist/components/account/settings/settings.js +5 -0
  7. package/dist/components/account/settings/user-profile.d.ts +5 -0
  8. package/dist/components/account/settings/user-profile.js +20 -0
  9. package/dist/components/auth/auth-provider.d.ts +9 -0
  10. package/dist/components/auth/auth-provider.js +12 -0
  11. package/dist/components/auth/auth.d.ts +23 -0
  12. package/dist/components/auth/auth.js +42 -0
  13. package/dist/components/auth/field-separator.d.ts +4 -0
  14. package/dist/components/auth/field-separator.js +5 -0
  15. package/dist/components/auth/forgot-password.d.ts +15 -0
  16. package/dist/components/auth/forgot-password.js +28 -0
  17. package/dist/components/auth/magic-link-button.d.ts +13 -0
  18. package/dist/components/auth/magic-link-button.js +19 -0
  19. package/dist/components/auth/magic-link.d.ts +17 -0
  20. package/dist/components/auth/magic-link.js +43 -0
  21. package/dist/components/auth/provider-buttons.d.ts +19 -0
  22. package/dist/components/auth/provider-buttons.js +34 -0
  23. package/dist/components/auth/reset-password.d.ts +11 -0
  24. package/dist/components/auth/reset-password.js +58 -0
  25. package/dist/components/auth/sign-in.d.ts +14 -0
  26. package/dist/components/auth/sign-in.js +64 -0
  27. package/dist/components/auth/sign-out.d.ts +9 -0
  28. package/dist/components/auth/sign-out.js +33 -0
  29. package/dist/components/auth/sign-up.d.ts +19 -0
  30. package/dist/components/auth/sign-up.js +71 -0
  31. package/dist/components/email/email-changed-email.d.ts +3 -0
  32. package/dist/components/email/email-changed-email.js +23 -0
  33. package/dist/components/email/email-verification-email.d.ts +3 -0
  34. package/dist/components/email/email-verification-email.js +23 -0
  35. package/dist/components/email/magic-link-email.d.ts +3 -0
  36. package/dist/components/email/magic-link-email.js +23 -0
  37. package/dist/components/email/new-device-email.d.ts +3 -0
  38. package/dist/components/email/new-device-email.js +23 -0
  39. package/dist/components/email/otp-email.d.ts +3 -0
  40. package/dist/components/email/otp-email.js +22 -0
  41. package/dist/components/email/password-changed-email.d.ts +3 -0
  42. package/dist/components/email/password-changed-email.js +23 -0
  43. package/dist/components/email/reset-password-email.d.ts +3 -0
  44. package/dist/components/email/reset-password-email.js +23 -0
  45. package/dist/components/settings/account/account-settings.d.ts +16 -0
  46. package/dist/components/settings/account/account-settings.js +20 -0
  47. package/dist/components/settings/account/accounts.d.ts +14 -0
  48. package/dist/components/settings/account/accounts.js +26 -0
  49. package/dist/components/settings/account/appearance.d.ts +16 -0
  50. package/dist/components/settings/account/appearance.js +28 -0
  51. package/dist/components/settings/account/change-avatar.d.ts +4 -0
  52. package/dist/components/settings/account/change-avatar.js +56 -0
  53. package/dist/components/settings/account/change-email.d.ts +15 -0
  54. package/dist/components/settings/account/change-email.js +29 -0
  55. package/dist/components/settings/account/manage-account.d.ts +19 -0
  56. package/dist/components/settings/account/manage-account.js +32 -0
  57. package/dist/components/settings/account/manage-accounts.d.ts +14 -0
  58. package/dist/components/settings/account/manage-accounts.js +37 -0
  59. package/dist/components/settings/account/theme-selector.d.ts +16 -0
  60. package/dist/components/settings/account/theme-selector.js +33 -0
  61. package/dist/components/settings/account/theme-settings.d.ts +16 -0
  62. package/dist/components/settings/account/theme-settings.js +31 -0
  63. package/dist/components/settings/account/user-profile.d.ts +11 -0
  64. package/dist/components/settings/account/user-profile.js +23 -0
  65. package/dist/components/settings/account-settings.d.ts +5 -0
  66. package/dist/components/settings/account-settings.js +6 -0
  67. package/dist/components/settings/account.d.ts +18 -0
  68. package/dist/components/settings/account.js +24 -0
  69. package/dist/components/settings/security/active-session.d.ts +14 -0
  70. package/dist/components/settings/security/active-session.js +53 -0
  71. package/dist/components/settings/security/active-sessions.d.ts +14 -0
  72. package/dist/components/settings/security/active-sessions.js +28 -0
  73. package/dist/components/settings/security/change-password.d.ts +15 -0
  74. package/dist/components/settings/security/change-password.js +79 -0
  75. package/dist/components/settings/security/connected-account.d.ts +16 -0
  76. package/dist/components/settings/security/connected-account.js +46 -0
  77. package/dist/components/settings/security/connected-accounts.d.ts +15 -0
  78. package/dist/components/settings/security/connected-accounts.js +31 -0
  79. package/dist/components/settings/security/danger-zone.d.ts +11 -0
  80. package/dist/components/settings/security/danger-zone.js +12 -0
  81. package/dist/components/settings/security/delete-user.d.ts +9 -0
  82. package/dist/components/settings/security/delete-user.js +47 -0
  83. package/dist/components/settings/security/linked-account.d.ts +16 -0
  84. package/dist/components/settings/security/linked-account.js +48 -0
  85. package/dist/components/settings/security/linked-accounts.d.ts +15 -0
  86. package/dist/components/settings/security/linked-accounts.js +42 -0
  87. package/dist/components/settings/security/security-settings.d.ts +17 -0
  88. package/dist/components/settings/security/security-settings.js +21 -0
  89. package/dist/components/settings/security/sessions.d.ts +14 -0
  90. package/dist/components/settings/security/sessions.js +40 -0
  91. package/dist/components/settings/settings/account-settings.d.ts +5 -0
  92. package/dist/components/settings/settings/account-settings.js +6 -0
  93. package/dist/components/settings/settings/user-profile.d.ts +5 -0
  94. package/dist/components/settings/settings/user-profile.js +20 -0
  95. package/dist/components/settings/settings.d.ts +24 -0
  96. package/dist/components/settings/settings.js +28 -0
  97. package/dist/components/settings/user-profile.d.ts +5 -0
  98. package/dist/components/settings/user-profile.js +20 -0
  99. package/dist/components/user/switch-account-item.d.ts +16 -0
  100. package/dist/components/user/switch-account-item.js +16 -0
  101. package/dist/components/user/switch-account-menu.d.ts +10 -0
  102. package/dist/components/user/switch-account-menu.js +29 -0
  103. package/dist/components/user/switch-account-popover.d.ts +10 -0
  104. package/dist/components/user/switch-account-popover.js +25 -0
  105. package/dist/components/user/switch-account-submenu.d.ts +10 -0
  106. package/dist/components/user/switch-account-submenu.js +25 -0
  107. package/dist/components/user/user-avatar.d.ts +25 -0
  108. package/dist/components/user/user-avatar.js +31 -0
  109. package/dist/components/user/user-button.d.ts +25 -0
  110. package/dist/components/user/user-button.js +24 -0
  111. package/dist/components/user/user-view.d.ts +22 -0
  112. package/dist/components/user/user-view.js +24 -0
  113. package/dist/core.d.ts +1 -0
  114. package/dist/core.js +1 -0
  115. package/dist/hooks/use-auth.d.ts +2 -0
  116. package/dist/hooks/use-auth.js +8 -0
  117. package/dist/hooks/use-redirect-to.d.ts +8 -0
  118. package/dist/hooks/use-redirect-to.js +19 -0
  119. package/dist/hooks/use-sign-in.d.ts +8 -0
  120. package/dist/hooks/use-sign-in.js +53 -0
  121. package/dist/index.d.ts +33 -0
  122. package/dist/index.js +34 -0
  123. package/dist/lib/utils.d.ts +2 -0
  124. package/dist/lib/utils.js +5 -0
  125. package/dist/react-exports.d.ts +1 -0
  126. package/dist/react-exports.js +1 -0
  127. package/dist/server.d.ts +1 -0
  128. package/dist/server.js +1 -0
  129. package/dist/src/components/auth/auth-provider.d.ts +8 -0
  130. package/dist/src/components/auth/auth-provider.js +13 -0
  131. package/dist/src/components/auth/auth.d.ts +22 -0
  132. package/dist/src/components/auth/auth.js +42 -0
  133. package/dist/src/components/auth/field-separator.d.ts +4 -0
  134. package/dist/src/components/auth/field-separator.js +5 -0
  135. package/dist/src/components/auth/forgot-password.d.ts +15 -0
  136. package/dist/src/components/auth/forgot-password.js +29 -0
  137. package/dist/src/components/auth/magic-link-button.d.ts +13 -0
  138. package/dist/src/components/auth/magic-link-button.js +20 -0
  139. package/dist/src/components/auth/magic-link.d.ts +17 -0
  140. package/dist/src/components/auth/magic-link.js +37 -0
  141. package/dist/src/components/auth/provider-buttons.d.ts +19 -0
  142. package/dist/src/components/auth/provider-buttons.js +35 -0
  143. package/dist/src/components/auth/reset-password.d.ts +11 -0
  144. package/dist/src/components/auth/reset-password.js +59 -0
  145. package/dist/src/components/auth/sign-in.d.ts +14 -0
  146. package/dist/src/components/auth/sign-in.js +58 -0
  147. package/dist/src/components/auth/sign-out.d.ts +11 -0
  148. package/dist/src/components/auth/sign-out.js +34 -0
  149. package/dist/src/components/auth/sign-up.d.ts +19 -0
  150. package/dist/src/components/auth/sign-up.js +65 -0
  151. package/dist/src/components/email/email-changed-email.d.ts +3 -0
  152. package/dist/src/components/email/email-changed-email.js +23 -0
  153. package/dist/src/components/email/email-verification-email.d.ts +3 -0
  154. package/dist/src/components/email/email-verification-email.js +23 -0
  155. package/dist/src/components/email/magic-link-email.d.ts +3 -0
  156. package/dist/src/components/email/magic-link-email.js +23 -0
  157. package/dist/src/components/email/new-device-email.d.ts +3 -0
  158. package/dist/src/components/email/new-device-email.js +23 -0
  159. package/dist/src/components/email/otp-email.d.ts +3 -0
  160. package/dist/src/components/email/otp-email.js +22 -0
  161. package/dist/src/components/email/password-changed-email.d.ts +3 -0
  162. package/dist/src/components/email/password-changed-email.js +23 -0
  163. package/dist/src/components/email/reset-password-email.d.ts +3 -0
  164. package/dist/src/components/email/reset-password-email.js +23 -0
  165. package/dist/src/components/settings/account/account-settings.d.ts +13 -0
  166. package/dist/src/components/settings/account/account-settings.js +19 -0
  167. package/dist/src/components/settings/account/appearance.d.ts +16 -0
  168. package/dist/src/components/settings/account/appearance.js +28 -0
  169. package/dist/src/components/settings/account/change-email.d.ts +15 -0
  170. package/dist/src/components/settings/account/change-email.js +35 -0
  171. package/dist/src/components/settings/account/manage-account.d.ts +19 -0
  172. package/dist/src/components/settings/account/manage-account.js +27 -0
  173. package/dist/src/components/settings/account/manage-accounts.d.ts +14 -0
  174. package/dist/src/components/settings/account/manage-accounts.js +27 -0
  175. package/dist/src/components/settings/account/user-profile.d.ts +13 -0
  176. package/dist/src/components/settings/account/user-profile.js +29 -0
  177. package/dist/src/components/settings/security/active-session.d.ts +14 -0
  178. package/dist/src/components/settings/security/active-session.js +35 -0
  179. package/dist/src/components/settings/security/active-sessions.d.ts +14 -0
  180. package/dist/src/components/settings/security/active-sessions.js +29 -0
  181. package/dist/src/components/settings/security/change-password.d.ts +15 -0
  182. package/dist/src/components/settings/security/change-password.js +57 -0
  183. package/dist/src/components/settings/security/linked-account.d.ts +16 -0
  184. package/dist/src/components/settings/security/linked-account.js +46 -0
  185. package/dist/src/components/settings/security/linked-accounts.d.ts +15 -0
  186. package/dist/src/components/settings/security/linked-accounts.js +31 -0
  187. package/dist/src/components/settings/security/security-settings.d.ts +15 -0
  188. package/dist/src/components/settings/security/security-settings.js +18 -0
  189. package/dist/src/components/settings/settings.d.ts +29 -0
  190. package/dist/src/components/settings/settings.js +39 -0
  191. package/dist/src/components/user/switch-account-item.d.ts +16 -0
  192. package/dist/src/components/user/switch-account-item.js +14 -0
  193. package/dist/src/components/user/switch-account-menu.d.ts +10 -0
  194. package/dist/src/components/user/switch-account-menu.js +23 -0
  195. package/dist/src/components/user/user-avatar.d.ts +24 -0
  196. package/dist/src/components/user/user-avatar.js +31 -0
  197. package/dist/src/components/user/user-button.d.ts +21 -0
  198. package/dist/src/components/user/user-button.js +24 -0
  199. package/dist/src/components/user/user-view.d.ts +21 -0
  200. package/dist/src/components/user/user-view.js +24 -0
  201. package/dist/src/core.d.ts +1 -0
  202. package/dist/src/core.js +1 -0
  203. package/dist/src/index.d.ts +31 -0
  204. package/dist/src/index.js +32 -0
  205. package/dist/src/lib/utils.d.ts +2 -0
  206. package/dist/src/lib/utils.js +5 -0
  207. package/dist/src/react-exports.d.ts +1 -0
  208. package/dist/src/react-exports.js +1 -0
  209. package/dist/styles.css +1 -0
  210. package/package.json +63 -0
  211. package/src/components/auth/auth-provider.tsx +27 -0
  212. package/src/components/auth/auth.tsx +87 -0
  213. package/src/components/auth/field-separator.tsx +14 -0
  214. package/src/components/auth/forgot-password.tsx +107 -0
  215. package/src/components/auth/magic-link-button.tsx +43 -0
  216. package/src/components/auth/magic-link.tsx +181 -0
  217. package/src/components/auth/provider-buttons.tsx +85 -0
  218. package/src/components/auth/reset-password.tsx +210 -0
  219. package/src/components/auth/sign-in.tsx +260 -0
  220. package/src/components/auth/sign-out.tsx +44 -0
  221. package/src/components/auth/sign-up.tsx +314 -0
  222. package/src/components/email/email-changed-email.tsx +38 -0
  223. package/src/components/email/email-verification-email.tsx +38 -0
  224. package/src/components/email/magic-link-email.tsx +38 -0
  225. package/src/components/email/new-device-email.tsx +38 -0
  226. package/src/components/email/otp-email.tsx +33 -0
  227. package/src/components/email/password-changed-email.tsx +38 -0
  228. package/src/components/email/reset-password-email.tsx +38 -0
  229. package/src/components/settings/account/account-settings.tsx +48 -0
  230. package/src/components/settings/account/appearance.tsx +157 -0
  231. package/src/components/settings/account/change-avatar.tsx +142 -0
  232. package/src/components/settings/account/change-email.tsx +111 -0
  233. package/src/components/settings/account/manage-account.tsx +123 -0
  234. package/src/components/settings/account/manage-accounts.tsx +78 -0
  235. package/src/components/settings/account/user-profile.tsx +103 -0
  236. package/src/components/settings/security/active-session.tsx +123 -0
  237. package/src/components/settings/security/active-sessions.tsx +77 -0
  238. package/src/components/settings/security/change-password.tsx +335 -0
  239. package/src/components/settings/security/danger-zone.tsx +32 -0
  240. package/src/components/settings/security/delete-user.tsx +187 -0
  241. package/src/components/settings/security/linked-account.tsx +136 -0
  242. package/src/components/settings/security/linked-accounts.tsx +98 -0
  243. package/src/components/settings/security/security-settings.tsx +43 -0
  244. package/src/components/settings/settings.tsx +96 -0
  245. package/src/components/user/switch-account-item.tsx +39 -0
  246. package/src/components/user/switch-account-menu.tsx +64 -0
  247. package/src/components/user/user-avatar.tsx +88 -0
  248. package/src/components/user/user-button.tsx +221 -0
  249. package/src/components/user/user-view.tsx +74 -0
  250. package/src/core.ts +1 -0
  251. package/src/index.tsx +35 -0
  252. package/src/react-exports.ts +1 -0
  253. package/src/styles.css +1 -0
@@ -0,0 +1,18 @@
1
+ import type { AnyAuthConfig } from "@better-auth-ui/react";
2
+ import type { AccountView } from "@better-auth-ui/react/core";
3
+ import { type TabsProps } from "@heroui/react";
4
+ export type AccountProps = AnyAuthConfig & {
5
+ className?: string;
6
+ path?: string;
7
+ view?: AccountView;
8
+ hideNav?: boolean;
9
+ };
10
+ /**
11
+ * Selects and renders the appropriate account view component.
12
+ *
13
+ * @param path - Route path used to resolve an account view when `view` is not provided
14
+ * @param view - Explicit auth view to render (e.g., "userProfile")
15
+ * @param hideNav - Hide the navigation tabs
16
+ */
17
+ export declare function Account({ className, view, path, hideNav, ...config }: AccountProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function ResponsiveTabs({ className, ...props }: TabsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ShieldCheckIcon, UserCircleIcon } from "@heroicons/react/24/outline";
3
+ import { cn, Tabs } from "@heroui/react";
4
+ import { useAuth } from "../../hooks/use-auth";
5
+ import { AccountSettings } from "./settings/account-settings";
6
+ /**
7
+ * Selects and renders the appropriate account view component.
8
+ *
9
+ * @param path - Route path used to resolve an account view when `view` is not provided
10
+ * @param view - Explicit auth view to render (e.g., "userProfile")
11
+ * @param hideNav - Hide the navigation tabs
12
+ */
13
+ export function Account({ className, view, path, hideNav, ...config }) {
14
+ const { basePaths, viewPaths } = useAuth(config);
15
+ if (!view && !path) {
16
+ throw new Error("[Better Auth UI] Either `view` or `path` must be provided");
17
+ }
18
+ const accountPathViews = Object.fromEntries(Object.entries(viewPaths.account).map(([k, v]) => [v, k]));
19
+ const currentView = view || (path ? accountPathViews[path] : undefined);
20
+ return (_jsxs(ResponsiveTabs, { selectedKey: currentView, className: "w-full", children: [_jsx(Tabs.ListContainer, { children: _jsxs(Tabs.List, { "aria-label": "Account", className: cn("overflow-auto md:w-64 lg:w-72 xl:w-80", hideNav && "hidden"), children: [_jsxs(Tabs.Tab, { id: "settings", href: `${basePaths.account}/${viewPaths.account.settings}`, className: "gap-1.5", children: [_jsx(UserCircleIcon, { className: "size-4" }), "Account", _jsx(Tabs.Indicator, {})] }), _jsxs(Tabs.Tab, { id: "security", href: `${basePaths.account}/${viewPaths.account.security}`, className: "gap-1.5", children: [_jsx(ShieldCheckIcon, { className: "size-4" }), "Security", _jsx(Tabs.Indicator, {})] })] }) }), _jsx(Tabs.Panel, { id: "settings", className: "md:pt-0 px-0 md:px-2", children: _jsx(AccountSettings, {}) }), _jsx(Tabs.Panel, { id: "security", children: "Security" })] }));
21
+ }
22
+ export function ResponsiveTabs({ className, ...props }) {
23
+ return (_jsxs(_Fragment, { children: [_jsx(Tabs, { className: cn("md:hidden", className), orientation: "horizontal", ...props }), _jsx(Tabs, { className: cn("hidden md:flex", className), orientation: "vertical", ...props })] }));
24
+ }
@@ -0,0 +1,5 @@
1
+ import type { AnyAuthConfig } from "@better-auth-ui/react";
2
+ export type AccountSettingsProps = AnyAuthConfig & {
3
+ className?: string;
4
+ };
5
+ export declare function AccountSettings({ className, ...config }: AccountSettingsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../../lib/utils";
3
+ import { UserProfile } from "./user-profile";
4
+ export function AccountSettings({ className, ...config }) {
5
+ return (_jsx("div", { className: cn("w-full flex flex-col gap-4 md:gap-6", className), children: _jsx(UserProfile, { ...config }) }));
6
+ }
@@ -0,0 +1,5 @@
1
+ import type { AnyAuthConfig } from "@better-auth-ui/react";
2
+ export type SettingsProps = AnyAuthConfig & {
3
+ className?: string;
4
+ };
5
+ export declare function Settings({ className, ...config }: SettingsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { UserProfile } from "./user-profile";
3
+ export function Settings({ className, ...config }) {
4
+ return (_jsx("div", { className: className, children: _jsx(UserProfile, { ...config }) }));
5
+ }
@@ -0,0 +1,5 @@
1
+ import { type AnyAuthConfig } from "@better-auth-ui/react";
2
+ export type UserProfileProps = AnyAuthConfig & {
3
+ className?: string;
4
+ };
5
+ export declare function UserProfile({ className, ...config }: UserProfileProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useUpdateUser } from "@better-auth-ui/react";
3
+ import { CheckIcon, PencilIcon } from "@heroicons/react/24/outline";
4
+ import { Button, Card, Description, FieldError, Fieldset, Form, Input, Label, Skeleton, Spinner, TextField } from "@heroui/react";
5
+ import { UserAvatar } from "../../user/user-avatar";
6
+ export function UserProfile({ className, ...config }) {
7
+ const { authClient, localization } = useAuth(config);
8
+ const { data: sessionData, isPending: isSessionPending } = authClient.useSession();
9
+ const [state, formAction, isPending] = useUpdateUser(config);
10
+ if (isSessionPending) {
11
+ return _jsx(UserProfileSkeleton, {});
12
+ }
13
+ return (_jsx(Card, { className: "p-4 md:p-6", children: _jsx(Form, { action: formAction, children: _jsxs(Fieldset, { className: "w-full gap-4 md:gap-6", children: [_jsx(Fieldset.Legend, { className: "text-xl", children: localization.account.profile }), _jsx(Description, {}), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Button, { isIconOnly: true, variant: "ghost", className: "p-0 h-auto w-auto rounded-full", children: [_jsx(UserAvatar, { size: "lg" }), _jsx("span", { className: "absolute right-0 bottom-0 size-3.5 rounded-full bg-background ring-2 ring-surface-quaternary flex items-center justify-center", children: _jsx(PencilIcon, { className: "size-2 text-muted" }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("p", { className: "text-sm font-medium", children: sessionData?.user?.displayUsername ||
14
+ sessionData?.user?.name ||
15
+ sessionData?.user?.email }), (sessionData?.user?.displayUsername ||
16
+ sessionData?.user?.name) && (_jsx("p", { className: "text-muted text-xs leading-none", children: sessionData?.user?.email }))] })] }), _jsx(Fieldset.Group, { children: _jsxs(TextField, { name: "name", defaultValue: sessionData?.user?.name || state.name, isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.name }), _jsx(Input, { placeholder: localization.auth.name }), _jsx(FieldError, {})] }) }), _jsx(Fieldset.Actions, { children: _jsxs(Button, { type: "submit", isPending: isPending, children: [isPending ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(CheckIcon, { className: "size-4" })), localization.account.saveChanges] }) })] }) }) }));
17
+ }
18
+ function UserProfileSkeleton() {
19
+ return (_jsxs(Card, { className: "p-4 md:p-6 gap-4 md:gap-6", children: [_jsx(Skeleton, { className: "h-7 w-16 rounded-xl" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "size-12 rounded-full" }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Skeleton, { className: "h-4 mt-0.5 w-24 rounded-lg" }), _jsx(Skeleton, { className: "h-3 mt-0.5 w-32 rounded-lg" })] })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-12 my-0.5 rounded-lg" }), _jsx(Skeleton, { className: "h-9 w-full rounded-xl" })] }), _jsx(Skeleton, { className: "h-10 md:h-9 w-36 rounded-full mt-1" })] }));
20
+ }
@@ -0,0 +1,9 @@
1
+ import { type AuthProviderProps as AuthProviderPropsPrimitive } from "@better-auth-ui/react";
2
+ export type AuthProviderProps = Omit<AuthProviderPropsPrimitive, "Link">;
3
+ /**
4
+ * Provides an authentication context by rendering an auth provider with the sonner toast handler injected, forwarding remaining configuration and rendering `children` inside it.
5
+ *
6
+ * @param children - React nodes to render inside the authentication provider
7
+ * @returns A React element that renders an authentication provider configured with the provided props and toast handler
8
+ */
9
+ export declare function AuthProvider({ children, navigate, ...config }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { AuthProvider as AuthProviderPrimitive } from "@better-auth-ui/react";
3
+ import { RouterProvider } from "@heroui/react";
4
+ /**
5
+ * Provides an authentication context by rendering an auth provider with the sonner toast handler injected, forwarding remaining configuration and rendering `children` inside it.
6
+ *
7
+ * @param children - React nodes to render inside the authentication provider
8
+ * @returns A React element that renders an authentication provider configured with the provided props and toast handler
9
+ */
10
+ export function AuthProvider({ children, navigate, ...config }) {
11
+ return (_jsx(AuthProviderPrimitive, { navigate: navigate, ...config, children: _jsx(RouterProvider, { navigate: (path) => navigate({ to: path }), children: children }) }));
12
+ }
@@ -0,0 +1,23 @@
1
+ import type { AuthView } from "@better-auth-ui/react/core";
2
+ import type { CardProps } from "@heroui/react";
3
+ import type { SocialLayout } from "./provider-buttons";
4
+ export type AuthProps = {
5
+ className?: string;
6
+ path?: string;
7
+ socialLayout?: SocialLayout;
8
+ socialPosition?: "top" | "bottom";
9
+ variant?: CardProps["variant"];
10
+ /** @remarks `AuthView` */
11
+ view?: AuthView;
12
+ };
13
+ /**
14
+ * Render the appropriate authentication view based on the provided `view` or `path`.
15
+ *
16
+ * @param path - Route path used to resolve an auth view when `view` is not provided
17
+ * @param socialLayout - Social layout to apply to sign-in/sign-up/magic-link views
18
+ * @param socialPosition - Position for social buttons ("top" or "bottom")
19
+ * @param variant - Variant to apply to the card
20
+ * @param view - Explicit auth view to render (e.g., "signIn", "signUp")
21
+ * @returns The React element for the resolved authentication view
22
+ */
23
+ export declare function Auth({ path, socialLayout, socialPosition, view, ...props }: AuthProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useAuth } from "@better-auth-ui/react";
3
+ import { ForgotPassword } from "./forgot-password";
4
+ import { MagicLink } from "./magic-link";
5
+ import { ResetPassword } from "./reset-password";
6
+ import { SignIn } from "./sign-in";
7
+ import { SignOut } from "./sign-out";
8
+ import { SignUp } from "./sign-up";
9
+ /**
10
+ * Render the appropriate authentication view based on the provided `view` or `path`.
11
+ *
12
+ * @param path - Route path used to resolve an auth view when `view` is not provided
13
+ * @param socialLayout - Social layout to apply to sign-in/sign-up/magic-link views
14
+ * @param socialPosition - Position for social buttons ("top" or "bottom")
15
+ * @param variant - Variant to apply to the card
16
+ * @param view - Explicit auth view to render (e.g., "signIn", "signUp")
17
+ * @returns The React element for the resolved authentication view
18
+ */
19
+ export function Auth({ path, socialLayout, socialPosition, view, ...props }) {
20
+ const { viewPaths } = useAuth();
21
+ if (!view && !path) {
22
+ throw new Error("[Better Auth UI] Either `view` or `path` must be provided");
23
+ }
24
+ const authPathViews = Object.fromEntries(Object.entries(viewPaths.auth).map(([k, v]) => [v, k]));
25
+ const currentView = view || (path ? authPathViews[path] : undefined);
26
+ switch (currentView) {
27
+ case "signIn":
28
+ return (_jsx(SignIn, { socialLayout: socialLayout, socialPosition: socialPosition, ...props }));
29
+ case "signUp":
30
+ return (_jsx(SignUp, { socialLayout: socialLayout, socialPosition: socialPosition, ...props }));
31
+ case "magicLink":
32
+ return (_jsx(MagicLink, { socialLayout: socialLayout, socialPosition: socialPosition, ...props }));
33
+ case "forgotPassword":
34
+ return _jsx(ForgotPassword, { ...props });
35
+ case "resetPassword":
36
+ return _jsx(ResetPassword, { ...props });
37
+ case "signOut":
38
+ return _jsx(SignOut, { ...props });
39
+ default:
40
+ throw new Error(`[Better Auth UI] Valid views are: ${Object.keys(viewPaths.auth).join(", ")}`);
41
+ }
42
+ }
@@ -0,0 +1,4 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function FieldSeparator({ children }: {
3
+ children: ReactNode;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Separator } from "@heroui/react";
3
+ export function FieldSeparator({ children }) {
4
+ return (_jsxs("div", { className: "flex items-center gap-4", children: [_jsx(Separator, { className: "flex-1" }), _jsx("p", { className: "text-xs text-muted shrink-0", children: children }), _jsx(Separator, { className: "flex-1" })] }));
5
+ }
@@ -0,0 +1,15 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type ForgotPasswordProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Render a card-based "Forgot Password" form that sends a password-reset email.
8
+ *
9
+ * The form displays an email input, submit button, and a link back to sign-in.
10
+ * Toasts are displayed on success or error via the `useForgotPassword` hook.
11
+ *
12
+ * @param className - Optional additional CSS class names applied to the card
13
+ * @returns The forgot-password form UI as a JSX element
14
+ */
15
+ export declare function ForgotPassword({ className, variant, ...props }: ForgotPasswordProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useRequestPasswordReset } from "@better-auth-ui/react";
3
+ import { Button, Card, cn, Description, FieldError, Form, Input, Label, Link, Spinner, TextField, toast } from "@heroui/react";
4
+ /**
5
+ * Render a card-based "Forgot Password" form that sends a password-reset email.
6
+ *
7
+ * The form displays an email input, submit button, and a link back to sign-in.
8
+ * Toasts are displayed on success or error via the `useForgotPassword` hook.
9
+ *
10
+ * @param className - Optional additional CSS class names applied to the card
11
+ * @returns The forgot-password form UI as a JSX element
12
+ */
13
+ export function ForgotPassword({ className, variant, ...props }) {
14
+ const { basePaths, localization, viewPaths, navigate } = useAuth();
15
+ const { mutate: requestPasswordReset, isPending } = useRequestPasswordReset({
16
+ onError: (error) => toast.danger(error.error?.message || error.message),
17
+ onSuccess: () => {
18
+ toast.success(localization.auth.passwordResetEmailSent);
19
+ navigate({ to: `${basePaths.auth}/${viewPaths.auth.signIn}` });
20
+ }
21
+ });
22
+ function handleSubmit(e) {
23
+ e.preventDefault();
24
+ const formData = new FormData(e.currentTarget);
25
+ requestPasswordReset({ email: formData.get("email") });
26
+ }
27
+ return (_jsxs(Card, { className: cn("w-full max-w-sm p-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.forgotPassword }) }), _jsx(Card.Content, { children: _jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { name: "email", type: "email", autoComplete: "email", isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { placeholder: localization.auth.emailPlaceholder, required: true, variant: variant === "transparent" ? "primary" : "secondary" }), _jsx(FieldError, {})] }), _jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.sendResetLink] })] }) }), _jsx(Card.Footer, { className: "flex-col", children: _jsxs(Description, { className: "text-sm", children: [localization.auth.rememberYourPassword, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signIn}`, className: "text-accent decoration-accent no-underline hover:underline", children: localization.auth.signIn })] }) })] }));
28
+ }
@@ -0,0 +1,13 @@
1
+ import type { AuthView } from "@better-auth-ui/core";
2
+ export type MagicLinkButtonProps = {
3
+ isPending: boolean;
4
+ view?: AuthView;
5
+ };
6
+ /**
7
+ * Renders a full-width tertiary link-style button that navigates to either the magic-link or sign-in route.
8
+ *
9
+ * @param isPending - If true, applies disabled styling and prevents interaction
10
+ * @param view - Current auth view; when `"magicLink"`, the button targets the sign-in/password variant
11
+ * @returns The rendered Link element with the appropriate href, icon, and label
12
+ */
13
+ export declare function MagicLinkButton({ isPending, view }: MagicLinkButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth } from "@better-auth-ui/react";
3
+ import { Envelope, Lock } from "@gravity-ui/icons";
4
+ import { cn, Link } from "@heroui/react";
5
+ import { buttonVariants } from "@heroui/styles";
6
+ /**
7
+ * Renders a full-width tertiary link-style button that navigates to either the magic-link or sign-in route.
8
+ *
9
+ * @param isPending - If true, applies disabled styling and prevents interaction
10
+ * @param view - Current auth view; when `"magicLink"`, the button targets the sign-in/password variant
11
+ * @returns The rendered Link element with the appropriate href, icon, and label
12
+ */
13
+ export function MagicLinkButton({ isPending, view }) {
14
+ const { basePaths, viewPaths, localization } = useAuth();
15
+ const isMagicLinkView = view === "magicLink";
16
+ return (_jsxs(Link, { href: `${basePaths.auth}/${isMagicLinkView ? viewPaths.auth.signIn : viewPaths.auth.magicLink}`, className: cn(buttonVariants({ variant: "tertiary" }), "w-full gap-2", isPending && "status-disabled pointer-events-none"), children: [isMagicLinkView ? _jsx(Lock, {}) : _jsx(Envelope, {}), localization.auth.continueWith.replace("{{provider}}", isMagicLinkView
17
+ ? localization.auth.password
18
+ : localization.auth.magicLink)] }));
19
+ }
@@ -0,0 +1,17 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ import { type SocialLayout } from "./provider-buttons";
3
+ export type MagicLinkProps = {
4
+ className?: string;
5
+ socialLayout?: SocialLayout;
6
+ socialPosition?: "top" | "bottom";
7
+ variant?: CardProps["variant"];
8
+ };
9
+ /**
10
+ * Render a card-based sign-in form that sends an email magic link and optionally shows social provider buttons.
11
+ *
12
+ * @param className - Additional CSS class names applied to the card container
13
+ * @param socialLayout - Layout style for social provider buttons
14
+ * @param socialPosition - Position of social provider buttons; `"top"` or `"bottom"`. Defaults to `"bottom"`.
15
+ * @returns The magic-link sign-in UI as a JSX element
16
+ */
17
+ export declare function MagicLink({ className, socialLayout, socialPosition, variant, ...props }: MagicLinkProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useSignInMagicLink, useSignInSocial } from "@better-auth-ui/react";
3
+ import { Button, Card, cn, Description, FieldError, Form, Input, Label, Link, Spinner, TextField, toast } from "@heroui/react";
4
+ import { useState } from "react";
5
+ import { FieldSeparator } from "./field-separator";
6
+ import { MagicLinkButton } from "./magic-link-button";
7
+ import { ProviderButtons } from "./provider-buttons";
8
+ /**
9
+ * Render a card-based sign-in form that sends an email magic link and optionally shows social provider buttons.
10
+ *
11
+ * @param className - Additional CSS class names applied to the card container
12
+ * @param socialLayout - Layout style for social provider buttons
13
+ * @param socialPosition - Position of social provider buttons; `"top"` or `"bottom"`. Defaults to `"bottom"`.
14
+ * @returns The magic-link sign-in UI as a JSX element
15
+ */
16
+ export function MagicLink({ className, socialLayout, socialPosition = "bottom", variant, ...props }) {
17
+ const { basePaths, baseURL, localization, redirectTo, socialProviders, viewPaths } = useAuth();
18
+ const [email, setEmail] = useState("");
19
+ const { mutate: signInMagicLink, isPending: magicLinkPending } = useSignInMagicLink({
20
+ onError: (error) => toast.danger(error.error?.message || error.message),
21
+ onSuccess: () => {
22
+ setEmail("");
23
+ toast.success(localization.auth.magicLinkSent);
24
+ }
25
+ });
26
+ const [socialRedirecting, setSocialRedirecting] = useState(false);
27
+ const { mutate: signInSocial, isPending: socialPending } = useSignInSocial({
28
+ onError: (error) => toast.danger(error.error?.message || error.message),
29
+ onSuccess: () => {
30
+ setSocialRedirecting(true);
31
+ setTimeout(() => {
32
+ setSocialRedirecting(false);
33
+ }, 5000);
34
+ }
35
+ });
36
+ const isPending = magicLinkPending || socialPending || socialRedirecting;
37
+ const handleSubmit = (e) => {
38
+ e.preventDefault();
39
+ signInMagicLink({ email, callbackURL: `${baseURL}${redirectTo}` });
40
+ };
41
+ const showSeparator = !!socialProviders?.length;
42
+ return (_jsxs(Card, { className: cn("w-full max-w-sm p-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, signInSocial: signInSocial, isPending: isPending })), showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or }))] })), _jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { name: "email", type: "email", autoComplete: "email", isDisabled: isPending, value: email, onChange: setEmail, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { placeholder: localization.auth.emailPlaceholder, required: true, variant: variant === "transparent" ? "primary" : "secondary" }), _jsx(FieldError, {})] }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.sendMagicLink] }), _jsx(MagicLinkButton, { view: "magicLink", isPending: isPending })] })] }), socialPosition === "bottom" && (_jsxs(_Fragment, { children: [showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or })), !!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending }))] }))] }), _jsx(Card.Footer, { className: "flex-col", children: _jsxs(Description, { className: "text-sm", children: [localization.auth.needToCreateAnAccount, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signUp}`, className: "text-accent decoration-accent no-underline hover:underline", children: localization.auth.signUp })] }) })] }));
43
+ }
@@ -0,0 +1,19 @@
1
+ import type { SocialProvider } from "better-auth/social-providers";
2
+ export type ProviderButtonsProps = {
3
+ isPending: boolean;
4
+ socialLayout?: SocialLayout;
5
+ signInSocial: (params: {
6
+ provider: SocialProvider;
7
+ callbackURL: string;
8
+ }) => void;
9
+ };
10
+ export type SocialLayout = "auto" | "horizontal" | "vertical" | "grid";
11
+ /**
12
+ * Render social provider sign-in buttons and handle initiation and pending state.
13
+ *
14
+ * @param isPending - Disables all provider buttons when true.
15
+ * @param socialLayout - Preferred layout for the buttons; `"auto"` picks `"horizontal"` when there are four or more providers, otherwise `"vertical"`.
16
+ * @param signInSocial - Callback invoked with the provider and callbackURL when a button is clicked.
17
+ * @returns The JSX element that renders the configured social provider buttons.
18
+ */
19
+ export declare function ProviderButtons({ isPending, socialLayout, signInSocial }: ProviderButtonsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getProviderName } from "@better-auth-ui/core";
3
+ import { providerIcons, useAuth } from "@better-auth-ui/react";
4
+ import { Button, cn } from "@heroui/react";
5
+ import { useMemo } from "react";
6
+ /**
7
+ * Render social provider sign-in buttons and handle initiation and pending state.
8
+ *
9
+ * @param isPending - Disables all provider buttons when true.
10
+ * @param socialLayout - Preferred layout for the buttons; `"auto"` picks `"horizontal"` when there are four or more providers, otherwise `"vertical"`.
11
+ * @param signInSocial - Callback invoked with the provider and callbackURL when a button is clicked.
12
+ * @returns The JSX element that renders the configured social provider buttons.
13
+ */
14
+ export function ProviderButtons({ isPending, socialLayout = "auto", signInSocial }) {
15
+ const { baseURL, localization, redirectTo, socialProviders } = useAuth();
16
+ const callbackURL = `${baseURL}${redirectTo}`;
17
+ const resolvedSocialLayout = useMemo(() => {
18
+ if (socialLayout === "auto") {
19
+ if (socialProviders?.length && socialProviders.length >= 4) {
20
+ return "horizontal";
21
+ }
22
+ return "vertical";
23
+ }
24
+ return socialLayout;
25
+ }, [socialLayout, socialProviders?.length]);
26
+ return (_jsx("div", { className: cn("gap-3", resolvedSocialLayout === "grid" && "grid grid-cols-2", resolvedSocialLayout === "vertical" && "flex flex-col", resolvedSocialLayout === "horizontal" && "flex flex-wrap"), children: socialProviders?.map((provider) => {
27
+ const ProviderIcon = providerIcons[provider];
28
+ return (_jsxs(Button, { className: cn("w-full", resolvedSocialLayout === "horizontal" && "flex-1"), variant: "tertiary", isPending: isPending, onPress: () => signInSocial({ provider, callbackURL }), children: [_jsx(ProviderIcon, {}), resolvedSocialLayout === "vertical"
29
+ ? localization.auth.continueWith.replace("{{provider}}", getProviderName(provider))
30
+ : resolvedSocialLayout === "grid"
31
+ ? getProviderName(provider)
32
+ : null] }, provider));
33
+ }) }));
34
+ }
@@ -0,0 +1,11 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type ResetPasswordProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Renders a reset password form that verifies a "token" URL parameter on mount and redirects to sign-in if absent.
8
+ *
9
+ * Renders password (and optional confirm-password) inputs with visibility toggles, applies min/max length constraints from the auth configuration, shows field errors, and submits the new password to the auth client.
10
+ */
11
+ export declare function ResetPassword({ className, variant, ...props }: ResetPasswordProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useResetPassword } from "@better-auth-ui/react";
3
+ import { Eye, EyeSlash } from "@gravity-ui/icons";
4
+ import { Button, Card, cn, Description, FieldError, Form, InputGroup, Label, Link, Spinner, TextField, toast } from "@heroui/react";
5
+ import { useEffect, useState } from "react";
6
+ /**
7
+ * Renders a reset password form that verifies a "token" URL parameter on mount and redirects to sign-in if absent.
8
+ *
9
+ * Renders password (and optional confirm-password) inputs with visibility toggles, applies min/max length constraints from the auth configuration, shows field errors, and submits the new password to the auth client.
10
+ */
11
+ export function ResetPassword({ className, variant, ...props }) {
12
+ const { basePaths, emailAndPassword, localization, viewPaths, navigate } = useAuth();
13
+ const { mutate: resetPassword, isPending } = useResetPassword({
14
+ onError: (error) => toast.danger(error.error?.message || error.message),
15
+ onSuccess: () => {
16
+ toast.success(localization.auth.passwordResetSuccess);
17
+ navigate({ to: `${basePaths.auth}/${viewPaths.auth.signIn}` });
18
+ }
19
+ });
20
+ const [isPasswordVisible, setIsPasswordVisible] = useState(false);
21
+ const [isConfirmPasswordVisible, setIsConfirmPasswordVisible] = useState(false);
22
+ useEffect(() => {
23
+ const searchParams = new URLSearchParams(window.location.search);
24
+ const token = searchParams.get("token");
25
+ if (!token) {
26
+ toast.danger(localization.auth.invalidResetPasswordToken);
27
+ navigate({ to: `${basePaths.auth}/${viewPaths.auth.signIn}` });
28
+ }
29
+ }, [
30
+ basePaths.auth,
31
+ localization.auth.invalidResetPasswordToken,
32
+ viewPaths.auth.signIn,
33
+ navigate
34
+ ]);
35
+ function handleSubmit(e) {
36
+ e.preventDefault();
37
+ const searchParams = new URLSearchParams(window.location.search);
38
+ const token = searchParams.get("token");
39
+ if (!token) {
40
+ toast.danger(localization.auth.invalidResetPasswordToken);
41
+ navigate({ to: `${basePaths.auth}/${viewPaths.auth.signIn}` });
42
+ return;
43
+ }
44
+ const formData = new FormData(e.currentTarget);
45
+ const password = formData.get("password");
46
+ const confirmPassword = formData.get("confirmPassword");
47
+ if (emailAndPassword?.confirmPassword && password !== confirmPassword) {
48
+ toast.danger(localization.auth.passwordsDoNotMatch);
49
+ return;
50
+ }
51
+ resetPassword({ token, newPassword: password });
52
+ }
53
+ return (_jsxs(Card, { className: cn("w-full max-w-sm p-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.resetPassword }) }), _jsx(Card.Content, { children: _jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { minLength: emailAndPassword?.minPasswordLength, maxLength: emailAndPassword?.maxPasswordLength, name: "password", autoComplete: "new-password", isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.password }), _jsxs(InputGroup, { variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(InputGroup.Input, { name: "password", placeholder: localization.auth.newPasswordPlaceholder, type: isPasswordVisible ? "text" : "password", required: true }), _jsx(InputGroup.Suffix, { className: "px-0", children: _jsx(Button, { isIconOnly: true, "aria-label": isPasswordVisible
54
+ ? localization.auth.hidePassword
55
+ : localization.auth.showPassword, size: "sm", variant: "ghost", onPress: () => setIsPasswordVisible(!isPasswordVisible), isDisabled: isPending, children: isPasswordVisible ? _jsx(EyeSlash, {}) : _jsx(Eye, {}) }) })] }), _jsx(FieldError, {})] }), emailAndPassword?.confirmPassword && (_jsxs(TextField, { minLength: emailAndPassword?.minPasswordLength, maxLength: emailAndPassword?.maxPasswordLength, name: "confirmPassword", autoComplete: "new-password", isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.confirmPassword }), _jsxs(InputGroup, { variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(InputGroup.Input, { placeholder: localization.auth.confirmPasswordPlaceholder, type: isConfirmPasswordVisible ? "text" : "password", required: true, name: "confirmPassword" }), _jsx(InputGroup.Suffix, { className: "px-0", children: _jsx(Button, { isIconOnly: true, "aria-label": isConfirmPasswordVisible
56
+ ? localization.auth.hidePassword
57
+ : localization.auth.showPassword, size: "sm", variant: "ghost", onPress: () => setIsConfirmPasswordVisible(!isConfirmPasswordVisible), isDisabled: isPending, children: isConfirmPasswordVisible ? _jsx(EyeSlash, {}) : _jsx(Eye, {}) }) })] }), _jsx(FieldError, {})] })), _jsx("div", { className: "flex flex-col gap-3", children: _jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.resetPassword] }) })] }) }), _jsx(Card.Footer, { className: "flex-col", children: _jsxs(Description, { className: "text-sm", children: [localization.auth.rememberYourPassword, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signIn}`, className: "text-accent decoration-accent no-underline hover:underline", children: localization.auth.signIn })] }) })] }));
58
+ }
@@ -0,0 +1,14 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ import { type SocialLayout } from "./provider-buttons";
3
+ export interface SignInProps {
4
+ className?: string;
5
+ socialLayout?: SocialLayout;
6
+ socialPosition?: "top" | "bottom";
7
+ variant?: CardProps["variant"];
8
+ }
9
+ /**
10
+ * Render the sign-in UI using auth context for configuration and localization.
11
+ *
12
+ * @returns The sign-in JSX element containing email/password fields, optional magic-link button, and social provider buttons.
13
+ */
14
+ export declare function SignIn({ className, socialLayout, socialPosition, variant, ...props }: SignInProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useSendVerificationEmail, useSignInEmail, useSignInSocial } from "@better-auth-ui/react";
3
+ import { Button, Card, Checkbox, cn, Description, FieldError, Form, Input, Label, Link, Spinner, TextField, toast } from "@heroui/react";
4
+ import { useState } from "react";
5
+ import { FieldSeparator } from "./field-separator";
6
+ import { MagicLinkButton } from "./magic-link-button";
7
+ import { ProviderButtons } from "./provider-buttons";
8
+ /**
9
+ * Render the sign-in UI using auth context for configuration and localization.
10
+ *
11
+ * @returns The sign-in JSX element containing email/password fields, optional magic-link button, and social provider buttons.
12
+ */
13
+ export function SignIn({ className, socialLayout, socialPosition = "bottom", variant, ...props }) {
14
+ const { basePaths, baseURL, emailAndPassword, localization, magicLink, redirectTo, socialProviders, viewPaths, navigate } = useAuth();
15
+ const [password, setPassword] = useState("");
16
+ const { mutate: sendVerificationEmail } = useSendVerificationEmail({
17
+ onError: (error) => toast.danger(error.error?.message || error.message),
18
+ onSuccess: () => toast.success(localization.auth.verificationEmailSent)
19
+ });
20
+ const { mutate: signInEmail, isPending: signInPending } = useSignInEmail({
21
+ onError: (error, { email }) => {
22
+ setPassword("");
23
+ if (error.error?.code === "EMAIL_NOT_VERIFIED") {
24
+ toast.danger(error.error?.message || error.message, {
25
+ actionProps: {
26
+ children: localization.auth.resend,
27
+ onClick: () => sendVerificationEmail({
28
+ email,
29
+ callbackURL: `${baseURL}${redirectTo}`
30
+ })
31
+ }
32
+ });
33
+ }
34
+ else {
35
+ toast.danger(error.error?.message || error.message);
36
+ }
37
+ },
38
+ onSuccess: () => navigate({ to: redirectTo })
39
+ });
40
+ const [socialRedirecting, setSocialRedirecting] = useState(false);
41
+ const { mutate: signInSocial, isPending: socialPending } = useSignInSocial({
42
+ onError: (error) => toast.danger(error.error?.message || error.message),
43
+ onSuccess: () => {
44
+ setSocialRedirecting(true);
45
+ setTimeout(() => {
46
+ setSocialRedirecting(false);
47
+ }, 5000);
48
+ }
49
+ });
50
+ const handleSubmit = (e) => {
51
+ e.preventDefault();
52
+ const formData = new FormData(e.currentTarget);
53
+ const email = formData.get("email");
54
+ const rememberMe = formData.get("rememberMe") === "on";
55
+ signInEmail({
56
+ email,
57
+ password,
58
+ ...(emailAndPassword?.rememberMe ? { rememberMe } : {})
59
+ });
60
+ };
61
+ const isPending = signInPending || socialPending || socialRedirecting;
62
+ const showSeparator = emailAndPassword?.enabled && !!socialProviders?.length;
63
+ 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, signInSocial: signInSocial, isPending: isPending })), 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: "email", autoComplete: "email", isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { placeholder: localization.auth.emailPlaceholder, 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 }) })] })), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.signIn] }), magicLink && (_jsx(MagicLinkButton, { view: "signIn", isPending: isPending }))] })] })), socialPosition === "bottom" && (_jsxs(_Fragment, { children: [showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or })), !!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending }))] }))] }), _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 })] }))] })] }));
64
+ }
@@ -0,0 +1,9 @@
1
+ export type SignOutProps = {
2
+ className?: string;
3
+ };
4
+ /**
5
+ * Initiates sign-out on mount and renders a loading card while sign-out proceeds.
6
+ *
7
+ * @returns A Card containing a centered Spinner shown during the sign-out process
8
+ */
9
+ export declare function SignOut({ className }: SignOutProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useAuth, useSignOut } from "@better-auth-ui/react";
3
+ import { cn, Spinner, toast } from "@heroui/react";
4
+ import { useEffect, useRef } from "react";
5
+ /**
6
+ * Initiates sign-out on mount and renders a loading card while sign-out proceeds.
7
+ *
8
+ * @returns A Card containing a centered Spinner shown during the sign-out process
9
+ */
10
+ export function SignOut({ className }) {
11
+ const { basePaths, navigate, viewPaths } = useAuth();
12
+ const { mutate: signOut } = useSignOut({
13
+ onError: (error) => {
14
+ toast.danger(error.error?.message || error.message);
15
+ navigate({
16
+ to: `${basePaths.auth}/${viewPaths.auth.signIn}`,
17
+ replace: true
18
+ });
19
+ },
20
+ onSuccess: () => navigate({
21
+ to: `${basePaths.auth}/${viewPaths.auth.signIn}`,
22
+ replace: true
23
+ })
24
+ });
25
+ const hasSignedOut = useRef(false);
26
+ useEffect(() => {
27
+ if (hasSignedOut.current)
28
+ return;
29
+ hasSignedOut.current = true;
30
+ signOut();
31
+ }, [signOut]);
32
+ return (_jsx(Spinner, { className: cn("mx-auto my-auto", className), color: "current" }));
33
+ }