{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"loading-state","title":"Loading State","description":"Pixel-grid loader with shimmer and elapsed time.","dependencies":[],"registryDependencies":["https://ward.so/r/foundation.json"],"files":[{"path":"registry/ward/LoadingState.tsx","content":"\"use client\";\n/*\n * MIT License\n *\n * Copyright (c) 2026 Shane Levine\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport \"./styles/foundation.css\";\n\nimport { useEffect, useState } from \"react\";\n\n/* ─────────────────────────────────────────────────────────\n * LOADING STATE — pixel-grid loader for long-running work\n *\n * Variants:\n *   Drive  — square cells, chevron wavefront driving right;\n *            the 650ms cycle is shorter than the sweep, so\n *            two fronts are always in flight\n *   Dots   — same wavefront, circular cells\n *   Orbit  — a comet lapping the grid perimeter\n *   Surfer — the Drive loader paired with a meme video below\n *\n * Paired with a shimmering label and a live elapsed timer\n * in mono tabular figures. Reduced motion freezes the grid\n * to its dim state; the timer still ticks.\n * ───────────────────────────────────────────────────────── */\n\nconst chevron = Array.from({ length: 9 }, (_, i) => {\n  const r = Math.floor(i / 3), c = i % 3;\n  return (c + Math.abs(r - 1)) * 90;\n});\n\nconst ORBIT_ORDER = [0, 1, 2, 5, 8, 7, 6, 3];\nconst orbit = Array.from({ length: 9 }, (_, i) => {\n  const k = ORBIT_ORDER.indexOf(i);\n  return k === -1 ? null : k * 110;\n});\n\nconst PATTERNS: Record<string, { delays: (number | null)[]; dur: number; round: boolean }> = {\n  Drive: { delays: chevron, dur: 650, round: false },\n  Dots: { delays: chevron, dur: 650, round: true },\n  Orbit: { delays: orbit, dur: 950, round: false },\n};\n\nfunction LoaderGrid({\n  delays,\n  dur,\n  round,\n}: {\n  delays: (number | null)[];\n  dur: number;\n  round: boolean;\n}) {\n  return (\n    <span aria-hidden className=\"grid shrink-0 grid-cols-[repeat(3,4px)] gap-[1.5px]\">\n      {delays.map((delay, index) => (\n        <span\n          key={index}\n          className={`size-[4px] bg-ink ${round ? \"rounded-full\" : \"rounded-[1px]\"}`}\n          style={{\n            opacity: delay === null ? 0.07 : 0.15,\n            animation: delay === null ? \"none\" : `pixel-on ${dur}ms ease-in-out ${delay}ms infinite`,\n          }}\n        />\n      ))}\n    </span>\n  );\n}\n\nfunction useElapsed() {\n  const [ds, setDs] = useState(0);\n  useEffect(() => {\n    const t = setInterval(() => setDs((d) => d + 1), 100);\n    return () => clearInterval(t);\n  }, []);\n  const total = ds / 10;\n  if (total < 60) return `${total.toFixed(1)}s`;\n  return `${Math.floor(total / 60)}m ${(total % 60).toFixed(1)}s`;\n}\n\nexport default function LoadingState({\n  label,\n  variant = \"Drive\",\n  videoSrc,\n}: {\n  label?: string;\n  variant?: string;\n  videoSrc?: string;\n}) {\n  const elapsed = useElapsed();\n  const surfer = variant === \"Surfer\";\n  const resolvedLabel = label ?? (surfer ? \"Background task\" : \"Working\");\n  const { delays, dur, round } = PATTERNS[variant] ?? PATTERNS.Drive;\n\n  const labelEl = (\n    <span\n      className=\"bg-clip-text text-[13px] font-medium text-transparent\"\n      style={{\n        backgroundImage:\n          \"linear-gradient(90deg, var(--ink-3) 35%, var(--ink) 50%, var(--ink-3) 65%)\",\n        backgroundSize: \"200% 100%\",\n        animation: \"shimmer-text 1.4s linear infinite\",\n      }}\n    >\n      {resolvedLabel}\n    </span>\n  );\n  const elapsedEl = <span className=\"font-mono text-[12px] text-ink-3 tabular-nums\">{elapsed}</span>;\n\n  if (surfer) {\n    return (\n      <div role=\"status\" className=\"flex w-fit flex-col items-start\">\n        <div className=\"flex items-center gap-2.5\">\n          <LoaderGrid {...PATTERNS.Drive} />\n          {labelEl}\n          {elapsedEl}\n        </div>\n\n        {/* the context card follows the status text it is illustrating */}\n        <div\n          className=\"mt-2 w-56 overflow-hidden rounded-[10px] shadow-overlay\"\n          style={{ animation: \"pop-in 200ms cubic-bezier(0.16,1,0.3,1) both\", transformOrigin: \"top left\" }}\n        >\n          <div className=\"relative aspect-video w-full\" style={{ background: \"var(--tooltip-bg)\" }}>\n            {videoSrc ? <video src={videoSrc} controls playsInline preload=\"none\" className=\"h-full w-full object-cover\" /> :\n              <div className=\"flex h-full w-full items-center justify-center font-mono text-[12px]\" style={{ color: \"var(--tooltip-fg)\" }}>Background work in progress</div>}\n\n          </div>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div role=\"status\" className=\"flex w-fit items-center gap-2.5\">\n      <LoaderGrid delays={delays} dur={dur} round={round} />\n      {labelEl}\n      {elapsedEl}\n    </div>\n  );\n}\n","type":"registry:component","target":"components/ward/LoadingState.tsx"}],"meta":{"variants":["Drive","Dots","Orbit","Surfer"],"version":"1.1.0","source":"https://github.com/slev12397/beautiful-ui/blob/44a274e598395ab61e7c96c26fda2758780253b7/components/primitives/LoadingState.tsx","access":"free"},"categories":["ai"],"type":"registry:component"}