UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

426 lines (425 loc) 19.3 kB
'use client'; import { jsxs as t, jsx as e, Fragment as Y } from "react/jsx-runtime"; import "@emotion/styled"; import { Button as E } from "../../ui/button/button.js"; import { Card as O, CardBody as S } from "../../ui/card/card.js"; import { Chip as _ } from "../../ui/chip/chip.js"; import { Tooltip as q } from "../../ui/tooltip/tooltip.js"; import { Modal as R, ModalHeader as W, ModalBody as Z, ModalFooter as J } from "../../ui/modal/modal.js"; import { ShieldCheckIcon as K, UserCircleIcon as I, CheckCircleIcon as Q, LinkIcon as F, ExclamationTriangleIcon as X } from "@heroicons/react/24/outline"; import M, { useState as k, useMemo as A, useCallback as N } from "react"; import { useAuth as U } from "../../../hooks/use-auth.js"; import { useConfig as P } from "../../../hooks/use-config.js"; import { useOAuth as j } from "../../../hooks/use-oauth.js"; import ee from "../../forms/form-wrapper.js"; const w = M.memo( ({ provider: a, className: c = "w-5 h-5" }) => ({ google: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ t("svg", { className: c, viewBox: "0 0 24 24", children: [ /* @__PURE__ */ e( "path", { fill: "#4285F4", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" } ), /* @__PURE__ */ e( "path", { fill: "#34A853", d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" } ), /* @__PURE__ */ e( "path", { fill: "#FBBC05", d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" } ), /* @__PURE__ */ e( "path", { fill: "#EA4335", d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" } ) ] }) ), github: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ e("svg", { className: c, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e("path", { d: "M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" }) }) ), microsoft: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ t("svg", { className: c, viewBox: "0 0 24 24", children: [ /* @__PURE__ */ e("path", { fill: "#f25022", d: "M1 1h10v10H1z" }), /* @__PURE__ */ e("path", { fill: "#00a4ef", d: "M13 1h10v10H13z" }), /* @__PURE__ */ e("path", { fill: "#7fba00", d: "M1 13h10v10H1z" }), /* @__PURE__ */ e("path", { fill: "#ffb900", d: "M13 13h10v10H13z" }) ] }) ), apple: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ e("svg", { className: c, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e("path", { d: "M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" }) }) ), facebook: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ e("svg", { className: c, fill: "#1877f2", viewBox: "0 0 24 24", children: /* @__PURE__ */ e("path", { d: "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" }) }) ), twitter: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ e("svg", { className: c, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e("path", { d: "M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z" }) }) ), linkedin: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ e("svg", { className: c, fill: "#0077b5", viewBox: "0 0 24 24", children: /* @__PURE__ */ e("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" }) }) ), discord: ( // biome-ignore lint/a11y/noSvgWithoutTitle: <explanation> /* @__PURE__ */ e("svg", { className: c, fill: "#5865f2", viewBox: "0 0 24 24", children: /* @__PURE__ */ e("path", { d: "M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419-.0001 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9554 2.4189-2.1568 2.4189Z" }) }) ) })[a] || /* @__PURE__ */ e(I, { className: c }) ); w.displayName = "ProviderIcon"; const z = M.memo( ({ provider: a, account: c, onUnlink: r, allowUnlink: u = !0, isLoading: i = !1, showDetails: h = !0 }) => { const { components: m } = P(), l = m.Button ?? E, [d, o] = k(!1), s = N(() => { r?.(), o(!1); }, [r]), f = (p) => ({ google: "Google", github: "GitHub", microsoft: "Microsoft", apple: "Apple", facebook: "Facebook", twitter: "Twitter", linkedin: "LinkedIn", discord: "Discord" })[p] || p.charAt(0).toUpperCase() + p.slice(1), g = (p) => { try { return new Date(p).toLocaleDateString(); } catch { return "Unknown"; } }; return /* @__PURE__ */ t(Y, { children: [ /* @__PURE__ */ e(O, { className: "w-full", children: /* @__PURE__ */ e(S, { className: "p-4", children: /* @__PURE__ */ e("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex-shrink-0", children: /* @__PURE__ */ e(w, { provider: a, className: "w-8 h-8" }) }), /* @__PURE__ */ t("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ t("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ e("h4", { className: "font-medium text-foreground", children: f(a) }), /* @__PURE__ */ e( _, { size: "sm", color: "success", variant: "flat", startContent: /* @__PURE__ */ e(Q, { className: "w-3 h-3" }), children: "Connected" } ) ] }), h && c && /* @__PURE__ */ t("div", { className: "mt-1 space-y-1", children: [ c.email && /* @__PURE__ */ e("p", { className: "text-sm text-default-500 truncate", children: c.email }), c.connectedAt && /* @__PURE__ */ t("p", { className: "text-xs text-default-400", children: [ "Connected ", g(c.connectedAt) ] }) ] }) ] }), /* @__PURE__ */ e("div", { className: "flex items-center gap-2", children: u && /* @__PURE__ */ e(q, { content: "Disconnect account", children: /* @__PURE__ */ e( l, { isIconOnly: !0, size: "sm", variant: "light", color: "danger", onPress: () => o(!0), isLoading: i, children: /* @__PURE__ */ e(F, { className: "w-4 h-4" }) } ) }) }) ] }) }) }) }), /* @__PURE__ */ t( R, { isOpen: d, onClose: () => o(!1), placement: "center", children: [ /* @__PURE__ */ e(W, { children: /* @__PURE__ */ t("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ e(X, { className: "w-5 h-5 text-warning-600" }), "Disconnect Account" ] }) }), /* @__PURE__ */ t(Z, { children: [ /* @__PURE__ */ t("p", { children: [ "Are you sure you want to disconnect your", " ", f(a), " account?" ] }), /* @__PURE__ */ t("p", { className: "text-sm text-default-500", children: [ "You won't be able to sign in using", " ", f(a), " until you reconnect it." ] }) ] }), /* @__PURE__ */ t(J, { children: [ /* @__PURE__ */ e(l, { variant: "light", onPress: () => o(!1), children: "Cancel" }), /* @__PURE__ */ e( l, { color: "danger", onPress: s, isLoading: i, children: "Disconnect" } ) ] }) ] } ) ] }); } ); z.displayName = "ConnectedAccountCard"; const V = M.memo( ({ provider: a, isConnected: c, onConnect: r, onDisconnect: u, isLoading: i, disabled: h = !1, allowUnlink: m = !0 }) => { const { components: l } = P(), d = l.Button ?? d, o = (s) => ({ google: "Google", github: "GitHub", microsoft: "Microsoft", apple: "Apple", facebook: "Facebook", twitter: "Twitter", linkedin: "LinkedIn", discord: "Discord" })[s] || s.charAt(0).toUpperCase() + s.slice(1); return c ? /* @__PURE__ */ t("div", { className: "flex items-center justify-between p-4 border border-success-200 bg-success-50 dark:bg-success-900/20 rounded-lg", children: [ /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e(w, { provider: a, className: "w-6 h-6" }), /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("p", { className: "font-medium text-foreground", children: o(a) }), /* @__PURE__ */ e("p", { className: "text-sm text-success-600 dark:text-success-400", children: "Connected" }) ] }) ] }), m && /* @__PURE__ */ e( d, { size: "sm", variant: "light", color: "danger", onPress: u, isLoading: i, isDisabled: h, startContent: /* @__PURE__ */ e(F, { className: "w-4 h-4" }), children: "Disconnect" } ) ] }) : /* @__PURE__ */ e( d, { variant: "bordered", className: "w-full justify-start p-4 h-auto", onPress: r, isLoading: i, isDisabled: h, startContent: /* @__PURE__ */ e(w, { provider: a, className: "w-6 h-6" }), children: /* @__PURE__ */ t("div", { className: "flex flex-col items-start", children: [ /* @__PURE__ */ t("span", { className: "font-medium", children: [ "Connect ", o(a) ] }), /* @__PURE__ */ t("span", { className: "text-xs text-default-500", children: [ "Link your ", o(a), " account" ] }) ] }) } ); } ); V.displayName = "ProviderConnectionButton"; function te({ onSuccess: a, onError: c, title: r = "Connected Accounts", subtitle: u = "Manage your connected social accounts", variant: i = "default", size: h = "md", className: m = "", providers: l, showStatus: d = !0, allowUnlink: o = !0, minimumConnections: s = 0 }) { const { providers: f, connectProvider: g, disconnectProvider: p, isProviderConnected: y, isLoading: $ } = j(), { user: b } = U(), { components: ne } = P(), [B, x] = k(null), D = A(() => l && l.length > 0 ? f.filter((n) => l.includes(n.name)) : f, [f, l]), v = A(() => b?.connectedAccounts ? b.connectedAccounts : [], [b]), G = N( async (n) => { try { x(n), await g(n), a?.(n, { action: "connect" }); } catch (C) { c?.( C instanceof Error ? C : new Error(`Failed to connect ${n}`) ); } finally { x(null); } }, [g, a, c] ), H = N( async (n) => { const C = v.length; if (s > 0 && C <= s) { c?.( new Error( `You must have at least ${s} connected account(s)` ) ); return; } try { x(n), await p(n), a?.(n, { action: "disconnect" }); } catch (L) { c?.( L instanceof Error ? L : new Error(`Failed to disconnect ${n}`) ); } finally { x(null); } }, [ p, v.length, s, a, c ] ), T = A( () => ({ size: h, variant: "flat", className: `space-y-6 ${m}`, title: r, subtitle: u, showCard: i === "card" }), [h, m, r, u, i] ); return /* @__PURE__ */ t(ee, { ...T, children: [ d && /* @__PURE__ */ t("div", { className: "flex items-center gap-2 p-4 bg-default-100 dark:bg-default-900/50 rounded-lg", children: [ /* @__PURE__ */ e(K, { className: "w-5 h-5 text-success-600" }), /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium text-foreground", children: "Account Security" }), /* @__PURE__ */ t("p", { className: "text-xs text-default-500", children: [ v.length, " account(s) connected", s > 0 && /* @__PURE__ */ t("span", { className: "ml-1", children: [ "(minimum ", s, " required)" ] }) ] }) ] }) ] }), v.length > 0 && /* @__PURE__ */ t("div", { className: "space-y-4", children: [ /* @__PURE__ */ e("h3", { className: "text-lg font-semibold text-foreground", children: "Connected Accounts" }), /* @__PURE__ */ e("div", { className: "space-y-3", children: v.map((n) => /* @__PURE__ */ e( z, { provider: n.provider, account: n, onUnlink: () => H(n.provider), allowUnlink: o, isLoading: B === n.provider }, n.provider )) }) ] }), /* @__PURE__ */ t("div", { className: "space-y-4", children: [ /* @__PURE__ */ e("h3", { className: "text-lg font-semibold text-foreground", children: v.length > 0 ? "Available Connections" : "Connect Your Accounts" }), /* @__PURE__ */ e("div", { className: "space-y-3", children: D.filter((n) => !y(n.name)).map((n) => /* @__PURE__ */ e( V, { provider: n.name, isConnected: !1, onConnect: () => G(n.name), onDisconnect: () => H(n.name), isLoading: B === n.name, disabled: $ }, n.name )) }), D.filter((n) => !y(n.name)).length === 0 && /* @__PURE__ */ t("div", { className: "text-center py-8", children: [ /* @__PURE__ */ e(I, { className: "w-12 h-12 text-default-400 mx-auto mb-4" }), /* @__PURE__ */ e("p", { className: "text-default-500", children: "All available accounts are connected" }) ] }) ] }), /* @__PURE__ */ e("div", { className: "text-center text-xs text-default-400 border-t pt-4", children: /* @__PURE__ */ e("p", { children: "Connected accounts can be used to sign in to your account. You can disconnect them at any time." }) }) ] }); } function ve(a) { return /* @__PURE__ */ e(te, { ...a, variant: "card" }); } function ge({ allowUnlink: a = !0, showDetails: c = !0, onUnlink: r }) { const { user: u } = U(), { disconnectProvider: i } = j(), [h, m] = k(null), l = N( async (o) => { try { m(o), await i(o), r?.(o); } catch (s) { console.error("Failed to unlink provider:", s); } finally { m(null); } }, [i, r] ), d = u?.connectedAccounts || []; return d.length === 0 ? /* @__PURE__ */ e("div", { className: "text-center py-4", children: /* @__PURE__ */ e("p", { className: "text-default-500 text-sm", children: "No connected accounts" }) }) : /* @__PURE__ */ e("div", { className: "space-y-3", children: d.map((o) => /* @__PURE__ */ e( z, { provider: o.provider, account: o, onUnlink: () => l(o.provider), allowUnlink: a, isLoading: h === o.provider, showDetails: c }, o.provider )) }); } export { te as AccountLinking, ve as AccountLinkingCard, ge as ConnectedAccountsList }; //# sourceMappingURL=link.js.map