import React, { forwardRef } from 'react'; export type SkeletonVariant = 'rect' | 'circle' | 'text'; export interface SkeletonProps extends Omit< React.HTMLAttributes, 'children' > { variant?: SkeletonVariant; width?: number | string; height?: number | string; /** For variant="text": render N stacked line bars. */ lines?: number; /** Screen-reader label announced via visually-hidden span. */ label?: React.ReactNode; } const toSize = (v: number | string | undefined): string | undefined => typeof v === 'number' ? `${v}px` : v; export const Skeleton = forwardRef( ( { variant = 'rect', width, height, lines = 1, label, className = '', style, ...rest }, ref ) => { const classes = [ 'skeleton', variant !== 'rect' && `skeleton--${variant}`, className ] .filter(Boolean) .join(' '); const mergedStyle: React.CSSProperties = { ...(style ?? {}), ...(width !== undefined && { width: toSize(width) }), ...(height !== undefined && { height: toSize(height) }) }; const hasInlineStyle = Object.keys(mergedStyle).length > 0; const isMultilineText = variant === 'text' && typeof lines === 'number' && lines > 0; return (
{isMultilineText && Array.from({ length: lines as number }, (_, i) => (
); } ); Skeleton.displayName = 'Skeleton';