130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
import React from 'react';
|
|
import { LucideIcon } from 'lucide-react';
|
|
import { Heading } from './Heading';
|
|
import { Button } from './Button';
|
|
import { Box } from './Box';
|
|
import { Stack } from './Stack';
|
|
import { Text } from './Text';
|
|
import { Icon } from './Icon';
|
|
import { ModalIcon } from './ModalIcon';
|
|
|
|
interface PageHeroProps {
|
|
title: string;
|
|
description?: string;
|
|
icon?: LucideIcon;
|
|
backgroundPattern?: React.ReactNode;
|
|
stats?: Array<{
|
|
icon?: LucideIcon;
|
|
value: string | number;
|
|
label: string;
|
|
color?: string;
|
|
animate?: boolean;
|
|
}>;
|
|
actions?: Array<{
|
|
label: string;
|
|
onClick: () => void;
|
|
variant?: 'primary' | 'secondary';
|
|
icon?: LucideIcon;
|
|
description?: string;
|
|
}>;
|
|
children?: React.ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export const PageHero = ({
|
|
title,
|
|
description,
|
|
icon,
|
|
backgroundPattern,
|
|
stats,
|
|
actions,
|
|
children,
|
|
className = ''
|
|
}: PageHeroProps) => (
|
|
<Box
|
|
as="section"
|
|
position="relative"
|
|
overflow="hidden"
|
|
rounded="2xl"
|
|
bg="bg-gradient-to-br from-iron-gray/80 via-deep-graphite to-iron-gray/60"
|
|
border={true}
|
|
borderColor="border-charcoal-outline/50"
|
|
className={className}
|
|
>
|
|
{/* Background Pattern */}
|
|
{backgroundPattern || (
|
|
<>
|
|
<Box position="absolute" top="0" right="0" width="96" height="96" bg="bg-primary-blue/5" rounded="full" blur="3xl" />
|
|
<Box position="absolute" bottom="0" left="0" width="64" height="64" bg="bg-neon-aqua/5" rounded="full" blur="3xl" />
|
|
</>
|
|
)}
|
|
|
|
<Box position="relative" maxWidth="7xl" mx="auto" px={8} py={10}>
|
|
<Box display="flex" flexDirection={{ base: 'col', lg: 'row' }} alignItems={{ lg: 'center' }} justifyContent="between" gap={8}>
|
|
{/* Main Content */}
|
|
<Box maxWidth="2xl">
|
|
{icon && (
|
|
<Stack direction="row" align="center" gap={3} mb={4}>
|
|
<ModalIcon icon={icon} />
|
|
<Heading level={1}>
|
|
{title}
|
|
</Heading>
|
|
</Stack>
|
|
)}
|
|
{!icon && (
|
|
<Heading level={1} mb={4}>
|
|
{title}
|
|
</Heading>
|
|
)}
|
|
{description && (
|
|
<Text size="lg" color="text-gray-400" block style={{ lineHeight: 1.625 }}>
|
|
{description}
|
|
</Text>
|
|
)}
|
|
|
|
{/* Stats */}
|
|
{stats && stats.length > 0 && (
|
|
<Box display="flex" flexWrap="wrap" gap={6} mt={6}>
|
|
{stats.map((stat, index) => (
|
|
<Stack key={index} direction="row" align="center" gap={2}>
|
|
{stat.icon ? (
|
|
<Icon icon={stat.icon} size={4} className={stat.color || 'text-primary-blue'} />
|
|
) : (
|
|
<Box width="2" height="2" rounded="full" className={`${stat.color || 'bg-primary-blue'} ${stat.animate ? 'animate-pulse' : ''}`} />
|
|
)}
|
|
<Text size="sm" color="text-gray-400">
|
|
<Text color="text-white" weight="semibold">{stat.value}</Text> {stat.label}
|
|
</Text>
|
|
</Stack>
|
|
))}
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
|
|
{/* Actions or Custom Content */}
|
|
{actions && actions.length > 0 && (
|
|
<Stack gap={4}>
|
|
{actions.map((action, index) => (
|
|
<Stack key={index} gap={2}>
|
|
<Button
|
|
variant={action.variant || 'primary'}
|
|
onClick={action.onClick}
|
|
icon={action.icon && <Icon icon={action.icon} size={5} />}
|
|
className="px-6 py-3"
|
|
>
|
|
{action.label}
|
|
</Button>
|
|
{action.description && (
|
|
<Text size="xs" color="text-gray-500" align="center" block>{action.description}</Text>
|
|
)}
|
|
</Stack>
|
|
))}
|
|
</Stack>
|
|
)}
|
|
|
|
{children}
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
);
|