@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
401 lines (400 loc) • 9.65 kB
JavaScript
'use client';
import { jsx as n, jsxs as P } from "react/jsx-runtime";
import "@emotion/styled";
import { Button as te } from "../ui/button/button.js";
import { Input as oe } from "../ui/input/input.js";
import { Chip as re } from "../ui/chip/chip.js";
import { Select as ae } from "../ui/select/select.js";
import i from "react";
import { useConfig as le } from "../../hooks/use-config.js";
import { FieldError as ne } from "./field-error.js";
import { useFormField as ie } from "./form-wrapper.js";
const x = [
{
code: "US",
name: "United States",
dialCode: "+1",
flag: "🇺🇸",
format: "(###) ###-####"
},
{
code: "CA",
name: "Canada",
dialCode: "+1",
flag: "🇨🇦",
format: "(###) ###-####"
},
{
code: "GB",
name: "United Kingdom",
dialCode: "+44",
flag: "🇬🇧",
format: "#### ### ####"
},
{
code: "AU",
name: "Australia",
dialCode: "+61",
flag: "🇦🇺",
format: "#### ### ###"
},
{
code: "DE",
name: "Germany",
dialCode: "+49",
flag: "🇩🇪",
format: "### ### ####"
},
{
code: "FR",
name: "France",
dialCode: "+33",
flag: "🇫🇷",
format: "## ## ## ## ##"
},
{
code: "ES",
name: "Spain",
dialCode: "+34",
flag: "🇪🇸",
format: "### ### ###"
},
{
code: "IT",
name: "Italy",
dialCode: "+39",
flag: "🇮🇹",
format: "### ### ####"
},
{
code: "JP",
name: "Japan",
dialCode: "+81",
flag: "🇯🇵",
format: "###-####-####"
},
{
code: "KR",
name: "South Korea",
dialCode: "+82",
flag: "🇰🇷",
format: "###-####-####"
},
{
code: "CN",
name: "China",
dialCode: "+86",
flag: "🇨🇳",
format: "### #### ####"
},
{
code: "IN",
name: "India",
dialCode: "+91",
flag: "🇮🇳",
format: "##### #####"
},
{
code: "BR",
name: "Brazil",
dialCode: "+55",
flag: "🇧🇷",
format: "(##) #####-####"
},
{
code: "MX",
name: "Mexico",
dialCode: "+52",
flag: "🇲🇽",
format: "### ### ####"
},
{
code: "AR",
name: "Argentina",
dialCode: "+54",
flag: "🇦🇷",
format: "### ###-####"
},
{
code: "RU",
name: "Russia",
dialCode: "+7",
flag: "🇷🇺",
format: "### ###-##-##"
},
{
code: "ZA",
name: "South Africa",
dialCode: "+27",
flag: "🇿🇦",
format: "## ### ####"
},
{
code: "EG",
name: "Egypt",
dialCode: "+20",
flag: "🇪🇬",
format: "### ### ####"
},
{
code: "NG",
name: "Nigeria",
dialCode: "+234",
flag: "🇳🇬",
format: "### ### ####"
},
{
code: "SG",
name: "Singapore",
dialCode: "+65",
flag: "🇸🇬",
format: "#### ####"
}
];
function O(s, r) {
const a = [], o = s.replace(/[^\d+]/g, "");
let t = o, h = o;
o.startsWith("+") ? (h = o, o.startsWith(r.dialCode) ? t = o.substring(r.dialCode.length) : a.push("Phone number does not match selected country")) : (h = r.dialCode + o, t = o), t.length < 7 ? a.push("Phone number is too short") : t.length > 15 && a.push("Phone number is too long");
let g = t;
return r.format && t.length >= 7 && (g = se(t, r.format)), {
isValid: a.length === 0,
e164: h,
national: t,
formatted: `${r.dialCode} ${g}`,
errors: a
};
}
function se(s, r) {
let a = r, o = 0;
for (let t = 0; t < r.length && o < s.length; t++)
r[t] === "#" && (a = a.substring(0, t) + s[o] + a.substring(t + 1), o++);
return a.substring(0, a.lastIndexOf("#") + 1);
}
function X(s) {
const r = s.replace(/[^\d+]/g, "");
if (!r.startsWith("+")) return null;
const a = [...x].sort(
(o, t) => t.dialCode.length - o.dialCode.length
);
for (const o of a)
if (r.startsWith(o.dialCode))
return o;
return null;
}
function be({
name: s = "phone",
label: r = "Phone Number",
placeholder: a = "Enter your phone number",
value: o = "",
onChange: t,
onBlur: h,
onFocus: g,
defaultCountry: E = "US",
preferredCountries: S = ["US", "CA", "GB"],
allowedCountries: k = [],
blockedCountries: y = [],
required: D = !1,
disabled: I = !1,
validateFormat: B = !0,
size: F = "md",
variant: R = "bordered",
className: K = "",
autoFocus: L = !1,
autoComplete: T = "tel",
error: p,
description: $,
showVerificationStatus: U = !1,
isVerified: G = !1,
onRequestVerification: A,
endContent: v
}) {
const { components: Z, organizationSettings: M } = le(), d = ie(s), z = Z.PhoneField;
if (z)
return /* @__PURE__ */ n(
z,
{
name: s,
label: r,
placeholder: a,
value: o,
onChange: t,
onBlur: h,
onFocus: g,
defaultCountry: E,
preferredCountries: S,
allowedCountries: k,
blockedCountries: y,
required: D,
disabled: I,
validateFormat: B,
size: F,
variant: R,
className: K,
autoFocus: L,
autoComplete: T,
error: p,
description: $,
showVerificationStatus: U,
isVerified: G,
onRequestVerification: A,
endContent: v
}
);
const [w, H] = i.useState(o), [C, J] = i.useState(() => {
if (o) {
const e = X(o);
if (e) return e;
}
return x.find((e) => e.code === E) || x[0];
}), f = t ? o : w, N = i.useMemo(() => {
const e = M?.phoneRestrictions?.allowedCountries;
return e && e.length > 0 ? e : k;
}, [k, M]), W = i.useMemo(() => {
const e = M?.phoneRestrictions?.blockedCountries;
return e && e.length > 0 ? [...y, ...e] : y;
}, [y, M]), b = i.useMemo(() => {
let e = x;
N.length > 0 && (e = e.filter(
(u) => N.includes(u.code)
)), W.length > 0 && (e = e.filter(
(u) => !W.includes(u.code)
));
const m = e.filter(
(u) => S.includes(u.code)
), c = e.filter((u) => !S.includes(u.code));
return [...m, ...c];
}, [
N,
W,
S
]), l = i.useMemo(() => !f || !B ? null : O(f, C), [f, C, B]), j = i.useMemo(() => {
const e = [];
return p && (Array.isArray(p) ? e.push(...p) : e.push(p)), d.error && (Array.isArray(d.error) ? e.push(...d.error) : e.push(d.error)), l && !l.isValid && e.push(...l.errors), e.length > 0 ? e : null;
}, [p, d.error, l]), Q = i.useCallback(
(e) => {
const m = X(e);
if (m && b.includes(m) && J(m), t && l)
t(l.e164, l.formatted);
else if (t) {
const c = e.replace(/[^\d+]/g, ""), u = c.startsWith("+") ? c : C.dialCode + c.replace(/^\d/, "");
t(u, e);
} else
H(e);
d.clearError && d.clearError();
},
[t, l, d, C, b]
), V = i.useCallback(
(e) => {
const m = b.find((c) => c.code === e);
if (m && (J(m), f)) {
const c = O(f, m);
t && t(c.e164, c.formatted);
}
},
[b, f, t]
), Y = i.useCallback(() => {
d.setTouched && d.setTouched(!0), h?.();
}, [d, h]), _ = i.useCallback(() => {
g?.();
}, [g]), q = i.useMemo(() => !U || v ? null : G ? /* @__PURE__ */ n(
re,
{
size: "sm",
color: "success",
variant: "flat",
startContent: /* @__PURE__ */ n(
"svg",
{
className: "w-3 h-3",
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
children: /* @__PURE__ */ n(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M5 13l4 4L19 7"
}
)
}
),
children: "Verified"
}
) : f && l?.isValid && A ? /* @__PURE__ */ n(
te,
{
size: "sm",
variant: "flat",
color: "primary",
onPress: A,
children: "Send SMS"
}
) : null, [
U,
v,
G,
f,
l,
A
]), ee = /* @__PURE__ */ n(
ae,
{
value: C.code,
onChange: (e) => V(e.target.value),
className: "min-w-32",
size: F,
variant: R,
isDisabled: I,
placeholder: "Country",
options: b.map((e) => ({
label: e.name,
value: e.code
}))
}
);
return /* @__PURE__ */ P("div", { className: `space-y-2 ${K}`, children: [
/* @__PURE__ */ P("div", { className: "flex gap-2", children: [
/* @__PURE__ */ n("div", { className: "shrink-0", children: ee }),
/* @__PURE__ */ n("div", { className: "flex-1", children: /* @__PURE__ */ n(
oe,
{
name: s,
label: r,
placeholder: a,
value: f,
onValueChange: Q,
onBlur: Y,
onFocus: _,
type: "tel",
isRequired: D,
isDisabled: I,
size: F,
variant: R,
autoFocus: L,
autoComplete: T,
description: $,
isInvalid: !!j,
errorMessage: "",
endContent: v || q
}
) })
] }),
j && /* @__PURE__ */ n(ne, { error: j, fieldName: s }),
l && l.isValid && /* @__PURE__ */ P("div", { className: "text-xs text-default-500", children: [
/* @__PURE__ */ n("span", { className: "font-medium", children: "Format:" }),
" ",
l.formatted
] }),
N.length > 0 && /* @__PURE__ */ P("div", { className: "text-xs text-default-500", children: [
/* @__PURE__ */ n("span", { className: "font-medium", children: "Allowed countries:" }),
" ",
N.join(", ")
] })
] });
}
export {
be as PhoneField
};
//# sourceMappingURL=phone-field.js.map