@better-auth-ui/heroui 1.6.0 → 1.6.1
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/magic-link.js +3 -2
- package/dist/components/auth/passkey-button.d.ts +4 -0
- package/dist/components/auth/passkey-button.js +13 -0
- package/dist/components/auth/sign-in.js +3 -2
- package/dist/components/settings/security/passkey.d.ts +8 -0
- package/dist/components/settings/security/passkey.js +14 -0
- package/dist/components/settings/security/passkeys.d.ts +6 -0
- package/dist/components/settings/security/passkeys.js +21 -0
- package/dist/components/settings/security/security-settings.js +3 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +6 -1
- package/src/components/auth/magic-link.tsx +4 -0
- package/src/components/auth/passkey-button.tsx +36 -0
- package/src/components/auth/sign-in.tsx +4 -0
- package/src/components/settings/security/passkey.tsx +51 -0
- package/src/components/settings/security/passkeys.tsx +104 -0
- package/src/components/settings/security/security-settings.tsx +3 -1
- package/src/index.tsx +2 -0
- package/dist/components/account/account.d.ts +0 -18
- package/dist/components/account/account.js +0 -24
- package/dist/components/account/settings/account-settings.d.ts +0 -5
- package/dist/components/account/settings/account-settings.js +0 -6
- package/dist/components/account/settings/settings.d.ts +0 -5
- package/dist/components/account/settings/settings.js +0 -5
- package/dist/components/account/settings/user-profile.d.ts +0 -5
- package/dist/components/account/settings/user-profile.js +0 -20
- package/dist/components/settings/account/accounts.d.ts +0 -14
- package/dist/components/settings/account/accounts.js +0 -26
- package/dist/components/settings/account/theme-selector.d.ts +0 -16
- package/dist/components/settings/account/theme-selector.js +0 -33
- package/dist/components/settings/account/theme-settings.d.ts +0 -16
- package/dist/components/settings/account/theme-settings.js +0 -31
- package/dist/components/settings/account-settings.d.ts +0 -5
- package/dist/components/settings/account-settings.js +0 -6
- package/dist/components/settings/account.d.ts +0 -18
- package/dist/components/settings/account.js +0 -24
- package/dist/components/settings/security/connected-account.d.ts +0 -16
- package/dist/components/settings/security/connected-account.js +0 -46
- package/dist/components/settings/security/connected-accounts.d.ts +0 -15
- package/dist/components/settings/security/connected-accounts.js +0 -31
- package/dist/components/settings/security/sessions.d.ts +0 -14
- package/dist/components/settings/security/sessions.js +0 -40
- package/dist/components/settings/settings/account-settings.d.ts +0 -5
- package/dist/components/settings/settings/account-settings.js +0 -6
- package/dist/components/settings/settings/user-profile.d.ts +0 -5
- package/dist/components/settings/settings/user-profile.js +0 -20
- package/dist/components/settings/user-profile.d.ts +0 -5
- package/dist/components/settings/user-profile.js +0 -20
- package/dist/components/user/switch-account-popover.d.ts +0 -10
- package/dist/components/user/switch-account-popover.js +0 -25
- package/dist/components/user/switch-account-submenu.d.ts +0 -10
- package/dist/components/user/switch-account-submenu.js +0 -25
- package/dist/hooks/use-auth.d.ts +0 -2
- package/dist/hooks/use-auth.js +0 -8
- package/dist/hooks/use-redirect-to.d.ts +0 -8
- package/dist/hooks/use-redirect-to.js +0 -19
- package/dist/hooks/use-sign-in.d.ts +0 -8
- package/dist/hooks/use-sign-in.js +0 -53
- package/dist/lib/utils.d.ts +0 -2
- package/dist/lib/utils.js +0 -5
- package/dist/server.d.ts +0 -1
- package/dist/server.js +0 -1
- package/dist/src/components/auth/auth-provider.d.ts +0 -8
- package/dist/src/components/auth/auth-provider.js +0 -13
- package/dist/src/components/auth/auth.d.ts +0 -22
- package/dist/src/components/auth/auth.js +0 -42
- package/dist/src/components/auth/field-separator.d.ts +0 -4
- package/dist/src/components/auth/field-separator.js +0 -5
- package/dist/src/components/auth/forgot-password.d.ts +0 -15
- package/dist/src/components/auth/forgot-password.js +0 -29
- package/dist/src/components/auth/magic-link-button.d.ts +0 -13
- package/dist/src/components/auth/magic-link-button.js +0 -20
- package/dist/src/components/auth/magic-link.d.ts +0 -17
- package/dist/src/components/auth/magic-link.js +0 -37
- package/dist/src/components/auth/provider-buttons.d.ts +0 -19
- package/dist/src/components/auth/provider-buttons.js +0 -35
- package/dist/src/components/auth/reset-password.d.ts +0 -11
- package/dist/src/components/auth/reset-password.js +0 -59
- package/dist/src/components/auth/sign-in.d.ts +0 -14
- package/dist/src/components/auth/sign-in.js +0 -58
- package/dist/src/components/auth/sign-out.d.ts +0 -11
- package/dist/src/components/auth/sign-out.js +0 -34
- package/dist/src/components/auth/sign-up.d.ts +0 -19
- package/dist/src/components/auth/sign-up.js +0 -65
- package/dist/src/components/email/email-changed-email.d.ts +0 -3
- package/dist/src/components/email/email-changed-email.js +0 -23
- package/dist/src/components/email/email-verification-email.d.ts +0 -3
- package/dist/src/components/email/email-verification-email.js +0 -23
- package/dist/src/components/email/magic-link-email.d.ts +0 -3
- package/dist/src/components/email/magic-link-email.js +0 -23
- package/dist/src/components/email/new-device-email.d.ts +0 -3
- package/dist/src/components/email/new-device-email.js +0 -23
- package/dist/src/components/email/otp-email.d.ts +0 -3
- package/dist/src/components/email/otp-email.js +0 -22
- package/dist/src/components/email/password-changed-email.d.ts +0 -3
- package/dist/src/components/email/password-changed-email.js +0 -23
- package/dist/src/components/email/reset-password-email.d.ts +0 -3
- package/dist/src/components/email/reset-password-email.js +0 -23
- package/dist/src/components/settings/account/account-settings.d.ts +0 -13
- package/dist/src/components/settings/account/account-settings.js +0 -19
- package/dist/src/components/settings/account/appearance.d.ts +0 -16
- package/dist/src/components/settings/account/appearance.js +0 -28
- package/dist/src/components/settings/account/change-email.d.ts +0 -15
- package/dist/src/components/settings/account/change-email.js +0 -35
- package/dist/src/components/settings/account/manage-account.d.ts +0 -19
- package/dist/src/components/settings/account/manage-account.js +0 -27
- package/dist/src/components/settings/account/manage-accounts.d.ts +0 -14
- package/dist/src/components/settings/account/manage-accounts.js +0 -27
- package/dist/src/components/settings/account/user-profile.d.ts +0 -13
- package/dist/src/components/settings/account/user-profile.js +0 -29
- package/dist/src/components/settings/security/active-session.d.ts +0 -14
- package/dist/src/components/settings/security/active-session.js +0 -35
- package/dist/src/components/settings/security/active-sessions.d.ts +0 -14
- package/dist/src/components/settings/security/active-sessions.js +0 -29
- package/dist/src/components/settings/security/change-password.d.ts +0 -15
- package/dist/src/components/settings/security/change-password.js +0 -57
- package/dist/src/components/settings/security/linked-account.d.ts +0 -16
- package/dist/src/components/settings/security/linked-account.js +0 -46
- package/dist/src/components/settings/security/linked-accounts.d.ts +0 -15
- package/dist/src/components/settings/security/linked-accounts.js +0 -31
- package/dist/src/components/settings/security/security-settings.d.ts +0 -15
- package/dist/src/components/settings/security/security-settings.js +0 -18
- package/dist/src/components/settings/settings.d.ts +0 -29
- package/dist/src/components/settings/settings.js +0 -39
- package/dist/src/components/user/switch-account-item.d.ts +0 -16
- package/dist/src/components/user/switch-account-item.js +0 -14
- package/dist/src/components/user/switch-account-menu.d.ts +0 -10
- package/dist/src/components/user/switch-account-menu.js +0 -23
- package/dist/src/components/user/user-avatar.d.ts +0 -24
- package/dist/src/components/user/user-avatar.js +0 -31
- package/dist/src/components/user/user-button.d.ts +0 -21
- package/dist/src/components/user/user-button.js +0 -24
- package/dist/src/components/user/user-view.d.ts +0 -21
- package/dist/src/components/user/user-view.js +0 -24
- package/dist/src/core.d.ts +0 -1
- package/dist/src/core.js +0 -1
- package/dist/src/index.d.ts +0 -31
- package/dist/src/index.js +0 -32
- package/dist/src/lib/utils.d.ts +0 -2
- package/dist/src/lib/utils.js +0 -5
- package/dist/src/react-exports.d.ts +0 -1
- package/dist/src/react-exports.js +0 -1
|
@@ -4,6 +4,7 @@ import { Button, Card, cn, Description, FieldError, Form, Input, Label, Link, Sp
|
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { FieldSeparator } from "./field-separator";
|
|
6
6
|
import { MagicLinkButton } from "./magic-link-button";
|
|
7
|
+
import { PasskeyButton } from "./passkey-button";
|
|
7
8
|
import { ProviderButtons } from "./provider-buttons";
|
|
8
9
|
/**
|
|
9
10
|
* Render a card-based sign-in form that sends an email magic link and optionally shows social provider buttons.
|
|
@@ -14,7 +15,7 @@ import { ProviderButtons } from "./provider-buttons";
|
|
|
14
15
|
* @returns The magic-link sign-in UI as a JSX element
|
|
15
16
|
*/
|
|
16
17
|
export function MagicLink({ className, socialLayout, socialPosition = "bottom", variant, ...props }) {
|
|
17
|
-
const { basePaths, baseURL, localization, redirectTo, socialProviders, viewPaths } = useAuth();
|
|
18
|
+
const { basePaths, baseURL, localization, passkey, redirectTo, socialProviders, viewPaths } = useAuth();
|
|
18
19
|
const [email, setEmail] = useState("");
|
|
19
20
|
const { mutate: signInMagicLink, isPending: magicLinkPending } = useSignInMagicLink({
|
|
20
21
|
onError: (error) => toast.danger(error.error?.message || error.message),
|
|
@@ -39,5 +40,5 @@ export function MagicLink({ className, socialLayout, socialPosition = "bottom",
|
|
|
39
40
|
signInMagicLink({ email, callbackURL: `${baseURL}${redirectTo}` });
|
|
40
41
|
};
|
|
41
42
|
const showSeparator = !!socialProviders?.length;
|
|
42
|
-
return (_jsxs(Card, { className: cn("w-full max-w-sm p-4 md:p-6", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl font-semibold mb-1", children: localization.auth.signIn }) }), _jsxs(Card.Content, { className: "gap-4", children: [socialPosition === "top" && (_jsxs(_Fragment, { children: [!!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending })), showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or }))] })), _jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { name: "email", type: "email", autoComplete: "email", isDisabled: isPending, value: email, onChange: setEmail, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { placeholder: localization.auth.emailPlaceholder, required: true, variant: variant === "transparent" ? "primary" : "secondary" }), _jsx(FieldError, {})] }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.sendMagicLink] }), _jsx(MagicLinkButton, { view: "magicLink", isPending: isPending })] })] }), socialPosition === "bottom" && (_jsxs(_Fragment, { children: [showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or })), !!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending }))] }))] }), _jsx(Card.Footer, { className: "flex-col", children: _jsxs(Description, { className: "text-sm", children: [localization.auth.needToCreateAnAccount, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signUp}`, className: "text-accent decoration-accent no-underline hover:underline", children: localization.auth.signUp })] }) })] }));
|
|
43
|
+
return (_jsxs(Card, { className: cn("w-full max-w-sm p-4 md:p-6", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl font-semibold mb-1", children: localization.auth.signIn }) }), _jsxs(Card.Content, { className: "gap-4", children: [socialPosition === "top" && (_jsxs(_Fragment, { children: [!!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending })), showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or }))] })), _jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { name: "email", type: "email", autoComplete: "email", isDisabled: isPending, value: email, onChange: setEmail, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { placeholder: localization.auth.emailPlaceholder, required: true, variant: variant === "transparent" ? "primary" : "secondary" }), _jsx(FieldError, {})] }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.sendMagicLink] }), _jsx(MagicLinkButton, { view: "magicLink", isPending: isPending }), passkey && _jsx(PasskeyButton, { isPending: isPending })] })] }), socialPosition === "bottom" && (_jsxs(_Fragment, { children: [showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or })), !!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending }))] }))] }), _jsx(Card.Footer, { className: "flex-col", children: _jsxs(Description, { className: "text-sm", children: [localization.auth.needToCreateAnAccount, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signUp}`, className: "text-accent decoration-accent no-underline hover:underline", children: localization.auth.signUp })] }) })] }));
|
|
43
44
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useSignInPasskey } from "@better-auth-ui/react";
|
|
3
|
+
import { Fingerprint } from "@gravity-ui/icons";
|
|
4
|
+
import { Button, Spinner, toast } from "@heroui/react";
|
|
5
|
+
export function PasskeyButton({ isPending }) {
|
|
6
|
+
const { localization, redirectTo, navigate } = useAuth();
|
|
7
|
+
const { mutate: signInPasskey, isPending: passkeyPending } = useSignInPasskey({
|
|
8
|
+
onError: (error) => toast.danger(error.error?.message || error.message),
|
|
9
|
+
onSuccess: () => navigate({ to: redirectTo })
|
|
10
|
+
});
|
|
11
|
+
const isDisabled = isPending || passkeyPending;
|
|
12
|
+
return (_jsxs(Button, { className: "w-full", variant: "tertiary", isDisabled: isDisabled, isPending: passkeyPending, onPress: () => signInPasskey(), children: [passkeyPending ? _jsx(Spinner, { color: "current", size: "sm" }) : _jsx(Fingerprint, {}), localization.auth.continueWith.replace("{{provider}}", localization.auth.passkey)] }));
|
|
13
|
+
}
|
|
@@ -4,6 +4,7 @@ import { Button, Card, Checkbox, cn, Description, FieldError, Form, Input, Label
|
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { FieldSeparator } from "./field-separator";
|
|
6
6
|
import { MagicLinkButton } from "./magic-link-button";
|
|
7
|
+
import { PasskeyButton } from "./passkey-button";
|
|
7
8
|
import { ProviderButtons } from "./provider-buttons";
|
|
8
9
|
/**
|
|
9
10
|
* Render the sign-in UI using auth context for configuration and localization.
|
|
@@ -11,7 +12,7 @@ import { ProviderButtons } from "./provider-buttons";
|
|
|
11
12
|
* @returns The sign-in JSX element containing email/password fields, optional magic-link button, and social provider buttons.
|
|
12
13
|
*/
|
|
13
14
|
export function SignIn({ className, socialLayout, socialPosition = "bottom", variant, ...props }) {
|
|
14
|
-
const { basePaths, baseURL, emailAndPassword, localization, magicLink, redirectTo, socialProviders, viewPaths, navigate } = useAuth();
|
|
15
|
+
const { basePaths, baseURL, emailAndPassword, localization, magicLink, passkey, redirectTo, socialProviders, viewPaths, navigate } = useAuth();
|
|
15
16
|
const [password, setPassword] = useState("");
|
|
16
17
|
const { mutate: sendVerificationEmail } = useSendVerificationEmail({
|
|
17
18
|
onError: (error) => toast.danger(error.error?.message || error.message),
|
|
@@ -60,5 +61,5 @@ export function SignIn({ className, socialLayout, socialPosition = "bottom", var
|
|
|
60
61
|
};
|
|
61
62
|
const isPending = signInPending || socialPending || socialRedirecting;
|
|
62
63
|
const showSeparator = emailAndPassword?.enabled && !!socialProviders?.length;
|
|
63
|
-
return (_jsxs(Card, { className: cn("w-full max-w-sm gap-4 md:p-6", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl font-semibold mb-1", children: localization.auth.signIn }) }), _jsxs(Card.Content, { className: "gap-4", children: [socialPosition === "top" && (_jsxs(_Fragment, { children: [!!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending })), showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or }))] })), emailAndPassword?.enabled && (_jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { name: "email", type: "email", autoComplete: "email", isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { placeholder: localization.auth.emailPlaceholder, variant: variant === "transparent" ? "primary" : "secondary", required: true }), _jsx(FieldError, {})] }), _jsxs(TextField, { minLength: emailAndPassword?.minPasswordLength, maxLength: emailAndPassword?.maxPasswordLength, name: "password", type: "password", autoComplete: "current-password", isDisabled: isPending, value: password, onChange: setPassword, children: [_jsx(Label, { children: localization.auth.password }), _jsx(Input, { placeholder: localization.auth.passwordPlaceholder, variant: variant === "transparent" ? "primary" : "secondary", required: true }), _jsx(FieldError, {})] }), emailAndPassword?.rememberMe && (_jsxs(Checkbox, { name: "rememberMe", isDisabled: isPending, variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(Checkbox.Control, { children: _jsx(Checkbox.Indicator, {}) }), _jsx(Checkbox.Content, { children: _jsx(Label, { children: localization.auth.rememberMe }) })] })), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.signIn] }), magicLink && (_jsx(MagicLinkButton, { view: "signIn", isPending: isPending }))] })] })), socialPosition === "bottom" && (_jsxs(_Fragment, { children: [showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or })), !!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending }))] }))] }), _jsxs(Card.Footer, { className: "flex-col gap-3", children: [emailAndPassword?.forgotPassword && (_jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.forgotPassword}`, className: "no-underline hover:underline", children: localization.auth.forgotPasswordLink })), emailAndPassword?.enabled && (_jsxs(Description, { className: "text-sm", children: [localization.auth.needToCreateAnAccount, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signUp}`, className: "text-accent no-underline hover:underline decoration-accent-hover", children: localization.auth.signUp })] }))] })] }));
|
|
64
|
+
return (_jsxs(Card, { className: cn("w-full max-w-sm gap-4 md:p-6", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl font-semibold mb-1", children: localization.auth.signIn }) }), _jsxs(Card.Content, { className: "gap-4", children: [socialPosition === "top" && (_jsxs(_Fragment, { children: [!!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending })), showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or }))] })), emailAndPassword?.enabled && (_jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { name: "email", type: "email", autoComplete: "email", isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { placeholder: localization.auth.emailPlaceholder, variant: variant === "transparent" ? "primary" : "secondary", required: true }), _jsx(FieldError, {})] }), _jsxs(TextField, { minLength: emailAndPassword?.minPasswordLength, maxLength: emailAndPassword?.maxPasswordLength, name: "password", type: "password", autoComplete: "current-password", isDisabled: isPending, value: password, onChange: setPassword, children: [_jsx(Label, { children: localization.auth.password }), _jsx(Input, { placeholder: localization.auth.passwordPlaceholder, variant: variant === "transparent" ? "primary" : "secondary", required: true }), _jsx(FieldError, {})] }), emailAndPassword?.rememberMe && (_jsxs(Checkbox, { name: "rememberMe", isDisabled: isPending, variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(Checkbox.Control, { children: _jsx(Checkbox.Indicator, {}) }), _jsx(Checkbox.Content, { children: _jsx(Label, { children: localization.auth.rememberMe }) })] })), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs(Button, { type: "submit", className: "w-full", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.auth.signIn] }), magicLink && (_jsx(MagicLinkButton, { view: "signIn", isPending: isPending })), passkey && _jsx(PasskeyButton, { isPending: isPending })] })] })), socialPosition === "bottom" && (_jsxs(_Fragment, { children: [showSeparator && (_jsx(FieldSeparator, { children: localization.auth.or })), !!socialProviders?.length && (_jsx(ProviderButtons, { socialLayout: socialLayout, signInSocial: signInSocial, isPending: isPending }))] }))] }), _jsxs(Card.Footer, { className: "flex-col gap-3", children: [emailAndPassword?.forgotPassword && (_jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.forgotPassword}`, className: "no-underline hover:underline", children: localization.auth.forgotPasswordLink })), emailAndPassword?.enabled && (_jsxs(Description, { className: "text-sm", children: [localization.auth.needToCreateAnAccount, " ", _jsx(Link, { href: `${basePaths.auth}/${viewPaths.auth.signUp}`, className: "text-accent no-underline hover:underline decoration-accent-hover", children: localization.auth.signUp })] }))] })] }));
|
|
64
65
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useDeletePasskey } from "@better-auth-ui/react";
|
|
3
|
+
import { Fingerprint, Xmark } from "@gravity-ui/icons";
|
|
4
|
+
import { Button, Spinner, toast } from "@heroui/react";
|
|
5
|
+
export function Passkey({ passkey }) {
|
|
6
|
+
const { localization } = useAuth();
|
|
7
|
+
const { mutate: deletePasskey, isPending } = useDeletePasskey({
|
|
8
|
+
onError: (error) => toast.danger(error.error?.message || error.message)
|
|
9
|
+
});
|
|
10
|
+
return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex size-10 shrink-0 items-center justify-center rounded-xl bg-surface-secondary", children: _jsx(Fingerprint, { className: "size-4.5" }) }), _jsxs("div", { className: "flex flex-col min-w-0", children: [_jsx("span", { className: "text-sm font-medium leading-tight", children: passkey.name || localization.auth.passkey }), _jsx("span", { className: "text-xs text-muted", children: new Date(passkey.createdAt).toLocaleString(undefined, {
|
|
11
|
+
dateStyle: "medium",
|
|
12
|
+
timeStyle: "short"
|
|
13
|
+
}) })] }), _jsxs(Button, { className: "ml-auto shrink-0", variant: "outline", size: "sm", isPending: isPending, onPress: () => deletePasskey({ id: passkey.id }), children: [isPending ? _jsx(Spinner, { color: "current", size: "sm" }) : _jsx(Xmark, {}), localization.settings.delete] })] }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type CardProps } from "@heroui/react";
|
|
2
|
+
export type PasskeysProps = {
|
|
3
|
+
className?: string;
|
|
4
|
+
variant?: CardProps["variant"];
|
|
5
|
+
};
|
|
6
|
+
export declare function Passkeys({ className, variant, ...props }: PasskeysProps & CardProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useAddPasskey, useAuth, useListUserPasskeys } from "@better-auth-ui/react";
|
|
3
|
+
import { Button, Card, cn, Skeleton, Spinner, toast } from "@heroui/react";
|
|
4
|
+
import { Passkey } from "./passkey";
|
|
5
|
+
export function Passkeys({ className, variant, ...props }) {
|
|
6
|
+
const { localization } = useAuth();
|
|
7
|
+
const { data: passkeys, isPending } = useListUserPasskeys({
|
|
8
|
+
throwOnError: (error) => {
|
|
9
|
+
if (error.error)
|
|
10
|
+
toast.danger(error.error.message);
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const { mutate: addPasskey, isPending: isAdding } = useAddPasskey({
|
|
15
|
+
onError: (error) => toast.danger(error.error?.message || error.message)
|
|
16
|
+
});
|
|
17
|
+
return (_jsxs("div", { children: [_jsx("h2", { className: cn("text-sm font-semibold mb-3"), children: localization.settings.passkeys }), _jsx(Card, { className: cn(className), variant: variant, ...props, children: _jsxs(Card.Content, { className: "gap-0", children: [_jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium leading-tight", children: localization.settings.passkeysDescription }), _jsx("p", { className: "text-muted text-xs mt-0.5", children: localization.settings.passkeysInstructions })] }), _jsxs(Button, { className: "shrink-0", size: "sm", isPending: isAdding, isDisabled: isPending, onPress: () => addPasskey(), children: [isAdding && _jsx(Spinner, { color: "current", size: "sm" }), localization.settings.addPasskey] })] }), isPending ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "border-b border-dashed -mx-4 md:-mx-6 my-4" }), _jsx(PasskeySkeleton, {})] })) : (passkeys?.map((passkey) => (_jsxs("div", { children: [_jsx("div", { className: "border-b border-dashed -mx-4 md:-mx-6 my-4" }), _jsx(Passkey, { passkey: passkey })] }, passkey.id))))] }) })] }));
|
|
18
|
+
}
|
|
19
|
+
function PasskeySkeleton() {
|
|
20
|
+
return (_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "size-10 rounded-xl" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-24 rounded-lg" }), _jsx(Skeleton, { className: "h-3 w-20 rounded-lg" })] })] }) }));
|
|
21
|
+
}
|
|
@@ -5,6 +5,7 @@ import { ActiveSessions } from "./active-sessions";
|
|
|
5
5
|
import { ChangePassword } from "./change-password";
|
|
6
6
|
import { DangerZone } from "./danger-zone";
|
|
7
7
|
import { LinkedAccounts } from "./linked-accounts";
|
|
8
|
+
import { Passkeys } from "./passkeys";
|
|
8
9
|
/**
|
|
9
10
|
* Renders the security settings layout including password management, linked accounts, and active sessions.
|
|
10
11
|
*
|
|
@@ -16,6 +17,6 @@ import { LinkedAccounts } from "./linked-accounts";
|
|
|
16
17
|
* @returns The security settings container as a JSX element.
|
|
17
18
|
*/
|
|
18
19
|
export function SecuritySettings({ className, variant, ...props }) {
|
|
19
|
-
const { deleteUser, emailAndPassword, socialProviders } = useAuth();
|
|
20
|
-
return (_jsxs("div", { className: cn("flex w-full flex-col gap-4 md:gap-6", className), ...props, children: [emailAndPassword?.enabled && _jsx(ChangePassword, { variant: variant }), !!socialProviders?.length && _jsx(LinkedAccounts, { variant: variant }), _jsx(ActiveSessions, { variant: variant }), deleteUser?.enabled && _jsx(DangerZone, { variant: variant })] }));
|
|
20
|
+
const { deleteUser, emailAndPassword, passkey, socialProviders } = useAuth();
|
|
21
|
+
return (_jsxs("div", { className: cn("flex w-full flex-col gap-4 md:gap-6", className), ...props, children: [emailAndPassword?.enabled && _jsx(ChangePassword, { variant: variant }), !!socialProviders?.length && _jsx(LinkedAccounts, { variant: variant }), passkey && _jsx(Passkeys, { variant: variant }), _jsx(ActiveSessions, { variant: variant }), deleteUser?.enabled && _jsx(DangerZone, { variant: variant })] }));
|
|
21
22
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./components/auth/auth-provider";
|
|
|
3
3
|
export * from "./components/auth/forgot-password";
|
|
4
4
|
export * from "./components/auth/magic-link";
|
|
5
5
|
export * from "./components/auth/magic-link-button";
|
|
6
|
+
export * from "./components/auth/passkey-button";
|
|
6
7
|
export * from "./components/auth/provider-buttons";
|
|
7
8
|
export * from "./components/auth/reset-password";
|
|
8
9
|
export * from "./components/auth/sign-in";
|
|
@@ -26,6 +27,7 @@ export * from "./components/settings/security/change-password";
|
|
|
26
27
|
export * from "./components/settings/security/danger-zone";
|
|
27
28
|
export * from "./components/settings/security/delete-user";
|
|
28
29
|
export * from "./components/settings/security/linked-accounts";
|
|
30
|
+
export * from "./components/settings/security/passkeys";
|
|
29
31
|
export * from "./components/settings/security/security-settings";
|
|
30
32
|
export * from "./components/settings/settings";
|
|
31
33
|
export * from "./components/user/user-avatar";
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./components/auth/auth-provider";
|
|
|
4
4
|
export * from "./components/auth/forgot-password";
|
|
5
5
|
export * from "./components/auth/magic-link";
|
|
6
6
|
export * from "./components/auth/magic-link-button";
|
|
7
|
+
export * from "./components/auth/passkey-button";
|
|
7
8
|
export * from "./components/auth/provider-buttons";
|
|
8
9
|
export * from "./components/auth/reset-password";
|
|
9
10
|
export * from "./components/auth/sign-in";
|
|
@@ -27,6 +28,7 @@ export * from "./components/settings/security/change-password";
|
|
|
27
28
|
export * from "./components/settings/security/danger-zone";
|
|
28
29
|
export * from "./components/settings/security/delete-user";
|
|
29
30
|
export * from "./components/settings/security/linked-accounts";
|
|
31
|
+
export * from "./components/settings/security/passkeys";
|
|
30
32
|
export * from "./components/settings/security/security-settings";
|
|
31
33
|
export * from "./components/settings/settings";
|
|
32
34
|
export * from "./components/user/user-avatar";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth-ui/heroui",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc && bun styles",
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
"react-dom": ">=19.2.5",
|
|
58
58
|
"bowser": ">=2.11.0"
|
|
59
59
|
},
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/better-auth-ui/better-auth-ui",
|
|
63
|
+
"directory": "packages/heroui"
|
|
64
|
+
},
|
|
60
65
|
"publishConfig": {
|
|
61
66
|
"access": "public"
|
|
62
67
|
}
|
|
@@ -22,6 +22,7 @@ import { type SyntheticEvent, useState } from "react"
|
|
|
22
22
|
|
|
23
23
|
import { FieldSeparator } from "./field-separator"
|
|
24
24
|
import { MagicLinkButton } from "./magic-link-button"
|
|
25
|
+
import { PasskeyButton } from "./passkey-button"
|
|
25
26
|
import { ProviderButtons, type SocialLayout } from "./provider-buttons"
|
|
26
27
|
|
|
27
28
|
export type MagicLinkProps = {
|
|
@@ -50,6 +51,7 @@ export function MagicLink({
|
|
|
50
51
|
basePaths,
|
|
51
52
|
baseURL,
|
|
52
53
|
localization,
|
|
54
|
+
passkey,
|
|
53
55
|
redirectTo,
|
|
54
56
|
socialProviders,
|
|
55
57
|
viewPaths
|
|
@@ -145,6 +147,8 @@ export function MagicLink({
|
|
|
145
147
|
</Button>
|
|
146
148
|
|
|
147
149
|
<MagicLinkButton view="magicLink" isPending={isPending} />
|
|
150
|
+
|
|
151
|
+
{passkey && <PasskeyButton isPending={isPending} />}
|
|
148
152
|
</div>
|
|
149
153
|
</Form>
|
|
150
154
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useAuth, useSignInPasskey } from "@better-auth-ui/react"
|
|
2
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
3
|
+
import { Button, Spinner, toast } from "@heroui/react"
|
|
4
|
+
|
|
5
|
+
export type PasskeyButtonProps = {
|
|
6
|
+
isPending: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function PasskeyButton({ isPending }: PasskeyButtonProps) {
|
|
10
|
+
const { localization, redirectTo, navigate } = useAuth()
|
|
11
|
+
|
|
12
|
+
const { mutate: signInPasskey, isPending: passkeyPending } = useSignInPasskey(
|
|
13
|
+
{
|
|
14
|
+
onError: (error) => toast.danger(error.error?.message || error.message),
|
|
15
|
+
onSuccess: () => navigate({ to: redirectTo })
|
|
16
|
+
}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
const isDisabled = isPending || passkeyPending
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Button
|
|
23
|
+
className="w-full"
|
|
24
|
+
variant="tertiary"
|
|
25
|
+
isDisabled={isDisabled}
|
|
26
|
+
isPending={passkeyPending}
|
|
27
|
+
onPress={() => signInPasskey()}
|
|
28
|
+
>
|
|
29
|
+
{passkeyPending ? <Spinner color="current" size="sm" /> : <Fingerprint />}
|
|
30
|
+
{localization.auth.continueWith.replace(
|
|
31
|
+
"{{provider}}",
|
|
32
|
+
localization.auth.passkey
|
|
33
|
+
)}
|
|
34
|
+
</Button>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -24,6 +24,7 @@ import { type SyntheticEvent, useState } from "react"
|
|
|
24
24
|
|
|
25
25
|
import { FieldSeparator } from "./field-separator"
|
|
26
26
|
import { MagicLinkButton } from "./magic-link-button"
|
|
27
|
+
import { PasskeyButton } from "./passkey-button"
|
|
27
28
|
import { ProviderButtons, type SocialLayout } from "./provider-buttons"
|
|
28
29
|
|
|
29
30
|
export interface SignInProps {
|
|
@@ -51,6 +52,7 @@ export function SignIn({
|
|
|
51
52
|
emailAndPassword,
|
|
52
53
|
localization,
|
|
53
54
|
magicLink,
|
|
55
|
+
passkey,
|
|
54
56
|
redirectTo,
|
|
55
57
|
socialProviders,
|
|
56
58
|
viewPaths,
|
|
@@ -212,6 +214,8 @@ export function SignIn({
|
|
|
212
214
|
{magicLink && (
|
|
213
215
|
<MagicLinkButton view="signIn" isPending={isPending} />
|
|
214
216
|
)}
|
|
217
|
+
|
|
218
|
+
{passkey && <PasskeyButton isPending={isPending} />}
|
|
215
219
|
</div>
|
|
216
220
|
</Form>
|
|
217
221
|
)}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useAuth, useDeletePasskey } from "@better-auth-ui/react"
|
|
2
|
+
import { Fingerprint, Xmark } from "@gravity-ui/icons"
|
|
3
|
+
import { Button, Spinner, toast } from "@heroui/react"
|
|
4
|
+
|
|
5
|
+
export type PasskeyProps = {
|
|
6
|
+
passkey: {
|
|
7
|
+
id: string
|
|
8
|
+
name?: string | null
|
|
9
|
+
createdAt: Date
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Passkey({ passkey }: PasskeyProps) {
|
|
14
|
+
const { localization } = useAuth()
|
|
15
|
+
|
|
16
|
+
const { mutate: deletePasskey, isPending } = useDeletePasskey({
|
|
17
|
+
onError: (error) => toast.danger(error.error?.message || error.message)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex items-center gap-3">
|
|
22
|
+
<div className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-surface-secondary">
|
|
23
|
+
<Fingerprint className="size-4.5" />
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div className="flex flex-col min-w-0">
|
|
27
|
+
<span className="text-sm font-medium leading-tight">
|
|
28
|
+
{passkey.name || localization.auth.passkey}
|
|
29
|
+
</span>
|
|
30
|
+
|
|
31
|
+
<span className="text-xs text-muted">
|
|
32
|
+
{new Date(passkey.createdAt).toLocaleString(undefined, {
|
|
33
|
+
dateStyle: "medium",
|
|
34
|
+
timeStyle: "short"
|
|
35
|
+
})}
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<Button
|
|
40
|
+
className="ml-auto shrink-0"
|
|
41
|
+
variant="outline"
|
|
42
|
+
size="sm"
|
|
43
|
+
isPending={isPending}
|
|
44
|
+
onPress={() => deletePasskey({ id: passkey.id })}
|
|
45
|
+
>
|
|
46
|
+
{isPending ? <Spinner color="current" size="sm" /> : <Xmark />}
|
|
47
|
+
{localization.settings.delete}
|
|
48
|
+
</Button>
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useAddPasskey,
|
|
3
|
+
useAuth,
|
|
4
|
+
useListUserPasskeys
|
|
5
|
+
} from "@better-auth-ui/react"
|
|
6
|
+
import {
|
|
7
|
+
Button,
|
|
8
|
+
Card,
|
|
9
|
+
type CardProps,
|
|
10
|
+
cn,
|
|
11
|
+
Skeleton,
|
|
12
|
+
Spinner,
|
|
13
|
+
toast
|
|
14
|
+
} from "@heroui/react"
|
|
15
|
+
|
|
16
|
+
import { Passkey } from "./passkey"
|
|
17
|
+
|
|
18
|
+
export type PasskeysProps = {
|
|
19
|
+
className?: string
|
|
20
|
+
variant?: CardProps["variant"]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function Passkeys({
|
|
24
|
+
className,
|
|
25
|
+
variant,
|
|
26
|
+
...props
|
|
27
|
+
}: PasskeysProps & CardProps) {
|
|
28
|
+
const { localization } = useAuth()
|
|
29
|
+
|
|
30
|
+
const { data: passkeys, isPending } = useListUserPasskeys({
|
|
31
|
+
throwOnError: (error) => {
|
|
32
|
+
if (error.error) toast.danger(error.error.message)
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const { mutate: addPasskey, isPending: isAdding } = useAddPasskey({
|
|
38
|
+
onError: (error) => toast.danger(error.error?.message || error.message)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div>
|
|
43
|
+
<h2 className={cn("text-sm font-semibold mb-3")}>
|
|
44
|
+
{localization.settings.passkeys}
|
|
45
|
+
</h2>
|
|
46
|
+
|
|
47
|
+
<Card className={cn(className)} variant={variant} {...props}>
|
|
48
|
+
<Card.Content className="gap-0">
|
|
49
|
+
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
50
|
+
<div>
|
|
51
|
+
<p className="text-sm font-medium leading-tight">
|
|
52
|
+
{localization.settings.passkeysDescription}
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<p className="text-muted text-xs mt-0.5">
|
|
56
|
+
{localization.settings.passkeysInstructions}
|
|
57
|
+
</p>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<Button
|
|
61
|
+
className="shrink-0"
|
|
62
|
+
size="sm"
|
|
63
|
+
isPending={isAdding}
|
|
64
|
+
isDisabled={isPending}
|
|
65
|
+
onPress={() => addPasskey()}
|
|
66
|
+
>
|
|
67
|
+
{isAdding && <Spinner color="current" size="sm" />}
|
|
68
|
+
{localization.settings.addPasskey}
|
|
69
|
+
</Button>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
{isPending ? (
|
|
73
|
+
<>
|
|
74
|
+
<div className="border-b border-dashed -mx-4 md:-mx-6 my-4" />
|
|
75
|
+
<PasskeySkeleton />
|
|
76
|
+
</>
|
|
77
|
+
) : (
|
|
78
|
+
passkeys?.map((passkey) => (
|
|
79
|
+
<div key={passkey.id}>
|
|
80
|
+
<div className="border-b border-dashed -mx-4 md:-mx-6 my-4" />
|
|
81
|
+
<Passkey passkey={passkey} />
|
|
82
|
+
</div>
|
|
83
|
+
))
|
|
84
|
+
)}
|
|
85
|
+
</Card.Content>
|
|
86
|
+
</Card>
|
|
87
|
+
</div>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function PasskeySkeleton() {
|
|
92
|
+
return (
|
|
93
|
+
<div className="flex items-center justify-between">
|
|
94
|
+
<div className="flex items-center gap-3">
|
|
95
|
+
<Skeleton className="size-10 rounded-xl" />
|
|
96
|
+
|
|
97
|
+
<div className="flex flex-col gap-1">
|
|
98
|
+
<Skeleton className="h-4 w-24 rounded-lg" />
|
|
99
|
+
<Skeleton className="h-3 w-20 rounded-lg" />
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -6,6 +6,7 @@ import { ActiveSessions } from "./active-sessions"
|
|
|
6
6
|
import { ChangePassword } from "./change-password"
|
|
7
7
|
import { DangerZone } from "./danger-zone"
|
|
8
8
|
import { LinkedAccounts } from "./linked-accounts"
|
|
9
|
+
import { Passkeys } from "./passkeys"
|
|
9
10
|
|
|
10
11
|
export type SecuritySettingsProps = {
|
|
11
12
|
className?: string
|
|
@@ -27,7 +28,7 @@ export function SecuritySettings({
|
|
|
27
28
|
variant,
|
|
28
29
|
...props
|
|
29
30
|
}: SecuritySettingsProps & ComponentProps<"div">) {
|
|
30
|
-
const { deleteUser, emailAndPassword, socialProviders } = useAuth()
|
|
31
|
+
const { deleteUser, emailAndPassword, passkey, socialProviders } = useAuth()
|
|
31
32
|
|
|
32
33
|
return (
|
|
33
34
|
<div
|
|
@@ -36,6 +37,7 @@ export function SecuritySettings({
|
|
|
36
37
|
>
|
|
37
38
|
{emailAndPassword?.enabled && <ChangePassword variant={variant} />}
|
|
38
39
|
{!!socialProviders?.length && <LinkedAccounts variant={variant} />}
|
|
40
|
+
{passkey && <Passkeys variant={variant} />}
|
|
39
41
|
<ActiveSessions variant={variant} />
|
|
40
42
|
{deleteUser?.enabled && <DangerZone variant={variant} />}
|
|
41
43
|
</div>
|
package/src/index.tsx
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./components/auth/auth-provider"
|
|
|
5
5
|
export * from "./components/auth/forgot-password"
|
|
6
6
|
export * from "./components/auth/magic-link"
|
|
7
7
|
export * from "./components/auth/magic-link-button"
|
|
8
|
+
export * from "./components/auth/passkey-button"
|
|
8
9
|
export * from "./components/auth/provider-buttons"
|
|
9
10
|
export * from "./components/auth/reset-password"
|
|
10
11
|
export * from "./components/auth/sign-in"
|
|
@@ -28,6 +29,7 @@ export * from "./components/settings/security/change-password"
|
|
|
28
29
|
export * from "./components/settings/security/danger-zone"
|
|
29
30
|
export * from "./components/settings/security/delete-user"
|
|
30
31
|
export * from "./components/settings/security/linked-accounts"
|
|
32
|
+
export * from "./components/settings/security/passkeys"
|
|
31
33
|
export * from "./components/settings/security/security-settings"
|
|
32
34
|
export * from "./components/settings/settings"
|
|
33
35
|
export * from "./components/user/user-avatar"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { AnyAuthConfig } from "@better-auth-ui/react";
|
|
2
|
-
import type { AccountView } from "@better-auth-ui/react/core";
|
|
3
|
-
import { type TabsProps } from "@heroui/react";
|
|
4
|
-
export type AccountProps = AnyAuthConfig & {
|
|
5
|
-
className?: string;
|
|
6
|
-
path?: string;
|
|
7
|
-
view?: AccountView;
|
|
8
|
-
hideNav?: boolean;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Selects and renders the appropriate account view component.
|
|
12
|
-
*
|
|
13
|
-
* @param path - Route path used to resolve an account view when `view` is not provided
|
|
14
|
-
* @param view - Explicit auth view to render (e.g., "userProfile")
|
|
15
|
-
* @param hideNav - Hide the navigation tabs
|
|
16
|
-
*/
|
|
17
|
-
export declare function Account({ className, view, path, hideNav, ...config }: AccountProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
-
export declare function ResponsiveTabs({ className, ...props }: TabsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { ShieldCheckIcon, UserCircleIcon } from "@heroicons/react/24/outline";
|
|
3
|
-
import { cn, Tabs } from "@heroui/react";
|
|
4
|
-
import { useAuth } from "../../hooks/use-auth";
|
|
5
|
-
import { AccountSettings } from "./settings/account-settings";
|
|
6
|
-
/**
|
|
7
|
-
* Selects and renders the appropriate account view component.
|
|
8
|
-
*
|
|
9
|
-
* @param path - Route path used to resolve an account view when `view` is not provided
|
|
10
|
-
* @param view - Explicit auth view to render (e.g., "userProfile")
|
|
11
|
-
* @param hideNav - Hide the navigation tabs
|
|
12
|
-
*/
|
|
13
|
-
export function Account({ className, view, path, hideNav, ...config }) {
|
|
14
|
-
const { basePaths, viewPaths } = useAuth(config);
|
|
15
|
-
if (!view && !path) {
|
|
16
|
-
throw new Error("[Better Auth UI] Either `view` or `path` must be provided");
|
|
17
|
-
}
|
|
18
|
-
const accountPathViews = Object.fromEntries(Object.entries(viewPaths.account).map(([k, v]) => [v, k]));
|
|
19
|
-
const currentView = view || (path ? accountPathViews[path] : undefined);
|
|
20
|
-
return (_jsxs(ResponsiveTabs, { selectedKey: currentView, className: "w-full", children: [_jsx(Tabs.ListContainer, { children: _jsxs(Tabs.List, { "aria-label": "Account", className: cn("overflow-auto md:w-64 lg:w-72 xl:w-80", hideNav && "hidden"), children: [_jsxs(Tabs.Tab, { id: "settings", href: `${basePaths.account}/${viewPaths.account.settings}`, className: "gap-1.5", children: [_jsx(UserCircleIcon, { className: "size-4" }), "Account", _jsx(Tabs.Indicator, {})] }), _jsxs(Tabs.Tab, { id: "security", href: `${basePaths.account}/${viewPaths.account.security}`, className: "gap-1.5", children: [_jsx(ShieldCheckIcon, { className: "size-4" }), "Security", _jsx(Tabs.Indicator, {})] })] }) }), _jsx(Tabs.Panel, { id: "settings", className: "md:pt-0 px-0 md:px-2", children: _jsx(AccountSettings, {}) }), _jsx(Tabs.Panel, { id: "security", children: "Security" })] }));
|
|
21
|
-
}
|
|
22
|
-
export function ResponsiveTabs({ className, ...props }) {
|
|
23
|
-
return (_jsxs(_Fragment, { children: [_jsx(Tabs, { className: cn("md:hidden", className), orientation: "horizontal", ...props }), _jsx(Tabs, { className: cn("hidden md:flex", className), orientation: "vertical", ...props })] }));
|
|
24
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { AnyAuthConfig } from "@better-auth-ui/react";
|
|
2
|
-
export type AccountSettingsProps = AnyAuthConfig & {
|
|
3
|
-
className?: string;
|
|
4
|
-
};
|
|
5
|
-
export declare function AccountSettings({ className, ...config }: AccountSettingsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from "../../../lib/utils";
|
|
3
|
-
import { UserProfile } from "./user-profile";
|
|
4
|
-
export function AccountSettings({ className, ...config }) {
|
|
5
|
-
return (_jsx("div", { className: cn("w-full flex flex-col gap-4 md:gap-6", className), children: _jsx(UserProfile, { ...config }) }));
|
|
6
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth, useUpdateUser } from "@better-auth-ui/react";
|
|
3
|
-
import { CheckIcon, PencilIcon } from "@heroicons/react/24/outline";
|
|
4
|
-
import { Button, Card, Description, FieldError, Fieldset, Form, Input, Label, Skeleton, Spinner, TextField } from "@heroui/react";
|
|
5
|
-
import { UserAvatar } from "../../user/user-avatar";
|
|
6
|
-
export function UserProfile({ className, ...config }) {
|
|
7
|
-
const { authClient, localization } = useAuth(config);
|
|
8
|
-
const { data: sessionData, isPending: isSessionPending } = authClient.useSession();
|
|
9
|
-
const [state, formAction, isPending] = useUpdateUser(config);
|
|
10
|
-
if (isSessionPending) {
|
|
11
|
-
return _jsx(UserProfileSkeleton, {});
|
|
12
|
-
}
|
|
13
|
-
return (_jsx(Card, { className: "p-4 md:p-6", children: _jsx(Form, { action: formAction, children: _jsxs(Fieldset, { className: "w-full gap-4 md:gap-6", children: [_jsx(Fieldset.Legend, { className: "text-xl", children: localization.account.profile }), _jsx(Description, {}), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Button, { isIconOnly: true, variant: "ghost", className: "p-0 h-auto w-auto rounded-full", children: [_jsx(UserAvatar, { size: "lg" }), _jsx("span", { className: "absolute right-0 bottom-0 size-3.5 rounded-full bg-background ring-2 ring-surface-quaternary flex items-center justify-center", children: _jsx(PencilIcon, { className: "size-2 text-muted" }) })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("p", { className: "text-sm font-medium", children: sessionData?.user?.displayUsername ||
|
|
14
|
-
sessionData?.user?.name ||
|
|
15
|
-
sessionData?.user?.email }), (sessionData?.user?.displayUsername ||
|
|
16
|
-
sessionData?.user?.name) && (_jsx("p", { className: "text-muted text-xs leading-none", children: sessionData?.user?.email }))] })] }), _jsx(Fieldset.Group, { children: _jsxs(TextField, { name: "name", defaultValue: sessionData?.user?.name || state.name, isDisabled: isPending, children: [_jsx(Label, { children: localization.auth.name }), _jsx(Input, { placeholder: localization.auth.name }), _jsx(FieldError, {})] }) }), _jsx(Fieldset.Actions, { children: _jsxs(Button, { type: "submit", isPending: isPending, children: [isPending ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(CheckIcon, { className: "size-4" })), localization.account.saveChanges] }) })] }) }) }));
|
|
17
|
-
}
|
|
18
|
-
function UserProfileSkeleton() {
|
|
19
|
-
return (_jsxs(Card, { className: "p-4 md:p-6 gap-4 md:gap-6", children: [_jsx(Skeleton, { className: "h-7 w-16 rounded-xl" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "size-12 rounded-full" }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Skeleton, { className: "h-4 mt-0.5 w-24 rounded-lg" }), _jsx(Skeleton, { className: "h-3 mt-0.5 w-32 rounded-lg" })] })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-12 my-0.5 rounded-lg" }), _jsx(Skeleton, { className: "h-9 w-full rounded-xl" })] }), _jsx(Skeleton, { className: "h-10 md:h-9 w-36 rounded-full mt-1" })] }));
|
|
20
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { type CardProps } from "@heroui/react";
|
|
2
|
-
export type AccountsProps = {
|
|
3
|
-
className?: string;
|
|
4
|
-
variant?: CardProps["variant"];
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Render a card that lists and manages all device sessions for the current user.
|
|
8
|
-
*
|
|
9
|
-
* Shows each session with user information and actions to switch to or revoke a session.
|
|
10
|
-
* When device session data is loading, a pending placeholder row is displayed.
|
|
11
|
-
*
|
|
12
|
-
* @returns A JSX element containing the accounts management card
|
|
13
|
-
*/
|
|
14
|
-
export declare function Accounts({ className, ...props }: AccountsProps & CardProps): import("react/jsx-runtime").JSX.Element;
|