@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,38 @@
1
+ import {
2
+ MagicLinkEmail as MagicLinkEmailPrimitive,
3
+ type MagicLinkEmailProps
4
+ } from "@better-auth-ui/react"
5
+ import { cn } from "@heroui/react"
6
+
7
+ export type { MagicLinkEmailProps } from "@better-auth-ui/react"
8
+
9
+ export function MagicLinkEmail({
10
+ colors,
11
+ classNames,
12
+ ...props
13
+ }: MagicLinkEmailProps) {
14
+ return (
15
+ <MagicLinkEmailPrimitive
16
+ colors={{
17
+ light: {
18
+ background: "#F5F5F5",
19
+ primary: "#0285F7",
20
+ primaryForeground: "#FCFCFC",
21
+ ...colors?.light
22
+ },
23
+ dark: {
24
+ background: "#060607",
25
+ primary: "#0584F6",
26
+ primaryForeground: "#FCFCFC",
27
+ ...colors?.dark
28
+ }
29
+ }}
30
+ classNames={{
31
+ ...classNames,
32
+ card: cn("border-none rounded-3xl", classNames?.card),
33
+ button: cn("rounded-full", classNames?.button)
34
+ }}
35
+ {...props}
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ NewDeviceEmail as NewDeviceEmailPrimitive,
3
+ type NewDeviceEmailProps
4
+ } from "@better-auth-ui/react"
5
+ import { cn } from "@heroui/react"
6
+
7
+ export type { NewDeviceEmailProps } from "@better-auth-ui/react"
8
+
9
+ export function NewDeviceEmail({
10
+ colors,
11
+ classNames,
12
+ ...props
13
+ }: NewDeviceEmailProps) {
14
+ return (
15
+ <NewDeviceEmailPrimitive
16
+ colors={{
17
+ light: {
18
+ background: "#F5F5F5",
19
+ primary: "#0285F7",
20
+ primaryForeground: "#FCFCFC",
21
+ ...colors?.light
22
+ },
23
+ dark: {
24
+ background: "#060607",
25
+ primary: "#0584F6",
26
+ primaryForeground: "#FCFCFC",
27
+ ...colors?.dark
28
+ }
29
+ }}
30
+ classNames={{
31
+ ...classNames,
32
+ card: cn("border-none rounded-3xl", classNames?.card),
33
+ button: cn("rounded-full", classNames?.button)
34
+ }}
35
+ {...props}
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,33 @@
1
+ import {
2
+ OtpEmail as OtpEmailPrimitive,
3
+ type OtpEmailProps
4
+ } from "@better-auth-ui/react"
5
+ import { cn } from "@heroui/react"
6
+
7
+ export type { OtpEmailProps } from "@better-auth-ui/react"
8
+
9
+ export function OtpEmail({ colors, classNames, ...props }: OtpEmailProps) {
10
+ return (
11
+ <OtpEmailPrimitive
12
+ colors={{
13
+ light: {
14
+ background: "#F5F5F5",
15
+ primary: "#0285F7",
16
+ primaryForeground: "#FCFCFC",
17
+ ...colors?.light
18
+ },
19
+ dark: {
20
+ background: "#060607",
21
+ primary: "#0584F6",
22
+ primaryForeground: "#FCFCFC",
23
+ ...colors?.dark
24
+ }
25
+ }}
26
+ classNames={{
27
+ ...classNames,
28
+ card: cn("border-none rounded-3xl", classNames?.card)
29
+ }}
30
+ {...props}
31
+ />
32
+ )
33
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ PasswordChangedEmail as PasswordChangedEmailPrimitive,
3
+ type PasswordChangedEmailProps
4
+ } from "@better-auth-ui/react"
5
+ import { cn } from "@heroui/react"
6
+
7
+ export type { PasswordChangedEmailProps } from "@better-auth-ui/react"
8
+
9
+ export function PasswordChangedEmail({
10
+ colors,
11
+ classNames,
12
+ ...props
13
+ }: PasswordChangedEmailProps) {
14
+ return (
15
+ <PasswordChangedEmailPrimitive
16
+ colors={{
17
+ light: {
18
+ background: "#F5F5F5",
19
+ primary: "#0285F7",
20
+ primaryForeground: "#FCFCFC",
21
+ ...colors?.light
22
+ },
23
+ dark: {
24
+ background: "#060607",
25
+ primary: "#0584F6",
26
+ primaryForeground: "#FCFCFC",
27
+ ...colors?.dark
28
+ }
29
+ }}
30
+ classNames={{
31
+ ...classNames,
32
+ card: cn("border-none rounded-3xl", classNames?.card),
33
+ button: cn("rounded-full", classNames?.button)
34
+ }}
35
+ {...props}
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ ResetPasswordEmail as ResetPasswordEmailPrimitive,
3
+ type ResetPasswordEmailProps
4
+ } from "@better-auth-ui/react"
5
+ import { cn } from "@heroui/react"
6
+
7
+ export type { ResetPasswordEmailProps } from "@better-auth-ui/react"
8
+
9
+ export function ResetPasswordEmail({
10
+ colors,
11
+ classNames,
12
+ ...props
13
+ }: ResetPasswordEmailProps) {
14
+ return (
15
+ <ResetPasswordEmailPrimitive
16
+ colors={{
17
+ light: {
18
+ background: "#F5F5F5",
19
+ primary: "#0285F7",
20
+ primaryForeground: "#FCFCFC",
21
+ ...colors?.light
22
+ },
23
+ dark: {
24
+ background: "#060607",
25
+ primary: "#0584F6",
26
+ primaryForeground: "#FCFCFC",
27
+ ...colors?.dark
28
+ }
29
+ }}
30
+ classNames={{
31
+ ...classNames,
32
+ card: cn("border-none rounded-3xl", classNames?.card),
33
+ button: cn("rounded-full", classNames?.button)
34
+ }}
35
+ {...props}
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,48 @@
1
+ import { useAuth } from "@better-auth-ui/react"
2
+ import { type CardProps, cn } from "@heroui/react"
3
+ import type { ComponentProps } from "react"
4
+ import { Appearance } from "./appearance"
5
+ import { ChangeEmail } from "./change-email"
6
+ import { ManageAccounts } from "./manage-accounts"
7
+ import { UserProfile } from "./user-profile"
8
+
9
+ export type AccountSettingsProps = {
10
+ className?: string
11
+ variant?: CardProps["variant"]
12
+ }
13
+
14
+ /**
15
+ * Renders the account settings layout including user profile, change email, appearance, and accounts management.
16
+ *
17
+ * UserProfile, ChangeEmail, and Appearance are always rendered; Accounts is rendered when `multiSession` is enabled.
18
+ *
19
+ * @param className - Optional additional CSS class names for the outer container.
20
+ * @param variant - Card variant forwarded to each account settings card.
21
+ * @returns The account settings container as a JSX element.
22
+ */
23
+ export function AccountSettings({
24
+ className,
25
+ variant,
26
+ ...props
27
+ }: AccountSettingsProps & ComponentProps<"div">) {
28
+ const {
29
+ multiSession,
30
+ emailAndPassword,
31
+ magicLink,
32
+ appearance: { setTheme }
33
+ } = useAuth()
34
+
35
+ return (
36
+ <div
37
+ className={cn("flex w-full flex-col gap-4 md:gap-6", className)}
38
+ {...props}
39
+ >
40
+ <UserProfile variant={variant} />
41
+ {(emailAndPassword?.enabled || magicLink) && (
42
+ <ChangeEmail variant={variant} />
43
+ )}
44
+ {setTheme && <Appearance variant={variant} />}
45
+ {multiSession && <ManageAccounts variant={variant} />}
46
+ </div>
47
+ )
48
+ }
@@ -0,0 +1,157 @@
1
+ import {
2
+ ThemePreviewDark,
3
+ ThemePreviewLight,
4
+ ThemePreviewSystem,
5
+ useAuth,
6
+ useSession
7
+ } from "@better-auth-ui/react"
8
+ import { Display, Moon, Sun } from "@gravity-ui/icons"
9
+ import {
10
+ Card,
11
+ type CardProps,
12
+ cn,
13
+ Label,
14
+ Radio,
15
+ RadioGroup
16
+ } from "@heroui/react"
17
+
18
+ export type AppearanceProps = {
19
+ className?: string
20
+ variant?: CardProps["variant"]
21
+ }
22
+
23
+ /**
24
+ * Renders a theme selector card with visual theme previews.
25
+ *
26
+ * Displays a card containing radio buttons for selecting between system, light,
27
+ * and dark themes. Each option shows a visual preview of the theme. Only renders
28
+ * if theme settings are configured (theme, setTheme, and themes are provided).
29
+ *
30
+ * @param className - Optional additional CSS class names for the card container.
31
+ * @returns A JSX element containing the theme selector card, or null if theme settings are not configured.
32
+ */
33
+ export function Appearance({
34
+ className,
35
+ variant,
36
+ ...props
37
+ }: AppearanceProps & CardProps) {
38
+ const {
39
+ localization,
40
+ appearance: { theme, setTheme, themes }
41
+ } = useAuth()
42
+ const { data: session } = useSession()
43
+
44
+ if (!setTheme || !themes?.length) {
45
+ return null
46
+ }
47
+
48
+ return (
49
+ <div>
50
+ <h2 className={cn("text-sm font-semibold mb-3")}>
51
+ {localization.settings.appearance}
52
+ </h2>
53
+
54
+ <Card className={cn("p-4 gap-4", className)} variant={variant} {...props}>
55
+ <Card.Content>
56
+ <Label>{localization.settings.theme}</Label>
57
+
58
+ <RadioGroup
59
+ variant={variant === "transparent" ? "secondary" : "primary"}
60
+ value={session ? theme : ""}
61
+ onChange={setTheme}
62
+ isDisabled={!session || !theme}
63
+ >
64
+ <div className="grid gap-3 grid-cols-2 sm:grid-cols-3">
65
+ {themes.includes("system") && (
66
+ <Radio
67
+ value="system"
68
+ className={cn(
69
+ "mt-0 px-4 pb-4 pt-3 rounded-xl",
70
+ "border border-transparent data-[selected=true]:border-accent data-[selected=true]:bg-accent/10",
71
+ "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10",
72
+ variant === "transparent"
73
+ ? "bg-surface"
74
+ : "bg-surface-secondary"
75
+ )}
76
+ >
77
+ <Radio.Content className="gap-2">
78
+ <div className="flex gap-2 justify-between">
79
+ <Label className="flex gap-2 items-center">
80
+ <Display className="text-muted" />
81
+ {localization.settings.system}
82
+ </Label>
83
+
84
+ <Radio.Control>
85
+ <Radio.Indicator />
86
+ </Radio.Control>
87
+ </div>
88
+
89
+ <ThemePreviewSystem className="w-full" />
90
+ </Radio.Content>
91
+ </Radio>
92
+ )}
93
+
94
+ {themes.includes("light") && (
95
+ <Radio
96
+ value="light"
97
+ className={cn(
98
+ "mt-0 px-4 pb-4 pt-3 rounded-xl",
99
+ "border border-transparent data-[selected=true]:border-accent data-[selected=true]:bg-accent/10",
100
+ "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10",
101
+ variant === "transparent"
102
+ ? "bg-surface"
103
+ : "bg-surface-secondary"
104
+ )}
105
+ >
106
+ <Radio.Content className="gap-2">
107
+ <div className="flex gap-2 justify-between">
108
+ <Label className="flex gap-2 items-center">
109
+ <Sun className="text-muted" />
110
+ {localization.settings.light}
111
+ </Label>
112
+
113
+ <Radio.Control>
114
+ <Radio.Indicator />
115
+ </Radio.Control>
116
+ </div>
117
+
118
+ <ThemePreviewLight className="w-full" />
119
+ </Radio.Content>
120
+ </Radio>
121
+ )}
122
+
123
+ {themes.includes("dark") && (
124
+ <Radio
125
+ value="dark"
126
+ className={cn(
127
+ "mt-0 px-4 pb-4 pt-3 rounded-xl",
128
+ "border border-transparent data-[selected=true]:border-accent data-[selected=true]:bg-accent/10",
129
+ "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10",
130
+ variant === "transparent"
131
+ ? "bg-surface"
132
+ : "bg-surface-secondary"
133
+ )}
134
+ >
135
+ <Radio.Content className="gap-2">
136
+ <div className="flex gap-2 justify-between">
137
+ <Label className="flex gap-2 items-center">
138
+ <Moon className="text-muted" />
139
+ {localization.settings.dark}
140
+ </Label>
141
+
142
+ <Radio.Control>
143
+ <Radio.Indicator />
144
+ </Radio.Control>
145
+ </div>
146
+
147
+ <ThemePreviewDark className="w-full" />
148
+ </Radio.Content>
149
+ </Radio>
150
+ )}
151
+ </div>
152
+ </RadioGroup>
153
+ </Card.Content>
154
+ </Card>
155
+ </div>
156
+ )
157
+ }
@@ -0,0 +1,142 @@
1
+ import { fileToBase64 } from "@better-auth-ui/core"
2
+ import { useAuth, useSession, useUpdateUser } from "@better-auth-ui/react"
3
+ import { CloudArrowUpIn, TrashBin } from "@gravity-ui/icons"
4
+ import { Button, cn, Dropdown, Label, Spinner, toast } from "@heroui/react"
5
+ import { type ChangeEvent, useRef, useState } from "react"
6
+
7
+ import { UserAvatar } from "../../user/user-avatar"
8
+
9
+ export type ChangeAvatarProps = {
10
+ className?: string
11
+ }
12
+
13
+ export function ChangeAvatar({ className }: ChangeAvatarProps) {
14
+ const { localization, avatar } = useAuth()
15
+ const { data: session } = useSession()
16
+
17
+ const { mutate: updateUser, isPending: updatePending } = useUpdateUser({
18
+ onError: (error) => toast.danger(error.error?.message || error.message)
19
+ })
20
+
21
+ const fileInputRef = useRef<HTMLInputElement>(null)
22
+ const [isUploading, setIsUploading] = useState(false)
23
+ const [isDeleting, setIsDeleting] = useState(false)
24
+
25
+ const isPending = updatePending || isUploading || isDeleting
26
+
27
+ async function handleFileChange(e: ChangeEvent<HTMLInputElement>) {
28
+ const file = e.target.files?.[0]
29
+ if (!file) return
30
+
31
+ e.target.value = ""
32
+
33
+ setIsUploading(true)
34
+
35
+ try {
36
+ const resized =
37
+ (await avatar.resize?.(file, avatar.size, avatar.extension)) || file
38
+
39
+ const image =
40
+ (await avatar.upload?.(resized)) || (await fileToBase64(resized))
41
+
42
+ updateUser(
43
+ { image },
44
+ {
45
+ onSuccess: () =>
46
+ toast.success(localization.settings.avatarChangedSuccess)
47
+ }
48
+ )
49
+ } catch (error) {
50
+ if (error instanceof Error) {
51
+ toast.danger(error.message)
52
+ }
53
+ }
54
+
55
+ setIsUploading(false)
56
+ }
57
+
58
+ async function handleDelete() {
59
+ const currentImage = session?.user.image
60
+
61
+ updateUser(
62
+ { image: null },
63
+ {
64
+ onSuccess: async () => {
65
+ if (currentImage) {
66
+ setIsDeleting(true)
67
+ try {
68
+ await avatar.delete?.(currentImage)
69
+ } finally {
70
+ setIsDeleting(false)
71
+ }
72
+ }
73
+
74
+ toast.success(localization.settings.avatarDeletedSuccess)
75
+ }
76
+ }
77
+ )
78
+ }
79
+
80
+ return (
81
+ <div className={cn("flex flex-col gap-1", className)}>
82
+ <Label isDisabled={!session}>{localization.settings.avatar}</Label>
83
+
84
+ <input
85
+ ref={fileInputRef}
86
+ type="file"
87
+ accept="image/*"
88
+ className="hidden"
89
+ onChange={handleFileChange}
90
+ />
91
+
92
+ <div className="flex items-center gap-4">
93
+ <Button
94
+ type="button"
95
+ isIconOnly
96
+ variant="ghost"
97
+ className="p-0 h-auto w-auto rounded-full"
98
+ isDisabled={!session || isPending}
99
+ onPress={() => fileInputRef.current?.click()}
100
+ >
101
+ <UserAvatar size="lg" isPending={isPending} />
102
+ </Button>
103
+
104
+ <Dropdown>
105
+ <Button
106
+ isDisabled={!session || isPending}
107
+ size="sm"
108
+ variant="secondary"
109
+ >
110
+ {isPending && <Spinner size="sm" />}
111
+
112
+ {localization.settings.changeAvatar}
113
+ </Button>
114
+
115
+ <Dropdown.Popover className="min-w-fit">
116
+ <Dropdown.Menu>
117
+ <Dropdown.Item
118
+ textValue={localization.settings.uploadAvatar}
119
+ onAction={() => fileInputRef.current?.click()}
120
+ >
121
+ <CloudArrowUpIn className="text-muted" />
122
+
123
+ <Label>{localization.settings.uploadAvatar}</Label>
124
+ </Dropdown.Item>
125
+
126
+ <Dropdown.Item
127
+ textValue={localization.settings.deleteAvatar}
128
+ isDisabled={!session?.user.image}
129
+ onAction={handleDelete}
130
+ variant="danger"
131
+ >
132
+ <TrashBin className="text-danger" />
133
+
134
+ <Label>{localization.settings.deleteAvatar}</Label>
135
+ </Dropdown.Item>
136
+ </Dropdown.Menu>
137
+ </Dropdown.Popover>
138
+ </Dropdown>
139
+ </div>
140
+ </div>
141
+ )
142
+ }
@@ -0,0 +1,111 @@
1
+ import { useAuth, useChangeEmail, useSession } from "@better-auth-ui/react"
2
+ import {
3
+ Button,
4
+ Card,
5
+ type CardProps,
6
+ cn,
7
+ FieldError,
8
+ Fieldset,
9
+ Form,
10
+ Input,
11
+ Label,
12
+ Skeleton,
13
+ Spinner,
14
+ TextField,
15
+ toast
16
+ } from "@heroui/react"
17
+ import type { SyntheticEvent } from "react"
18
+
19
+ export type ChangeEmailProps = {
20
+ className?: string
21
+ variant?: CardProps["variant"]
22
+ }
23
+
24
+ /**
25
+ * Render a card containing a form to view and update the authenticated user's email.
26
+ *
27
+ * Shows a loading skeleton until session data is available, displays the current
28
+ * email as the form's default value, and sends a verification email to the
29
+ * new address upon successful submission.
30
+ *
31
+ * @returns A JSX element rendering the change-email card and form
32
+ */
33
+ export function ChangeEmail({
34
+ className,
35
+ variant,
36
+ ...props
37
+ }: ChangeEmailProps & CardProps) {
38
+ const { localization, baseURL, viewPaths } = useAuth()
39
+ const { data: session } = useSession()
40
+
41
+ const { mutate: changeEmail, isPending } = useChangeEmail({
42
+ onSuccess: () => toast.success(localization.settings.changeEmailSuccess),
43
+ onError: (error) => toast.danger(error.error?.message || error.message)
44
+ })
45
+
46
+ function handleSubmit(e: SyntheticEvent<HTMLFormElement>) {
47
+ e.preventDefault()
48
+ const formData = new FormData(e.currentTarget)
49
+
50
+ changeEmail({
51
+ newEmail: formData.get("email") as string,
52
+ callbackURL: `${baseURL}/${viewPaths.settings.account}`
53
+ })
54
+ }
55
+
56
+ return (
57
+ <div>
58
+ <h2 className={cn("text-sm font-semibold mb-3")}>
59
+ {localization.settings.changeEmail}
60
+ </h2>
61
+
62
+ <Card className={cn("p-4 gap-4", className)} variant={variant} {...props}>
63
+ <Card.Content>
64
+ <Form onSubmit={handleSubmit}>
65
+ <Fieldset className="w-full gap-4">
66
+ <Fieldset.Group>
67
+ <TextField
68
+ key={session?.user.email}
69
+ name="email"
70
+ type="email"
71
+ defaultValue={session?.user.email}
72
+ isDisabled={isPending || !session}
73
+ >
74
+ <Label>{localization.auth.email}</Label>
75
+
76
+ {session ? (
77
+ <Input
78
+ required
79
+ variant={
80
+ variant === "transparent" ? "primary" : "secondary"
81
+ }
82
+ autoComplete="email"
83
+ placeholder={localization.auth.emailPlaceholder}
84
+ />
85
+ ) : (
86
+ <Skeleton className="h-10 md:h-9 w-full rounded-xl" />
87
+ )}
88
+
89
+ <FieldError />
90
+ </TextField>
91
+ </Fieldset.Group>
92
+
93
+ <Fieldset.Actions>
94
+ <Button
95
+ type="submit"
96
+ isPending={isPending}
97
+ isDisabled={!session}
98
+ size="sm"
99
+ >
100
+ {isPending && <Spinner color="current" size="sm" />}
101
+
102
+ {localization.settings.updateEmail}
103
+ </Button>
104
+ </Fieldset.Actions>
105
+ </Fieldset>
106
+ </Form>
107
+ </Card.Content>
108
+ </Card>
109
+ </div>
110
+ )
111
+ }