"use client"; import { Children, cloneElement, isValidElement, ReactNode } from "react"; /* ------------------------------------------------------------------ */ /* ShimmerSkeleton */ /* ------------------------------------------------------------------ */ interface ShimmerSkeletonProps { className?: string; width?: string | number; height?: string | number; rounded?: "sm" | "md" | "lg" | "xl" | "2xl" | "full"; variant?: "text" | "circle" | "card" | "rect"; } const roundedMap: Record = { sm: "rounded-sm", md: "rounded-md", lg: "rounded-lg", xl: "rounded-xl", "2xl": "rounded-2xl", full: "rounded-full", }; const variantDefaults: Record< string, { width?: string | number; height?: string | number; rounded: string } > = { text: { width: "100%", height: "1em", rounded: "rounded" }, circle: { width: 40, height: 40, rounded: "rounded-full" }, card: { width: "100%", height: 120, rounded: "rounded-xl" }, rect: { width: "100%", height: 40, rounded: "rounded-md" }, }; export function ShimmerSkeleton({ className = "", width, height, rounded, variant = "rect", }: ShimmerSkeletonProps) { const defaults = variantDefaults[variant] ?? variantDefaults.rect; const resolvedWidth = width ?? defaults?.width ?? "100%"; const resolvedHeight = height ?? defaults?.height ?? 40; const resolvedRounded = rounded ? roundedMap[rounded] ?? "rounded-md" : defaults?.rounded ?? "rounded-md"; return (
); } /* ------------------------------------------------------------------ */ /* ShimmerGroup – staggers children entrance by 50ms each */ /* ------------------------------------------------------------------ */ interface ShimmerGroupProps { children: ReactNode; staggerMs?: number; className?: string; } export function ShimmerGroup({ children, staggerMs = 50, className, }: ShimmerGroupProps) { return (
{Children.map(children, (child, index) => { if (!isValidElement(child)) return child; return cloneElement(child as React.ReactElement<{ style?: React.CSSProperties }>, { style: { ...(child.props as { style?: React.CSSProperties }).style, animationDelay: `${index * staggerMs}ms`, }, }); })}
); } /* ------------------------------------------------------------------ */ /* Inline