@better-auth-ui/heroui 1.6.3 → 1.6.5

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 (204) hide show
  1. package/dist/components/auth/additional-field.d.ts +4 -0
  2. package/dist/components/auth/additional-field.js +270 -0
  3. package/dist/components/auth/auth-provider.d.ts +4 -6
  4. package/dist/components/auth/auth.d.ts +1 -1
  5. package/dist/components/auth/auth.js +54 -28
  6. package/dist/components/{settings/security → auth/delete-user}/danger-zone.d.ts +1 -1
  7. package/dist/components/{settings/security → auth/delete-user}/danger-zone.js +1 -1
  8. package/dist/components/auth/delete-user/delete-user.js +95 -0
  9. package/dist/components/{email → auth/email}/email-changed-email.d.ts +2 -2
  10. package/dist/components/{email → auth/email}/email-changed-email.js +7 -7
  11. package/dist/components/{email → auth/email}/email-verification-email.d.ts +2 -2
  12. package/dist/components/{email → auth/email}/email-verification-email.js +7 -7
  13. package/dist/components/auth/email/magic-link-email.d.ts +3 -0
  14. package/dist/components/{email → auth/email}/magic-link-email.js +7 -7
  15. package/dist/components/auth/email/new-device-email.d.ts +3 -0
  16. package/dist/components/{email → auth/email}/new-device-email.js +7 -7
  17. package/dist/components/auth/email/otp-email.d.ts +3 -0
  18. package/dist/components/{email → auth/email}/otp-email.js +6 -6
  19. package/dist/components/{email → auth/email}/password-changed-email.d.ts +2 -2
  20. package/dist/components/{email → auth/email}/password-changed-email.js +7 -7
  21. package/dist/components/{email → auth/email}/reset-password-email.d.ts +2 -2
  22. package/dist/components/{email → auth/email}/reset-password-email.js +7 -7
  23. package/dist/components/auth/forgot-password.d.ts +1 -1
  24. package/dist/components/auth/forgot-password.js +70 -48
  25. package/dist/components/auth/magic-link/magic-link-button.d.ts +11 -0
  26. package/dist/components/auth/magic-link/magic-link-button.js +20 -0
  27. package/dist/components/auth/magic-link/magic-link.d.ts +16 -0
  28. package/dist/components/auth/magic-link/magic-link.js +87 -0
  29. package/dist/components/{settings/account → auth/multi-session}/manage-account.d.ts +2 -6
  30. package/dist/components/auth/multi-session/manage-account.js +49 -0
  31. package/dist/components/auth/multi-session/manage-accounts.js +34 -0
  32. package/dist/components/{user/switch-account-menu.d.ts → auth/multi-session/switch-account-submenu-content.d.ts} +3 -3
  33. package/dist/components/auth/multi-session/switch-account-submenu-content.js +29 -0
  34. package/dist/components/{user/switch-account-item.d.ts → auth/multi-session/switch-account-submenu-item.d.ts} +2 -2
  35. package/dist/components/auth/multi-session/switch-account-submenu-item.js +20 -0
  36. package/dist/components/auth/multi-session/switch-account-submenu.d.ts +13 -0
  37. package/dist/components/auth/multi-session/switch-account-submenu.js +24 -0
  38. package/dist/components/auth/passkey/passkey-button.d.ts +6 -0
  39. package/dist/components/auth/passkey/passkey-button.js +24 -0
  40. package/dist/components/auth/passkey/passkey.js +45 -0
  41. package/dist/components/auth/passkey/passkeys.js +106 -0
  42. package/dist/components/auth/provider-button.d.ts +6 -8
  43. package/dist/components/auth/provider-button.js +16 -20
  44. package/dist/components/auth/provider-buttons.d.ts +3 -4
  45. package/dist/components/auth/provider-buttons.js +9 -10
  46. package/dist/components/auth/reset-password.js +110 -107
  47. package/dist/components/{settings → auth/settings}/account/account-settings.d.ts +5 -2
  48. package/dist/components/auth/settings/account/account-settings.js +20 -0
  49. package/dist/components/auth/settings/account/change-avatar.js +89 -0
  50. package/dist/components/{settings → auth/settings}/account/change-email.js +17 -17
  51. package/dist/components/{settings → auth/settings}/account/user-profile.d.ts +2 -3
  52. package/dist/components/auth/settings/account/user-profile.js +86 -0
  53. package/dist/components/auth/settings/security/active-session.js +59 -0
  54. package/dist/components/{settings → auth/settings}/security/active-sessions.js +4 -4
  55. package/dist/components/auth/settings/security/change-password.js +189 -0
  56. package/dist/components/auth/settings/security/linked-account.js +56 -0
  57. package/dist/components/{settings → auth/settings}/security/linked-accounts.js +5 -5
  58. package/dist/components/{settings → auth/settings}/security/security-settings.d.ts +1 -1
  59. package/dist/components/auth/settings/security/security-settings.js +22 -0
  60. package/dist/components/auth/settings/settings.js +45 -0
  61. package/dist/components/auth/sign-in.js +66 -85
  62. package/dist/components/auth/sign-out.js +11 -11
  63. package/dist/components/auth/sign-up.js +128 -148
  64. package/dist/components/{settings/account → auth/theme}/appearance.d.ts +3 -6
  65. package/dist/components/auth/theme/appearance.js +72 -0
  66. package/dist/components/auth/theme/theme-toggle-item.d.ts +5 -0
  67. package/dist/components/auth/theme/theme-toggle-item.js +58 -0
  68. package/dist/components/auth/user/user-avatar.js +29 -0
  69. package/dist/components/{user → auth/user}/user-button.d.ts +2 -6
  70. package/dist/components/auth/user/user-button.js +55 -0
  71. package/dist/components/auth/user/user-view.js +37 -0
  72. package/dist/components/auth/username/sign-in-username.d.ts +14 -0
  73. package/dist/components/auth/username/sign-in-username.js +148 -0
  74. package/dist/components/auth/username/username-field.d.ts +8 -0
  75. package/dist/components/auth/username/username-field.js +53 -0
  76. package/dist/email.d.ts +7 -0
  77. package/dist/email.js +9 -0
  78. package/dist/index.d.ts +12 -27
  79. package/dist/index.js +20 -35
  80. package/dist/lib/auth/auth-plugin.d.ts +44 -0
  81. package/dist/lib/auth/delete-user-plugin.d.ts +17 -0
  82. package/dist/lib/auth/delete-user-plugin.js +10 -0
  83. package/dist/lib/auth/magic-link-plugin.d.ts +31 -0
  84. package/dist/lib/auth/magic-link-plugin.js +13 -0
  85. package/dist/lib/auth/multi-session-plugin.d.ts +18 -0
  86. package/dist/lib/auth/multi-session-plugin.js +12 -0
  87. package/dist/lib/auth/passkey-plugin.d.ts +20 -0
  88. package/dist/lib/auth/passkey-plugin.js +12 -0
  89. package/dist/lib/auth/theme-plugin.d.ts +68 -0
  90. package/dist/lib/auth/theme-plugin.js +23 -0
  91. package/dist/lib/auth/username-plugin.d.ts +63 -0
  92. package/dist/lib/auth/username-plugin.js +18 -0
  93. package/dist/plugins.d.ts +20 -0
  94. package/dist/plugins.js +21 -0
  95. package/package.json +19 -21
  96. package/src/components/auth/additional-field.tsx +554 -0
  97. package/src/components/auth/auth-provider.tsx +5 -6
  98. package/src/components/auth/auth.tsx +99 -35
  99. package/src/components/{settings/security → auth/delete-user}/danger-zone.tsx +1 -1
  100. package/src/components/{settings/security → auth/delete-user}/delete-user.tsx +30 -20
  101. package/src/components/{email → auth/email}/email-changed-email.tsx +2 -2
  102. package/src/components/{email → auth/email}/email-verification-email.tsx +2 -2
  103. package/src/components/{email → auth/email}/magic-link-email.tsx +2 -2
  104. package/src/components/{email → auth/email}/new-device-email.tsx +2 -2
  105. package/src/components/{email → auth/email}/otp-email.tsx +2 -2
  106. package/src/components/{email → auth/email}/password-changed-email.tsx +2 -2
  107. package/src/components/{email → auth/email}/reset-password-email.tsx +2 -2
  108. package/src/components/auth/forgot-password.tsx +42 -17
  109. package/src/components/auth/magic-link/magic-link-button.tsx +61 -0
  110. package/src/components/auth/{magic-link.tsx → magic-link/magic-link.tsx} +55 -40
  111. package/src/components/{settings/account → auth/multi-session}/manage-account.tsx +16 -13
  112. package/src/components/{settings/account → auth/multi-session}/manage-accounts.tsx +14 -5
  113. package/src/components/{user/switch-account-menu.tsx → auth/multi-session/switch-account-submenu-content.tsx} +18 -11
  114. package/src/components/{user/switch-account-item.tsx → auth/multi-session/switch-account-submenu-item.tsx} +17 -5
  115. package/src/components/auth/multi-session/switch-account-submenu.tsx +50 -0
  116. package/src/components/auth/passkey/passkey-button.tsx +56 -0
  117. package/src/components/{settings/security → auth/passkey}/passkey.tsx +20 -6
  118. package/src/components/auth/passkey/passkeys.tsx +196 -0
  119. package/src/components/auth/provider-button.tsx +27 -29
  120. package/src/components/auth/provider-buttons.tsx +6 -11
  121. package/src/components/auth/reset-password.tsx +13 -7
  122. package/src/components/{settings → auth/settings}/account/account-settings.tsx +15 -13
  123. package/src/components/{settings → auth/settings}/account/change-avatar.tsx +4 -3
  124. package/src/components/{settings → auth/settings}/account/change-email.tsx +3 -3
  125. package/src/components/auth/settings/account/user-profile.tsx +171 -0
  126. package/src/components/{settings → auth/settings}/security/active-session.tsx +8 -5
  127. package/src/components/{settings → auth/settings}/security/active-sessions.tsx +3 -3
  128. package/src/components/{settings → auth/settings}/security/change-password.tsx +14 -9
  129. package/src/components/{settings → auth/settings}/security/linked-account.tsx +12 -8
  130. package/src/components/{settings → auth/settings}/security/linked-accounts.tsx +2 -2
  131. package/src/components/{settings → auth/settings}/security/security-settings.tsx +7 -6
  132. package/src/components/{settings → auth/settings}/settings.tsx +2 -2
  133. package/src/components/auth/sign-in.tsx +50 -61
  134. package/src/components/auth/sign-out.tsx +2 -2
  135. package/src/components/auth/sign-up.tsx +119 -131
  136. package/src/components/{settings/account → auth/theme}/appearance.tsx +17 -24
  137. package/src/components/auth/theme/theme-toggle-item.tsx +110 -0
  138. package/src/components/{user → auth/user}/user-avatar.tsx +12 -4
  139. package/src/components/{user → auth/user}/user-button.tsx +15 -88
  140. package/src/components/{user → auth/user}/user-view.tsx +12 -4
  141. package/src/components/auth/username/sign-in-username.tsx +299 -0
  142. package/src/components/auth/username/username-field.tsx +124 -0
  143. package/src/email.ts +9 -0
  144. package/src/index.tsx +12 -27
  145. package/src/lib/auth/auth-plugin.ts +67 -0
  146. package/src/lib/auth/delete-user-plugin.ts +15 -0
  147. package/src/lib/auth/magic-link-plugin.ts +26 -0
  148. package/src/lib/auth/multi-session-plugin.ts +17 -0
  149. package/src/lib/auth/passkey-plugin.ts +17 -0
  150. package/src/lib/auth/theme-plugin.ts +86 -0
  151. package/src/lib/auth/username-plugin.tsx +30 -0
  152. package/src/plugins.ts +23 -0
  153. package/dist/components/auth/magic-link-button.d.ts +0 -13
  154. package/dist/components/auth/magic-link-button.js +0 -16
  155. package/dist/components/auth/magic-link.d.ts +0 -17
  156. package/dist/components/auth/magic-link.js +0 -99
  157. package/dist/components/auth/passkey-button.d.ts +0 -4
  158. package/dist/components/auth/passkey-button.js +0 -21
  159. package/dist/components/email/magic-link-email.d.ts +0 -3
  160. package/dist/components/email/new-device-email.d.ts +0 -3
  161. package/dist/components/email/otp-email.d.ts +0 -3
  162. package/dist/components/settings/account/account-settings.js +0 -23
  163. package/dist/components/settings/account/appearance.js +0 -71
  164. package/dist/components/settings/account/change-avatar.js +0 -89
  165. package/dist/components/settings/account/manage-account.js +0 -48
  166. package/dist/components/settings/account/manage-accounts.js +0 -33
  167. package/dist/components/settings/account/user-profile.js +0 -103
  168. package/dist/components/settings/security/active-session.js +0 -59
  169. package/dist/components/settings/security/change-password.js +0 -189
  170. package/dist/components/settings/security/delete-user.js +0 -92
  171. package/dist/components/settings/security/linked-account.js +0 -56
  172. package/dist/components/settings/security/passkey.js +0 -43
  173. package/dist/components/settings/security/passkeys.js +0 -53
  174. package/dist/components/settings/security/security-settings.js +0 -25
  175. package/dist/components/settings/settings.js +0 -45
  176. package/dist/components/user/switch-account-item.js +0 -20
  177. package/dist/components/user/switch-account-menu.js +0 -28
  178. package/dist/components/user/user-avatar.js +0 -29
  179. package/dist/components/user/user-button.js +0 -91
  180. package/dist/components/user/user-view.js +0 -37
  181. package/dist/core.d.ts +0 -1
  182. package/dist/core.js +0 -1
  183. package/dist/react-exports.d.ts +0 -1
  184. package/dist/react-exports.js +0 -1
  185. package/src/components/auth/magic-link-button.tsx +0 -43
  186. package/src/components/auth/passkey-button.tsx +0 -35
  187. package/src/components/settings/account/user-profile.tsx +0 -217
  188. package/src/components/settings/security/passkeys.tsx +0 -96
  189. package/src/core.ts +0 -1
  190. package/src/react-exports.ts +0 -1
  191. /package/dist/components/{settings/security → auth/delete-user}/delete-user.d.ts +0 -0
  192. /package/dist/components/{settings/account → auth/multi-session}/manage-accounts.d.ts +0 -0
  193. /package/dist/components/{settings/security → auth/passkey}/passkey.d.ts +0 -0
  194. /package/dist/components/{settings/security → auth/passkey}/passkeys.d.ts +0 -0
  195. /package/dist/components/{settings → auth/settings}/account/change-avatar.d.ts +0 -0
  196. /package/dist/components/{settings → auth/settings}/account/change-email.d.ts +0 -0
  197. /package/dist/components/{settings → auth/settings}/security/active-session.d.ts +0 -0
  198. /package/dist/components/{settings → auth/settings}/security/active-sessions.d.ts +0 -0
  199. /package/dist/components/{settings → auth/settings}/security/change-password.d.ts +0 -0
  200. /package/dist/components/{settings → auth/settings}/security/linked-account.d.ts +0 -0
  201. /package/dist/components/{settings → auth/settings}/security/linked-accounts.d.ts +0 -0
  202. /package/dist/components/{settings → auth/settings}/settings.d.ts +0 -0
  203. /package/dist/components/{user → auth/user}/user-avatar.d.ts +0 -0
  204. /package/dist/components/{user → auth/user}/user-view.d.ts +0 -0
