@better-auth-ui/heroui 1.6.5 → 1.6.7

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 (49) hide show
  1. package/dist/components/auth/api-key/api-key-skeleton.d.ts +1 -0
  2. package/dist/components/auth/api-key/api-key-skeleton.js +21 -0
  3. package/dist/components/auth/api-key/api-key.d.ts +5 -0
  4. package/dist/components/auth/api-key/api-key.js +55 -0
  5. package/dist/components/auth/api-key/api-keys-empty.d.ts +4 -0
  6. package/dist/components/auth/api-key/api-keys-empty.js +35 -0
  7. package/dist/components/auth/api-key/api-keys.d.ts +6 -0
  8. package/dist/components/auth/api-key/api-keys.js +41 -0
  9. package/dist/components/auth/api-key/create-api-key-dialog.d.ts +5 -0
  10. package/dist/components/auth/api-key/create-api-key-dialog.js +74 -0
  11. package/dist/components/auth/api-key/delete-api-key-dialog.d.ts +7 -0
  12. package/dist/components/auth/api-key/delete-api-key-dialog.js +51 -0
  13. package/dist/components/auth/api-key/new-api-key-dialog.d.ts +7 -0
  14. package/dist/components/auth/api-key/new-api-key-dialog.js +60 -0
  15. package/dist/components/auth/delete-user/danger-zone.js +1 -1
  16. package/dist/components/auth/delete-user/delete-user.js +53 -53
  17. package/dist/components/auth/multi-session/switch-account-submenu-content.js +14 -15
  18. package/dist/components/auth/passkey/add-passkey-dialog.d.ts +0 -0
  19. package/dist/components/auth/passkey/delete-passkey-dialog.d.ts +0 -0
  20. package/dist/components/auth/passkey/passkey-skeleton.d.ts +0 -0
  21. package/dist/components/auth/passkey/passkeys-empty.d.ts +0 -0
  22. package/dist/components/auth/settings/security/linked-account.js +1 -1
  23. package/dist/components/auth/theme/theme-toggle-item.js +50 -46
  24. package/dist/components/auth/user/user-button.d.ts +27 -2
  25. package/dist/components/auth/user/user-button.js +48 -29
  26. package/dist/lib/auth/api-key-plugin.d.ts +23 -0
  27. package/dist/lib/auth/api-key-plugin.js +10 -0
  28. package/dist/plugins.d.ts +2 -0
  29. package/dist/plugins.js +22 -20
  30. package/package.json +14 -14
  31. package/src/components/auth/api-key/api-key-skeleton.tsx +17 -0
  32. package/src/components/auth/api-key/api-key.tsx +64 -0
  33. package/src/components/auth/api-key/api-keys-empty.tsx +33 -0
  34. package/src/components/auth/api-key/api-keys.tsx +71 -0
  35. package/src/components/auth/api-key/create-api-key-dialog.tsx +134 -0
  36. package/src/components/auth/api-key/delete-api-key-dialog.tsx +92 -0
  37. package/src/components/auth/api-key/new-api-key-dialog.tsx +94 -0
  38. package/src/components/auth/delete-user/danger-zone.tsx +1 -1
  39. package/src/components/auth/delete-user/delete-user.tsx +2 -7
  40. package/src/components/auth/multi-session/switch-account-submenu-content.tsx +1 -3
  41. package/src/components/auth/passkey/add-passkey-dialog.tsx +0 -0
  42. package/src/components/auth/passkey/delete-passkey-dialog.tsx +0 -0
  43. package/src/components/auth/passkey/passkey-skeleton.tsx +0 -0
  44. package/src/components/auth/passkey/passkeys-empty.tsx +0 -0
  45. package/src/components/auth/settings/security/linked-account.tsx +2 -4
  46. package/src/components/auth/theme/theme-toggle-item.tsx +2 -1
  47. package/src/components/auth/user/user-button.tsx +80 -13
  48. package/src/lib/auth/api-key-plugin.ts +15 -0
  49. package/src/plugins.ts +2 -0
