website refactor
This commit is contained in:
121
apps/website/ui/LoadingWrapper.tsx
Normal file
121
apps/website/ui/LoadingWrapper.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { LoadingWrapperProps } from '@/ui/state-types';
|
||||
import { Text } from '@/ui/Text';
|
||||
import React from 'react';
|
||||
|
||||
export function LoadingWrapper({
|
||||
variant = 'spinner',
|
||||
message = 'Loading...',
|
||||
size = 'md',
|
||||
skeletonCount = 3,
|
||||
cardConfig,
|
||||
ariaLabel = 'Loading content',
|
||||
}: LoadingWrapperProps) {
|
||||
const sizeMap = {
|
||||
sm: { spinner: '1rem', inline: 'xs' as const, card: '6rem' },
|
||||
md: { spinner: '2.5rem', inline: 'sm' as const, card: '8rem' },
|
||||
lg: { spinner: '4rem', inline: 'base' as const, card: '10rem' },
|
||||
};
|
||||
|
||||
const spinnerSize = sizeMap[size].spinner;
|
||||
const inlineSize = sizeMap[size].inline;
|
||||
const cardHeight = cardConfig?.height || sizeMap[size].card;
|
||||
|
||||
const Spinner = ({ size: s }: { size: string }) => (
|
||||
<Box
|
||||
style={{
|
||||
width: s,
|
||||
height: s,
|
||||
border: '2px solid var(--ui-color-intent-primary)',
|
||||
borderTopColor: 'transparent',
|
||||
borderRadius: '9999px'
|
||||
}}
|
||||
className="animate-spin"
|
||||
/>
|
||||
);
|
||||
|
||||
switch (variant) {
|
||||
case 'spinner':
|
||||
return (
|
||||
<Box display="flex" flexDirection="col" alignItems="center" justifyContent="center" minHeight="12rem" role="status" aria-label={ariaLabel}>
|
||||
<Spinner size={spinnerSize} />
|
||||
<Box marginTop={3}>
|
||||
<Text variant="low" size="sm">{message}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'skeleton':
|
||||
return (
|
||||
<Box display="flex" flexDirection="col" gap={3} role="status" aria-label={ariaLabel}>
|
||||
{Array.from({ length: skeletonCount }).map((_, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
fullWidth
|
||||
bg="var(--ui-color-bg-surface-muted)"
|
||||
rounded="lg"
|
||||
style={{ height: cardHeight, opacity: 0.5 }}
|
||||
className="animate-pulse"
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'full-screen':
|
||||
return (
|
||||
<Box position="fixed" inset={0} zIndex={100} bg="var(--ui-color-bg-base)" display="flex" alignItems="center" justifyContent="center" padding={6} role="status" aria-label={ariaLabel}>
|
||||
<Box textAlign="center">
|
||||
<Box display="flex" justifyContent="center" marginBottom={4}>
|
||||
<Spinner size="4rem" />
|
||||
</Box>
|
||||
<Text variant="high" size="lg" weight="medium" block>{message}</Text>
|
||||
<Text variant="low" size="sm" block marginTop={1}>This may take a moment...</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'inline':
|
||||
return (
|
||||
<Box display="inline-flex" alignItems="center" gap={2} role="status" aria-label={ariaLabel}>
|
||||
<Spinner size="1rem" />
|
||||
<Text variant="low" size={inlineSize}>{message}</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'card':
|
||||
const cardCount = cardConfig?.count || 3;
|
||||
return (
|
||||
<Box display="grid" gap={4} role="status" aria-label={ariaLabel}>
|
||||
{Array.from({ length: cardCount }).map((_, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
bg="var(--ui-color-bg-surface-muted)"
|
||||
rounded="xl"
|
||||
style={{ height: cardHeight, border: '1px solid var(--ui-color-border-default)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||
>
|
||||
<Spinner size="2rem" />
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function FullScreenLoading({ message = 'Loading...' }: Pick<LoadingWrapperProps, 'message'>) {
|
||||
return <LoadingWrapper variant="full-screen" message={message} />;
|
||||
}
|
||||
|
||||
export function InlineLoading({ message = 'Loading...', size = 'sm' }: Pick<LoadingWrapperProps, 'message' | 'size'>) {
|
||||
return <LoadingWrapper variant="inline" message={message} size={size} />;
|
||||
}
|
||||
|
||||
export function SkeletonLoading({ skeletonCount = 3 }: Pick<LoadingWrapperProps, 'skeletonCount'>) {
|
||||
return <LoadingWrapper variant="skeleton" skeletonCount={skeletonCount} />;
|
||||
}
|
||||
|
||||
export function CardLoading({ cardConfig }: Pick<LoadingWrapperProps, 'cardConfig'>) {
|
||||
return <LoadingWrapper variant="card" cardConfig={cardConfig} />;
|
||||
}
|
||||
Reference in New Issue
Block a user