@better-auth-ui/heroui 1.6.7 → 1.6.8
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/api-key/create-api-key-dialog.js +2 -2
- package/dist/components/auth/passkey/add-passkey-dialog.d.ts +5 -0
- package/dist/components/auth/passkey/add-passkey-dialog.js +65 -0
- package/dist/components/auth/passkey/delete-passkey-dialog.d.ts +11 -0
- package/dist/components/auth/passkey/delete-passkey-dialog.js +47 -0
- package/dist/components/auth/passkey/passkey-skeleton.d.ts +1 -0
- package/dist/components/auth/passkey/passkey-skeleton.js +17 -0
- package/dist/components/auth/passkey/passkey.d.ts +2 -5
- package/dist/components/auth/passkey/passkey.js +16 -13
- package/dist/components/auth/passkey/passkeys-empty.d.ts +4 -0
- package/dist/components/auth/passkey/passkeys-empty.js +35 -0
- package/dist/components/auth/passkey/passkeys.js +35 -99
- package/dist/lib/auth/passkey-plugin.d.ts +4 -1
- package/package.json +1 -1
- package/src/components/auth/api-key/create-api-key-dialog.tsx +2 -2
- package/src/components/auth/passkey/add-passkey-dialog.tsx +109 -0
- package/src/components/auth/passkey/delete-passkey-dialog.tsx +95 -0
- package/src/components/auth/passkey/passkey-skeleton.tsx +16 -0
- package/src/components/auth/passkey/passkey.tsx +23 -22
- package/src/components/auth/passkey/passkeys-empty.tsx +35 -0
- package/src/components/auth/passkey/passkeys.tsx +33 -154
|
@@ -16,8 +16,8 @@ function v({ isOpen: v, onOpenChange: y }) {
|
|
|
16
16
|
children: /* @__PURE__ */ g(o.Container, { children: /* @__PURE__ */ g(o.Dialog, { children: /* @__PURE__ */ _(l, {
|
|
17
17
|
onSubmit: (e) => {
|
|
18
18
|
e.preventDefault();
|
|
19
|
-
let t = new FormData(e.target).get("name");
|
|
20
|
-
C(t ? { name: t
|
|
19
|
+
let t = new FormData(e.target).get("name")?.trim();
|
|
20
|
+
C(t ? { name: t } : void 0, { onSuccess: (e) => {
|
|
21
21
|
j(!1), O(t), A(e.key), E(!0);
|
|
22
22
|
} });
|
|
23
23
|
},
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { passkeyPlugin as e } from "../../../lib/auth/passkey-plugin.js";
|
|
2
|
+
import { useAddPasskey as t, useAuth as n, useAuthPlugin as r } from "@better-auth-ui/react";
|
|
3
|
+
import { Fingerprint as i } from "@gravity-ui/icons";
|
|
4
|
+
import { AlertDialog as a, Button as o, FieldError as s, Form as c, Input as l, Label as u, Spinner as d, TextField as f } from "@heroui/react";
|
|
5
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/auth/passkey/add-passkey-dialog.tsx
|
|
7
|
+
function h({ isOpen: h, onOpenChange: g }) {
|
|
8
|
+
let { authClient: _, localization: v } = n(), { localization: y } = r(e), { mutate: b, isPending: x } = t(_), S = (e) => {
|
|
9
|
+
g(e);
|
|
10
|
+
};
|
|
11
|
+
return /* @__PURE__ */ p(a.Backdrop, {
|
|
12
|
+
isOpen: h,
|
|
13
|
+
onOpenChange: S,
|
|
14
|
+
children: /* @__PURE__ */ p(a.Container, { children: /* @__PURE__ */ p(a.Dialog, { children: /* @__PURE__ */ m(c, {
|
|
15
|
+
onSubmit: (e) => {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
let t = new FormData(e.target).get("name")?.trim();
|
|
18
|
+
b(t ? { name: t } : void 0, { onSuccess: () => S(!1) });
|
|
19
|
+
},
|
|
20
|
+
children: [
|
|
21
|
+
/* @__PURE__ */ p(a.CloseTrigger, {}),
|
|
22
|
+
/* @__PURE__ */ m(a.Header, { children: [/* @__PURE__ */ p(a.Icon, {
|
|
23
|
+
status: "default",
|
|
24
|
+
children: /* @__PURE__ */ p(i, {})
|
|
25
|
+
}), /* @__PURE__ */ p(a.Heading, { children: y.addPasskey })] }),
|
|
26
|
+
/* @__PURE__ */ m(a.Body, {
|
|
27
|
+
className: "overflow-visible",
|
|
28
|
+
children: [/* @__PURE__ */ p("p", {
|
|
29
|
+
className: "text-muted text-sm",
|
|
30
|
+
children: y.passkeysDescription
|
|
31
|
+
}), /* @__PURE__ */ m(f, {
|
|
32
|
+
className: "mt-4",
|
|
33
|
+
id: "name",
|
|
34
|
+
name: "name",
|
|
35
|
+
isDisabled: x,
|
|
36
|
+
children: [
|
|
37
|
+
/* @__PURE__ */ p(u, { children: y.name }),
|
|
38
|
+
/* @__PURE__ */ p(l, {
|
|
39
|
+
autoFocus: !0,
|
|
40
|
+
placeholder: v.settings.optional,
|
|
41
|
+
variant: "secondary"
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ p(s, {})
|
|
44
|
+
]
|
|
45
|
+
})]
|
|
46
|
+
}),
|
|
47
|
+
/* @__PURE__ */ m(a.Footer, { children: [/* @__PURE__ */ p(o, {
|
|
48
|
+
slot: "close",
|
|
49
|
+
variant: "tertiary",
|
|
50
|
+
isDisabled: x,
|
|
51
|
+
children: v.settings.cancel
|
|
52
|
+
}), /* @__PURE__ */ m(o, {
|
|
53
|
+
type: "submit",
|
|
54
|
+
isPending: x,
|
|
55
|
+
children: [x && /* @__PURE__ */ p(d, {
|
|
56
|
+
color: "current",
|
|
57
|
+
size: "sm"
|
|
58
|
+
}), y.addPasskey]
|
|
59
|
+
})] })
|
|
60
|
+
]
|
|
61
|
+
}) }) })
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { h as AddPasskeyDialog };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ListedPasskey = {
|
|
2
|
+
id: string;
|
|
3
|
+
name?: string | null;
|
|
4
|
+
createdAt: Date;
|
|
5
|
+
};
|
|
6
|
+
export type DeletePasskeyDialogProps = {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
onOpenChange: (open: boolean) => void;
|
|
9
|
+
passkey: ListedPasskey;
|
|
10
|
+
};
|
|
11
|
+
export declare function DeletePasskeyDialog({ isOpen, onOpenChange, passkey }: DeletePasskeyDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { passkeyPlugin as e } from "../../../lib/auth/passkey-plugin.js";
|
|
2
|
+
import { useAuth as t, useAuthPlugin as n, useDeletePasskey as r } from "@better-auth-ui/react";
|
|
3
|
+
import { Fingerprint as i } from "@gravity-ui/icons";
|
|
4
|
+
import { AlertDialog as a, Button as o, Input as s, Label as c, Spinner as l, TextField as u } from "@heroui/react";
|
|
5
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/auth/passkey/delete-passkey-dialog.tsx
|
|
7
|
+
function p({ isOpen: p, onOpenChange: m, passkey: h }) {
|
|
8
|
+
let { authClient: g, localization: _ } = t(), { localization: v } = n(e), y = h.name || v.passkey, { mutate: b, isPending: x } = r(g, { onSuccess: () => m(!1) });
|
|
9
|
+
return /* @__PURE__ */ d(a.Backdrop, {
|
|
10
|
+
isOpen: p,
|
|
11
|
+
onOpenChange: m,
|
|
12
|
+
children: /* @__PURE__ */ d(a.Container, { children: /* @__PURE__ */ f(a.Dialog, { children: [
|
|
13
|
+
/* @__PURE__ */ d(a.CloseTrigger, {}),
|
|
14
|
+
/* @__PURE__ */ f(a.Header, { children: [/* @__PURE__ */ d(a.Icon, {
|
|
15
|
+
status: "danger",
|
|
16
|
+
children: /* @__PURE__ */ d(i, {})
|
|
17
|
+
}), /* @__PURE__ */ d(a.Heading, { children: v.deletePasskeyTitle })] }),
|
|
18
|
+
/* @__PURE__ */ f(a.Body, {
|
|
19
|
+
className: "flex flex-col gap-4 overflow-visible",
|
|
20
|
+
children: [/* @__PURE__ */ d("p", {
|
|
21
|
+
className: "text-muted text-sm",
|
|
22
|
+
children: v.deletePasskeyWarning
|
|
23
|
+
}), /* @__PURE__ */ f(u, {
|
|
24
|
+
value: y,
|
|
25
|
+
variant: "secondary",
|
|
26
|
+
children: [/* @__PURE__ */ d(c, { children: h.name || v.passkey }), /* @__PURE__ */ d(s, { readOnly: !0 })]
|
|
27
|
+
})]
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ f(a.Footer, { children: [/* @__PURE__ */ d(o, {
|
|
30
|
+
slot: "close",
|
|
31
|
+
variant: "tertiary",
|
|
32
|
+
isDisabled: x,
|
|
33
|
+
children: _.settings.cancel
|
|
34
|
+
}), /* @__PURE__ */ f(o, {
|
|
35
|
+
variant: "danger",
|
|
36
|
+
onPress: () => b({ id: h.id }),
|
|
37
|
+
isPending: x,
|
|
38
|
+
children: [x && /* @__PURE__ */ d(l, {
|
|
39
|
+
color: "current",
|
|
40
|
+
size: "sm"
|
|
41
|
+
}), v.deletePasskeyTitle]
|
|
42
|
+
})] })
|
|
43
|
+
] }) })
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { p as DeletePasskeyDialog };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function PasskeySkeleton(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Skeleton as e } from "@heroui/react";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
//#region src/components/auth/passkey/passkey-skeleton.tsx
|
|
4
|
+
function r() {
|
|
5
|
+
return /* @__PURE__ */ t("div", {
|
|
6
|
+
className: "flex items-center justify-between",
|
|
7
|
+
children: /* @__PURE__ */ n("div", {
|
|
8
|
+
className: "flex items-center gap-3",
|
|
9
|
+
children: [/* @__PURE__ */ t(e, { className: "size-10 rounded-xl" }), /* @__PURE__ */ n("div", {
|
|
10
|
+
className: "flex flex-col gap-1",
|
|
11
|
+
children: [/* @__PURE__ */ t(e, { className: "h-4 w-28 rounded-lg" }), /* @__PURE__ */ t(e, { className: "h-3 w-32 rounded-lg" })]
|
|
12
|
+
})]
|
|
13
|
+
})
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { r as PasskeySkeleton };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
+
import { ListedPasskey } from './delete-passkey-dialog';
|
|
1
2
|
export type PasskeyProps = {
|
|
2
|
-
passkey:
|
|
3
|
-
id: string;
|
|
4
|
-
name?: string | null;
|
|
5
|
-
createdAt: Date;
|
|
6
|
-
};
|
|
3
|
+
passkey: ListedPasskey;
|
|
7
4
|
};
|
|
8
5
|
export declare function Passkey({ passkey }: PasskeyProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { passkeyPlugin as e } from "../../../lib/auth/passkey-plugin.js";
|
|
2
|
-
import {
|
|
2
|
+
import { DeletePasskeyDialog as t } from "./delete-passkey-dialog.js";
|
|
3
|
+
import { useAuth as n, useAuthPlugin as r } from "@better-auth-ui/react";
|
|
3
4
|
import { Fingerprint as i, Xmark as a } from "@gravity-ui/icons";
|
|
4
|
-
import { Button as o
|
|
5
|
+
import { Button as o } from "@heroui/react";
|
|
6
|
+
import { useState as s } from "react";
|
|
5
7
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
6
8
|
//#region src/components/auth/passkey/passkey.tsx
|
|
7
9
|
function u({ passkey: u }) {
|
|
8
|
-
let {
|
|
10
|
+
let { localization: d } = n(), { localization: f } = r(e), [p, m] = s(!1), h = u.name || f.passkey;
|
|
9
11
|
return /* @__PURE__ */ l("div", {
|
|
10
12
|
className: "flex items-center gap-3",
|
|
11
13
|
children: [
|
|
@@ -14,10 +16,10 @@ function u({ passkey: u }) {
|
|
|
14
16
|
children: /* @__PURE__ */ c(i, { className: "size-4.5" })
|
|
15
17
|
}),
|
|
16
18
|
/* @__PURE__ */ l("div", {
|
|
17
|
-
className: "flex
|
|
19
|
+
className: "flex min-w-0 flex-col",
|
|
18
20
|
children: [/* @__PURE__ */ c("span", {
|
|
19
|
-
className: "text-sm font-medium leading-tight",
|
|
20
|
-
children:
|
|
21
|
+
className: "truncate text-sm font-medium leading-tight",
|
|
22
|
+
children: h
|
|
21
23
|
}), /* @__PURE__ */ c("span", {
|
|
22
24
|
className: "text-xs text-muted",
|
|
23
25
|
children: new Date(u.createdAt).toLocaleString(void 0, {
|
|
@@ -30,13 +32,14 @@ function u({ passkey: u }) {
|
|
|
30
32
|
className: "ml-auto shrink-0",
|
|
31
33
|
variant: "outline",
|
|
32
34
|
size: "sm",
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
onPress: () => m(!0),
|
|
36
|
+
"aria-label": f.deletePasskey.replace("{{name}}", h),
|
|
37
|
+
children: [/* @__PURE__ */ c(a, {}), d.settings.delete]
|
|
38
|
+
}),
|
|
39
|
+
/* @__PURE__ */ c(t, {
|
|
40
|
+
isOpen: p,
|
|
41
|
+
onOpenChange: m,
|
|
42
|
+
passkey: u
|
|
40
43
|
})
|
|
41
44
|
]
|
|
42
45
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { passkeyPlugin as e } from "../../../lib/auth/passkey-plugin.js";
|
|
2
|
+
import { useAuthPlugin as t } from "@better-auth-ui/react";
|
|
3
|
+
import { Fingerprint as n } from "@gravity-ui/icons";
|
|
4
|
+
import { Button as r } from "@heroui/react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/auth/passkey/passkeys-empty.tsx
|
|
7
|
+
function o({ onAddPress: o }) {
|
|
8
|
+
let { localization: s } = t(e);
|
|
9
|
+
return /* @__PURE__ */ a("div", {
|
|
10
|
+
className: "flex flex-col items-center justify-center gap-4",
|
|
11
|
+
children: [
|
|
12
|
+
/* @__PURE__ */ i("div", {
|
|
13
|
+
className: "flex size-10 items-center justify-center rounded-xl bg-surface-secondary",
|
|
14
|
+
children: /* @__PURE__ */ i(n, { className: "size-4.5" })
|
|
15
|
+
}),
|
|
16
|
+
/* @__PURE__ */ a("div", {
|
|
17
|
+
className: "flex flex-col items-center justify-center gap-1 text-center",
|
|
18
|
+
children: [/* @__PURE__ */ i("p", {
|
|
19
|
+
className: "text-sm font-semibold",
|
|
20
|
+
children: s.noPasskeys
|
|
21
|
+
}), /* @__PURE__ */ i("p", {
|
|
22
|
+
className: "text-muted text-xs",
|
|
23
|
+
children: s.passkeysDescription
|
|
24
|
+
})]
|
|
25
|
+
}),
|
|
26
|
+
/* @__PURE__ */ i(r, {
|
|
27
|
+
size: "sm",
|
|
28
|
+
onPress: o,
|
|
29
|
+
children: s.addPasskey
|
|
30
|
+
})
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { o as PasskeysEmpty };
|
|
@@ -1,106 +1,42 @@
|
|
|
1
1
|
import { passkeyPlugin as e } from "../../../lib/auth/passkey-plugin.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { AddPasskeyDialog as t } from "./add-passkey-dialog.js";
|
|
3
|
+
import { Passkey as n } from "./passkey.js";
|
|
4
|
+
import { PasskeySkeleton as r } from "./passkey-skeleton.js";
|
|
5
|
+
import { PasskeysEmpty as i } from "./passkeys-empty.js";
|
|
6
|
+
import { useAuth as a, useAuthPlugin as o, useListPasskeys as s } from "@better-auth-ui/react";
|
|
7
|
+
import { Button as c, Card as l, cn as u } from "@heroui/react";
|
|
8
|
+
import { useState as d } from "react";
|
|
9
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
8
10
|
//#region src/components/auth/passkey/passkeys.tsx
|
|
9
|
-
function
|
|
10
|
-
let { authClient:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}), /* @__PURE__ */ b(l, {
|
|
21
|
-
className: _(m),
|
|
22
|
-
variant: S,
|
|
23
|
-
...w,
|
|
24
|
-
children: /* @__PURE__ */ x(l.Content, {
|
|
25
|
-
className: "gap-0",
|
|
26
|
-
children: [/* @__PURE__ */ x("div", {
|
|
27
|
-
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
28
|
-
children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
29
|
-
className: "text-sm font-medium leading-tight",
|
|
30
|
-
children: D.passkeysDescription
|
|
31
|
-
}), /* @__PURE__ */ b("p", {
|
|
32
|
-
className: "text-muted text-xs mt-0.5",
|
|
33
|
-
children: D.passkeysInstructions
|
|
34
|
-
})] }), /* @__PURE__ */ x(s, { children: [/* @__PURE__ */ b(c, {
|
|
11
|
+
function m({ className: m, variant: h, ...g }) {
|
|
12
|
+
let { authClient: _ } = a(), { localization: v } = o(e), { data: y, isPending: b } = s(_), [x, S] = d(!1);
|
|
13
|
+
return /* @__PURE__ */ p("div", {
|
|
14
|
+
className: u("flex flex-col gap-3", m),
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ p("div", {
|
|
17
|
+
className: "flex items-end justify-between gap-3",
|
|
18
|
+
children: [/* @__PURE__ */ f("h2", {
|
|
19
|
+
className: "text-sm font-semibold truncate",
|
|
20
|
+
children: v.passkeys
|
|
21
|
+
}), /* @__PURE__ */ f(c, {
|
|
35
22
|
className: "shrink-0",
|
|
36
23
|
size: "sm",
|
|
37
|
-
isDisabled:
|
|
38
|
-
onPress: () =>
|
|
39
|
-
children:
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
className: "overflow-visible",
|
|
53
|
-
children: [/* @__PURE__ */ b("p", {
|
|
54
|
-
className: "text-muted text-sm",
|
|
55
|
-
children: D.passkeysInstructions
|
|
56
|
-
}), /* @__PURE__ */ x(g, {
|
|
57
|
-
className: "mt-4",
|
|
58
|
-
name: "passkey-name",
|
|
59
|
-
isDisabled: j,
|
|
60
|
-
value: P,
|
|
61
|
-
onChange: F,
|
|
62
|
-
children: [
|
|
63
|
-
/* @__PURE__ */ b(p, { children: D.passkey }),
|
|
64
|
-
/* @__PURE__ */ b(f, {
|
|
65
|
-
autoFocus: !0,
|
|
66
|
-
placeholder: E.settings.optional,
|
|
67
|
-
variant: "secondary"
|
|
68
|
-
}),
|
|
69
|
-
/* @__PURE__ */ b(u, {})
|
|
70
|
-
]
|
|
71
|
-
})]
|
|
72
|
-
}),
|
|
73
|
-
/* @__PURE__ */ x(s.Footer, { children: [/* @__PURE__ */ b(c, {
|
|
74
|
-
slot: "close",
|
|
75
|
-
variant: "tertiary",
|
|
76
|
-
isDisabled: j,
|
|
77
|
-
children: E.settings.cancel
|
|
78
|
-
}), /* @__PURE__ */ x(c, {
|
|
79
|
-
type: "submit",
|
|
80
|
-
isPending: j,
|
|
81
|
-
children: [j && /* @__PURE__ */ b(h, {
|
|
82
|
-
color: "current",
|
|
83
|
-
size: "sm"
|
|
84
|
-
}), D.addPasskey]
|
|
85
|
-
})] })
|
|
86
|
-
]
|
|
87
|
-
}) }) })
|
|
88
|
-
})] })]
|
|
89
|
-
}), k ? /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b("div", { className: "border-b border-dashed -mx-4 md:-mx-6 my-4" }), /* @__PURE__ */ b(C, {})] }) : O?.map((e) => /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("div", { className: "border-b border-dashed -mx-4 md:-mx-6 my-4" }), /* @__PURE__ */ b(t, { passkey: e })] }, e.id))]
|
|
90
|
-
})
|
|
91
|
-
})] });
|
|
92
|
-
}
|
|
93
|
-
function C() {
|
|
94
|
-
return /* @__PURE__ */ b("div", {
|
|
95
|
-
className: "flex items-center justify-between",
|
|
96
|
-
children: /* @__PURE__ */ x("div", {
|
|
97
|
-
className: "flex items-center gap-3",
|
|
98
|
-
children: [/* @__PURE__ */ b(m, { className: "size-10 rounded-xl" }), /* @__PURE__ */ x("div", {
|
|
99
|
-
className: "flex flex-col gap-1",
|
|
100
|
-
children: [/* @__PURE__ */ b(m, { className: "h-4 w-24 rounded-lg" }), /* @__PURE__ */ b(m, { className: "h-3 w-20 rounded-lg" })]
|
|
101
|
-
})]
|
|
102
|
-
})
|
|
24
|
+
isDisabled: b,
|
|
25
|
+
onPress: () => S(!0),
|
|
26
|
+
children: v.addPasskey
|
|
27
|
+
})]
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ f(l, {
|
|
30
|
+
variant: h,
|
|
31
|
+
...g,
|
|
32
|
+
children: /* @__PURE__ */ f(l.Content, { children: b ? /* @__PURE__ */ f(r, {}) : y?.length ? y.map((e, t) => /* @__PURE__ */ p("div", { children: [t > 0 && /* @__PURE__ */ f("div", { className: "border-b border-dashed -mx-4 my-4" }), /* @__PURE__ */ f(n, { passkey: e })] }, e.id)) : /* @__PURE__ */ f(i, { onAddPress: () => S(!0) }) })
|
|
33
|
+
}),
|
|
34
|
+
/* @__PURE__ */ f(t, {
|
|
35
|
+
isOpen: x,
|
|
36
|
+
onOpenChange: S
|
|
37
|
+
})
|
|
38
|
+
]
|
|
103
39
|
});
|
|
104
40
|
}
|
|
105
41
|
//#endregion
|
|
106
|
-
export {
|
|
42
|
+
export { m as Passkeys };
|
|
@@ -8,9 +8,12 @@ export declare const passkeyPlugin: ((options?: PasskeyPluginOptions | undefined
|
|
|
8
8
|
passkey: string;
|
|
9
9
|
addPasskey: string;
|
|
10
10
|
deletePasskey: string;
|
|
11
|
+
deletePasskeyTitle: string;
|
|
12
|
+
deletePasskeyWarning: string;
|
|
11
13
|
passkeys: string;
|
|
12
14
|
passkeysDescription: string;
|
|
13
|
-
|
|
15
|
+
noPasskeys: string;
|
|
16
|
+
name: string;
|
|
14
17
|
};
|
|
15
18
|
id: "passkey";
|
|
16
19
|
}, "id"> & {
|
package/package.json
CHANGED
|
@@ -54,9 +54,9 @@ export function CreateApiKeyDialog({
|
|
|
54
54
|
e.preventDefault()
|
|
55
55
|
|
|
56
56
|
const formData = new FormData(e.target as HTMLFormElement)
|
|
57
|
-
const name = formData.get("name") as string
|
|
57
|
+
const name = (formData.get("name") as string)?.trim()
|
|
58
58
|
|
|
59
|
-
createApiKey(name ? { name
|
|
59
|
+
createApiKey(name ? { name } : undefined, {
|
|
60
60
|
onSuccess: (result) => {
|
|
61
61
|
handleOpenChange(false)
|
|
62
62
|
setKeyName(name)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type PasskeyAuthClient,
|
|
3
|
+
useAddPasskey,
|
|
4
|
+
useAuth,
|
|
5
|
+
useAuthPlugin
|
|
6
|
+
} from "@better-auth-ui/react"
|
|
7
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
8
|
+
import {
|
|
9
|
+
AlertDialog,
|
|
10
|
+
Button,
|
|
11
|
+
FieldError,
|
|
12
|
+
Form,
|
|
13
|
+
Input,
|
|
14
|
+
Label,
|
|
15
|
+
Spinner,
|
|
16
|
+
TextField
|
|
17
|
+
} from "@heroui/react"
|
|
18
|
+
import type { SyntheticEvent } from "react"
|
|
19
|
+
|
|
20
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
21
|
+
|
|
22
|
+
export type AddPasskeyDialogProps = {
|
|
23
|
+
isOpen: boolean
|
|
24
|
+
onOpenChange: (open: boolean) => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function AddPasskeyDialog({
|
|
28
|
+
isOpen,
|
|
29
|
+
onOpenChange
|
|
30
|
+
}: AddPasskeyDialogProps) {
|
|
31
|
+
const { authClient, localization } = useAuth()
|
|
32
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
33
|
+
|
|
34
|
+
const { mutate: addPasskey, isPending: isAdding } = useAddPasskey(
|
|
35
|
+
authClient as PasskeyAuthClient
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const handleOpenChange = (open: boolean) => {
|
|
39
|
+
onOpenChange(open)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
|
|
43
|
+
e.preventDefault()
|
|
44
|
+
|
|
45
|
+
const formData = new FormData(e.target as HTMLFormElement)
|
|
46
|
+
const name = (formData.get("name") as string)?.trim()
|
|
47
|
+
|
|
48
|
+
addPasskey(name ? { name } : undefined, {
|
|
49
|
+
onSuccess: () => handleOpenChange(false)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<AlertDialog.Backdrop isOpen={isOpen} onOpenChange={handleOpenChange}>
|
|
55
|
+
<AlertDialog.Container>
|
|
56
|
+
<AlertDialog.Dialog>
|
|
57
|
+
<Form onSubmit={handleSubmit}>
|
|
58
|
+
<AlertDialog.CloseTrigger />
|
|
59
|
+
|
|
60
|
+
<AlertDialog.Header>
|
|
61
|
+
<AlertDialog.Icon status="default">
|
|
62
|
+
<Fingerprint />
|
|
63
|
+
</AlertDialog.Icon>
|
|
64
|
+
|
|
65
|
+
<AlertDialog.Heading>
|
|
66
|
+
{passkeyLocalization.addPasskey}
|
|
67
|
+
</AlertDialog.Heading>
|
|
68
|
+
</AlertDialog.Header>
|
|
69
|
+
|
|
70
|
+
<AlertDialog.Body className="overflow-visible">
|
|
71
|
+
<p className="text-muted text-sm">
|
|
72
|
+
{passkeyLocalization.passkeysDescription}
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<TextField
|
|
76
|
+
className="mt-4"
|
|
77
|
+
id="name"
|
|
78
|
+
name="name"
|
|
79
|
+
isDisabled={isAdding}
|
|
80
|
+
>
|
|
81
|
+
<Label>{passkeyLocalization.name}</Label>
|
|
82
|
+
|
|
83
|
+
<Input
|
|
84
|
+
autoFocus
|
|
85
|
+
placeholder={localization.settings.optional}
|
|
86
|
+
variant="secondary"
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
<FieldError />
|
|
90
|
+
</TextField>
|
|
91
|
+
</AlertDialog.Body>
|
|
92
|
+
|
|
93
|
+
<AlertDialog.Footer>
|
|
94
|
+
<Button slot="close" variant="tertiary" isDisabled={isAdding}>
|
|
95
|
+
{localization.settings.cancel}
|
|
96
|
+
</Button>
|
|
97
|
+
|
|
98
|
+
<Button type="submit" isPending={isAdding}>
|
|
99
|
+
{isAdding && <Spinner color="current" size="sm" />}
|
|
100
|
+
|
|
101
|
+
{passkeyLocalization.addPasskey}
|
|
102
|
+
</Button>
|
|
103
|
+
</AlertDialog.Footer>
|
|
104
|
+
</Form>
|
|
105
|
+
</AlertDialog.Dialog>
|
|
106
|
+
</AlertDialog.Container>
|
|
107
|
+
</AlertDialog.Backdrop>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type PasskeyAuthClient,
|
|
3
|
+
useAuth,
|
|
4
|
+
useAuthPlugin,
|
|
5
|
+
useDeletePasskey
|
|
6
|
+
} from "@better-auth-ui/react"
|
|
7
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
8
|
+
import {
|
|
9
|
+
AlertDialog,
|
|
10
|
+
Button,
|
|
11
|
+
Input,
|
|
12
|
+
Label,
|
|
13
|
+
Spinner,
|
|
14
|
+
TextField
|
|
15
|
+
} from "@heroui/react"
|
|
16
|
+
|
|
17
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
18
|
+
|
|
19
|
+
export type ListedPasskey = {
|
|
20
|
+
id: string
|
|
21
|
+
name?: string | null
|
|
22
|
+
createdAt: Date
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type DeletePasskeyDialogProps = {
|
|
26
|
+
isOpen: boolean
|
|
27
|
+
onOpenChange: (open: boolean) => void
|
|
28
|
+
passkey: ListedPasskey
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function DeletePasskeyDialog({
|
|
32
|
+
isOpen,
|
|
33
|
+
onOpenChange,
|
|
34
|
+
passkey
|
|
35
|
+
}: DeletePasskeyDialogProps) {
|
|
36
|
+
const { authClient, localization } = useAuth()
|
|
37
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
38
|
+
|
|
39
|
+
const passkeyName = passkey.name || passkeyLocalization.passkey
|
|
40
|
+
|
|
41
|
+
const { mutate: deletePasskey, isPending: isDeleting } = useDeletePasskey(
|
|
42
|
+
authClient as PasskeyAuthClient,
|
|
43
|
+
{
|
|
44
|
+
onSuccess: () => onOpenChange(false)
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<AlertDialog.Backdrop isOpen={isOpen} onOpenChange={onOpenChange}>
|
|
50
|
+
<AlertDialog.Container>
|
|
51
|
+
<AlertDialog.Dialog>
|
|
52
|
+
<AlertDialog.CloseTrigger />
|
|
53
|
+
|
|
54
|
+
<AlertDialog.Header>
|
|
55
|
+
<AlertDialog.Icon status="danger">
|
|
56
|
+
<Fingerprint />
|
|
57
|
+
</AlertDialog.Icon>
|
|
58
|
+
|
|
59
|
+
<AlertDialog.Heading>
|
|
60
|
+
{passkeyLocalization.deletePasskeyTitle}
|
|
61
|
+
</AlertDialog.Heading>
|
|
62
|
+
</AlertDialog.Header>
|
|
63
|
+
|
|
64
|
+
<AlertDialog.Body className="flex flex-col gap-4 overflow-visible">
|
|
65
|
+
<p className="text-muted text-sm">
|
|
66
|
+
{passkeyLocalization.deletePasskeyWarning}
|
|
67
|
+
</p>
|
|
68
|
+
|
|
69
|
+
<TextField value={passkeyName} variant="secondary">
|
|
70
|
+
<Label>{passkey.name || passkeyLocalization.passkey}</Label>
|
|
71
|
+
|
|
72
|
+
<Input readOnly />
|
|
73
|
+
</TextField>
|
|
74
|
+
</AlertDialog.Body>
|
|
75
|
+
|
|
76
|
+
<AlertDialog.Footer>
|
|
77
|
+
<Button slot="close" variant="tertiary" isDisabled={isDeleting}>
|
|
78
|
+
{localization.settings.cancel}
|
|
79
|
+
</Button>
|
|
80
|
+
|
|
81
|
+
<Button
|
|
82
|
+
variant="danger"
|
|
83
|
+
onPress={() => deletePasskey({ id: passkey.id })}
|
|
84
|
+
isPending={isDeleting}
|
|
85
|
+
>
|
|
86
|
+
{isDeleting && <Spinner color="current" size="sm" />}
|
|
87
|
+
|
|
88
|
+
{passkeyLocalization.deletePasskeyTitle}
|
|
89
|
+
</Button>
|
|
90
|
+
</AlertDialog.Footer>
|
|
91
|
+
</AlertDialog.Dialog>
|
|
92
|
+
</AlertDialog.Container>
|
|
93
|
+
</AlertDialog.Backdrop>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Skeleton } from "@heroui/react"
|
|
2
|
+
|
|
3
|
+
export function PasskeySkeleton() {
|
|
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-32 rounded-lg" />
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -1,29 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type PasskeyAuthClient,
|
|
3
|
-
useAuth,
|
|
4
|
-
useAuthPlugin,
|
|
5
|
-
useDeletePasskey
|
|
6
|
-
} from "@better-auth-ui/react"
|
|
1
|
+
import { useAuth, useAuthPlugin } from "@better-auth-ui/react"
|
|
7
2
|
import { Fingerprint, Xmark } from "@gravity-ui/icons"
|
|
8
|
-
import { Button
|
|
3
|
+
import { Button } from "@heroui/react"
|
|
4
|
+
import { useState } from "react"
|
|
9
5
|
|
|
10
6
|
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
7
|
+
import {
|
|
8
|
+
DeletePasskeyDialog,
|
|
9
|
+
type ListedPasskey
|
|
10
|
+
} from "./delete-passkey-dialog"
|
|
11
11
|
|
|
12
12
|
export type PasskeyProps = {
|
|
13
|
-
passkey:
|
|
14
|
-
id: string
|
|
15
|
-
name?: string | null
|
|
16
|
-
createdAt: Date
|
|
17
|
-
}
|
|
13
|
+
passkey: ListedPasskey
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
export function Passkey({ passkey }: PasskeyProps) {
|
|
21
|
-
const {
|
|
17
|
+
const { localization } = useAuth()
|
|
22
18
|
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
23
|
-
|
|
24
|
-
const { mutate: deletePasskey, isPending } = useDeletePasskey(
|
|
25
|
-
authClient as PasskeyAuthClient
|
|
26
|
-
)
|
|
19
|
+
const [deleteOpen, setDeleteOpen] = useState(false)
|
|
27
20
|
|
|
28
21
|
const passkeyName = passkey.name || passkeyLocalization.passkey
|
|
29
22
|
|
|
@@ -33,8 +26,10 @@ export function Passkey({ passkey }: PasskeyProps) {
|
|
|
33
26
|
<Fingerprint className="size-4.5" />
|
|
34
27
|
</div>
|
|
35
28
|
|
|
36
|
-
<div className="flex
|
|
37
|
-
<span className="text-sm font-medium leading-tight">
|
|
29
|
+
<div className="flex min-w-0 flex-col">
|
|
30
|
+
<span className="truncate text-sm font-medium leading-tight">
|
|
31
|
+
{passkeyName}
|
|
32
|
+
</span>
|
|
38
33
|
|
|
39
34
|
<span className="text-xs text-muted">
|
|
40
35
|
{new Date(passkey.createdAt).toLocaleString(undefined, {
|
|
@@ -48,16 +43,22 @@ export function Passkey({ passkey }: PasskeyProps) {
|
|
|
48
43
|
className="ml-auto shrink-0"
|
|
49
44
|
variant="outline"
|
|
50
45
|
size="sm"
|
|
51
|
-
|
|
52
|
-
onPress={() => deletePasskey({ id: passkey.id })}
|
|
46
|
+
onPress={() => setDeleteOpen(true)}
|
|
53
47
|
aria-label={passkeyLocalization.deletePasskey.replace(
|
|
54
48
|
"{{name}}",
|
|
55
49
|
passkeyName
|
|
56
50
|
)}
|
|
57
51
|
>
|
|
58
|
-
|
|
52
|
+
<Xmark />
|
|
53
|
+
|
|
59
54
|
{localization.settings.delete}
|
|
60
55
|
</Button>
|
|
56
|
+
|
|
57
|
+
<DeletePasskeyDialog
|
|
58
|
+
isOpen={deleteOpen}
|
|
59
|
+
onOpenChange={setDeleteOpen}
|
|
60
|
+
passkey={passkey}
|
|
61
|
+
/>
|
|
61
62
|
</div>
|
|
62
63
|
)
|
|
63
64
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useAuthPlugin } from "@better-auth-ui/react"
|
|
2
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
3
|
+
import { Button } from "@heroui/react"
|
|
4
|
+
|
|
5
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
6
|
+
|
|
7
|
+
export type PasskeysEmptyProps = {
|
|
8
|
+
onAddPress: () => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function PasskeysEmpty({ onAddPress }: PasskeysEmptyProps) {
|
|
12
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
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
|
+
<Fingerprint 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">
|
|
22
|
+
{passkeyLocalization.noPasskeys}
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
<p className="text-muted text-xs">
|
|
26
|
+
{passkeyLocalization.passkeysDescription}
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<Button size="sm" onPress={onAddPress}>
|
|
31
|
+
{passkeyLocalization.addPasskey}
|
|
32
|
+
</Button>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type PasskeyAuthClient,
|
|
3
|
-
useAddPasskey,
|
|
4
3
|
useAuth,
|
|
5
4
|
useAuthPlugin,
|
|
6
5
|
useListPasskeys
|
|
7
6
|
} from "@better-auth-ui/react"
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
AlertDialog,
|
|
11
|
-
Button,
|
|
12
|
-
Card,
|
|
13
|
-
type CardProps,
|
|
14
|
-
cn,
|
|
15
|
-
FieldError,
|
|
16
|
-
Form,
|
|
17
|
-
Input,
|
|
18
|
-
Label,
|
|
19
|
-
Skeleton,
|
|
20
|
-
Spinner,
|
|
21
|
-
TextField
|
|
22
|
-
} from "@heroui/react"
|
|
23
|
-
import { type SyntheticEvent, useState } from "react"
|
|
7
|
+
import { Button, Card, type CardProps, cn } from "@heroui/react"
|
|
8
|
+
import { useState } from "react"
|
|
24
9
|
|
|
25
10
|
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
11
|
+
import { AddPasskeyDialog } from "./add-passkey-dialog"
|
|
26
12
|
import { Passkey } from "./passkey"
|
|
13
|
+
import { PasskeySkeleton } from "./passkey-skeleton"
|
|
14
|
+
import { PasskeysEmpty } from "./passkeys-empty"
|
|
27
15
|
|
|
28
16
|
export type PasskeysProps = {
|
|
29
17
|
className?: string
|
|
@@ -35,162 +23,53 @@ export function Passkeys({
|
|
|
35
23
|
variant,
|
|
36
24
|
...props
|
|
37
25
|
}: PasskeysProps & Omit<CardProps, "children">) {
|
|
38
|
-
const { authClient
|
|
26
|
+
const { authClient } = useAuth()
|
|
39
27
|
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
40
28
|
|
|
41
29
|
const { data: passkeys, isPending } = useListPasskeys(
|
|
42
30
|
authClient as PasskeyAuthClient
|
|
43
31
|
)
|
|
44
|
-
const { mutate: addPasskey, isPending: isAdding } = useAddPasskey(
|
|
45
|
-
authClient as PasskeyAuthClient
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
const [nameOpen, setNameOpen] = useState(false)
|
|
49
|
-
const [name, setName] = useState("")
|
|
50
32
|
|
|
51
|
-
const
|
|
52
|
-
setNameOpen(open)
|
|
53
|
-
setName("")
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const handleSubmit = async (e: SyntheticEvent<HTMLFormElement>) => {
|
|
57
|
-
e.preventDefault()
|
|
58
|
-
|
|
59
|
-
addPasskey(
|
|
60
|
-
{ name: name.trim() || undefined },
|
|
61
|
-
{
|
|
62
|
-
onSuccess: () => {
|
|
63
|
-
setNameOpen(false)
|
|
64
|
-
setName("")
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
}
|
|
33
|
+
const [addOpen, setAddOpen] = useState(false)
|
|
69
34
|
|
|
70
35
|
return (
|
|
71
|
-
<div>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
</p>
|
|
87
|
-
</div>
|
|
88
|
-
|
|
89
|
-
<AlertDialog>
|
|
90
|
-
<Button
|
|
91
|
-
className="shrink-0"
|
|
92
|
-
size="sm"
|
|
93
|
-
isDisabled={isPending}
|
|
94
|
-
onPress={() => setNameOpen(true)}
|
|
95
|
-
>
|
|
96
|
-
{passkeyLocalization.addPasskey}
|
|
97
|
-
</Button>
|
|
98
|
-
|
|
99
|
-
<AlertDialog.Backdrop
|
|
100
|
-
isOpen={nameOpen}
|
|
101
|
-
onOpenChange={handleDialogOpenChange}
|
|
102
|
-
>
|
|
103
|
-
<AlertDialog.Container>
|
|
104
|
-
<AlertDialog.Dialog>
|
|
105
|
-
<Form onSubmit={handleSubmit}>
|
|
106
|
-
<AlertDialog.CloseTrigger />
|
|
107
|
-
|
|
108
|
-
<AlertDialog.Header>
|
|
109
|
-
<AlertDialog.Icon status="default">
|
|
110
|
-
<Fingerprint />
|
|
111
|
-
</AlertDialog.Icon>
|
|
112
|
-
|
|
113
|
-
<AlertDialog.Heading>
|
|
114
|
-
{passkeyLocalization.addPasskey}
|
|
115
|
-
</AlertDialog.Heading>
|
|
116
|
-
</AlertDialog.Header>
|
|
117
|
-
|
|
118
|
-
<AlertDialog.Body className="overflow-visible">
|
|
119
|
-
<p className="text-muted text-sm">
|
|
120
|
-
{passkeyLocalization.passkeysInstructions}
|
|
121
|
-
</p>
|
|
122
|
-
|
|
123
|
-
<TextField
|
|
124
|
-
className="mt-4"
|
|
125
|
-
name="passkey-name"
|
|
126
|
-
isDisabled={isAdding}
|
|
127
|
-
value={name}
|
|
128
|
-
onChange={setName}
|
|
129
|
-
>
|
|
130
|
-
<Label>{passkeyLocalization.passkey}</Label>
|
|
131
|
-
|
|
132
|
-
<Input
|
|
133
|
-
autoFocus
|
|
134
|
-
placeholder={localization.settings.optional}
|
|
135
|
-
variant="secondary"
|
|
136
|
-
/>
|
|
137
|
-
|
|
138
|
-
<FieldError />
|
|
139
|
-
</TextField>
|
|
140
|
-
</AlertDialog.Body>
|
|
141
|
-
|
|
142
|
-
<AlertDialog.Footer>
|
|
143
|
-
<Button
|
|
144
|
-
slot="close"
|
|
145
|
-
variant="tertiary"
|
|
146
|
-
isDisabled={isAdding}
|
|
147
|
-
>
|
|
148
|
-
{localization.settings.cancel}
|
|
149
|
-
</Button>
|
|
150
|
-
|
|
151
|
-
<Button type="submit" isPending={isAdding}>
|
|
152
|
-
{isAdding && <Spinner color="current" size="sm" />}
|
|
153
|
-
|
|
154
|
-
{passkeyLocalization.addPasskey}
|
|
155
|
-
</Button>
|
|
156
|
-
</AlertDialog.Footer>
|
|
157
|
-
</Form>
|
|
158
|
-
</AlertDialog.Dialog>
|
|
159
|
-
</AlertDialog.Container>
|
|
160
|
-
</AlertDialog.Backdrop>
|
|
161
|
-
</AlertDialog>
|
|
162
|
-
</div>
|
|
36
|
+
<div className={cn("flex flex-col gap-3", className)}>
|
|
37
|
+
<div className="flex items-end justify-between gap-3">
|
|
38
|
+
<h2 className="text-sm font-semibold truncate">
|
|
39
|
+
{passkeyLocalization.passkeys}
|
|
40
|
+
</h2>
|
|
41
|
+
|
|
42
|
+
<Button
|
|
43
|
+
className="shrink-0"
|
|
44
|
+
size="sm"
|
|
45
|
+
isDisabled={isPending}
|
|
46
|
+
onPress={() => setAddOpen(true)}
|
|
47
|
+
>
|
|
48
|
+
{passkeyLocalization.addPasskey}
|
|
49
|
+
</Button>
|
|
50
|
+
</div>
|
|
163
51
|
|
|
52
|
+
<Card variant={variant} {...props}>
|
|
53
|
+
<Card.Content>
|
|
164
54
|
{isPending ? (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
</>
|
|
55
|
+
<PasskeySkeleton />
|
|
56
|
+
) : !passkeys?.length ? (
|
|
57
|
+
<PasskeysEmpty onAddPress={() => setAddOpen(true)} />
|
|
169
58
|
) : (
|
|
170
|
-
passkeys
|
|
59
|
+
passkeys.map((passkey, index) => (
|
|
171
60
|
<div key={passkey.id}>
|
|
172
|
-
|
|
61
|
+
{index > 0 && (
|
|
62
|
+
<div className="border-b border-dashed -mx-4 my-4" />
|
|
63
|
+
)}
|
|
64
|
+
|
|
173
65
|
<Passkey passkey={passkey} />
|
|
174
66
|
</div>
|
|
175
67
|
))
|
|
176
68
|
)}
|
|
177
69
|
</Card.Content>
|
|
178
70
|
</Card>
|
|
179
|
-
</div>
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
71
|
|
|
183
|
-
|
|
184
|
-
return (
|
|
185
|
-
<div className="flex items-center justify-between">
|
|
186
|
-
<div className="flex items-center gap-3">
|
|
187
|
-
<Skeleton className="size-10 rounded-xl" />
|
|
188
|
-
|
|
189
|
-
<div className="flex flex-col gap-1">
|
|
190
|
-
<Skeleton className="h-4 w-24 rounded-lg" />
|
|
191
|
-
<Skeleton className="h-3 w-20 rounded-lg" />
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
72
|
+
<AddPasskeyDialog isOpen={addOpen} onOpenChange={setAddOpen} />
|
|
194
73
|
</div>
|
|
195
74
|
)
|
|
196
75
|
}
|