website refactor
This commit is contained in:
151
apps/website/components/shared/Modal.tsx
Normal file
151
apps/website/components/shared/Modal.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import { X } from 'lucide-react';
|
||||
import { ReactNode, useEffect } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { IconButton } from '@/ui/IconButton';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
export interface ModalProps {
|
||||
children: ReactNode;
|
||||
isOpen: boolean;
|
||||
onClose?: () => void;
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
title?: string;
|
||||
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
||||
primaryActionLabel?: string;
|
||||
onPrimaryAction?: () => void;
|
||||
secondaryActionLabel?: string;
|
||||
onSecondaryAction?: () => void;
|
||||
footer?: ReactNode;
|
||||
description?: string;
|
||||
icon?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
}
|
||||
|
||||
export const Modal = ({
|
||||
children,
|
||||
isOpen,
|
||||
onClose,
|
||||
onOpenChange,
|
||||
title,
|
||||
size = 'md',
|
||||
primaryActionLabel,
|
||||
onPrimaryAction,
|
||||
secondaryActionLabel,
|
||||
onSecondaryAction,
|
||||
footer,
|
||||
description,
|
||||
icon,
|
||||
actions
|
||||
}: ModalProps) => {
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = 'unset';
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = 'unset';
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
const sizeMap = {
|
||||
sm: '24rem',
|
||||
md: '32rem',
|
||||
lg: '48rem',
|
||||
xl: '64rem',
|
||||
full: '100%',
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
if (onClose) onClose();
|
||||
if (onOpenChange) onOpenChange(false);
|
||||
};
|
||||
|
||||
return createPortal(
|
||||
<Box
|
||||
position="fixed"
|
||||
inset={0}
|
||||
zIndex={100}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
padding={4}
|
||||
bg="rgba(0, 0, 0, 0.8)"
|
||||
>
|
||||
<Box
|
||||
position="absolute"
|
||||
inset={0}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
|
||||
<Surface
|
||||
variant="default"
|
||||
rounded="lg"
|
||||
shadow="xl"
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: sizeMap[size],
|
||||
maxHeight: '90vh',
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
border: '1px solid var(--ui-color-border-default)'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="between"
|
||||
padding={4}
|
||||
borderBottom
|
||||
>
|
||||
<Box display="flex" alignItems="center" gap={3}>
|
||||
{icon}
|
||||
<Box>
|
||||
{title && <Heading level={3}>{title}</Heading>}
|
||||
{description && <Box marginTop={1}><Text size="sm" variant="low">{description}</Text></Box>}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="center" gap={2}>
|
||||
{actions}
|
||||
<IconButton icon={X} onClick={handleClose} variant="ghost" title="Close modal" />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box flex={1} overflow="auto" padding={6}>
|
||||
{children}
|
||||
</Box>
|
||||
|
||||
{(footer || primaryActionLabel || secondaryActionLabel) && (
|
||||
<Box padding={4} borderTop bg="rgba(255,255,255,0.02)" display="flex" justifyContent="end" gap={3}>
|
||||
{footer}
|
||||
{secondaryActionLabel && (
|
||||
<Button
|
||||
onClick={onSecondaryAction || handleClose}
|
||||
variant="ghost"
|
||||
>
|
||||
{secondaryActionLabel}
|
||||
</Button>
|
||||
)}
|
||||
{primaryActionLabel && (
|
||||
<Button
|
||||
onClick={onPrimaryAction}
|
||||
variant="primary"
|
||||
>
|
||||
{primaryActionLabel}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Surface>
|
||||
</Box>,
|
||||
document.body
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user