Loading State
spectrumui/loading-stateFreeBlock
A pixel-grid loader with a shimmering label and live elapsed timer.
Install it
npx shadcn@latest add https://ui.spectrumhq.in/r/loading-state.jsonSource
1"use client";23import { useEffect, useState } from "react";4import { cn } from "@/lib/utils";56const KEYFRAMES = `7@keyframes su-pixel-on { 0%, 100% { opacity: 0.15 } 40% { opacity: 1 } }8@keyframes su-shimmer-text { 0% { background-position: 200% 0 } 100% { background-position: -200% 0 } }9`;1011const chevron = Array.from({ length: 9 }, (_, i) => {12 const r = Math.floor(i / 3),13 c = i % 3;14 return (c + Math.abs(r - 1)) * 90;15});1617const ORBIT_ORDER = [0, 1, 2, 5, 8, 7, 6, 3];18const orbit = Array.from({ length: 9 }, (_, i) => {19 const k = ORBIT_ORDER.indexOf(i);20 return k === -1 ? null : k * 110;21});2223export type LoadingStateVariant = "Drive" | "Dots" | "Orbit";2425const PATTERNS: Record<26 LoadingStateVariant,27 { delays: (number | null)[]; dur: number; round: boolean }28> = {29 Drive: { delays: chevron, dur: 650, round: false },30 Dots: { delays: chevron, dur: 650, round: true },31 Orbit: { delays: orbit, dur: 950, round: false },32};3334function useElapsed() {35 const [ds, setDs] = useState(0);36 useEffect(() => {37 const t = setInterval(() => setDs((d) => d + 1), 100);38 return () => clearInterval(t);39 }, []);40 const total = ds / 10;41 if (total < 60) return `${total.toFixed(1)}s`;42 return `${Math.floor(total / 60)}m ${(total % 60).toFixed(1)}s`;43}4445export interface LoadingStateProps {46 label?: string;47 variant?: LoadingStateVariant;48 className?: string;49}5051export function LoadingState({52 label = "Churning",53 variant = "Drive",54 className,55}: LoadingStateProps) {56 const elapsed = useElapsed();57 const { delays, dur, round } = PATTERNS[variant] ?? PATTERNS.Drive;5859 return (60 <div61 className={cn(62 "flex w-fit items-center gap-2.5 text-neutral-900 dark:text-neutral-100",63 className,64 )}65 >66 <style dangerouslySetInnerHTML={{ __html: KEYFRAMES }} />6768 <span aria-hidden className="grid grid-cols-[repeat(3,4px)] gap-[1.5px]">69 {delays.map((d, i) => (70 <span71 key={i}72 className={cn(73 "size-[4px] bg-current motion-reduce:!animate-none",74 round ? "rounded-full" : "rounded-[1px]",75 )}76 style={{77 opacity: d === null ? 0.07 : 0.15,78 animation:79 d === null80 ? "none"81 : `su-pixel-on ${dur}ms ease-in-out ${d}ms infinite`,82 }}83 />84 ))}85 </span>8687 <span88// … truncated
Files it writes
More from spectrumui
All 182 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Agent Planagent-plan | spectrumui | Blocks | Free | 1 dep | |
| Agent Stepsagent-steps | spectrumui | Blocks | Free | 1 dep · 2 files | |
| Approval Cardapproval-card | spectrumui | Blocks | Free | 1 dep | |
| Chat Empty Statechat-empty-state | spectrumui | Blocks | Free | 1 dep · 2 files | |
| Citation Sourcescitation-sources | spectrumui | Blocks | Free | no deps · 2 files | |
| Code Blockcode-block | spectrumui | Blocks | Free | 1 dep | |
| Conversation Listconversation-list | spectrumui | Blocks | Free | 1 dep | |
| Diff Viewdiff-view | spectrumui | Blocks | Free | 1 dep |
