@better-auth-ui/heroui 1.6.5 → 1.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/auth/delete-user/danger-zone.js +1 -1
- package/dist/components/auth/delete-user/delete-user.js +53 -53
- package/dist/components/auth/theme/theme-toggle-item.js +50 -46
- package/dist/components/auth/user/user-button.d.ts +27 -2
- package/dist/components/auth/user/user-button.js +48 -29
- package/package.json +12 -12
- package/src/components/auth/delete-user/danger-zone.tsx +1 -1
- package/src/components/auth/delete-user/delete-user.tsx +2 -7
- package/src/components/auth/theme/theme-toggle-item.tsx +2 -1
- package/src/components/auth/user/user-button.tsx +80 -13
|
@@ -9,7 +9,7 @@ function a({ className: a, variant: o, ...s }) {
|
|
|
9
9
|
className: n("flex w-full flex-col", a),
|
|
10
10
|
...s,
|
|
11
11
|
children: [/* @__PURE__ */ r("h2", {
|
|
12
|
-
className: n("text-sm font-semibold mb-3"),
|
|
12
|
+
className: n("text-sm font-semibold mb-3 text-danger"),
|
|
13
13
|
children: c.settings.dangerZone
|
|
14
14
|
}), /* @__PURE__ */ r(e, { variant: o })]
|
|
15
15
|
});
|
|
@@ -2,88 +2,88 @@ import { deleteUserPlugin as e } from "../../../lib/auth/delete-user-plugin.js";
|
|
|
2
2
|
import { authQueryKeys as t } from "@better-auth-ui/core";
|
|
3
3
|
import { useAuth as n, useAuthPlugin as r, useDeleteUser as i, useListAccounts as a } from "@better-auth-ui/react";
|
|
4
4
|
import { TriangleExclamation as o } from "@gravity-ui/icons";
|
|
5
|
-
import { AlertDialog as s, Button as c, Card as l, FieldError as u, Form as d, Input as f, Label as p, Spinner as m, TextField as h,
|
|
6
|
-
import { useState as
|
|
7
|
-
import { jsx as
|
|
8
|
-
import { useQueryClient as
|
|
5
|
+
import { AlertDialog as s, Button as c, Card as l, FieldError as u, Form as d, Input as f, Label as p, Spinner as m, TextField as h, toast as g } from "@heroui/react";
|
|
6
|
+
import { useState as _ } from "react";
|
|
7
|
+
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
8
|
+
import { useQueryClient as b } from "@tanstack/react-query";
|
|
9
9
|
//#region src/components/auth/delete-user/delete-user.tsx
|
|
10
|
-
function
|
|
11
|
-
let { authClient:
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
e.preventDefault(),
|
|
15
|
-
|
|
16
|
-
to: `${
|
|
10
|
+
function x({ className: x, variant: S, ...C }) {
|
|
11
|
+
let { authClient: w, basePaths: T, localization: E, navigate: D, viewPaths: O } = n(), { localization: k, sendDeleteAccountVerification: A } = r(e), { data: j } = a(w), M = b(), [N, P] = _(!1), [F, I] = _(""), L = j?.some((e) => e.providerId === "credential"), R = !A && L, { mutate: z, isPending: B } = i(w), V = (e) => {
|
|
12
|
+
P(e), I("");
|
|
13
|
+
}, H = async (e) => {
|
|
14
|
+
e.preventDefault(), z({ ...R ? { password: F } : {} }, { onSuccess: () => {
|
|
15
|
+
P(!1), I(""), A ? g.success(k.deleteUserVerificationSent) : (g.success(k.deleteUserSuccess), M.removeQueries({ queryKey: t.all }), D({
|
|
16
|
+
to: `${T.auth}/${O.auth.signIn}`,
|
|
17
17
|
replace: !0
|
|
18
18
|
}));
|
|
19
19
|
} });
|
|
20
20
|
};
|
|
21
|
-
return /* @__PURE__ */
|
|
22
|
-
className:
|
|
23
|
-
variant:
|
|
24
|
-
...
|
|
25
|
-
children: /* @__PURE__ */
|
|
21
|
+
return /* @__PURE__ */ v(l, {
|
|
22
|
+
className: x,
|
|
23
|
+
variant: S,
|
|
24
|
+
...C,
|
|
25
|
+
children: /* @__PURE__ */ y(l.Content, {
|
|
26
26
|
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
27
|
-
children: [/* @__PURE__ */
|
|
27
|
+
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
28
28
|
className: "text-sm font-medium leading-tight",
|
|
29
|
-
children:
|
|
30
|
-
}), /* @__PURE__ */
|
|
29
|
+
children: k.deleteUser
|
|
30
|
+
}), /* @__PURE__ */ v("p", {
|
|
31
31
|
className: "text-muted text-xs mt-0.5",
|
|
32
|
-
children:
|
|
33
|
-
})] }), /* @__PURE__ */
|
|
34
|
-
isDisabled: !
|
|
32
|
+
children: k.deleteUserDescription
|
|
33
|
+
})] }), /* @__PURE__ */ y(s, { children: [/* @__PURE__ */ v(c, {
|
|
34
|
+
isDisabled: !j,
|
|
35
35
|
size: "sm",
|
|
36
|
-
variant: "danger",
|
|
37
|
-
onPress: () =>
|
|
38
|
-
children:
|
|
39
|
-
}), /* @__PURE__ */
|
|
40
|
-
isOpen:
|
|
41
|
-
onOpenChange:
|
|
42
|
-
children: /* @__PURE__ */
|
|
43
|
-
onSubmit:
|
|
36
|
+
variant: "danger-soft",
|
|
37
|
+
onPress: () => P(!0),
|
|
38
|
+
children: k.deleteUser
|
|
39
|
+
}), /* @__PURE__ */ v(s.Backdrop, {
|
|
40
|
+
isOpen: N,
|
|
41
|
+
onOpenChange: V,
|
|
42
|
+
children: /* @__PURE__ */ v(s.Container, { children: /* @__PURE__ */ v(s.Dialog, { children: /* @__PURE__ */ y(d, {
|
|
43
|
+
onSubmit: H,
|
|
44
44
|
children: [
|
|
45
|
-
/* @__PURE__ */
|
|
46
|
-
/* @__PURE__ */
|
|
45
|
+
/* @__PURE__ */ v(s.CloseTrigger, {}),
|
|
46
|
+
/* @__PURE__ */ y(s.Header, { children: [/* @__PURE__ */ v(s.Icon, {
|
|
47
47
|
status: "danger",
|
|
48
|
-
children: /* @__PURE__ */
|
|
49
|
-
}), /* @__PURE__ */
|
|
50
|
-
/* @__PURE__ */
|
|
48
|
+
children: /* @__PURE__ */ v(o, {})
|
|
49
|
+
}), /* @__PURE__ */ v(s.Heading, { children: k.deleteUser })] }),
|
|
50
|
+
/* @__PURE__ */ y(s.Body, {
|
|
51
51
|
className: "overflow-visible",
|
|
52
|
-
children: [/* @__PURE__ */
|
|
52
|
+
children: [/* @__PURE__ */ v("p", {
|
|
53
53
|
className: "text-muted text-sm",
|
|
54
|
-
children:
|
|
55
|
-
}),
|
|
54
|
+
children: k.deleteUserDescription
|
|
55
|
+
}), R && /* @__PURE__ */ y(h, {
|
|
56
56
|
className: "mt-4",
|
|
57
57
|
name: "password",
|
|
58
58
|
type: "password",
|
|
59
|
-
isDisabled:
|
|
60
|
-
value:
|
|
61
|
-
onChange:
|
|
59
|
+
isDisabled: B,
|
|
60
|
+
value: F,
|
|
61
|
+
onChange: I,
|
|
62
62
|
children: [
|
|
63
|
-
/* @__PURE__ */
|
|
64
|
-
/* @__PURE__ */
|
|
63
|
+
/* @__PURE__ */ v(p, { children: E.auth.password }),
|
|
64
|
+
/* @__PURE__ */ v(f, {
|
|
65
65
|
autoComplete: "current-password",
|
|
66
|
-
placeholder:
|
|
66
|
+
placeholder: E.auth.passwordPlaceholder,
|
|
67
67
|
required: !0,
|
|
68
68
|
variant: "secondary"
|
|
69
69
|
}),
|
|
70
|
-
/* @__PURE__ */
|
|
70
|
+
/* @__PURE__ */ v(u, {})
|
|
71
71
|
]
|
|
72
72
|
})]
|
|
73
73
|
}),
|
|
74
|
-
/* @__PURE__ */
|
|
74
|
+
/* @__PURE__ */ y(s.Footer, { children: [/* @__PURE__ */ v(c, {
|
|
75
75
|
slot: "close",
|
|
76
76
|
variant: "tertiary",
|
|
77
|
-
isDisabled:
|
|
78
|
-
children:
|
|
79
|
-
}), /* @__PURE__ */
|
|
77
|
+
isDisabled: B,
|
|
78
|
+
children: E.settings.cancel
|
|
79
|
+
}), /* @__PURE__ */ y(c, {
|
|
80
80
|
type: "submit",
|
|
81
81
|
variant: "danger",
|
|
82
|
-
isPending:
|
|
83
|
-
children: [
|
|
82
|
+
isPending: B,
|
|
83
|
+
children: [B && /* @__PURE__ */ v(m, {
|
|
84
84
|
color: "current",
|
|
85
85
|
size: "sm"
|
|
86
|
-
}),
|
|
86
|
+
}), k.deleteUser]
|
|
87
87
|
})] })
|
|
88
88
|
]
|
|
89
89
|
}) }) })
|
|
@@ -92,4 +92,4 @@ function S({ className: S, variant: C, ...w }) {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
//#endregion
|
|
95
|
-
export {
|
|
95
|
+
export { x as DeleteUser };
|
|
@@ -1,58 +1,62 @@
|
|
|
1
1
|
import { themePlugin as e } from "../../../lib/auth/theme-plugin.js";
|
|
2
2
|
import { useAuthPlugin as t } from "@better-auth-ui/react";
|
|
3
|
-
import { Display as n, Moon as r,
|
|
4
|
-
import { Dropdown as
|
|
5
|
-
import { jsx as
|
|
3
|
+
import { Display as n, Moon as r, Palette as i, Sun as a } from "@gravity-ui/icons";
|
|
4
|
+
import { Dropdown as o, Label as s, Tabs as c } from "@heroui/react";
|
|
5
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/auth/theme/theme-toggle-item.tsx
|
|
7
|
-
var
|
|
8
|
-
function
|
|
9
|
-
let { useTheme:
|
|
10
|
-
return /* @__PURE__ */
|
|
7
|
+
var d = "[role=\"tab\"][aria-selected=\"true\"]";
|
|
8
|
+
function f() {
|
|
9
|
+
let { useTheme: f, localization: p } = t(e), { theme: m, setTheme: h, themes: g = [] } = f();
|
|
10
|
+
return /* @__PURE__ */ u(o.Item, {
|
|
11
11
|
className: "py-1 pe-2",
|
|
12
12
|
onFocus: (e) => {
|
|
13
|
-
e.target === e.currentTarget && e.currentTarget.querySelector(
|
|
13
|
+
e.target === e.currentTarget && e.currentTarget.querySelector(d)?.focus({ preventScroll: !0 });
|
|
14
14
|
},
|
|
15
15
|
shouldCloseOnSelect: !1,
|
|
16
|
-
children: [
|
|
17
|
-
className: "
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
children:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ l(i, { className: "text-muted" }),
|
|
18
|
+
/* @__PURE__ */ l(s, { children: p.theme }),
|
|
19
|
+
/* @__PURE__ */ l(c, {
|
|
20
|
+
className: "ml-auto",
|
|
21
|
+
selectedKey: m,
|
|
22
|
+
onSelectionChange: (e) => h(e),
|
|
23
|
+
children: /* @__PURE__ */ l(c.ListContainer, {
|
|
24
|
+
onKeyDown: (e) => {
|
|
25
|
+
if (e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
|
|
26
|
+
let t = e.target;
|
|
27
|
+
if (t.getAttribute("role") !== "tab") return;
|
|
28
|
+
let n = t.closest("[role=\"menuitem\"]"), r = n?.closest("[role=\"menu\"]");
|
|
29
|
+
if (!n || !r) return;
|
|
30
|
+
let i = Array.from(r.querySelectorAll("[role=\"menuitem\"]:not([data-disabled])")), a = i.indexOf(n);
|
|
31
|
+
if (a === -1) return;
|
|
32
|
+
let o = i[e.key === "ArrowDown" ? a + 1 : a - 1];
|
|
33
|
+
o && (e.preventDefault(), o.focus());
|
|
34
|
+
},
|
|
35
|
+
children: /* @__PURE__ */ u(c.List, {
|
|
36
|
+
"aria-label": p.theme,
|
|
37
|
+
className: "*:h-5 *:w-5 *:p-0",
|
|
38
|
+
children: [
|
|
39
|
+
g.includes("system") && /* @__PURE__ */ u(c.Tab, {
|
|
40
|
+
id: "system",
|
|
41
|
+
"aria-label": p.system,
|
|
42
|
+
children: [/* @__PURE__ */ l(n, { className: "size-3" }), /* @__PURE__ */ l(c.Indicator, {})]
|
|
43
|
+
}),
|
|
44
|
+
g.includes("light") && /* @__PURE__ */ u(c.Tab, {
|
|
45
|
+
id: "light",
|
|
46
|
+
"aria-label": p.light,
|
|
47
|
+
children: [/* @__PURE__ */ l(a, { className: "size-3" }), /* @__PURE__ */ l(c.Indicator, {})]
|
|
48
|
+
}),
|
|
49
|
+
g.includes("dark") && /* @__PURE__ */ u(c.Tab, {
|
|
50
|
+
id: "dark",
|
|
51
|
+
"aria-label": p.dark,
|
|
52
|
+
children: [/* @__PURE__ */ l(r, { className: "size-3" }), /* @__PURE__ */ l(c.Indicator, {})]
|
|
53
|
+
})
|
|
54
|
+
]
|
|
55
|
+
})
|
|
52
56
|
})
|
|
53
57
|
})
|
|
54
|
-
|
|
58
|
+
]
|
|
55
59
|
});
|
|
56
60
|
}
|
|
57
61
|
//#endregion
|
|
58
|
-
export {
|
|
62
|
+
export { f as ThemeToggleItem };
|
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
import { ButtonProps, DropdownPopoverProps } from '@heroui/react';
|
|
1
|
+
import { ButtonProps, DropdownItemProps, DropdownPopoverProps } from '@heroui/react';
|
|
2
|
+
import { ReactElement, ReactNode } from 'react';
|
|
3
|
+
/** Auth states a `UserButton` link can be visible in. */
|
|
4
|
+
export type UserButtonLinkVisibility = "authenticated" | "unauthenticated" | "always";
|
|
5
|
+
/** A simple link entry rendered as a `Dropdown.Item` in the `UserButton` menu. */
|
|
6
|
+
export type UserButtonLink = {
|
|
7
|
+
/** Visible label. */
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
/** Destination URL. */
|
|
10
|
+
href: string;
|
|
11
|
+
/** Optional leading icon. Sized/coloured to match built-in items. */
|
|
12
|
+
icon?: ReactNode;
|
|
13
|
+
/** Forwarded to the underlying `Dropdown.Item`. */
|
|
14
|
+
variant?: DropdownItemProps["variant"];
|
|
15
|
+
/**
|
|
16
|
+
* When this link is visible based on auth state.
|
|
17
|
+
* @default "always"
|
|
18
|
+
*/
|
|
19
|
+
visibility?: UserButtonLinkVisibility;
|
|
20
|
+
};
|
|
2
21
|
export type UserButtonProps = {
|
|
3
22
|
className?: string;
|
|
4
23
|
size?: "default" | "icon";
|
|
@@ -8,6 +27,10 @@ export type UserButtonProps = {
|
|
|
8
27
|
*/
|
|
9
28
|
placement?: DropdownPopoverProps["placement"];
|
|
10
29
|
variant?: ButtonProps["variant"];
|
|
30
|
+
/** Additional menu entries rendered above the built-in items. */
|
|
31
|
+
links?: (UserButtonLink | ReactElement)[];
|
|
32
|
+
/** Hide the built-in "Settings" link. Useful when replacing it via `links`. */
|
|
33
|
+
hideSettings?: boolean;
|
|
11
34
|
};
|
|
12
35
|
/**
|
|
13
36
|
* Render a user account dropdown button that shows account actions.
|
|
@@ -16,6 +39,8 @@ export type UserButtonProps = {
|
|
|
16
39
|
* @param placement - Dropdown popover placement (e.g., "bottom", "top-start", "bottom-end")
|
|
17
40
|
* @param size - "icon" renders an avatar-only trigger; "default" renders a button with label and chevron
|
|
18
41
|
* @param variant - Button visual variant passed to the underlying Button component
|
|
42
|
+
* @param links - Additional menu entries rendered above the built-in items
|
|
43
|
+
* @param hideSettings - Hide the built-in "Settings" link
|
|
19
44
|
* @returns The user button and its dropdown menu as a JSX element
|
|
20
45
|
*/
|
|
21
|
-
export declare function UserButton({ className, placement, size, variant }: UserButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
export declare function UserButton({ className, placement, size, variant, links, hideSettings }: UserButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,54 +2,73 @@ import { UserAvatar as e } from "./user-avatar.js";
|
|
|
2
2
|
import { UserView as t } from "./user-view.js";
|
|
3
3
|
import { useAuth as n, useSession as r } from "@better-auth-ui/react";
|
|
4
4
|
import { ArrowRightFromSquare as i, ArrowRightToSquare as a, ChevronsExpandVertical as o, Gear as s, PersonPlus as c } from "@gravity-ui/icons";
|
|
5
|
-
import { Button as l, Dropdown as u, Label as d,
|
|
5
|
+
import { Button as l, Dropdown as u, Label as d, cn as f } from "@heroui/react";
|
|
6
|
+
import { isValidElement as p } from "react";
|
|
6
7
|
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/auth/user/user-button.tsx
|
|
8
|
-
function _(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
function _(e, t) {
|
|
10
|
+
if (p(e)) return e;
|
|
11
|
+
let { label: n, href: r, icon: i, variant: a } = e;
|
|
12
|
+
return /* @__PURE__ */ g(u.Item, {
|
|
13
|
+
href: r,
|
|
14
|
+
variant: a,
|
|
15
|
+
textValue: typeof n == "string" ? n : void 0,
|
|
16
|
+
children: [i, /* @__PURE__ */ h(d, { children: n })]
|
|
17
|
+
}, t);
|
|
18
|
+
}
|
|
19
|
+
function v({ className: v, placement: y = "bottom", size: b = "default", variant: x = "ghost", links: S, hideSettings: C = !1 }) {
|
|
20
|
+
let { authClient: w, basePaths: T, viewPaths: E, localization: D, plugins: O } = n(), { data: k, isPending: A } = r(w), j = O.flatMap((e) => e.userMenuItems?.map((t, n) => /* @__PURE__ */ h(t, {}, `${e.id}-${n.toString()}`)) ?? []), M = S?.flatMap((e, t) => {
|
|
21
|
+
if (!p(e)) {
|
|
22
|
+
let t = e.visibility ?? "always";
|
|
23
|
+
if (t === "authenticated" && !k || t === "unauthenticated" && k) return [];
|
|
24
|
+
}
|
|
25
|
+
return [_(e, `user-button-link-${t.toString()}`)];
|
|
26
|
+
});
|
|
27
|
+
return /* @__PURE__ */ g(u, { children: [b === "icon" ? /* @__PURE__ */ h(u.Trigger, {
|
|
28
|
+
className: f("rounded-full", v),
|
|
12
29
|
children: /* @__PURE__ */ h(e, {})
|
|
13
30
|
}) : /* @__PURE__ */ g(l, {
|
|
14
|
-
variant:
|
|
15
|
-
className:
|
|
16
|
-
children: [
|
|
31
|
+
variant: x,
|
|
32
|
+
className: f("h-auto font-normal justify-start px-3 py-2 text-left", v),
|
|
33
|
+
children: [k || A ? /* @__PURE__ */ h(t, { isPending: A }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(e, {}), /* @__PURE__ */ h("p", {
|
|
17
34
|
className: "text-sm font-medium",
|
|
18
|
-
children:
|
|
35
|
+
children: D.auth.account
|
|
19
36
|
})] }), /* @__PURE__ */ h(o, { className: "ml-auto size-3.5" })]
|
|
20
37
|
}), /* @__PURE__ */ g(u.Popover, {
|
|
21
|
-
placement:
|
|
38
|
+
placement: y,
|
|
22
39
|
className: "min-w-40 md:min-w-56 max-w-[48svw]",
|
|
23
|
-
children: [
|
|
40
|
+
children: [k && /* @__PURE__ */ h("div", {
|
|
24
41
|
className: "px-3 pt-3 pb-1",
|
|
25
42
|
children: /* @__PURE__ */ h(t, {})
|
|
26
|
-
}), /* @__PURE__ */ h(u.Menu, { children:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
}), /* @__PURE__ */ h(u.Menu, { children: k ? /* @__PURE__ */ g(m, { children: [
|
|
44
|
+
M,
|
|
45
|
+
!C && /* @__PURE__ */ g(u.Item, {
|
|
46
|
+
textValue: D.settings.settings,
|
|
47
|
+
href: `${T.settings}/${E.settings.account}`,
|
|
48
|
+
children: [/* @__PURE__ */ h(s, { className: "text-muted" }), /* @__PURE__ */ h(d, { children: D.settings.settings })]
|
|
31
49
|
}),
|
|
32
|
-
|
|
33
|
-
/* @__PURE__ */ h(f, {}),
|
|
50
|
+
j,
|
|
34
51
|
/* @__PURE__ */ g(u.Item, {
|
|
35
|
-
textValue:
|
|
36
|
-
href: `${
|
|
37
|
-
|
|
52
|
+
textValue: D.auth.signOut,
|
|
53
|
+
href: `${T.auth}/${E.auth.signOut}`,
|
|
54
|
+
variant: "danger",
|
|
55
|
+
children: [/* @__PURE__ */ h(i, { className: "text-danger" }), /* @__PURE__ */ h(d, { children: D.auth.signOut })]
|
|
38
56
|
})
|
|
39
57
|
] }) : /* @__PURE__ */ g(m, { children: [
|
|
58
|
+
M,
|
|
40
59
|
/* @__PURE__ */ g(u.Item, {
|
|
41
|
-
textValue:
|
|
42
|
-
href: `${
|
|
43
|
-
children: [/* @__PURE__ */ h(a, { className: "text-muted" }), /* @__PURE__ */ h(d, { children:
|
|
60
|
+
textValue: D.auth.signIn,
|
|
61
|
+
href: `${T.auth}/${E.auth.signIn}`,
|
|
62
|
+
children: [/* @__PURE__ */ h(a, { className: "text-muted" }), /* @__PURE__ */ h(d, { children: D.auth.signIn })]
|
|
44
63
|
}),
|
|
45
64
|
/* @__PURE__ */ g(u.Item, {
|
|
46
|
-
textValue:
|
|
47
|
-
href: `${
|
|
48
|
-
children: [/* @__PURE__ */ h(c, { className: "text-muted" }), /* @__PURE__ */ h(d, { children:
|
|
65
|
+
textValue: D.auth.signUp,
|
|
66
|
+
href: `${T.auth}/${E.auth.signUp}`,
|
|
67
|
+
children: [/* @__PURE__ */ h(c, { className: "text-muted" }), /* @__PURE__ */ h(d, { children: D.auth.signUp })]
|
|
49
68
|
}),
|
|
50
|
-
|
|
69
|
+
j
|
|
51
70
|
] }) })]
|
|
52
71
|
})] });
|
|
53
72
|
}
|
|
54
73
|
//#endregion
|
|
55
|
-
export {
|
|
74
|
+
export { v as UserButton };
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth-ui/heroui",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "vite build",
|
|
7
|
-
"dev": "
|
|
7
|
+
"dev": "tsc --watch",
|
|
8
8
|
"test": "vitest"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@gravity-ui/icons": "^2.18.0",
|
|
31
|
-
"@heroui/react": "^3.0.
|
|
32
|
-
"@heroui/styles": "^3.0.
|
|
31
|
+
"@heroui/react": "^3.0.4",
|
|
32
|
+
"@heroui/styles": "^3.0.4",
|
|
33
33
|
"@internationalized/date": "^3.12.1",
|
|
34
34
|
"@tanstack/react-pacer": "^0.22.0",
|
|
35
|
-
"@tanstack/react-query": "^5.100.
|
|
35
|
+
"@tanstack/react-query": "^5.100.9",
|
|
36
36
|
"@testing-library/react": "^16.3.2",
|
|
37
37
|
"@testing-library/user-event": "^14.5.2",
|
|
38
38
|
"@types/react": "^19.2.14",
|
|
@@ -41,22 +41,22 @@
|
|
|
41
41
|
"better-auth": "^1.6.9",
|
|
42
42
|
"bowser": "^2.11.0",
|
|
43
43
|
"playwright": "^1.59.1",
|
|
44
|
-
"react": "^19.2.
|
|
45
|
-
"react-dom": "^19.2.
|
|
44
|
+
"react": "^19.2.6",
|
|
45
|
+
"react-dom": "^19.2.6",
|
|
46
46
|
"vitest": "^4.1.5"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@better-auth-ui/core": "*",
|
|
50
50
|
"@better-auth-ui/react": "*",
|
|
51
51
|
"@gravity-ui/icons": ">=2.18.0",
|
|
52
|
-
"@heroui/react": ">=3.0.
|
|
53
|
-
"@heroui/styles": ">=3.0.
|
|
52
|
+
"@heroui/react": ">=3.0.4",
|
|
53
|
+
"@heroui/styles": ">=3.0.4",
|
|
54
54
|
"@internationalized/date": ">=3.12.1",
|
|
55
55
|
"@tanstack/react-pacer": ">=0.22.0",
|
|
56
|
-
"@tanstack/react-query": ">=5.100.
|
|
56
|
+
"@tanstack/react-query": ">=5.100.9",
|
|
57
57
|
"better-auth": ">=1.6.9",
|
|
58
|
-
"react": ">=19.2.
|
|
59
|
-
"react-dom": ">=19.2.
|
|
58
|
+
"react": ">=19.2.6",
|
|
59
|
+
"react-dom": ">=19.2.6",
|
|
60
60
|
"bowser": ">=2.11.0"
|
|
61
61
|
},
|
|
62
62
|
"repository": {
|
|
@@ -22,7 +22,7 @@ export function DangerZone({
|
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
24
|
<div className={cn("flex w-full flex-col", className)} {...props}>
|
|
25
|
-
<h2 className={cn("text-sm font-semibold mb-3")}>
|
|
25
|
+
<h2 className={cn("text-sm font-semibold mb-3 text-danger")}>
|
|
26
26
|
{localization.settings.dangerZone}
|
|
27
27
|
</h2>
|
|
28
28
|
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
Button,
|
|
12
12
|
Card,
|
|
13
13
|
type CardProps,
|
|
14
|
-
cn,
|
|
15
14
|
FieldError,
|
|
16
15
|
Form,
|
|
17
16
|
Input,
|
|
@@ -91,11 +90,7 @@ export function DeleteUser({
|
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
return (
|
|
94
|
-
<Card
|
|
95
|
-
className={cn("border border-danger", className)}
|
|
96
|
-
variant={variant}
|
|
97
|
-
{...props}
|
|
98
|
-
>
|
|
93
|
+
<Card className={className} variant={variant} {...props}>
|
|
99
94
|
<Card.Content className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
100
95
|
<div>
|
|
101
96
|
<p className="text-sm font-medium leading-tight">
|
|
@@ -111,7 +106,7 @@ export function DeleteUser({
|
|
|
111
106
|
<Button
|
|
112
107
|
isDisabled={!accounts}
|
|
113
108
|
size="sm"
|
|
114
|
-
variant="danger"
|
|
109
|
+
variant="danger-soft"
|
|
115
110
|
onPress={() => setConfirmOpen(true)}
|
|
116
111
|
>
|
|
117
112
|
{deleteUserLocalization.deleteUser}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useAuthPlugin } from "@better-auth-ui/react"
|
|
2
|
-
import { Display, Moon, Sun } from "@gravity-ui/icons"
|
|
2
|
+
import { Display, Moon, Palette, Sun } from "@gravity-ui/icons"
|
|
3
3
|
import { Dropdown, Label, Tabs } from "@heroui/react"
|
|
4
4
|
|
|
5
5
|
import { themePlugin } from "../../../lib/auth/theme-plugin"
|
|
@@ -67,6 +67,7 @@ export function ThemeToggleItem() {
|
|
|
67
67
|
// menu-item activation.
|
|
68
68
|
shouldCloseOnSelect={false}
|
|
69
69
|
>
|
|
70
|
+
<Palette className="text-muted" />
|
|
70
71
|
<Label>{localization.theme}</Label>
|
|
71
72
|
|
|
72
73
|
<Tabs
|
|
@@ -11,14 +11,38 @@ import {
|
|
|
11
11
|
type ButtonProps,
|
|
12
12
|
cn,
|
|
13
13
|
Dropdown,
|
|
14
|
+
type DropdownItemProps,
|
|
14
15
|
type DropdownPopoverProps,
|
|
15
|
-
Label
|
|
16
|
-
Separator
|
|
16
|
+
Label
|
|
17
17
|
} from "@heroui/react"
|
|
18
|
+
import { isValidElement, type ReactElement, type ReactNode } from "react"
|
|
18
19
|
|
|
19
20
|
import { UserAvatar } from "./user-avatar"
|
|
20
21
|
import { UserView } from "./user-view"
|
|
21
22
|
|
|
23
|
+
/** Auth states a `UserButton` link can be visible in. */
|
|
24
|
+
export type UserButtonLinkVisibility =
|
|
25
|
+
| "authenticated"
|
|
26
|
+
| "unauthenticated"
|
|
27
|
+
| "always"
|
|
28
|
+
|
|
29
|
+
/** A simple link entry rendered as a `Dropdown.Item` in the `UserButton` menu. */
|
|
30
|
+
export type UserButtonLink = {
|
|
31
|
+
/** Visible label. */
|
|
32
|
+
label: ReactNode
|
|
33
|
+
/** Destination URL. */
|
|
34
|
+
href: string
|
|
35
|
+
/** Optional leading icon. Sized/coloured to match built-in items. */
|
|
36
|
+
icon?: ReactNode
|
|
37
|
+
/** Forwarded to the underlying `Dropdown.Item`. */
|
|
38
|
+
variant?: DropdownItemProps["variant"]
|
|
39
|
+
/**
|
|
40
|
+
* When this link is visible based on auth state.
|
|
41
|
+
* @default "always"
|
|
42
|
+
*/
|
|
43
|
+
visibility?: UserButtonLinkVisibility
|
|
44
|
+
}
|
|
45
|
+
|
|
22
46
|
export type UserButtonProps = {
|
|
23
47
|
className?: string
|
|
24
48
|
size?: "default" | "icon"
|
|
@@ -28,6 +52,30 @@ export type UserButtonProps = {
|
|
|
28
52
|
*/
|
|
29
53
|
placement?: DropdownPopoverProps["placement"]
|
|
30
54
|
variant?: ButtonProps["variant"]
|
|
55
|
+
/** Additional menu entries rendered above the built-in items. */
|
|
56
|
+
links?: (UserButtonLink | ReactElement)[]
|
|
57
|
+
/** Hide the built-in "Settings" link. Useful when replacing it via `links`. */
|
|
58
|
+
hideSettings?: boolean
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function renderUserLink(
|
|
62
|
+
link: UserButtonLink | ReactElement,
|
|
63
|
+
fallbackKey: string
|
|
64
|
+
): ReactNode {
|
|
65
|
+
if (isValidElement(link)) return link
|
|
66
|
+
|
|
67
|
+
const { label, href, icon, variant } = link
|
|
68
|
+
return (
|
|
69
|
+
<Dropdown.Item
|
|
70
|
+
key={fallbackKey}
|
|
71
|
+
href={href}
|
|
72
|
+
variant={variant}
|
|
73
|
+
textValue={typeof label === "string" ? label : undefined}
|
|
74
|
+
>
|
|
75
|
+
{icon}
|
|
76
|
+
<Label>{label}</Label>
|
|
77
|
+
</Dropdown.Item>
|
|
78
|
+
)
|
|
31
79
|
}
|
|
32
80
|
|
|
33
81
|
/**
|
|
@@ -37,13 +85,17 @@ export type UserButtonProps = {
|
|
|
37
85
|
* @param placement - Dropdown popover placement (e.g., "bottom", "top-start", "bottom-end")
|
|
38
86
|
* @param size - "icon" renders an avatar-only trigger; "default" renders a button with label and chevron
|
|
39
87
|
* @param variant - Button visual variant passed to the underlying Button component
|
|
88
|
+
* @param links - Additional menu entries rendered above the built-in items
|
|
89
|
+
* @param hideSettings - Hide the built-in "Settings" link
|
|
40
90
|
* @returns The user button and its dropdown menu as a JSX element
|
|
41
91
|
*/
|
|
42
92
|
export function UserButton({
|
|
43
93
|
className,
|
|
44
94
|
placement = "bottom",
|
|
45
95
|
size = "default",
|
|
46
|
-
variant = "ghost"
|
|
96
|
+
variant = "ghost",
|
|
97
|
+
links,
|
|
98
|
+
hideSettings = false
|
|
47
99
|
}: UserButtonProps) {
|
|
48
100
|
const { authClient, basePaths, viewPaths, localization, plugins } = useAuth()
|
|
49
101
|
|
|
@@ -55,6 +107,16 @@ export function UserButton({
|
|
|
55
107
|
<Item key={`${plugin.id}-${index.toString()}`} />
|
|
56
108
|
)) ?? []
|
|
57
109
|
)
|
|
110
|
+
|
|
111
|
+
const userLinks = links?.flatMap((link, index) => {
|
|
112
|
+
if (!isValidElement(link)) {
|
|
113
|
+
const visibility = link.visibility ?? "always"
|
|
114
|
+
if (visibility === "authenticated" && !session) return []
|
|
115
|
+
if (visibility === "unauthenticated" && session) return []
|
|
116
|
+
}
|
|
117
|
+
return [renderUserLink(link, `user-button-link-${index.toString()}`)]
|
|
118
|
+
})
|
|
119
|
+
|
|
58
120
|
return (
|
|
59
121
|
<Dropdown>
|
|
60
122
|
{size === "icon" ? (
|
|
@@ -96,30 +158,35 @@ export function UserButton({
|
|
|
96
158
|
<Dropdown.Menu>
|
|
97
159
|
{session ? (
|
|
98
160
|
<>
|
|
99
|
-
|
|
100
|
-
textValue={localization.settings.settings}
|
|
101
|
-
href={`${basePaths.settings}/${viewPaths.settings.account}`}
|
|
102
|
-
>
|
|
103
|
-
<Gear className="text-muted" />
|
|
161
|
+
{userLinks}
|
|
104
162
|
|
|
105
|
-
|
|
106
|
-
|
|
163
|
+
{!hideSettings && (
|
|
164
|
+
<Dropdown.Item
|
|
165
|
+
textValue={localization.settings.settings}
|
|
166
|
+
href={`${basePaths.settings}/${viewPaths.settings.account}`}
|
|
167
|
+
>
|
|
168
|
+
<Gear className="text-muted" />
|
|
107
169
|
|
|
108
|
-
|
|
170
|
+
<Label>{localization.settings.settings}</Label>
|
|
171
|
+
</Dropdown.Item>
|
|
172
|
+
)}
|
|
109
173
|
|
|
110
|
-
|
|
174
|
+
{userMenuItems}
|
|
111
175
|
|
|
112
176
|
<Dropdown.Item
|
|
113
177
|
textValue={localization.auth.signOut}
|
|
114
178
|
href={`${basePaths.auth}/${viewPaths.auth.signOut}`}
|
|
179
|
+
variant="danger"
|
|
115
180
|
>
|
|
116
|
-
<ArrowRightFromSquare className="text-
|
|
181
|
+
<ArrowRightFromSquare className="text-danger" />
|
|
117
182
|
|
|
118
183
|
<Label>{localization.auth.signOut}</Label>
|
|
119
184
|
</Dropdown.Item>
|
|
120
185
|
</>
|
|
121
186
|
) : (
|
|
122
187
|
<>
|
|
188
|
+
{userLinks}
|
|
189
|
+
|
|
123
190
|
<Dropdown.Item
|
|
124
191
|
textValue={localization.auth.signIn}
|
|
125
192
|
href={`${basePaths.auth}/${viewPaths.auth.signIn}`}
|