@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
402 lines (389 loc) • 8.97 kB
JavaScript
'use client';
import { jsx as i, jsxs as b } from "react/jsx-runtime";
import { useTheme as E } from "../../../theme/context.js";
import { css as t, keyframes as s } from "@emotion/react";
import n from "@emotion/styled";
import L, { useRef as w, useCallback as y, useEffect as u } from "react";
import { createPortal as q } from "react-dom";
const P = s`
from {
opacity: 0;
}
to {
opacity: 1;
}
`, F = s`
from {
opacity: 1;
}
to {
opacity: 0;
}
`, H = s`
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
`, N = s`
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.95);
}
`, T = (e) => {
const { theme: o, size: r = "md" } = e;
switch (r) {
case "xs":
return t`
max-width: 320px;
width: 90vw;
`;
case "sm":
return t`
max-width: 400px;
width: 90vw;
`;
case "md":
return t`
max-width: 512px;
width: 90vw;
`;
case "lg":
return t`
max-width: 640px;
width: 90vw;
`;
case "xl":
return t`
max-width: 768px;
width: 90vw;
`;
case "2xl":
return t`
max-width: 896px;
width: 90vw;
`;
case "3xl":
return t`
max-width: 1024px;
width: 90vw;
`;
case "4xl":
return t`
max-width: 1152px;
width: 90vw;
`;
case "5xl":
return t`
max-width: 1280px;
width: 90vw;
`;
case "full":
return t`
width: 100vw;
height: 100vh;
max-width: none;
max-height: none;
border-radius: 0;
margin: 0;
`;
default:
return t`
max-width: 512px;
width: 90vw;
`;
}
}, W = (e) => {
const { theme: o, radius: r = "lg", size: d } = e;
if (d === "full") return t``;
switch (r) {
case "none":
return t`border-radius: ${o.borderRadius.none};`;
case "sm":
return t`border-radius: ${o.borderRadius.sm};`;
case "md":
return t`border-radius: ${o.borderRadius.md};`;
case "lg":
return t`border-radius: ${o.borderRadius.lg};`;
case "xl":
return t`border-radius: ${o.borderRadius.xl};`;
default:
return t`border-radius: ${o.borderRadius.lg};`;
}
}, A = (e) => {
const { placement: o = "center", size: r } = e;
if (r === "full")
return t`
justify-content: center;
align-items: center;
`;
switch (o) {
case "top":
return t`
justify-content: flex-start;
align-items: flex-start;
padding-top: 3rem;
`;
case "top-center":
return t`
justify-content: center;
align-items: flex-start;
padding-top: 3rem;
`;
case "center":
return t`
justify-content: center;
align-items: center;
`;
case "bottom":
return t`
justify-content: flex-start;
align-items: flex-end;
padding-bottom: 3rem;
`;
case "bottom-center":
return t`
justify-content: center;
align-items: flex-end;
padding-bottom: 3rem;
`;
default:
return t`
justify-content: center;
align-items: center;
@media (max-height: 640px) {
align-items: flex-start;
padding-top: 2rem;
padding-bottom: 2rem;
}
`;
}
}, G = (e) => {
const { theme: o, backdrop: r = "opaque" } = e;
switch (r) {
case "transparent":
return t`
background-color: transparent;
`;
case "opaque":
return t`
background-color: rgba(0, 0, 0, 0.5);
`;
case "blur":
return t`
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(4px);
`;
default:
return t`
background-color: rgba(0, 0, 0, 0.5);
`;
}
}, J = n.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: ${(e) => e.theme.zIndex.modal};
display: flex;
padding: ${(e) => e.theme.spacing[4]};
animation: ${(e) => e.isClosing ? F : P}
${(e) => e.theme.transitions.normal};
${G}
${A}
/* Custom CSS prop */
${(e) => e.css}
`, K = n.div`
position: relative;
background-color: ${(e) => e.theme.colors.background.primary};
box-shadow: ${(e) => e.theme.shadows.xl};
display: flex;
flex-direction: column;
outline: none;
animation: ${(e) => e.isClosing ? N : H}
${(e) => e.theme.transitions.normal};
${T}
${W}
${(e) => e.scrollBehavior === "inside" && t`
max-height: calc(100vh - 2 * ${e.theme.spacing[4]});
overflow: hidden;
`}
${(e) => e.scrollBehavior === "outside" && t`
max-height: none;
overflow: visible;
`}
`, Q = n.button`
position: absolute;
top: ${(e) => e.theme.spacing[4]};
right: ${(e) => e.theme.spacing[4]};
background: none;
border: none;
cursor: pointer;
padding: ${(e) => e.theme.spacing[2]};
border-radius: ${(e) => e.theme.borderRadius.sm};
color: ${(e) => e.theme.colors.text.secondary};
transition: all ${(e) => e.theme.transitions.fast};
z-index: 10;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: ${(e) => e.theme.colors.neutral[100]};
color: ${(e) => e.theme.colors.text.primary};
}
&:focus {
outline: 2px solid ${(e) => e.theme.colors.border.focus};
outline-offset: 2px;
}
`, U = () => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <explanation>
/* @__PURE__ */ b(
"svg",
{
width: "16",
height: "16",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
children: [
/* @__PURE__ */ i("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
/* @__PURE__ */ i("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
]
}
)
), V = L.forwardRef(
({
children: e,
isOpen: o = !1,
onClose: r,
size: d = "md",
radius: $ = "lg",
placement: v = "auto",
hideCloseButton: k = !1,
isDismissable: h = !0,
isKeyboardDismissDisabled: f = !1,
scrollBehavior: c = "normal",
backdrop: R = "opaque",
className: j,
css: C,
portalContainer: M,
...S
}, X) => {
const { theme: p } = E(), l = w(null), g = w(null), m = y(
(a) => {
!f && a.key === "Escape" && o && r?.();
},
[o, r, f]
), z = y(
(a) => {
h && a.target === g.current && r?.();
},
[h, r]
);
u(() => {
o && l.current && l.current.focus();
}, [o]), u(() => {
if (o && c !== "normal") {
const a = window.getComputedStyle(document.body).overflow;
return document.body.style.overflow = "hidden", () => {
document.body.style.overflow = a;
};
}
}, [o, c]), u(() => {
if (o)
return document.addEventListener("keydown", m), () => document.removeEventListener("keydown", m);
}, [o, m]);
const x = {
...S,
size: d,
radius: $,
placement: v,
backdrop: R,
scrollBehavior: c,
className: j,
theme: p,
css: C
};
if (!o)
return null;
const B = /* @__PURE__ */ i(
J,
{
ref: g,
onClick: z,
...x,
children: /* @__PURE__ */ b(
K,
{
ref: l,
tabIndex: -1,
role: "dialog",
"aria-modal": "true",
...x,
children: [
!k && /* @__PURE__ */ i(
Q,
{
theme: p,
onClick: r,
"aria-label": "Close modal",
children: /* @__PURE__ */ i(U, {})
}
),
e
]
}
)
}
), I = M || document.body;
return q(B, I);
}
);
V.displayName = "Modal";
const te = n.div`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[2]};
padding: ${(e) => e.theme.spacing[6]} ${(e) => e.theme.spacing[6]} 0;
padding-right: ${(e) => e.theme.spacing[12]}; /* Space for close button */
${(e) => e.css}
`, oe = n.div`
display: flex;
flex-direction: column;
gap: ${(e) => e.theme.spacing[4]};
padding: ${(e) => e.theme.spacing[6]};
flex: 1;
${(e) => e.scrollBehavior === "inside" && t`
overflow-y: auto;
max-height: none;
`}
${(e) => e.css}
`, re = n.div`
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: ${(e) => e.theme.spacing[3]};
padding: 0 ${(e) => e.theme.spacing[6]} ${(e) => e.theme.spacing[6]};
${(e) => e.css}
`;
export {
V as Modal,
oe as ModalBody,
re as ModalFooter,
te as ModalHeader
};
//# sourceMappingURL=modal.js.map