Sessions 5-7a: 955 tests, deployment ready
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
|
||||
type Size = number | string;
|
||||
|
||||
const pulseStyle = (extra: CSSProperties = {}): CSSProperties => ({
|
||||
background: 'var(--bg-2)',
|
||||
animation: 'skeleton-pulse 1.5s ease-in-out infinite',
|
||||
...extra,
|
||||
});
|
||||
|
||||
export function SkeletonBox({
|
||||
width,
|
||||
height,
|
||||
borderRadius = 8,
|
||||
style,
|
||||
}: {
|
||||
width?: Size;
|
||||
height?: Size;
|
||||
borderRadius?: number;
|
||||
style?: CSSProperties;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
aria-hidden
|
||||
style={pulseStyle({
|
||||
width,
|
||||
height,
|
||||
borderRadius,
|
||||
...style,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkeletonLine({ width = '80%', height = 14 }: { width?: Size; height?: Size }) {
|
||||
return <SkeletonBox width={width} height={height} borderRadius={4} style={{ marginBottom: 8 }} />;
|
||||
}
|
||||
|
||||
export function SkeletonGradeCard() {
|
||||
return (
|
||||
<div className="surface" style={{ padding: 16, display: 'grid', gap: 12 }} aria-label="Loading grade card" role="status">
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<SkeletonBox width={60} height={20} borderRadius={999} />
|
||||
<SkeletonBox width={36} height={36} borderRadius={999} />
|
||||
</div>
|
||||
<SkeletonBox width="70%" height={22} borderRadius={6} />
|
||||
<SkeletonBox width="45%" height={14} borderRadius={4} />
|
||||
<div style={{ display: 'flex', justifyContent: 'center', padding: '8px 0' }}>
|
||||
<SkeletonBox width={72} height={72} borderRadius={12} />
|
||||
</div>
|
||||
<SkeletonLine width="90%" />
|
||||
<SkeletonLine width="80%" />
|
||||
<SkeletonLine width="75%" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkeletonGameCard() {
|
||||
return (
|
||||
<div
|
||||
className="surface"
|
||||
style={{ padding: 16, display: 'flex', gap: 12, alignItems: 'stretch' }}
|
||||
aria-label="Loading game card"
|
||||
role="status"
|
||||
>
|
||||
<SkeletonBox width={3} height={56} borderRadius={2} />
|
||||
<div style={{ flex: 1, display: 'grid', gap: 8 }}>
|
||||
<SkeletonBox width="55%" height={18} borderRadius={4} />
|
||||
<SkeletonBox width="35%" height={14} borderRadius={4} />
|
||||
</div>
|
||||
<SkeletonBox width={60} height={20} borderRadius={999} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkeletonRow({ count = 3 }: { count?: number }) {
|
||||
return (
|
||||
<div style={{ display: 'grid', gap: 8 }} aria-label="Loading" role="status">
|
||||
{Array.from({ length: count }, (_, i) => (
|
||||
<SkeletonGameCard key={i} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkeletonGradeRail({ count = 4 }: { count?: number }) {
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: 12, overflowX: 'auto', padding: '4px 4px 12px' }} aria-label="Loading grade rail" role="status">
|
||||
{Array.from({ length: count }, (_, i) => (
|
||||
<div key={i} style={{ minWidth: 220, flex: '0 0 auto' }}>
|
||||
<SkeletonGradeCard />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user