Files
Stirling-PDF/frontend/shared/components/Skeleton.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

68 lines
1.8 KiB
TypeScript
Raw Normal View History

2026-06-02 17:08:24 +01:00
import "@shared/components/Skeleton.css";
export interface SkeletonProps {
/** Width as a CSS length (`100%`, `12rem`, …). Defaults to 100%. */
width?: string | number;
/** Height as a CSS length. Defaults to 0.75rem. */
height?: string | number;
/** Shape preset. `text` = pill-rounded line, `rect` = card / image area. */
shape?: "text" | "rect" | "circle";
/** For text shape, the number of stacked lines to render. */
lines?: number;
className?: string;
}
/**
* Shimmering placeholder for content that hasn't loaded yet. Replaces the
* ad-hoc `linear-gradient(...) shimmer 1.4s` blocks that were duplicated
* across the deployed-pipelines table, the activity feed, and the doc-type
* grid.
*/
export function Skeleton({
width = "100%",
height,
shape = "text",
lines = 1,
className,
}: SkeletonProps) {
const dim = (v: string | number | undefined) =>
typeof v === "number" ? `${v}px` : v;
if (shape === "text" && lines > 1) {
return (
<span
className={["sui-skel-lines", className ?? ""]
.filter(Boolean)
.join(" ")}
>
{Array.from({ length: lines }).map((_, i) => (
<span
key={i}
className="sui-skel sui-skel--text"
style={{
// Last line is slightly shorter — feels more natural.
width: i === lines - 1 ? "70%" : (dim(width) ?? "100%"),
height: dim(height) ?? "0.75rem",
}}
/>
))}
</span>
);
}
return (
<span
className={["sui-skel", `sui-skel--${shape}`, className ?? ""]
.filter(Boolean)
.join(" ")}
style={{
width: dim(width),
height:
dim(height) ??
(shape === "text" ? "0.75rem" : shape === "circle" ? "2rem" : "6rem"),
}}
aria-hidden
/>
);
}