@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
628 lines (625 loc) • 17.6 kB
JavaScript
'use client';
import { jsx as t, jsxs as f, Fragment as D } from "react/jsx-runtime";
import n from "@emotion/styled";
import { Button as R } from "../ui/button/button.js";
import { Input as we } from "../ui/input/input.js";
import { Chip as xe } from "../ui/chip/chip.js";
import { Progress as J } from "../ui/progress/progress.js";
import { useConfig as $e } from "../../hooks/use-config.js";
import { useTheme as F } from "../../theme/context.js";
import { EyeSlashIcon as Se, EyeIcon as ke, ExclamationTriangleIcon as Ce, CheckIcon as qe } from "@heroicons/react/24/outline";
import { AnimatePresence as C, motion as K } from "framer-motion";
import h from "react";
import { FieldError as ve } from "./field-error.js";
import { useFormField as Pe } from "./form-wrapper.js";
import { generatePasswordSuggestions as Ie, getPasswordStrength as Le } from "../auth/sign-up/index.js";
const ze = n.div`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[2]};
`, Re = n(R)`
color: ${(e) => e.theme.colors.text.quaternary};
&:hover {
color: ${(e) => e.theme.colors.text.tertiary};
}
`, X = n.svg`
width: ${(e) => e.theme.spacing[4]};
height: ${(e) => e.theme.spacing[4]};
color: ${(e) => e.theme.colors.text.quaternary};
`, Q = n(K.div)`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[2]};
`, Y = n.div`
display: flex;
align-items: center;
justify-content: space-between;
font-size: ${(e) => e.theme.fontSizes.sm};
`, Fe = n.span`
color: ${(e) => e.theme.colors.text.secondary};
`, Me = n.span`
font-weight: ${(e) => e.theme.fontWeights.medium};
color: ${(e) => {
switch (e.strengthColor) {
case "danger":
return e.theme.colors.danger[500];
case "warning":
return e.theme.colors.warning[500];
case "primary":
return e.theme.colors.primary[500];
case "success":
return e.theme.colors.success[500];
default:
return e.theme.colors.text.primary;
}
}};
`, Ae = n.div`
font-size: ${(e) => e.theme.fontSizes.xs};
color: ${(e) => e.theme.colors.text.tertiary};
`, Oe = n(K.div)`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[2]};
`, Te = n.div`
font-size: ${(e) => e.theme.fontSizes.sm};
font-weight: ${(e) => e.theme.fontWeights.medium};
color: ${(e) => e.theme.colors.text.primary};
`, Ee = n.div`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[1]};
`, Ve = n.div`
display: flex;
align-items: center;
gap: ${(e) => e.theme.spacing[2]};
font-size: ${(e) => e.theme.fontSizes.xs};
`, Ne = n.div`
width: ${(e) => e.theme.spacing[4]};
height: ${(e) => e.theme.spacing[4]};
border-radius: ${(e) => e.theme.borderRadius.full};
display: flex;
align-items: center;
justify-content: center;
background-color: ${(e) => e.met ? e.theme.colors.success[100] : e.theme.colors.background.tertiary};
color: ${(e) => e.met ? e.theme.colors.success[600] : e.theme.colors.text.quaternary};
`, Ue = n.svg`
width: ${(e) => e.theme.spacing[3]};
height: ${(e) => e.theme.spacing[3]};
`, We = n.div`
width: 6px;
height: 6px;
border-radius: ${(e) => e.theme.borderRadius.full};
background-color: currentColor;
`, Ge = n.span`
color: ${(e) => e.met ? e.theme.colors.success[600] : e.theme.colors.text.tertiary};
`, _e = n.div`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[2]};
`, je = n.div`
font-size: ${(e) => e.theme.fontSizes.xs};
color: ${(e) => e.theme.colors.text.tertiary};
`, Be = n.div`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[1]};
`, Ze = n.button`
font-size: ${(e) => e.theme.fontSizes.xs};
font-family: monospace;
color: ${(e) => e.theme.colors.primary[600]};
background: none;
border: none;
cursor: pointer;
text-align: left;
padding: 0;
&:hover {
color: ${(e) => e.theme.colors.primary[800]};
text-decoration: underline;
}
`, He = n.div`
font-size: ${(e) => e.theme.fontSizes.xs};
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[1]};
`, De = n.div`
display: flex;
align-items: center;
gap: ${(e) => e.theme.spacing[1]};
color: ${(e) => e.theme.colors.text.tertiary};
`, Xe = n.svg`
width: ${(e) => e.theme.spacing[3]};
height: ${(e) => e.theme.spacing[3]};
`, Je = n.div`
font-size: ${(e) => e.theme.fontSizes.xs};
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[1]};
`, Ke = n.div`
display: flex;
align-items: center;
gap: ${(e) => e.theme.spacing[1]};
color: ${(e) => e.met ? e.theme.colors.success[600] : e.theme.colors.text.quaternary};
`, Qe = n.svg`
width: ${(e) => e.theme.spacing[3]};
height: ${(e) => e.theme.spacing[3]};
opacity: ${(e) => e.met ? 1 : 0.3};
`, L = {
minLength: 8,
maxLength: 128,
requireUppercase: !0,
requireLowercase: !0,
requireNumbers: !0,
requireSpecialChars: !0,
preventCommon: !0,
preventUserInfo: !1
}, Ye = [
"password",
"123456",
"123456789",
"qwerty",
"abc123",
"password123",
"admin",
"letmein",
"welcome",
"monkey",
"1234567890",
"iloveyou"
], z = /[!@#$%^&*()_+=\[\]{}|;':",./<>?~`-]/;
function et(e, s, m) {
const a = [];
let o = 0;
const u = [];
if (s.minLength) {
const i = e.length >= s.minLength;
a.push({
id: "length",
label: `At least ${s.minLength} characters`,
met: i,
required: !0
}), i ? o += 1 : u.push(
`Password must be at least ${s.minLength} characters long`
);
}
if (s.requireUppercase) {
const i = /[A-Z]/.test(e);
a.push({
id: "uppercase",
label: "One uppercase letter",
met: i,
required: !0
}), i ? o += 1 : u.push("Password must contain at least one uppercase letter");
}
if (s.requireLowercase) {
const i = /[a-z]/.test(e);
a.push({
id: "lowercase",
label: "One lowercase letter",
met: i,
required: !0
}), i ? o += 1 : u.push("Password must contain at least one lowercase letter");
}
if (s.requireNumbers) {
const i = /\d/.test(e);
a.push({
id: "number",
label: "One number",
met: i,
required: !0
}), i ? o += 1 : u.push("Password must contain at least one number");
}
if (s.requireSpecialChars) {
const i = z.test(e);
a.push({
id: "special",
label: "One special character",
met: i,
required: !0
}), i ? o += 1 : u.push(
"Password must contain at least one special character (!@#$%^&*()_+-=[]{}|;':\",./<>?~`)"
);
}
if (s.preventCommon) {
const i = Ye.includes(e.toLowerCase());
a.push({
id: "common",
label: "Not a common password",
met: !i,
required: !1
}), i && (o = Math.max(0, o - 2), u.push("This is a commonly used password"));
}
if (e.length >= 12 && (o += 1), e.length >= 16 && (o += 1), /[A-Z].*[A-Z]/.test(e) && (o += 0.5), /\d.*\d/.test(e) && (o += 0.5), z.test(e)) {
const i = e.match(
new RegExp(z.source, "g")
);
i && i.length >= 2 && (o += 0.5);
}
o = Math.min(4, Math.floor(o * 0.8));
let l, d, p;
switch (o) {
case 0:
case 1:
l = "Very Weak", d = "danger", p = 20;
break;
case 2:
l = "Weak", d = "warning", p = 40;
break;
case 3:
l = "Good", d = "primary", p = 70;
break;
case 4:
l = "Strong", d = "success", p = 100;
break;
default:
l = "Very Weak", d = "danger", p = 0;
}
return {
score: o,
label: l,
color: d,
percentage: p,
feedback: u,
requirements: a
};
}
function tt({
name: e = "password",
label: s = "Password",
placeholder: m = "Enter your password",
value: a = "",
onChange: o,
onBlur: u,
onFocus: l,
required: d = !1,
disabled: p = !1,
showStrength: i = !0,
showRequirements: M = !1,
showSuggestions: te = !1,
enableGenerate: re = !1,
allowToggle: A = !0,
rules: w = L,
size: O = "md",
radius: se = "md",
variant: T = "bordered",
className: E = "",
autoFocus: V = !1,
autoComplete: N = "current-password",
isConfirmation: S = !1,
originalPassword: k,
error: x,
description: U,
startContent: ne,
advanceRequirements: W
}) {
const { theme: c } = F(), { components: q, organizationSettings: G } = $e(), g = Pe(e), oe = q.Input ?? we;
q.Button ?? R;
const _ = q.PasswordField;
if (_)
return /* @__PURE__ */ t(
_,
{
name: e,
label: s,
placeholder: m,
value: a,
onChange: o,
onBlur: u,
onFocus: l,
required: d,
disabled: p,
showStrength: i,
showRequirements: M,
allowToggle: A,
rules: w,
size: O,
variant: T,
className: E,
autoFocus: V,
autoComplete: N,
isConfirmation: S,
originalPassword: k,
error: x,
description: U
}
);
const [ie, ce] = h.useState(a), [v, ae] = h.useState(!1), [j, B] = h.useState(!1), [Z, le] = h.useState(
[]
), y = o ? a : ie, H = h.useMemo(() => {
const r = G?.passwordPolicy;
return r ? {
...L,
...w,
minLength: r.minLength || w.minLength,
requireUppercase: r.requireUppercase ?? w.requireUppercase,
requireLowercase: r.requireLowercase ?? w.requireLowercase,
requireNumbers: r.requireNumbers ?? w.requireNumbers,
requireSpecialChars: r.requireSpecialChars ?? w.requireSpecialChars
} : { ...L, ...w };
}, [w, G]), b = h.useMemo(() => y ? et(y, H) : null, [y, H]), P = h.useMemo(() => !S || !k || !y ? null : k !== y ? "Passwords do not match" : null, [S, k, y]), I = h.useMemo(() => {
const r = [];
return x && (Array.isArray(x) ? r.push(...x) : r.push(x)), g.error && (Array.isArray(g.error) ? r.push(...g.error) : r.push(g.error)), P && r.push(P), r.length > 0 ? r : null;
}, [x, g.error, P]), he = h.useCallback(
(r) => {
o ? o(r) : ce(r), g.clearError && g.clearError();
},
[o, g]
), me = h.useCallback(() => {
B(!1), g.setTouched && g.setTouched(!0), u?.();
}, [g, u]), ue = h.useCallback(() => {
B(!0), l?.();
}, [l]), de = h.useCallback(() => {
ae((r) => !r);
}, []), ge = M && (j || y), fe = te && (j || y), pe = i && y && !S, ye = h.useCallback(() => {
const r = Ie();
le(r);
}, []);
return /* @__PURE__ */ f(ze, { theme: c, className: E, children: [
/* @__PURE__ */ t(
oe,
{
name: e,
label: s,
placeholder: m,
value: y,
onBlur: me,
onFocus: ue,
type: v ? "text" : "password",
isRequired: d,
isDisabled: p,
onChange: (r) => he(typeof r == "string" ? r : r.target.value),
required: d,
disabled: p,
size: O,
radius: se,
variant: T,
autoFocus: V,
autoComplete: N,
description: U,
isInvalid: !!I,
errorMessage: "",
startContent: ne,
endContent: A && /* @__PURE__ */ t(
Re,
{
theme: c,
isIconOnly: !0,
variant: "ghost",
size: "sm",
type: "button",
color: "secondary",
onClick: de,
"aria-label": v ? "Hide password" : "Show password",
children: v ? /* @__PURE__ */ t(X, { theme: c, as: Se }) : /* @__PURE__ */ t(X, { theme: c, as: ke })
}
)
}
),
I && /* @__PURE__ */ t(ve, { error: I, fieldName: e }),
/* @__PURE__ */ t(C, { children: pe && b && /* @__PURE__ */ f(
Q,
{
theme: c,
initial: { opacity: 0, height: 0 },
animate: { opacity: 1, height: "auto" },
exit: { opacity: 0, height: 0 },
children: [
/* @__PURE__ */ f(Y, { theme: c, children: [
/* @__PURE__ */ t(Fe, { theme: c, children: "Password strength:" }),
/* @__PURE__ */ t(
Me,
{
theme: c,
strengthColor: b.color,
children: b.label
}
)
] }),
/* @__PURE__ */ t(
J,
{
value: b.percentage,
color: b.color,
size: "xs"
}
),
b.feedback.length > 0 && /* @__PURE__ */ t(Ae, { theme: c, children: b.feedback[0] })
]
}
) }),
/* @__PURE__ */ t(C, { children: ge && b && /* @__PURE__ */ t(
Oe,
{
theme: c,
initial: { opacity: 0, height: 0 },
animate: { opacity: 1, height: "auto" },
exit: { opacity: 0, height: 0 },
children: W ? /* @__PURE__ */ t(
rt,
{
password: a,
requirements: W
}
) : /* @__PURE__ */ f(D, { children: [
/* @__PURE__ */ t(Te, { theme: c, children: "Password Requirements:" }),
/* @__PURE__ */ t(Ee, { theme: c, children: b.requirements.map((r) => /* @__PURE__ */ f(Ve, { theme: c, children: [
/* @__PURE__ */ t(Ne, { theme: c, met: r.met, children: r.met ? /* @__PURE__ */ t(
Ue,
{
theme: c,
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
children: /* @__PURE__ */ t(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M5 13l4 4L19 7"
}
)
}
) : /* @__PURE__ */ t(We, { theme: c }) }),
/* @__PURE__ */ t(Ge, { theme: c, met: r.met, children: r.label })
] }, r.id)) })
] })
}
) }),
/* @__PURE__ */ t(C, { children: fe && b && /* @__PURE__ */ t(_e, { theme: c, children: Z.length > 0 && /* @__PURE__ */ f(D, { children: [
/* @__PURE__ */ t(je, { theme: c, children: "Suggested passwords:" }),
/* @__PURE__ */ t(Be, { theme: c, children: Z.map((r, be) => /* @__PURE__ */ t(
Ze,
{
theme: c,
type: "button",
children: r
},
be
)) })
] }) }) }),
/* @__PURE__ */ t(C, { children: re && /* @__PURE__ */ t(
R,
{
type: "button",
variant: "light",
size: "sm",
onPress: ye,
children: "Generate secure password"
}
) })
] });
}
const ee = h.memo(tt);
function yt({
originalPassword: e,
...s
}) {
return /* @__PURE__ */ t(
ee,
{
...s,
name: s.name || "passwordConfirmation",
label: s.label || "Confirm Password",
placeholder: s.placeholder || "Confirm your password",
autoComplete: "new-password",
isConfirmation: !0,
originalPassword: e,
showStrength: !1,
showRequirements: !1
}
);
}
function rt({
password: e,
requirements: s
}) {
const { theme: m } = F(), a = Le(e), o = (l) => {
switch (l) {
case "weak":
return "danger";
case "fair":
return "warning";
case "good":
return "primary";
case "strong":
return "success";
default:
return "default";
}
}, u = (l) => {
switch (l) {
case "weak":
return "Weak";
case "fair":
return "Fair";
case "good":
return "Good";
case "strong":
return "Strong";
default:
return "";
}
};
return e ? /* @__PURE__ */ f(Q, { theme: m, children: [
/* @__PURE__ */ f(Y, { theme: m, children: [
/* @__PURE__ */ t(
J,
{
value: a.score / 6 * 100,
color: o(a.strength),
size: "xs",
style: { flex: 1 }
}
),
/* @__PURE__ */ t(
xe,
{
size: "sm",
color: o(a.strength),
variant: "flat",
children: u(a.strength)
}
)
] }),
a.feedback.length > 0 && /* @__PURE__ */ t(He, { theme: m, children: a.feedback.map((l, d) => /* @__PURE__ */ f(De, { theme: m, children: [
/* @__PURE__ */ t(Xe, { theme: m, as: Ce }),
/* @__PURE__ */ t("span", { children: l })
] }, d)) }),
s && /* @__PURE__ */ f(Je, { theme: m, children: [
s.minLength && /* @__PURE__ */ t(
$,
{
met: e.length >= s.minLength,
text: `At least ${s.minLength} characters`
}
),
s.requireUppercase && /* @__PURE__ */ t(
$,
{
met: /[A-Z]/.test(e),
text: "One uppercase letter"
}
),
s.requireLowercase && /* @__PURE__ */ t(
$,
{
met: /[a-z]/.test(e),
text: "One lowercase letter"
}
),
s.requireNumbers && /* @__PURE__ */ t(
$,
{
met: /\d/.test(e),
text: "One number"
}
),
s.requireSymbols && /* @__PURE__ */ t(
$,
{
met: /[!@#$%^&*(),.?":{}|<>]/.test(e),
text: "One symbol"
}
)
] })
] }) : null;
}
function $({
met: e,
text: s
}) {
const { theme: m } = F();
return /* @__PURE__ */ f(Ke, { theme: m, met: e, children: [
/* @__PURE__ */ t(Qe, { theme: m, met: e, as: qe }),
/* @__PURE__ */ t("span", { children: s })
] });
}
var bt = ee;
export {
yt as PasswordConfirmationField,
ee as PasswordField,
tt as PasswordFieldComponent,
X as StyledSvg,
bt as default
};
//# sourceMappingURL=password-field.js.map