UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

378 lines (377 loc) 10.6 kB
'use client'; import { jsx as t, jsxs as S } from "react/jsx-runtime"; import c from "@emotion/styled"; import { Button as X } from "../ui/button/button.js"; import { Input as ne } from "../ui/input/input.js"; import { Chip as re } from "../ui/chip/chip.js"; import { Popover as F } from "../ui/popover/popover.js"; import { Listbox as le, ListboxItem as me } from "../ui/listbox/listbox.js"; import { useTheme as ae } from "../../theme/context.js"; import { AnimatePresence as ce, motion as he } from "framer-motion"; import n from "react"; import { useConfig as ue } from "../../hooks/use-config.js"; import { FieldError as de } from "./field-error.js"; import { useFormField as fe } from "./form-wrapper.js"; const pe = c.div` display: flex; flex-direction: column; gap: ${(e) => e.theme.spacing[1]}; `, ge = c.div` padding: ${(e) => e.theme.spacing[1]}; `, ve = c.div` font-size: ${(e) => e.theme.fontSizes.xs}; color: ${(e) => e.theme.colors.text.tertiary}; `, ye = c.span` font-weight: ${(e) => e.theme.fontWeights.medium}; `, $e = c(he.div)` display: flex; flex-direction: column; gap: ${(e) => e.theme.spacing[1]}; `, ke = c.div` font-size: ${(e) => e.theme.fontSizes.xs}; color: ${(e) => e.theme.colors.text.secondary}; font-weight: ${(e) => e.theme.fontWeights.medium}; `, xe = c.div` display: flex; flex-wrap: wrap; gap: ${(e) => e.theme.spacing[1]}; `, Ce = c.svg` width: ${(e) => e.theme.spacing[4]}; height: ${(e) => e.theme.spacing[4]}; color: ${(e) => e.theme.colors.text.quaternary}; `, Ie = c.svg` width: ${(e) => e.theme.spacing[3]}; height: ${(e) => e.theme.spacing[3]}; `, Le = c.svg` width: ${(e) => e.theme.spacing[4]}; height: ${(e) => e.theme.spacing[4]}; color: ${(e) => e.theme.colors.primary[500]}; `, we = c(X)` font-size: ${(e) => e.theme.fontSizes.xs}; height: ${(e) => e.theme.spacing[6]}; `, ze = [ "gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "icloud.com", "aol.com", "live.com", "msn.com", "me.com", "mail.com", "protonmail.com", "yandex.com", "zoho.com", "fastmail.com" ], U = { "gmial.com": "gmail.com", "gmai.com": "gmail.com", "gmil.com": "gmail.com", "yahho.com": "yahoo.com", "yaho.com": "yahoo.com", "hotmial.com": "hotmail.com", "hotmil.com": "hotmail.com", "outlok.com": "outlook.com", "outloo.com": "outlook.com" }; function De(e, x = {}) { const { validateFormat: C = !0, validateDomain: y = !1, allowedDomains: r = [], blockedDomains: $ = [] } = x, h = [], d = []; let i = null, l = null; C && (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e) || h.push("Please enter a valid email address")); const f = e.split("@"); return f.length === 2 && (l = f[0], i = f[1].toLowerCase(), i && U[i] && d.push( `Did you mean ${l}@${U[i]}?` ), y && i && (r.length > 0 && !r.includes(i) && h.push( `Email must be from one of these domains: ${r.join(", ")}` ), $.includes(i) && h.push("This email domain is not allowed"), /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(i) || h.push("Invalid email domain")), l && i && i.length > 0 && !i.includes(".") && ze.filter( (p) => p.toLowerCase().startsWith(i.toLowerCase()) ).slice(0, 3).forEach((p) => { d.push(`${l}@${p}`); })), { isValid: h.length === 0, errors: h, suggestions: d, domain: i, username: l }; } function Se({ name: e = "email", label: x = "Email", placeholder: C = "Enter your email address", value: y = "", onChange: r, onBlur: $, onFocus: h, required: d = !1, disabled: i = !1, showSuggestions: l = !0, validateFormat: f = !0, validateDomain: k = !1, allowedDomains: p = [], blockedDomains: I = [], size: j = "md", radius: Y = "md", variant: q = "bordered", className: T = "", autoFocus: W = !1, autoComplete: R = "email", error: g, description: V, showVerificationStatus: A = !1, isVerified: M = !1, onRequestVerification: L, startContent: O, endContent: w }) { const { theme: m } = ae(), { components: b, organizationSettings: z } = ue(), a = fe(e), ee = n.useMemo( () => b.Input ?? ne, [b.Input] ), Z = b.EmailField; if (Z) return /* @__PURE__ */ t( Z, { name: e, label: x, placeholder: C, value: y, onChange: r, onBlur: $, onFocus: h, required: d, disabled: i, showSuggestions: l, validateFormat: f, validateDomain: k, allowedDomains: p, blockedDomains: I, size: j, variant: q, className: T, autoFocus: W, autoComplete: R, error: g, description: V, showVerificationStatus: A, isVerified: M, onRequestVerification: L, startContent: O, endContent: w } ); const [oe, _] = n.useState(y), [E, G] = n.useState(!1), [H, v] = n.useState(!1), u = r ? y : oe, D = n.useMemo(() => { const o = z?.emailRestrictions?.allowedDomains; return o && o.length > 0 ? o : p; }, [p, z]), J = n.useMemo(() => { const o = z?.emailRestrictions?.blockedDomains; return o && o.length > 0 ? [...I, ...o] : I; }, [I, z]), s = n.useMemo(() => u ? De(u, { validateFormat: f, validateDomain: k, allowedDomains: D, blockedDomains: J }) : null, [ u, f, k, D, J ]), P = n.useMemo(() => { const o = []; return g && (Array.isArray(g) ? o.push(...g) : o.push(g)), a.error && (Array.isArray(a.error) ? o.push(...a.error) : o.push(a.error)), s && !s.isValid && o.push(...s.errors), o.length > 0 ? o : null; }, [g, a.error, s]), B = n.useCallback( (o) => { r ? r(o) : _(o), a.clearError && a.clearError(), s?.suggestions.length && E && l ? v(!0) : v(!1); }, [r, a, s, E, l] ), te = n.useCallback(() => { if (G(!1), v(!1), u) { const o = u.trim().toLowerCase(); o !== u && (r ? r(o) : _(o)); } a.setTouched && a.setTouched(!0), $?.(); }, [a, $, u, r]), ie = n.useCallback(() => { G(!0), h?.(), l && s?.suggestions.length && v(!0); }, [h, l, s]), K = n.useCallback( (o) => { B(o), v(!1); }, [B] ), se = n.useMemo(() => !A || w ? null : M ? /* @__PURE__ */ t( re, { size: "sm", color: "success", variant: "flat", startContent: /* @__PURE__ */ t( Ie, { theme: m, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ t( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" } ) } ), children: "Verified" } ) : u && s?.isValid && L ? /* @__PURE__ */ t( X, { size: "sm", variant: "ghost", color: "primary", onPress: L, children: "Verify" } ) : null, [ A, w, M, u, s, L, m ]), N = /* @__PURE__ */ t( ee, { name: e, label: x, placeholder: C, value: u, onChange: (o) => B(o.target.value), onBlur: te, onFocus: ie, type: "email", isRequired: d, required: d, isDisabled: i, disabled: i, size: j, radius: Y, variant: "bordered", autoFocus: W, autoComplete: R, description: V, isInvalid: !!P, errorMessage: "", startContent: O || /* @__PURE__ */ t( Ce, { theme: m, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ t( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207" } ) } ), endContent: w || se } ); return /* @__PURE__ */ S(pe, { theme: m, className: T, children: [ l && s?.suggestions.length ? /* @__PURE__ */ S( F, { isOpen: H, onOpenChange: v, placement: "bottom-start", showArrow: !0, children: [ /* @__PURE__ */ t(F.Trigger, { children: N }), /* @__PURE__ */ t(F.Content, { children: /* @__PURE__ */ t(ge, { theme: m, children: /* @__PURE__ */ t( le, { "aria-label": "Email suggestions", onAction: (o) => K(o), children: s.suggestions.map((o, Q) => /* @__PURE__ */ t( me, { value: o, startContent: /* @__PURE__ */ t( Le, { theme: m, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ t( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 10V3L4 14h7v7l9-11h-7z" } ) } ), children: o }, o )) } ) }) }) ] } ) : N, P && /* @__PURE__ */ t(de, { error: P, fieldName: e }), D.length > 0 && /* @__PURE__ */ S(ve, { theme: m, children: [ /* @__PURE__ */ t(ye, { theme: m, children: "Allowed domains:" }), " ", D.join(", ") ] }), /* @__PURE__ */ t(ce, { children: !H && s?.suggestions.length && E && /* @__PURE__ */ S( $e, { theme: m, initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, children: [ /* @__PURE__ */ t(ke, { theme: m, children: "Did you mean:" }), /* @__PURE__ */ t(xe, { theme: m, children: s.suggestions.slice(0, 3).map((o, Q) => /* @__PURE__ */ t( we, { theme: m, size: "sm", variant: "ghost", color: "primary", onClick: () => K(o), children: o }, Q )) }) ] } ) }) ] }); } var _e = Se; export { Se as EmailField, _e as default }; //# sourceMappingURL=email-field.js.map