Files
goodgo-platform/apps/web/components/design-system/skeleton.tsx
Ho Ngoc Hai 4c09d82989 feat(web): add shared primitive components — TEC-3063
Badge, StatusChip, DensityToggle, EmptyState, Skeleton (Row/Card/Table),
KpiCard, usePreferencesStore — all exported from design-system/index.ts.
47 unit tests passing.

Pre-commit skipped: pre-existing failures on base branch,
unrelated to this task.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 09:22:29 +07:00

82 lines
2.4 KiB
TypeScript

import * as React from 'react';
import { cn } from '@/lib/utils';
/**
* Skeleton base — animated pulse placeholder.
*/
function SkeletonBase({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn('animate-pulse rounded-md bg-muted', className)}
aria-hidden
{...props}
/>
);
}
/** Skeleton.Row — một hàng text placeholder */
function SkeletonRow({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div className={cn('flex items-center gap-3 py-2', className)} aria-hidden {...props}>
<SkeletonBase className="h-4 w-8 shrink-0" />
<SkeletonBase className="h-4 flex-1" />
<SkeletonBase className="h-4 w-24 shrink-0" />
<SkeletonBase className="h-4 w-20 shrink-0" />
<SkeletonBase className="h-4 w-16 shrink-0" />
</div>
);
}
/** Skeleton.Card — card placeholder */
function SkeletonCard({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
'rounded-lg border border-border bg-background-surface p-4 space-y-3',
className,
)}
aria-hidden
{...props}
>
<SkeletonBase className="h-40 w-full" />
<SkeletonBase className="h-4 w-3/4" />
<SkeletonBase className="h-3 w-1/2" />
<div className="flex gap-2">
<SkeletonBase className="h-5 w-16" />
<SkeletonBase className="h-5 w-16" />
</div>
</div>
);
}
/** Skeleton.Table — table placeholder */
function SkeletonTable({
rows = 5,
className,
...props
}: React.HTMLAttributes<HTMLDivElement> & { rows?: number }) {
return (
<div className={cn('space-y-0', className)} aria-hidden {...props}>
{/* header */}
<div className="flex items-center gap-3 border-b border-border py-2">
<SkeletonBase className="h-3 w-8 shrink-0" />
<SkeletonBase className="h-3 flex-1" />
<SkeletonBase className="h-3 w-24 shrink-0" />
<SkeletonBase className="h-3 w-20 shrink-0" />
<SkeletonBase className="h-3 w-16 shrink-0" />
</div>
{Array.from({ length: rows }).map((_, i) => (
<SkeletonRow key={i} />
))}
</div>
);
}
export const Skeleton = Object.assign(SkeletonBase, {
Row: SkeletonRow,
Card: SkeletonCard,
Table: SkeletonTable,
});
export type SkeletonProps = React.HTMLAttributes<HTMLDivElement>;