73 lines
1.6 KiB
JavaScript
73 lines
1.6 KiB
JavaScript
import React, { useEffect } from "react";
|
|
|
|
const STYLE_ID = "skeleton-shimmer-styles";
|
|
|
|
const injectSkeletonStyles = () => {
|
|
if (typeof document === "undefined") return;
|
|
if (document.getElementById(STYLE_ID)) return;
|
|
const style = document.createElement("style");
|
|
style.id = STYLE_ID;
|
|
style.textContent = `
|
|
.skeleton-shimmer {
|
|
position: relative;
|
|
overflow: hidden;
|
|
background: #e5e7eb;
|
|
}
|
|
.skeleton-shimmer::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
transform: translateX(-100%);
|
|
background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.7) 50%, rgba(255,255,255,0) 100%);
|
|
animation: skeleton-shimmer 1.6s infinite;
|
|
}
|
|
@keyframes skeleton-shimmer {
|
|
100% { transform: translateX(100%); }
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
};
|
|
|
|
export const Skeleton = ({
|
|
width = "100%",
|
|
height = 12,
|
|
rounded = 6,
|
|
className = "",
|
|
}) => {
|
|
useEffect(() => {
|
|
injectSkeletonStyles();
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
className={`skeleton-shimmer ${className}`}
|
|
style={{ width, height, borderRadius: rounded }}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export const SkeletonText = ({
|
|
lines = 3,
|
|
gap = 10,
|
|
height = 12,
|
|
className = "",
|
|
}) => {
|
|
useEffect(() => {
|
|
injectSkeletonStyles();
|
|
}, []);
|
|
|
|
return (
|
|
<div className={className} style={{ display: "grid", gap }}>
|
|
{Array.from({ length: lines }).map((_, idx) => (
|
|
<Skeleton
|
|
key={idx}
|
|
height={height}
|
|
width={idx === lines - 1 ? "70%" : "100%"}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Skeleton;
|