{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"thunder-loader","type":"registry:ui","title":"Thunder Loader","description":"A lightning bolt that charges up in stuttering surges while arcs crackle around it, then flashes white, throws sparks and discharges. Pass progress for a determinate meter.","dependencies":[],"registryDependencies":[],"files":[{"path":"registry/ui/thunder-loader.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { useId, type CSSProperties } from \"react\";\n\nexport interface ThunderLoaderProps {\n  /** Px. */\n  size?: number;\n  color?: string;\n  /** Speed multiplier of the charge / discharge cycle. */\n  speed?: number;\n  /** 0-100 for a determinate charge meter. Omit to loop forever. */\n  progress?: number;\n  /** Announced to screen readers. */\n  label?: string;\n  className?: string;\n}\n\nconst BOLT = \"M37 3 13 37h16l-4 24 26-36H34z\";\n// Hand-placed arcs that crackle around the bolt.\nconst ARCS = [\"M8 14l5 3-3 3 6 2\", \"M52 40l5-2-2 5 5 1\", \"M47 8l3 5-4 1 3 5\", \"M11 50l5-4 1 5 4-4\"];\nconst SPARKS = [0, 60, 120, 180, 240, 300];\n\nconst css = `\n.thl{--c:1.9s}\n.thl-level{transform-box:fill-box;transform:translateY(100%);animation:thl-charge calc(var(--c)*var(--d)) linear infinite}\n@keyframes thl-charge{0%{transform:translateY(100%)}14%{transform:translateY(78%)}17%{transform:translateY(84%)}33%{transform:translateY(52%)}36%{transform:translateY(58%)}52%{transform:translateY(22%)}55%{transform:translateY(27%)}64%,78%{transform:translateY(0)}90%,100%{transform:translateY(100%)}}\n.thl-flash{opacity:0;animation:thl-flash calc(var(--c)*var(--d)) linear infinite}\n@keyframes thl-flash{0%,63%{opacity:0}65%{opacity:1}68%{opacity:.25}70%{opacity:1}84%,100%{opacity:0}}\n.thl-body{transform-origin:50% 50%;animation:thl-pop calc(var(--c)*var(--d)) cubic-bezier(.3,1.6,.5,1) infinite}\n@keyframes thl-pop{0%,62%{transform:scale(1)}66%{transform:scale(1.12) rotate(-3deg)}74%{transform:scale(.97)}82%,100%{transform:scale(1)}}\n.thl-arc{opacity:0;animation:thl-arc calc(var(--c)*var(--d)) steps(1) infinite}\n@keyframes thl-arc{0%{opacity:0}27%{opacity:.7}29%{opacity:0}47%{opacity:.9}48%{opacity:0}50%{opacity:.6}52%{opacity:0}60%{opacity:1}62%{opacity:0}64%{opacity:1}68%{opacity:.3}70%{opacity:1}74%{opacity:0}}\n.thl-spark{opacity:0;animation:thl-spark calc(var(--c)*var(--d)) ease-out infinite}\n@keyframes thl-spark{0%,64%{opacity:0;transform:rotate(var(--a)) translateY(-14px) scale(1)}66%{opacity:1}82%,100%{opacity:0;transform:rotate(var(--a)) translateY(-30px) scale(.2)}}\n.thl-det .thl-arc{animation:thl-idle 1.3s steps(1) infinite}\n@keyframes thl-idle{0%,100%{opacity:0}40%{opacity:.8}44%{opacity:0}71%{opacity:.5}73%{opacity:0}}\n.thl-det .thl-flash{animation:thl-flash-once .6s ease-out forwards}\n@keyframes thl-flash-once{0%{opacity:1}100%{opacity:0}}\n@media (prefers-reduced-motion:reduce){.thl-level{animation:none;transform:none}.thl-flash,.thl-arc,.thl-spark{animation:none;opacity:0}.thl-body{animation:none}}\n`;\n\nexport function ThunderLoader({\n  size = 96,\n  color = \"#facc15\",\n  speed = 1,\n  progress,\n  label = \"Charging\",\n  className,\n}: ThunderLoaderProps) {\n  const clip = `thl-${useId().replace(/[^\\w-]/g, \"\")}`;\n  const det = progress !== undefined;\n  const p = det ? Math.min(100, Math.max(0, progress)) : 0;\n  const style = { width: size, height: size, color, \"--d\": 1 / Math.max(0.1, speed) } as CSSProperties;\n\n  return (\n    <span\n      role={det ? \"progressbar\" : \"status\"}\n      aria-label={label}\n      aria-valuenow={det ? Math.round(p) : undefined}\n      aria-valuemin={det ? 0 : undefined}\n      aria-valuemax={det ? 100 : undefined}\n      className={`thl relative inline-block shrink-0 ${det ? \"thl-det\" : \"\"} ${className ?? \"\"}`}\n      style={style}\n    >\n      <style href=\"thunder-loader\" precedence=\"default\">\n        {css}\n      </style>\n      {!det && <span className=\"sr-only\">{label}</span>}\n      <svg aria-hidden viewBox=\"0 0 64 64\" className=\"size-full overflow-visible\">\n        <defs>\n          <clipPath id={clip}>\n            <rect\n              className={det ? \"transition-transform duration-500 ease-out\" : \"thl-level\"}\n              x=\"0\"\n              y=\"0\"\n              width=\"64\"\n              height=\"64\"\n              style={det ? { transformBox: \"fill-box\", transform: `translateY(${100 - p}%)` } : undefined}\n            />\n          </clipPath>\n        </defs>\n        <g className={det ? undefined : \"thl-body\"}>\n          <path\n            d={BOLT}\n            fill=\"currentColor\"\n            fillOpacity={0.08}\n            stroke=\"currentColor\"\n            strokeOpacity={0.45}\n            strokeWidth=\"2\"\n            strokeLinejoin=\"round\"\n          />\n          <path\n            d={BOLT}\n            fill=\"currentColor\"\n            stroke=\"currentColor\"\n            strokeWidth=\"2\"\n            strokeLinejoin=\"round\"\n            clipPath={`url(#${clip})`}\n            style={{ filter: \"drop-shadow(0 0 4px currentColor)\" }}\n          />\n          {(!det || p >= 100) && (\n            <path\n              key={det ? \"done\" : \"loop\"}\n              className=\"thl-flash\"\n              d={BOLT}\n              fill=\"#fff\"\n              stroke=\"#fff\"\n              strokeWidth=\"2\"\n              strokeLinejoin=\"round\"\n              style={{ filter: \"drop-shadow(0 0 6px currentColor) drop-shadow(0 0 14px currentColor)\" }}\n            />\n          )}\n        </g>\n        {ARCS.map((d, i) => (\n          <path\n            key={d}\n            className=\"thl-arc\"\n            d={d}\n            fill=\"none\"\n            stroke=\"currentColor\"\n            strokeWidth=\"1.6\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            style={{ animationDelay: `${-i * 0.23}s`, filter: \"drop-shadow(0 0 2px currentColor)\" }}\n          />\n        ))}\n        {!det &&\n          SPARKS.map((a) => (\n            <circle\n              key={a}\n              className=\"thl-spark\"\n              cx=\"32\"\n              cy=\"32\"\n              r=\"1.6\"\n              fill=\"currentColor\"\n              style={{ \"--a\": `${a}deg`, transformOrigin: \"32px 32px\" } as CSSProperties}\n            />\n          ))}\n      </svg>\n    </span>\n  );\n}\n"}]}