Files
Stirling-PDF/frontend/shared/auth/ui/AuthShell.tsx
T
James BruntonandGitHub 1816bad1ba Unified auth for portal and editor (#6725)
# Description of Changes
Refactor frontend auth to the shared folder and hook it up to both the
portal and editor so they share the same system. Also adds various tasks
to help run the portal, including `task dev:portal` to spawn the portal
with the backend, and `task dev:portal:proxy` to spawn the editor,
portal and backend, and a reverse proxy (at localhost:3000) to allow you
to use both at once to simulate how this will actually be deployed,
allowing you to check whether the seamless transition between the two
actually works.
2026-06-22 16:22:36 +00:00

74 lines
2.4 KiB
TypeScript

import { useEffect, useRef, useState, type ReactNode } from "react";
import styles from "@shared/auth/ui/AuthShell.module.css";
export interface AuthShellProps {
children: ReactNode;
/** Optional panel shown beside the form on wide/tall viewports (the carousel). */
rightPanel?: ReactNode;
/** Optional fixed footer slot (the editor passes its legal/cookie footer). */
footer?: ReactNode;
}
/**
* The login card shell shared by the editor and the portal: a centered card
* that expands to two columns (form + right panel) on wide/tall viewports and
* collapses to a single column otherwise. Purely presentational - callers
* provide the form (children), the right panel, and an optional footer.
*/
export function AuthShell({ children, rightPanel, footer }: AuthShellProps) {
const cardRef = useRef<HTMLDivElement | null>(null);
const [hideRightPanel, setHideRightPanel] = useState(false);
useEffect(() => {
const update = () => {
// Use viewport to avoid hysteresis when the card is already single-column.
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const cardWidthIfTwoCols = Math.min(1180, viewportWidth * 0.96); // min(73.75rem, 96vw)
const columnWidth = cardWidthIfTwoCols / 2;
const tooNarrow = columnWidth < 470;
const tooShort = viewportHeight < 740;
setHideRightPanel(tooNarrow || tooShort);
};
update();
window.addEventListener("resize", update);
window.addEventListener("orientationchange", update);
return () => {
window.removeEventListener("resize", update);
window.removeEventListener("orientationchange", update);
};
}, []);
const showRightPanel = Boolean(rightPanel) && !hideRightPanel;
return (
<div className={styles.authContainer}>
<div
ref={cardRef}
className={`${styles.authCard} ${showRightPanel ? styles.authCardTwoColumns : ""}`}
>
<div className={styles.authLeftPanel}>
<div className={styles.authContent}>{children}</div>
</div>
{showRightPanel && rightPanel}
</div>
{footer && (
<div
style={{
position: "fixed",
bottom: 0,
left: 0,
right: 0,
width: "100%",
zIndex: 10,
}}
>
{footer}
</div>
)}
</div>
);
}
export default AuthShell;