{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"html-css-loading-spinner-button-with-busy-state","type":"registry:component","title":"HTML CSS Loading Spinner Button With Busy State","description":"","dependencies":[],"files":[{"path":"components/we-inc/html-css-loading-spinner-button-with-busy-state.tsx","type":"registry:component","content":"import { useEffect, useRef, useState } from \"react\";\n\n// A submit button that shows a spinner while it waits, blocks double clicks, then confirms.\nconst SAVE_MS = 1800;\n\nfunction Spinner() {\n  return (\n    <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\" className=\"animate-spin motion-reduce:animate-none\">\n      <circle cx=\"12\" cy=\"12\" r=\"9\" fill=\"none\" stroke=\"currentColor\" strokeOpacity=\"0.3\" strokeWidth=\"3\" />\n      <path d=\"M21 12a9 9 0 0 0-9-9\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n\nexport default function HtmlCssLoadingSpinnerButtonWithBusyState() {\n  const [state, setState] = useState<\"idle\" | \"busy\" | \"done\">(\"idle\");\n  const timer = useRef<number>();\n  useEffect(() => () => window.clearTimeout(timer.current), []);\n\n  const save = () => {\n    if (state === \"busy\") return;\n    setState(\"busy\");\n    timer.current = window.setTimeout(() => {\n      setState(\"done\");\n      timer.current = window.setTimeout(() => setState(\"idle\"), 2000);\n    }, SAVE_MS);\n  };\n\n  const btn = \"inline-flex min-h-[44px] min-w-[176px] items-center justify-center gap-2 rounded-md px-5 text-sm font-semibold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-900\";\n  return (\n    <section className=\"bg-slate-900 px-5 py-14\">\n      <div className=\"mx-auto max-w-md rounded-xl border border-slate-700 bg-slate-800 p-6\">\n        <h2 className=\"text-base font-semibold text-white\">Invoice settings</h2>\n        <label htmlFor=\"hb3-terms\" className=\"mt-4 block text-sm text-slate-300\">Payment terms</label>\n        <input id=\"hb3-terms\" defaultValue=\"Net 30 days\" className=\"mt-1.5 h-11 w-full rounded-md border border-slate-600 bg-slate-900 px-3 text-sm text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300\" />\n        <div className=\"mt-5 flex flex-wrap items-center gap-3\">\n          <button type=\"button\" onClick={save} aria-busy={state === \"busy\"} aria-disabled={state === \"busy\"} className={`${btn} bg-indigo-500 text-white transition-colors hover:bg-indigo-400 aria-disabled:cursor-wait aria-disabled:bg-indigo-500/70`}>\n            {state === \"busy\" && <Spinner />}\n            {state === \"idle\" ? \"Save changes\" : state === \"busy\" ? \"Saving\" : \"Saved\"}\n          </button>\n          <button type=\"button\" aria-busy=\"true\" aria-disabled=\"true\" className={`${btn} cursor-wait border border-slate-600 text-slate-200`}>\n            <Spinner />\n            Sending invoice\n          </button>\n        </div>\n        <p role=\"status\" className=\"mt-3 h-5 text-sm text-emerald-300\">{state === \"done\" ? \"Payment terms saved.\" : \"\"}</p>\n      </div>\n    </section>\n  );\n}\n"}]}
