"use client"; import type { CSSProperties, ReactNode } from "react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; const SHAPES = { text: { grid: "grid gap-2.5", bones: ["h-3.5 w-full rounded", "h-3.5 w-11/12 rounded", "h-3.5 w-3/5 rounded"] }, list: { grid: "grid grid-cols-[2.5rem_1fr] items-center gap-x-3 gap-y-2", bones: ["row-span-2 size-10 rounded-full", "h-3.5 w-2/5 self-end rounded", "h-3 w-4/5 self-start rounded"] }, card: { grid: "grid gap-3", bones: ["h-28 w-full rounded-xl", "h-4 w-3/5 rounded", "h-3 w-full rounded", "h-3 w-4/5 rounded"] }, }; const WIPE = "linear-gradient(100deg, #000 calc(var(--sf-wipe) - 30%), transparent var(--sf-wipe))"; type SkeletonFlowProps = { /** True shows the placeholder, false shows the children. */ loading: boolean; /** The real content. */ children: ReactNode; /** Placeholder layout. Pick the one closest to the content. */ shape?: keyof typeof SHAPES; /** How many times the shape repeats. */ count?: number; /** Read by screen readers while loading. */ label?: string; /** Colour of the light that runs through the blocks. */ accent?: string; /** Replaces the block look. */ className?: string; }; /* SkeletonFlow: a light runs from one placeholder block to the next, then the real content wipes in as the placeholder folds away. */ export function SkeletonFlow({ loading, children, shape = "text", count = 1, label = "Loading", accent = "#a78bfa", className = "bg-white/10" }: SkeletonFlowProps) { const reduce = useReducedMotion(); const { grid, bones } = SHAPES[shape]; const move = reduce ? { duration: 0 } : { duration: 0.5, ease: [0.16, 1, 0.3, 1] as const }; return (