@@ -0,0 +1,554 @@
1
+ import { resolveInputType } from "@better-auth-ui/core"
2
+ import { useAuth } from "@better-auth-ui/react"
3
+ import { Check, Copy } from "@gravity-ui/icons"
4
+ import {
5
+ Button,
6
+ Calendar,
7
+ Checkbox,
8
+ ComboBox,
9
+ DateField,
10
+ DatePicker,
11
+ FieldError,
12
+ Input,
13
+ InputGroup,
14
+ Label,
15
+ ListBox,
16
+ NumberField,
17
+ Select,
18
+ Slider,
19
+ Switch,
20
+ TextArea,
21
+ TextField,
22
+ TimeField,
23
+ type TimeValue,
24
+ toast
25
+ } from "@heroui/react"
26
+ import {
27
+ type CalendarDate,
28
+ type CalendarDateTime,
29
+ fromDate,
30
+ getLocalTimeZone,
31
+ parseDate,
32
+ parseDateTime,
33
+ toCalendarDate,
34
+ toCalendarDateTime
35
+ } from "@internationalized/date"
36
+ import { useRef, useState } from "react"
37
+
38
+ import type { AdditionalFieldProps } from "../../lib/auth/auth-plugin"
39
+
40
+ // Re-exported here so the main entrypoint surface is unchanged.
41
+ export type { AdditionalFieldProps } from "../../lib/auth/auth-plugin"
42
+
43
+ /** Convert a `defaultValue` into a `CalendarDate` for HeroUI date inputs. */
44
+ function toDateValue(value: unknown): CalendarDate | undefined {
45
+ if (value instanceof Date) {
46
+ return toCalendarDate(fromDate(value, getLocalTimeZone()))
47
+ }
48
+
49
+ if (typeof value === "string") {
50
+ try {
51
+ return parseDate(value.slice(0, 10))
52
+ } catch {
53
+ return undefined
54
+ }
55
+ }
56
+
57
+ return undefined
58
+ }
59
+
60
+ /** Convert a `defaultValue` into a `CalendarDateTime` for datetime inputs. */
61
+ function toDateTimeValue(value: unknown): CalendarDateTime | undefined {
62
+ if (value instanceof Date) {
63
+ return toCalendarDateTime(fromDate(value, getLocalTimeZone()))
64
+ }
65
+
66
+ if (typeof value === "string") {
67
+ try {
68
+ // Strip trailing `Z` or timezone offset for `parseDateTime`.
69
+ return parseDateTime(value.replace(/(Z|[+-]\d{2}:?\d{2})$/, ""))
70
+ } catch {
71
+ return undefined
72
+ }
73
+ }
74
+
75
+ return undefined
76
+ }
77
+
78
+ /**
79
+ * Icon-only copy button used as an `InputGroup.Suffix`. `getValue` is invoked
80
+ * lazily on click so the button copies the input's *live* value rather than a
81
+ * stale snapshot — important when paired with editable inputs.
82
+ */
83
+ function CopyButton({
84
+ getValue,
85
+ isDisabled
86
+ }: {
87
+ getValue: () => string | undefined
88
+ isDisabled?: boolean
89
+ }) {
90
+ const { localization } = useAuth()
91
+ const [copied, setCopied] = useState(false)
92
+
93
+ async function handleCopy() {
94
+ const value = getValue()
95
+ if (!value) return
96
+
97
+ try {
98
+ await navigator.clipboard.writeText(value)
99
+ setCopied(true)
100
+ setTimeout(() => setCopied(false), 1500)
101
+ } catch (error) {
102
+ toast.danger(error instanceof Error ? error.message : String(error))
103
+ }
104
+ }
105
+
106
+ return (
107
+ <Button
108
+ isIconOnly
109
+ aria-label={localization.settings.copyToClipboard}
110
+ size="sm"
111
+ variant="ghost"
112
+ isDisabled={isDisabled}
113
+ onPress={handleCopy}
114
+ >
115
+ {copied ? <Check className="size-4" /> : <Copy className="size-4" />}
116
+ </Button>
117
+ )
118
+ }
119
+
120
+ /** Renders a single additional user field via HeroUI v3 components. */
121
+ export function AdditionalField({
122
+ name,
123
+ field,
124
+ isPending,
125
+ variant
126
+ }: AdditionalFieldProps) {
127
+ const { localization } = useAuth()
128
+ const inputType = resolveInputType(field)
129
+ const inputVariant = variant === "transparent" ? "primary" : "secondary"
130
+ // Used by `inputType: "input"` with `copyable: true` so the copy button
131
+ // reads the input's *live* value rather than a stale `defaultValue`.
132
+ const inputRef = useRef<HTMLInputElement>(null)
133
+
134
+ if (field.render) {
135
+ return <>{field.render({ name, field, isPending, variant })}</>
136
+ }
137
+
138
+ if (inputType === "hidden") {
139
+ return (
140
+ <input
141
+ type="hidden"
142
+ name={name}
143
+ value={
144
+ field.defaultValue == null
145
+ ? ""
146
+ : field.defaultValue instanceof Date
147
+ ? field.defaultValue.toISOString()
148
+ : String(field.defaultValue)
149
+ }
150
+ />
151
+ )
152
+ }
153
+
154
+ if (inputType === "textarea") {
155
+ return (
156
+ <TextField
157
+ name={name}
158
+ defaultValue={
159
+ field.defaultValue == null ? undefined : String(field.defaultValue)
160
+ }
161
+ isDisabled={isPending}
162
+ isReadOnly={field.readOnly}
163
+ >
164
+ <Label>{field.label}</Label>
165
+
166
+ <TextArea
167
+ placeholder={field.placeholder}
168
+ required={field.required}
169
+ variant={inputVariant}
170
+ />
171
+
172
+ <FieldError />
173
+ </TextField>
174
+ )
175
+ }
176
+
177
+ if (inputType === "number") {
178
+ const maxFractionDigits = field.formatOptions?.maximumFractionDigits
179
+
180
+ return (
181
+ <NumberField
182
+ name={name}
183
+ defaultValue={
184
+ typeof field.defaultValue === "number"
185
+ ? field.defaultValue
186
+ : field.defaultValue != null && field.defaultValue !== ""
187
+ ? Number(field.defaultValue)
188
+ : undefined
189
+ }
190
+ minValue={field.min}
191
+ maxValue={field.max}
192
+ step={
193
+ field.step ?? (maxFractionDigits ? 1 / 10 ** maxFractionDigits : 1)
194
+ }
195
+ formatOptions={field.formatOptions}
196
+ isDisabled={isPending}
197
+ isReadOnly={field.readOnly}
198
+ variant={inputVariant}
199
+ >
200
+ <Label>{field.label}</Label>
201
+
202
+ <NumberField.Group>
203
+ <NumberField.DecrementButton />
204
+ <NumberField.Input
205
+ placeholder={field.placeholder}
206
+ required={field.required}
207
+ />
208
+ <NumberField.IncrementButton />
209
+ </NumberField.Group>
210
+
211
+ <FieldError />
212
+ </NumberField>
213
+ )
214
+ }
215
+
216
+ if (inputType === "slider") {
217
+ const maxFractionDigits = field.formatOptions?.maximumFractionDigits
218
+
219
+ return (
220
+ <Slider
221
+ defaultValue={
222
+ typeof field.defaultValue === "number"
223
+ ? field.defaultValue
224
+ : field.defaultValue != null
225
+ ? Number(field.defaultValue)
226
+ : undefined
227
+ }
228
+ minValue={field.min ?? 0}
229
+ maxValue={field.max ?? 100}
230
+ step={
231
+ field.step ?? (maxFractionDigits ? 1 / 10 ** maxFractionDigits : 1)
232
+ }
233
+ formatOptions={field.formatOptions}
234
+ isDisabled={isPending || field.readOnly}
235
+ className="flex flex-col gap-2"
236
+ >
237
+ <div className="flex items-center justify-between gap-2">
238
+ <Label>{field.label}</Label>
239
+ <Slider.Output className="text-sm text-muted" />
240
+ </div>
241
+
242
+ <Slider.Track>
243
+ <Slider.Fill />
244
+ <Slider.Thumb name={name} />
245
+ </Slider.Track>
246
+ </Slider>
247
+ )
248
+ }
249
+
250
+ if (inputType === "switch") {
251
+ return (
252
+ <Switch
253
+ name={name}
254
+ defaultSelected={
255
+ field.defaultValue === true || field.defaultValue === "true"
256
+ }
257
+ isDisabled={isPending}
258
+ isReadOnly={field.readOnly}
259
+ >
260
+ <Switch.Control>
261
+ <Switch.Thumb />
262
+ </Switch.Control>
263
+
264
+ <Label>{field.label}</Label>
265
+ </Switch>
266
+ )
267
+ }
268
+
269
+ if (inputType === "checkbox") {
270
+ return (
271
+ <Checkbox
272
+ name={name}
273
+ defaultSelected={
274
+ field.defaultValue === true || field.defaultValue === "true"
275
+ }
276
+ isDisabled={isPending}
277
+ isReadOnly={field.readOnly}
278
+ isRequired={field.required}
279
+ variant={inputVariant}
280
+ >
281
+ <Checkbox.Control>
282
+ <Checkbox.Indicator />
283
+ </Checkbox.Control>
284
+
285
+ <Checkbox.Content>
286
+ <Label>{field.label}</Label>
287
+ </Checkbox.Content>
288
+ </Checkbox>
289
+ )
290
+ }
291
+
292
+ if (inputType === "select") {
293
+ return (
294
+ <Select
295
+ className="[&[data-required=true]>.label]:after:content-none"
296
+ name={name}
297
+ defaultValue={
298
+ field.defaultValue != null ? String(field.defaultValue) : undefined
299
+ }
300
+ placeholder={field.placeholder}
301
+ isDisabled={isPending || field.readOnly}
302
+ isRequired={field.required}
303
+ variant={inputVariant}
304
+ fullWidth
305
+ >
306
+ <Label>{field.label}</Label>
307
+
308
+ <Select.Trigger>
309
+ <Select.Value />
310
+ <Select.Indicator />
311
+ </Select.Trigger>
312
+
313
+ <Select.Popover>
314
+ <ListBox>
315
+ {field.options?.map((option) => (
316
+ <ListBox.Item
317
+ key={option.value}
318
+ id={option.value}
319
+ textValue={
320
+ typeof option.label === "string" ? option.label : option.value
321
+ }
322
+ >
323
+ {option.label}
324
+
325
+ <ListBox.ItemIndicator />
326
+ </ListBox.Item>
327
+ ))}
328
+ </ListBox>
329
+ </Select.Popover>
330
+
331
+ <FieldError />
332
+ </Select>
333
+ )
334
+ }
335
+
336
+ if (inputType === "combobox") {
337
+ return (
338
+ <ComboBox
339
+ className="[&[data-required=true]>.label]:after:content-none"
340
+ name={name}
341
+ defaultSelectedKey={
342
+ field.defaultValue != null ? String(field.defaultValue) : undefined
343
+ }
344
+ isDisabled={isPending}
345
+ isReadOnly={field.readOnly}
346
+ isRequired={field.required}
347
+ variant={inputVariant}
348
+ fullWidth
349
+ >
350
+ <Label>{field.label}</Label>
351
+
352
+ <ComboBox.InputGroup>
353
+ <Input placeholder={field.placeholder} />
354
+ <ComboBox.Trigger />
355
+ </ComboBox.InputGroup>
356
+
357
+ <ComboBox.Popover>
358
+ <ListBox>
359
+ {field.options?.map((option) => (
360
+ <ListBox.Item
361
+ key={option.value}
362
+ id={option.value}
363
+ textValue={
364
+ typeof option.label === "string" ? option.label : option.value
365
+ }
366
+ >
367
+ {option.label}
368
+
369
+ <ListBox.ItemIndicator />
370
+ </ListBox.Item>
371
+ ))}
372
+ </ListBox>
373
+ </ComboBox.Popover>
374
+
375
+ <FieldError />
376
+ </ComboBox>
377
+ )
378
+ }
379
+
380
+ if (inputType === "date" || inputType === "datetime") {
381
+ const isDateTime = inputType === "datetime"
382
+ const defaultValue = isDateTime
383
+ ? toDateTimeValue(field.defaultValue)
384
+ : toDateValue(field.defaultValue)
385
+
386
+ return (
387
+ <DatePicker
388
+ className="w-full [&[data-required=true]>.label]:after:content-none"
389
+ name={name}
390
+ defaultValue={defaultValue}
391
+ granularity={isDateTime ? "minute" : "day"}
392
+ isDisabled={isPending}
393
+ isReadOnly={field.readOnly}
394
+ isRequired={field.required}
395
+ >
396
+ {({ state }) => (
397
+ <>
398
+ <Label>{field.label}</Label>
399
+
400
+ <DateField.Group variant={inputVariant} fullWidth>
401
+ <DateField.Input>
402
+ {(segment) => <DateField.Segment segment={segment} />}
403
+ </DateField.Input>
404
+
405
+ <DateField.Suffix>
406
+ <DatePicker.Trigger>
407
+ <DatePicker.TriggerIndicator />
408
+ </DatePicker.Trigger>
409
+ </DateField.Suffix>
410
+ </DateField.Group>
411
+
412
+ <FieldError />
413
+
414
+ <DatePicker.Popover className="flex flex-col gap-3">
415
+ <Calendar
416
+ aria-label={
417
+ typeof field.label === "string" ? field.label : name
418
+ }
419
+ >
420
+ <Calendar.Header>
421
+ <Calendar.YearPickerTrigger>
422
+ <Calendar.YearPickerTriggerHeading />
423
+ <Calendar.YearPickerTriggerIndicator />
424
+ </Calendar.YearPickerTrigger>
425
+ <Calendar.NavButton slot="previous" />
426
+ <Calendar.NavButton slot="next" />
427
+ </Calendar.Header>
428
+
429
+ <Calendar.Grid>
430
+ <Calendar.GridHeader>
431
+ {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
432
+ </Calendar.GridHeader>
433
+
434
+ <Calendar.GridBody>
435
+ {(date) => <Calendar.Cell date={date} />}
436
+ </Calendar.GridBody>
437
+ </Calendar.Grid>
438
+
439
+ <Calendar.YearPickerGrid>
440
+ <Calendar.YearPickerGridBody>
441
+ {({ year }) => <Calendar.YearPickerCell year={year} />}
442
+ </Calendar.YearPickerGridBody>
443
+ </Calendar.YearPickerGrid>
444
+ </Calendar>
445
+
446
+ {isDateTime && (
447
+ <div className="flex items-center justify-between">
448
+ <Label>{localization.settings.time}</Label>
449
+
450
+ <TimeField
451
+ aria-label={localization.settings.time}
452
+ granularity="minute"
453
+ value={state.timeValue}
454
+ onChange={(v) => state.setTimeValue(v as TimeValue)}
455
+ >
456
+ <TimeField.Group variant="secondary">
457
+ <TimeField.Input>
458
+ {(segment) => <TimeField.Segment segment={segment} />}
459
+ </TimeField.Input>
460
+ </TimeField.Group>
461
+ </TimeField>
462
+ </div>
463
+ )}
464
+ </DatePicker.Popover>
465
+ </>
466
+ )}
467
+ </DatePicker>
468
+ )
469
+ }
470
+
471
+ // inputType === "input"
472
+ const hasPrefix = field.prefix != null
473
+ const hasSuffix = field.suffix != null || field.copyable
474
+
475
+ // When `inputType: "input"` is paired with `type: "number"`, restrict the
476
+ // native input to numbers. `formatOptions.maximumFractionDigits` enables
477
+ // fractional input via `step`.
478
+ const isNumeric = field.type === "number"
479
+ const maxFractionDigits = field.formatOptions?.maximumFractionDigits
480
+ const nativeInputType = isNumeric ? "number" : undefined
481
+ const nativeInputMode = isNumeric
482
+ ? maxFractionDigits
483
+ ? "decimal"
484
+ : "numeric"
485
+ : undefined
486
+ const nativeStep = maxFractionDigits ? 1 / 10 ** maxFractionDigits : undefined
487
+
488
+ if (hasPrefix || hasSuffix) {
489
+ return (
490
+ <TextField
491
+ name={name}
492
+ defaultValue={
493
+ field.defaultValue == null ? undefined : String(field.defaultValue)
494
+ }
495
+ isDisabled={isPending}
496
+ isReadOnly={field.readOnly}
497
+ >
498
+ <Label>{field.label}</Label>
499
+
500
+ <InputGroup variant={inputVariant}>
501
+ {hasPrefix && <InputGroup.Prefix>{field.prefix}</InputGroup.Prefix>}
502
+
503
+ <InputGroup.Input
504
+ ref={inputRef}
505
+ placeholder={field.placeholder}
506
+ required={field.required}
507
+ type={nativeInputType}
508
+ inputMode={nativeInputMode}
509
+ step={nativeStep}
510
+ />
511
+
512
+ {field.copyable ? (
513
+ <InputGroup.Suffix className="px-0">
514
+ <CopyButton
515
+ getValue={() => inputRef.current?.value}
516
+ isDisabled={isPending}
517
+ />
518
+ </InputGroup.Suffix>
519
+ ) : (
520
+ field.suffix != null && (
521
+ <InputGroup.Suffix>{field.suffix}</InputGroup.Suffix>
522
+ )
523
+ )}
524
+ </InputGroup>
525
+
526
+ <FieldError />
527
+ </TextField>
528
+ )
529
+ }
530
+
531
+ return (
532
+ <TextField
533
+ name={name}
534
+ defaultValue={
535
+ field.defaultValue == null ? undefined : String(field.defaultValue)
536
+ }
537
+ isDisabled={isPending}
538
+ isReadOnly={field.readOnly}
539
+ >
540
+ <Label>{field.label}</Label>
541
+
542
+ <Input
543
+ placeholder={field.placeholder}
544
+ required={field.required}
545
+ variant={inputVariant}
546
+ type={nativeInputType}
547
+ inputMode={nativeInputMode}
548
+ step={nativeStep}
549
+ />
550
+
551
+ <FieldError />
552
+ </TextField>
553
+ )
554
+ }
@@ -1,18 +1,17 @@
1
1
  import {
2
+ type AuthClient,
2
3
  AuthProvider as AuthProviderPrimitive,
3
4
  type AuthProviderProps as AuthProviderPropsPrimitive
4
5
  } from "@better-auth-ui/react"
5
6
  import { RouterProvider } from "@heroui/react"
6
-
7
7
  import { ErrorToaster } from "./error-toaster"
8
8
 
9
- export type AuthProviderProps = Omit<AuthProviderPropsPrimitive, "Link">
9
+ export type AuthProviderProps<TAuthClient = AuthClient> =
10
+ AuthProviderPropsPrimitive<TAuthClient>
10
11
 
11
12
  /**
12
- * Provides an authentication context by rendering an auth provider with the sonner toast handler injected, forwarding remaining configuration and rendering `children` inside it.
13
- *
14
- * @param children - React nodes to render inside the authentication provider
15
- * @returns A React element that renders an authentication provider configured with the provided props and toast handler
13
+ * Heroui-flavored `AuthProvider`. Wraps the primitive provider with a
14
+ * heroui `RouterProvider` and the heroui `ErrorToaster`.
16
15
  */
17
16
  export function AuthProvider({
18
17
  children,