ui-auth/dist/AuthSlidePanel.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

201 lines
No EOL
7.5 KiB
JavaScript

import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
* AuthSlidePanel Component
*
* Slide-out panel from edge of screen.
* Ideal for header login buttons and quick auth access.
*/
import { useState, useCallback, useEffect } from 'react';
import styled, { keyframes, css } from '@lilith/ui-styled-components';
import { X } from 'lucide-react';
import { ZINDEX_LAYERS } from '@lilith/ui-zname';
import { getAuthTheme } from './themes';
// GlassCard available for future use if needed
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 Overlay = styled.div `
position: fixed;
inset: 0;
z-index: ${ZINDEX_LAYERS.overlay};
background: rgba(0, 0, 0, 0.7);
${({ $blur }) => $blur && 'backdrop-filter: blur(8px);'}
animation: ${({ $isOpen }) => ($isOpen ? fadeIn : fadeOut)} 0.3s ease forwards;
pointer-events: ${({ $isOpen }) => ($isOpen ? 'auto' : 'none')};
`;
const slideInRight = keyframes `
from { transform: translateX(100%); }
to { transform: translateX(0); }
`;
const slideOutRight = keyframes `
from { transform: translateX(0); }
to { transform: translateX(100%); }
`;
const slideInLeft = keyframes `
from { transform: translateX(-100%); }
to { transform: translateX(0); }
`;
const slideOutLeft = keyframes `
from { transform: translateX(0); }
to { transform: translateX(-100%); }
`;
const PanelContainer = styled.div `
position: fixed;
top: 0;
${({ $direction }) => ($direction === 'right' ? 'right: 0;' : 'left: 0;')}
z-index: ${ZINDEX_LAYERS.modal};
width: ${({ $width }) => $width}px;
max-width: 100vw;
height: 100vh;
background: ${({ $panelTheme }) => $panelTheme.overlayGradient};
box-shadow: ${({ $direction }) => $direction === 'right'
? '-20px 0 60px rgba(0, 0, 0, 0.5)'
: '20px 0 60px rgba(0, 0, 0, 0.5)'};
overflow-y: auto;
${({ $isOpen, $direction }) => {
const isRight = $direction === 'right';
return css `
animation: ${$isOpen
? isRight
? slideInRight
: slideInLeft
: isRight
? slideOutRight
: slideOutLeft}
0.3s ease forwards;
`;
}}
`;
const PanelInner = styled.div `
display: flex;
flex-direction: column;
min-height: 100%;
padding: 24px;
`;
const Header = styled.div `
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 32px;
padding-bottom: 16px;
border-bottom: 1px solid ${({ $panelTheme }) => $panelTheme.glassBorder};
`;
const Title = styled.h2 `
font-size: 1.5rem;
font-weight: 700;
margin: 0;
background: linear-gradient(
135deg,
#ffffff 0%,
${({ $panelTheme }) => $panelTheme.primary} 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
`;
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;
&: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: 24px;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 4px;
`;
const Tab = styled.button `
flex: 1;
padding: 12px 16px;
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.9rem;
font-weight: ${({ $active }) => ($active ? 600 : 500)};
cursor: pointer;
transition: all 0.2s ease;
&:hover {
color: ${({ $panelTheme }) => $panelTheme.primary};
background: ${({ $panelTheme }) => $panelTheme.primary}10;
}
`;
const FormContainer = styled.div `
flex: 1;
`;
export function AuthSlidePanel({ isOpen, mode: initialMode = 'login', direction = 'right', theme = 'default', customTheme, authHandler, ssoUrl, defaultRole, showRoleSelector = true, overlayBlur = true, width = 420, onSuccess, onClose, onError, allowModeSwitch = true, className, }) {
const [mode, setMode] = useState(initialMode);
const [isAnimating, setIsAnimating] = useState(false);
const panelTheme = customTheme || getAuthTheme(theme);
// Sync internal mode with initialMode prop when it changes
useEffect(() => {
setMode(initialMode);
}, [initialMode]);
// 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(_Fragment, { children: [_jsx(Overlay, { "$isOpen": isOpen, "$blur": overlayBlur, onClick: onClose, onAnimationEnd: () => !isOpen && setIsAnimating(false) }), _jsx(PanelContainer, { "$isOpen": isOpen, "$direction": direction, "$width": width, "$panelTheme": panelTheme, className: className, onAnimationStart: () => setIsAnimating(true), onAnimationEnd: () => !isOpen && setIsAnimating(false), children: _jsxs(PanelInner, { children: [_jsxs(Header, { "$panelTheme": panelTheme, children: [_jsx(Title, { "$panelTheme": panelTheme, children: mode === 'login' ? 'Welcome Back' : 'Join Us' }), _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 })) })] }) })] }));
}
//# sourceMappingURL=AuthSlidePanel.js.map