@@ -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, Sun as i } from "@gravity-ui/icons";
4
- import { Dropdown as a, Label as o, Tabs as s } from "@heroui/react";
5
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
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 u = "[role=\"tab\"][aria-selected=\"true\"]";
8
- function d() {
9
- let { useTheme: d, localization: f } = t(e), { theme: p, setTheme: m, themes: h = [] } = d();
10
- return /* @__PURE__ */ l(a.Item, {
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(u)?.focus({ preventScroll: !0 });
13
+ e.target === e.currentTarget && e.currentTarget.querySelector(d)?.focus({ preventScroll: !0 });
14
14
  },
15
15
  shouldCloseOnSelect: !1,
16
- children: [/* @__PURE__ */ c(o, { children: f.theme }), /* @__PURE__ */ c(s, {
17
- className: "ml-auto",
18
- selectedKey: p,
19
- onSelectionChange: (e) => m(e),
20
- children: /* @__PURE__ */ c(s.ListContainer, {
21
- onKeyDown: (e) => {
22
- if (e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
23
- let t = e.target;
24
- if (t.getAttribute("role") !== "tab") return;
25
- let n = t.closest("[role=\"menuitem\"]"), r = n?.closest("[role=\"menu\"]");
26
- if (!n || !r) return;
27
- let i = Array.from(r.querySelectorAll("[role=\"menuitem\"]:not([data-disabled])")), a = i.indexOf(n);
28
- if (a === -1) return;
29
- let o = i[e.key === "ArrowDown" ? a + 1 : a - 1];
30
- o && (e.preventDefault(), o.focus());
31
- },
32
- children: /* @__PURE__ */ l(s.List, {
33
- "aria-label": f.theme,
34
- className: "*:h-5 *:w-5 *:p-0",
35
- children: [
36
- h.includes("system") && /* @__PURE__ */ l(s.Tab, {
37
- id: "system",
38
- "aria-label": f.system,
39
- children: [/* @__PURE__ */ c(n, { className: "size-3" }), /* @__PURE__ */ c(s.Indicator, {})]
40
- }),
41
- h.includes("light") && /* @__PURE__ */ l(s.Tab, {
42
- id: "light",
43
- "aria-label": f.light,
44
- children: [/* @__PURE__ */ c(i, { className: "size-3" }), /* @__PURE__ */ c(s.Indicator, {})]
45
- }),
46
- h.includes("dark") && /* @__PURE__ */ l(s.Tab, {
47
- id: "dark",
48
- "aria-label": f.dark,
49
- children: [/* @__PURE__ */ c(r, { className: "size-3" }), /* @__PURE__ */ c(s.Indicator, {})]
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 { d as ThemeToggleItem };
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, Separator as f, cn as p } from "@heroui/react";
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 _({ className: _, placement: v = "bottom", size: y = "default", variant: b = "ghost" }) {
9
- let { authClient: x, basePaths: S, viewPaths: C, localization: w, plugins: T } = n(), { data: E, isPending: D } = r(x), O = T.flatMap((e) => e.userMenuItems?.map((t, n) => /* @__PURE__ */ h(t, {}, `${e.id}-${n.toString()}`)) ?? []);
10
- return /* @__PURE__ */ g(u, { children: [y === "icon" ? /* @__PURE__ */ h(u.Trigger, {
11
- className: p("rounded-full", _),
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: b,
15
- className: p("h-auto font-normal justify-start px-3 py-2 text-left", _),
16
- children: [E || D ? /* @__PURE__ */ h(t, { isPending: D }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(e, {}), /* @__PURE__ */ h("p", {
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: w.auth.account
35
+ children: D.auth.account
19
36
  })] }), /* @__PURE__ */ h(o, { className: "ml-auto size-3.5" })]
20
37
  }), /* @__PURE__ */ g(u.Popover, {
21
- placement: v,
38
+ placement: y,
22
39
  className: "min-w-40 md:min-w-56 max-w-[48svw]",
23
- children: [E && /* @__PURE__ */ h("div", {
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: E ? /* @__PURE__ */ g(m, { children: [
27
- /* @__PURE__ */ g(u.Item, {
28
- textValue: w.settings.settings,
29
- href: `${S.settings}/${C.settings.account}`,
30
- children: [/* @__PURE__ */ h(s, { className: "text-muted" }), /* @__PURE__ */ h(d, { children: w.settings.settings })]
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
- O,
33
- /* @__PURE__ */ h(f, {}),
50
+ j,
34
51
  /* @__PURE__ */ g(u.Item, {
35
- textValue: w.auth.signOut,
36
- href: `${S.auth}/${C.auth.signOut}`,
37
- children: [/* @__PURE__ */ h(i, { className: "text-muted" }), /* @__PURE__ */ h(d, { children: w.auth.signOut })]
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: w.auth.signIn,
42
- href: `${S.auth}/${C.auth.signIn}`,
43
- children: [/* @__PURE__ */ h(a, { className: "text-muted" }), /* @__PURE__ */ h(d, { children: w.auth.signIn })]
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: w.auth.signUp,
47
- href: `${S.auth}/${C.auth.signUp}`,
48
- children: [/* @__PURE__ */ h(c, { className: "text-muted" }), /* @__PURE__ */ h(d, { children: w.auth.signUp })]
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
- O
69
+ j
51
70
  ] }) })]
52
71
  })] });
53
72
  }
54
73
  //#endregion
55
- export { _ as UserButton };
74
+ export { v as UserButton };
@@ -0,0 +1,23 @@
1
+ import { ApiKeyPluginOptions } from '@better-auth-ui/core/plugins';
2
+ import { ApiKeys } from '../../components/auth/api-key/api-keys';
3
+ export declare const apiKeyPlugin: ((options?: ApiKeyPluginOptions | undefined) => Omit<{
4
+ securityCards: (typeof ApiKeys)[];
5
+ localization: {
6
+ apiKey: string;
7
+ apiKeys: string;
8
+ apiKeysDescription: string;
9
+ createApiKey: string;
10
+ noApiKeys: string;
11
+ name: string;
12
+ newApiKey: string;
13
+ newApiKeyWarning: string;
14
+ deleteApiKey: string;
15
+ deleteApiKeyWarning: string;
16
+ dismissNewKey: string;
17
+ };
18
+ id: "apiKey";
19
+ }, "id"> & {
20
+ id: "apiKey";
21
+ }) & {
22
+ id: "apiKey";
23
+ };
@@ -0,0 +1,10 @@
1
+ import { ApiKeys as e } from "../../components/auth/api-key/api-keys.js";
2
+ import { createAuthPlugin as t } from "@better-auth-ui/core";
3
+ import { apiKeyPlugin as n } from "@better-auth-ui/core/plugins";
4
+ //#region src/lib/auth/api-key-plugin.ts
5
+ var r = t(n.id, (t = {}) => ({
6
+ ...n(t),
7
+ securityCards: [e]
8
+ }));
9
+ //#endregion
10
+ export { r as apiKeyPlugin };
package/dist/plugins.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './components/auth/api-key/api-keys';
1
2
  export * from './components/auth/delete-user/danger-zone';
2
3
  export * from './components/auth/delete-user/delete-user';
3
4
  export * from './components/auth/magic-link/magic-link';
@@ -11,6 +12,7 @@ export * from './components/auth/theme/appearance';
11
12
  export * from './components/auth/theme/theme-toggle-item';
12
13
  export * from './components/auth/username/sign-in-username';
13
14
  export * from './components/auth/username/username-field';
15
+ export * from './lib/auth/api-key-plugin';
14
16
  export * from './lib/auth/auth-plugin';
15
17
  export * from './lib/auth/delete-user-plugin';
16
18
  export * from './lib/auth/magic-link-plugin';
package/dist/plugins.js CHANGED
@@ -1,21 +1,23 @@
1
1
  "use client";
2
- import { deleteUserPlugin as e } from "./lib/auth/delete-user-plugin.js";
3
- import { DeleteUser as t } from "./components/auth/delete-user/delete-user.js";
4
- import { DangerZone as n } from "./components/auth/delete-user/danger-zone.js";
5
- import { magicLinkPlugin as r } from "./lib/auth/magic-link-plugin.js";
6
- import { MagicLink as i } from "./components/auth/magic-link/magic-link.js";
7
- import { ManageAccounts as a } from "./components/auth/multi-session/manage-accounts.js";
8
- import { SwitchAccountSubmenuItem as o } from "./components/auth/multi-session/switch-account-submenu-item.js";
9
- import { SwitchAccountSubmenuContent as s } from "./components/auth/multi-session/switch-account-submenu-content.js";
10
- import { SwitchAccountSubmenu as c } from "./components/auth/multi-session/switch-account-submenu.js";
11
- import { multiSessionPlugin as l } from "./lib/auth/multi-session-plugin.js";
12
- import { ManageAccount as u } from "./components/auth/multi-session/manage-account.js";
13
- import { passkeyPlugin as d } from "./lib/auth/passkey-plugin.js";
14
- import { Passkeys as f } from "./components/auth/passkey/passkeys.js";
15
- import { ThemeToggleItem as p } from "./components/auth/theme/theme-toggle-item.js";
16
- import { themePlugin as m } from "./lib/auth/theme-plugin.js";
17
- import { Appearance as h } from "./components/auth/theme/appearance.js";
18
- import { UsernameField as g } from "./components/auth/username/username-field.js";
19
- import { usernamePlugin as _ } from "./lib/auth/username-plugin.js";
20
- import { SignInUsername as v } from "./components/auth/username/sign-in-username.js";
21
- export { h as Appearance, n as DangerZone, t as DeleteUser, i as MagicLink, u as ManageAccount, a as ManageAccounts, f as Passkeys, v as SignInUsername, c as SwitchAccountSubmenu, s as SwitchAccountSubmenuContent, o as SwitchAccountSubmenuItem, p as ThemeToggleItem, g as UsernameField, e as deleteUserPlugin, r as magicLinkPlugin, l as multiSessionPlugin, d as passkeyPlugin, m as themePlugin, _ as usernamePlugin };
2
+ import { apiKeyPlugin as e } from "./lib/auth/api-key-plugin.js";
3
+ import { ApiKeys as t } from "./components/auth/api-key/api-keys.js";
4
+ import { deleteUserPlugin as n } from "./lib/auth/delete-user-plugin.js";
5
+ import { DeleteUser as r } from "./components/auth/delete-user/delete-user.js";
6
+ import { DangerZone as i } from "./components/auth/delete-user/danger-zone.js";
7
+ import { magicLinkPlugin as a } from "./lib/auth/magic-link-plugin.js";
8
+ import { MagicLink as o } from "./components/auth/magic-link/magic-link.js";
9
+ import { ManageAccounts as s } from "./components/auth/multi-session/manage-accounts.js";
10
+ import { SwitchAccountSubmenuItem as c } from "./components/auth/multi-session/switch-account-submenu-item.js";
11
+ import { SwitchAccountSubmenuContent as l } from "./components/auth/multi-session/switch-account-submenu-content.js";
12
+ import { SwitchAccountSubmenu as u } from "./components/auth/multi-session/switch-account-submenu.js";
13
+ import { multiSessionPlugin as d } from "./lib/auth/multi-session-plugin.js";
14
+ import { ManageAccount as f } from "./components/auth/multi-session/manage-account.js";
15
+ import { passkeyPlugin as p } from "./lib/auth/passkey-plugin.js";
16
+ import { Passkeys as m } from "./components/auth/passkey/passkeys.js";
17
+ import { ThemeToggleItem as h } from "./components/auth/theme/theme-toggle-item.js";
18
+ import { themePlugin as g } from "./lib/auth/theme-plugin.js";
19
+ import { Appearance as _ } from "./components/auth/theme/appearance.js";
20
+ import { UsernameField as v } from "./components/auth/username/username-field.js";
21
+ import { usernamePlugin as y } from "./lib/auth/username-plugin.js";
22
+ import { SignInUsername as b } from "./components/auth/username/sign-in-username.js";
23
+ export { t as ApiKeys, _ as Appearance, i as DangerZone, r as DeleteUser, o as MagicLink, f as ManageAccount, s as ManageAccounts, m as Passkeys, b as SignInUsername, u as SwitchAccountSubmenu, l as SwitchAccountSubmenuContent, c as SwitchAccountSubmenuItem, h as ThemeToggleItem, v as UsernameField, e as apiKeyPlugin, n as deleteUserPlugin, a as magicLinkPlugin, d as multiSessionPlugin, p as passkeyPlugin, g as themePlugin, y as usernamePlugin };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@better-auth-ui/heroui",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "vite build",
7
- "dev": "vite build --watch",
7
+ "dev": "tsc --watch",
8
8
  "test": "vitest"
9
9
  },
10
10
  "files": [
@@ -28,35 +28,35 @@
28
28
  },
29
29
  "devDependencies": {
30
30
  "@gravity-ui/icons": "^2.18.0",
31
- "@heroui/react": "^3.0.2",
32
- "@heroui/styles": "^3.0.2",
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.8",
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",
39
39
  "@vitejs/plugin-react": "^6.0.1",
40
40
  "@vitest/browser-playwright": "^4.1.5",
41
- "better-auth": "^1.6.9",
41
+ "better-auth": "^1.6.10",
42
42
  "bowser": "^2.11.0",
43
43
  "playwright": "^1.59.1",
44
- "react": "^19.2.5",
45
- "react-dom": "^19.2.5",
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.2",
53
- "@heroui/styles": ">=3.0.2",
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.8",
57
- "better-auth": ">=1.6.9",
58
- "react": ">=19.2.5",
59
- "react-dom": ">=19.2.5",
56
+ "@tanstack/react-query": ">=5.100.9",
57
+ "better-auth": ">=1.6.10",
58
+ "react": ">=19.2.6",
59
+ "react-dom": ">=19.2.6",
60
60
  "bowser": ">=2.11.0"
61
61
  },
62
62
  "repository": {
@@ -0,0 +1,17 @@
1
+ import { Skeleton } from "@heroui/react"
2
+
3
+ export function ApiKeySkeleton() {
4
+ return (
5
+ <div className="flex items-center justify-between">
6
+ <div className="flex items-center gap-3">
7
+ <Skeleton className="size-10 rounded-xl" />
8
+
9
+ <div className="flex flex-col gap-1">
10
+ <Skeleton className="h-4 w-28 rounded-lg" />
11
+ <Skeleton className="h-3 w-36 rounded-lg" />
12
+ <Skeleton className="h-3 w-32 rounded-lg" />
13
+ </div>
14
+ </div>
15
+ </div>
16
+ )
17
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ type ListedApiKey,
3
+ useAuth,
4
+ useAuthPlugin
5
+ } from "@better-auth-ui/react"
6
+ import { Key, Xmark } from "@gravity-ui/icons"
7
+ import { Button } from "@heroui/react"
8
+ import { useState } from "react"
9
+
10
+ import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin"
11
+ import { DeleteApiKeyDialog } from "./delete-api-key-dialog"
12
+
13
+ export type ApiKeyProps = {
14
+ apiKey: ListedApiKey
15
+ }
16
+
17
+ export function ApiKey({ apiKey }: ApiKeyProps) {
18
+ const { localization } = useAuth()
19
+ const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)
20
+ const [deleteOpen, setDeleteOpen] = useState(false)
21
+
22
+ const preview = `${apiKey.start}${"*".repeat(16)}`
23
+
24
+ return (
25
+ <div className="flex items-center gap-3">
26
+ <div className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-surface-secondary">
27
+ <Key className="size-4.5" />
28
+ </div>
29
+
30
+ <div className="flex flex-col min-w-0">
31
+ <span className="text-sm font-medium leading-tight truncate">
32
+ {apiKey.name || apiKeyLocalization.apiKey}
33
+ </span>
34
+
35
+ <span className="text-xs text-muted font-mono truncate">{preview}</span>
36
+
37
+ <span className="text-xs text-muted">
38
+ {new Date(apiKey.createdAt).toLocaleString(undefined, {
39
+ dateStyle: "medium",
40
+ timeStyle: "short"
41
+ })}
42
+ </span>
43
+ </div>
44
+
45
+ <Button
46
+ className="ml-auto shrink-0"
47
+ variant="outline"
48
+ size="sm"
49
+ onPress={() => setDeleteOpen(true)}
50
+ aria-label={apiKeyLocalization.deleteApiKey}
51
+ >
52
+ <Xmark />
53
+
54
+ {localization.settings.delete}
55
+ </Button>
56
+
57
+ <DeleteApiKeyDialog
58
+ isOpen={deleteOpen}
59
+ onOpenChange={setDeleteOpen}
60
+ apiKey={apiKey}
61
+ />
62
+ </div>
63
+ )
64
+ }
@@ -0,0 +1,33 @@
1
+ import { useAuthPlugin } from "@better-auth-ui/react"
2
+ import { Key } from "@gravity-ui/icons"
3
+ import { Button } from "@heroui/react"
4
+
5
+ import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin"
6
+
7
+ export type ApiKeysEmptyProps = {
8
+ onCreatePress: () => void
9
+ }
10
+
11
+ export function ApiKeysEmpty({ onCreatePress }: ApiKeysEmptyProps) {
12
+ const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)
13
+
14
+ return (
15
+ <div className="flex flex-col items-center justify-center gap-4">
16
+ <div className="flex size-10 items-center justify-center rounded-xl bg-surface-secondary">
17
+ <Key className="size-4.5" />
18
+ </div>
19
+
20
+ <div className="flex flex-col items-center justify-center gap-1 text-center">
21
+ <p className="text-sm font-semibold">{apiKeyLocalization.noApiKeys}</p>
22
+
23
+ <p className="text-muted text-xs">
24
+ {apiKeyLocalization.apiKeysDescription}
25
+ </p>
26
+ </div>
27
+
28
+ <Button size="sm" onPress={onCreatePress}>
29
+ {apiKeyLocalization.createApiKey}
30
+ </Button>
31
+ </div>
32
+ )
33
+ }
@@ -0,0 +1,71 @@
1
+ import {
2
+ type ApiKeyAuthClient,
3
+ useAuth,
4
+ useAuthPlugin,
5
+ useListApiKeys
6
+ } from "@better-auth-ui/react"
7
+ import { Button, Card, type CardProps, cn } from "@heroui/react"
8
+ import { useState } from "react"
9
+
10
+ import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin"
11
+ import { ApiKey } from "./api-key"
12
+ import { ApiKeySkeleton } from "./api-key-skeleton"
13
+ import { ApiKeysEmpty } from "./api-keys-empty"
14
+ import { CreateApiKeyDialog } from "./create-api-key-dialog"
15
+
16
+ export type ApiKeysProps = {
17
+ className?: string
18
+ variant?: CardProps["variant"]
19
+ }
20
+
21
+ export function ApiKeys({ className, variant }: ApiKeysProps) {
22
+ const { authClient } = useAuth()
23
+ const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)
24
+
25
+ const { data: listData, isPending } = useListApiKeys(
26
+ authClient as ApiKeyAuthClient
27
+ )
28
+
29
+ const [createOpen, setCreateOpen] = useState(false)
30
+
31
+ return (
32
+ <div className={cn("flex flex-col gap-3", className)}>
33
+ <div className="flex items-end justify-between gap-3">
34
+ <h2 className="text-sm font-semibold truncate">
35
+ {apiKeyLocalization.apiKeys}
36
+ </h2>
37
+
38
+ <Button
39
+ className="shrink-0"
40
+ size="sm"
41
+ isDisabled={isPending}
42
+ onPress={() => setCreateOpen(true)}
43
+ >
44
+ {apiKeyLocalization.createApiKey}
45
+ </Button>
46
+ </div>
47
+
48
+ <Card variant={variant}>
49
+ <Card.Content>
50
+ {isPending ? (
51
+ <ApiKeySkeleton />
52
+ ) : !listData?.apiKeys.length ? (
53
+ <ApiKeysEmpty onCreatePress={() => setCreateOpen(true)} />
54
+ ) : (
55
+ listData?.apiKeys.map((key, index) => (
56
+ <div key={key.id}>
57
+ {index > 0 && (
58
+ <div className="border-b border-dashed -mx-4 my-4" />
59
+ )}
60
+
61
+ <ApiKey apiKey={key} />
62
+ </div>
63
+ ))
64
+ )}
65
+ </Card.Content>
66
+ </Card>
67
+
68
+ <CreateApiKeyDialog isOpen={createOpen} onOpenChange={setCreateOpen} />
69
+ </div>
70
+ )
71
+ }