Download src/components/shared/SharedSkeleton.tsx from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 4.93 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/shared/SharedSkeleton.tsx
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/components/shared/SharedSkeleton.tsx
-
curl -L -o SharedSkeleton.tsx https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/shared/SharedSkeleton.tsx
4.93 kB
| "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<HTMLDivElement> { | |
| 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<SkeletonVariant, { width: string; height: string }> = { | |
| 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 ( | |
| <div className={clsx("space-y-2", className)} {...props}> | |
| {Array.from({ length: lines }).map((_, i) => ( | |
| <SharedSkeleton | |
| key={i} | |
| variant="text" | |
| animated={animated} | |
| width={i === 0 ? "80%" : i === lines - 1 ? "60%" : "100%"} | |
| /> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| if (variant === "list" && lines > 1) { | |
| return ( | |
| <div className={clsx("space-y-2", className)} {...props}> | |
| {Array.from({ length: lines }).map((_, i) => ( | |
| <SharedSkeleton key={i} variant="list" animated={animated} /> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div | |
| className={clsx( | |
| "bg-[var(--bg-elevated)]", | |
| "border", | |
| "border-[var(--border-subtle)]", | |
| variant !== "circular" && variant !== "avatar" && "rounded-md", | |
| variant === "circular" && "rounded-full", | |
| variant === "avatar" && "rounded-full", | |
| animationClasses, | |
| className, | |
| )} | |
| style={style} | |
| {...props} | |
| /> | |
| ); | |
| } | |
| // ============================================================================= | |
| // Skeleton Containers | |
| // ============================================================================= | |
| // Card skeleton (matching SharedCard) | |
| export function SkeletonCard({ | |
| lines = 3, | |
| className, | |
| ...props | |
| }: { lines?: number } & Omit<SkeletonProps, "variant">) { | |
| return ( | |
| <SharedSkeleton | |
| variant="card" | |
| className={clsx("rounded-xl", className)} | |
| {...props} | |
| > | |
| <div className="space-y-3 p-7"> | |
| <SharedSkeleton variant="heading" animated={props.animated} /> | |
| {Array.from({ length: lines }).map((_, i) => ( | |
| <SharedSkeleton | |
| key={i} | |
| variant="text" | |
| animated={props.animated} | |
| width={i === lines - 1 ? "75%" : "100%"} | |
| /> | |
| ))} | |
| </div> | |
| </SharedSkeleton> | |
| ); | |
| } | |
| // Button skeleton | |
| export function SkeletonButton({ className, ...props }: Omit<SkeletonProps, "variant">) { | |
| return ( | |
| <SharedSkeleton | |
| variant="button" | |
| className={clsx("rounded-lg", className)} | |
| {...props} | |
| /> | |
| ); | |
| } | |
| // Input skeleton | |
| export function SkeletonInput({ className, ...props }: Omit<SkeletonProps, "variant">) { | |
| return ( | |
| <SharedSkeleton | |
| variant="text" | |
| height="44px" | |
| className={clsx("rounded-lg w-full", className)} | |
| {...props} | |
| /> | |
| ); | |
| } | |
| // Avatar skeleton | |
| export function SkeletonAvatar({ | |
| size = 40, | |
| className, | |
| ...props | |
| }: { size?: number } & Omit<SkeletonProps, "variant" | "width" | "height">) { | |
| return ( | |
| <SharedSkeleton | |
| variant="circular" | |
| width={size} | |
| height={size} | |
| className={className} | |
| {...props} | |
| /> | |
| ); | |
| } | |