import * as React from 'react';
import { cn } from '@/lib/utils';
/**
* Skeleton — a loading placeholder.
*
* Uses a travelling shimmer rather than a pulse: on the app's translucent glass
* surfaces a pulsing block reads as a rendering glitch, while a shimmer reads as
* progress. The shimmer is disabled automatically under
* `prefers-reduced-motion` (see index.css).
*/
export function Skeleton({ className, radius = 'md', ...props }) {
return (
);
}
/**
* The composed patterns below exist so pages never hand-assemble skeletons.
* Each mirrors the real component's geometry, which is what makes the swap from
* loading to loaded invisible.
*/
/** Paragraph placeholder. The last line is short, as real text is. */
export function SkeletonText({ lines = 3, className }) {
return (
{Array.from({ length: lines }).map((_, i) => (
1 ? 'w-2/3' : 'w-full')}
/>
))}
);
}
/** Matches KpiCard: icon tile, big number, caption. */
export function SkeletonKpi({ className }) {
return (
);
}
/** Matches a list row: avatar, two lines of text, trailing value. */
export function SkeletonRow({ className }) {
return (
);
}
/** Matches a card in a grid: title, meta, stat row. */
export function SkeletonCard({ className }) {
return (
{[0, 1, 2].map((i) => (
))}
);
}
/** Matches DataTable: header row plus n body rows. */
export function SkeletonTable({ rows = 5, columns = 4, className }) {
return (
{Array.from({ length: columns }).map((_, i) => (
))}
{Array.from({ length: rows }).map((_, r) => (
{Array.from({ length: columns }).map((_, c) => (
))}
))}
);
}
/** Matches ChartContainer: title, legend, plot area. */
export function SkeletonChart({ className, height = 240 }) {
return (
{[0.5, 0.8, 0.35, 0.95, 0.6, 0.75].map((h, i) => (
))}
);
}
/** n SkeletonRows inside a surface — the common "loading a list" case. */
export function SkeletonList({ rows = 5, className }) {
return (
{Array.from({ length: rows }).map((_, i) => (
))}
);
}