ui-auth/dist/AuthFullscreenPanel.js
autocommit 55daa6ed6a chore: initial package split from monorepo
Package: @lilith/ui-auth
Split from: lilith/ui.git or lilith/build.git
Publish workflow: calls lilith/workflows/.forgejo/workflows/publish-npm.yml@main
2026-04-20 01:10:50 -07:00

259 lines
No EOL
8.6 KiB
JavaScript

import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* AuthFullscreenPanel Component
*
* Full-screen takeover with centered glass card.
* Ideal for dedicated auth pages and immersive sign-up flows.
*/
import { useState, useCallback, useEffect } from 'react';
import styled, { keyframes } from '@lilith/ui-styled-components';
import { X } from 'lucide-react';
import { ZINDEX_LAYERS, ZINDEX_LOCAL } from '@lilith/ui-zname';
import { getAuthTheme } from './themes';
import { GlassCard } from './backgrounds/GlassCard';
import { LoginForm } from './LoginForm';
import { RegisterForm } from './RegisterForm';
const fadeIn = keyframes `
from {
opacity: 0;
}
to {
opacity: 1;
}
`;
const fadeOut = keyframes `
from {
opacity: 1;
}
to {
opacity: 0;
}
`;
const scaleIn = keyframes `
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
`;
const scaleOut = keyframes `
from {
opacity: 1;
transform: scale(1) translateY(0);
}
to {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
`;
const Overlay = styled.div `
position: fixed;
inset: 0;
z-index: ${ZINDEX_LAYERS.overlay};
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
overflow-y: auto;
${({ $hasImage, $imageUrl }) => $hasImage && $imageUrl
? `background: url(${$imageUrl}) center/cover no-repeat fixed;`
: 'background: #0a0a0f;'}
&::before {
content: '';
position: absolute;
inset: 0;
background: ${({ $panelTheme }) => $panelTheme.overlayGradient};
${({ $blur }) => $blur && 'backdrop-filter: blur(8px);'}
}
animation: ${({ $isOpen }) => ($isOpen ? fadeIn : fadeOut)} 0.3s ease forwards;
pointer-events: ${({ $isOpen }) => ($isOpen ? 'auto' : 'none')};
`;
const CardContainer = styled.div `
position: relative;
z-index: ${ZINDEX_LOCAL.content};
width: 100%;
max-width: 480px;
animation: ${({ $isOpen }) => ($isOpen ? scaleIn : scaleOut)} 0.4s ease forwards;
`;
const StyledGlassCard = styled(GlassCard) `
padding: 40px;
@media (max-width: 600px) {
padding: 24px;
}
`;
const Header = styled.div `
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 32px;
`;
const TitleGroup = styled.div `
display: flex;
flex-direction: column;
gap: 8px;
`;
const Title = styled.h1 `
font-size: 2rem;
font-weight: 700;
margin: 0;
background: linear-gradient(
135deg,
#ffffff 0%,
${({ $panelTheme }) => $panelTheme.primary} 50%,
${({ $panelTheme }) => $panelTheme.gradientTo} 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: ${({ $panelTheme }) => $panelTheme.textGlow};
@media (max-width: 600px) {
font-size: 1.5rem;
}
`;
const Subtitle = styled.p `
font-size: 0.95rem;
color: rgba(255, 255, 255, 0.6);
margin: 0;
`;
const CloseButton = styled.button `
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.1);
border: 1px solid ${({ $panelTheme }) => $panelTheme.glassBorder};
border-radius: 50%;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.2s ease;
flex-shrink: 0;
margin-left: 16px;
&:hover {
background: ${({ $panelTheme }) => $panelTheme.primary}20;
border-color: ${({ $panelTheme }) => $panelTheme.primary};
color: ${({ $panelTheme }) => $panelTheme.primary};
transform: rotate(90deg);
}
svg {
width: 20px;
height: 20px;
}
`;
const TabContainer = styled.div `
display: flex;
margin-bottom: 32px;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 4px;
`;
const Tab = styled.button `
flex: 1;
padding: 14px 20px;
background: ${({ $active, $panelTheme }) => $active ? $panelTheme.primary + '20' : 'transparent'};
border: none;
border-radius: 8px;
color: ${({ $active, $panelTheme }) => $active ? $panelTheme.primary : 'rgba(255, 255, 255, 0.6)'};
font-size: 0.95rem;
font-weight: ${({ $active }) => ($active ? 600 : 500)};
cursor: pointer;
transition: all 0.2s ease;
position: relative;
${({ $active, $panelTheme }) => $active &&
`
&::after {
content: '';
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 2px;
background: ${$panelTheme.primary};
border-radius: 1px;
}
`}
&:hover {
color: ${({ $panelTheme }) => $panelTheme.primary};
background: ${({ $panelTheme }) => $panelTheme.primary}10;
}
`;
const FormContainer = styled.div `
/* Form inherits styles from LoginForm/RegisterForm */
`;
const Branding = styled.div `
position: absolute;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 8px;
color: rgba(255, 255, 255, 0.4);
font-size: 0.75rem;
&::before {
content: '';
width: 6px;
height: 6px;
border-radius: 50%;
background: ${({ $panelTheme }) => $panelTheme.primary};
box-shadow: 0 0 8px ${({ $panelTheme }) => $panelTheme.glow};
}
`;
export function AuthFullscreenPanel({ isOpen, mode: initialMode = 'login', theme = 'default', customTheme, backgroundImage, authHandler, ssoUrl, defaultRole, showRoleSelector = true, glassEffect = true, backgroundBlur = true, onSuccess, onClose, onError, allowModeSwitch = true, showCloseButton = true, className, }) {
const [mode, setMode] = useState(initialMode);
const [isAnimating, setIsAnimating] = useState(false);
const panelTheme = customTheme || getAuthTheme(theme);
// Handle escape key
useEffect(() => {
const handleEscape = (e) => {
if (e.key === 'Escape' && isOpen) {
onClose();
}
};
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, [isOpen, onClose]);
// Lock body scroll when open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
}
else {
document.body.style.overflow = '';
}
return () => {
document.body.style.overflow = '';
};
}, [isOpen]);
const handleModeSwitch = useCallback((newMode) => {
setMode(newMode);
}, []);
const handleSuccess = useCallback((user) => {
onSuccess?.(user);
onClose();
}, [onSuccess, onClose]);
const handleError = useCallback((error) => {
onError?.(error);
}, [onError]);
if (!isOpen && !isAnimating) {
return null;
}
return (_jsxs(Overlay, { "$isOpen": isOpen, "$hasImage": !!backgroundImage, "$imageUrl": backgroundImage, "$blur": backgroundBlur, "$panelTheme": panelTheme, className: className, onAnimationEnd: () => !isOpen && setIsAnimating(false), onAnimationStart: () => setIsAnimating(true), children: [_jsx(CardContainer, { "$isOpen": isOpen, "$panelTheme": panelTheme, children: _jsxs(StyledGlassCard, { panelTheme: panelTheme, glowBorder: glassEffect, blur: glassEffect ? 20 : 0, "$panelTheme": panelTheme, children: [_jsxs(Header, { children: [_jsxs(TitleGroup, { children: [_jsx(Title, { "$panelTheme": panelTheme, children: mode === 'login' ? 'Welcome Back' : 'Create Account' }), _jsx(Subtitle, { children: mode === 'login'
? 'Sign in to continue your journey'
: 'Join our community today' })] }), showCloseButton && (_jsx(CloseButton, { onClick: onClose, "$panelTheme": panelTheme, "aria-label": "Close", children: _jsx(X, {}) }))] }), allowModeSwitch && (_jsxs(TabContainer, { "$panelTheme": panelTheme, children: [_jsx(Tab, { "$active": mode === 'login', "$panelTheme": panelTheme, onClick: () => handleModeSwitch('login'), children: "Sign In" }), _jsx(Tab, { "$active": mode === 'register', "$panelTheme": panelTheme, onClick: () => handleModeSwitch('register'), children: "Create Account" })] })), _jsx(FormContainer, { children: mode === 'login' ? (_jsx(LoginForm, { authHandler: authHandler, ssoUrl: ssoUrl, onSuccess: handleSuccess, onError: handleError, onSwitchToRegister: allowModeSwitch ? () => handleModeSwitch('register') : undefined })) : (_jsx(RegisterForm, { authHandler: authHandler, ssoUrl: ssoUrl, defaultRole: defaultRole, hideRoleSelector: !showRoleSelector, onSuccess: handleSuccess, onError: handleError, onSwitchToLogin: allowModeSwitch ? () => handleModeSwitch('login') : undefined })) })] }) }), _jsx(Branding, { "$panelTheme": panelTheme, children: "Powered by Lilith" })] }));
}
//# sourceMappingURL=AuthFullscreenPanel.js.map