"use client"; import { HTMLAttributes } from "react"; import { clsx } from "clsx"; // ============================================================================= // Skeleton Types // ============================================================================= export type SkeletonVariant = | "text" | "heading" | "paragraph" | "card" | "button" | "avatar" | "image" | "list" | "circular"; export interface SkeletonProps extends HTMLAttributes { variant?: SkeletonVariant; lines?: number; width?: string | number; height?: string | number; animated?: boolean; } // ============================================================================= // SharedSkeleton Component // ============================================================================= export function SharedSkeleton({ variant = "text", lines = 1, width, height, animated = true, className, ...props }: SkeletonProps) { // Default dimensions for each variant const defaults: Record = { text: { width: "100%", height: "1em" }, heading: { width: "60%", height: "1.5em" }, paragraph: { width: "100%", height: "1em" }, card: { width: "100%", height: "120px" }, button: { width: "120px", height: "40px" }, avatar: { width: "40px", height: "40px" }, image: { width: "100%", height: "200px" }, list: { width: "100%", height: "40px" }, circular: { width: "40px", height: "40px" }, }; const d = defaults[variant]; const w = width || d.width; const h = height || d.height; // Build style object const style: React.CSSProperties = { width: typeof w === "number" ? `${w}px` : w, height: typeof h === "number" ? `${h}px` : h, }; // Animation classes const animationClasses = animated ? "animate-skeleton-pulse" : ""; // Variant-specific classes (reserved) const _variantClasses = clsx({ circular: "rounded-full", card: "rounded-lg", button: "rounded-lg", avatar: "rounded-full", image: "rounded-lg", // Default to rounded for text/paragraph/heading/list }); if (variant === "paragraph" && lines > 1) { return (
{Array.from({ length: lines }).map((_, i) => ( ))}
); } if (variant === "list" && lines > 1) { return (
{Array.from({ length: lines }).map((_, i) => ( ))}
); } return (
); } // ============================================================================= // Skeleton Containers // ============================================================================= // Card skeleton (matching SharedCard) export function SkeletonCard({ lines = 3, className, ...props }: { lines?: number } & Omit) { return (
{Array.from({ length: lines }).map((_, i) => ( ))}
); } // Button skeleton export function SkeletonButton({ className, ...props }: Omit) { return ( ); } // Input skeleton export function SkeletonInput({ className, ...props }: Omit) { return ( ); } // Avatar skeleton export function SkeletonAvatar({ size = 40, className, ...props }: { size?: number } & Omit) { return ( ); }