{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-autosize",
  "title": "useAutosize",
  "description": "Grows a textarea with what you write between its minimum and maximum height, animating each change without ever touching the text.",
  "registryDependencies": [
    "https://hextaui.com/r/motion.json"
  ],
  "files": [
    {
      "path": "hooks/use-autosize.ts",
      "content": "import * as React from \"react\"\n\nimport { easeOut, prefersReducedMotion } from \"@/lib/motion\"\n\nfunction useAutosize(\n  ref: React.RefObject<HTMLTextAreaElement | null>,\n  enabled: boolean,\n  value: unknown\n) {\n  const fitRef = React.useRef<((animate: boolean) => void) | null>(null)\n\n  React.useLayoutEffect(() => {\n    const textarea = ref.current\n    if (!enabled || !textarea) {\n      return\n    }\n\n    let animation: Animation | null = null\n    let settled = 0\n    let width = 0\n\n    const fit = (animate: boolean) => {\n      const from = animation\n        ? parseFloat(getComputedStyle(textarea).height)\n        : settled\n      animation?.cancel()\n      animation = null\n\n      textarea.style.height = \"auto\"\n      const style = getComputedStyle(textarea)\n      const border =\n        parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth)\n      const max = parseFloat(style.maxHeight)\n      const min = parseFloat(style.minHeight)\n      const natural = textarea.scrollHeight + border\n      const capped = Number.isFinite(max) ? Math.min(natural, max) : natural\n      const target = Number.isFinite(min) ? Math.max(capped, min) : capped\n      textarea.style.height = `${target}px`\n      textarea.style.overflowY = natural > target + 1 ? \"auto\" : \"hidden\"\n      settled = target\n\n      if (\n        animate &&\n        from > 0 &&\n        Math.abs(from - target) > 0.5 &&\n        typeof textarea.animate === \"function\" &&\n        !prefersReducedMotion()\n      ) {\n        const overflow = textarea.style.overflowY\n        textarea.style.overflowY = \"hidden\"\n        const running = textarea.animate(\n          [{ height: `${from}px` }, { height: `${target}px` }],\n          { duration: 180, easing: easeOut }\n        )\n        animation = running\n        running.onfinish = () => {\n          if (animation === running) {\n            animation = null\n            textarea.style.overflowY = overflow\n          }\n        }\n      }\n    }\n\n    fitRef.current = fit\n    fit(false)\n\n    const onInput = (event: Event) => {\n      if (event.target === textarea) {\n        fit(true)\n      }\n    }\n    window.addEventListener(\"input\", onInput)\n    const observer =\n      typeof ResizeObserver === \"undefined\"\n        ? null\n        : new ResizeObserver(() => {\n            if (textarea.clientWidth !== width) {\n              width = textarea.clientWidth\n              if (!animation) {\n                fit(false)\n              }\n            }\n          })\n    observer?.observe(textarea)\n\n    return () => {\n      fitRef.current = null\n      window.removeEventListener(\"input\", onInput)\n      observer?.disconnect()\n      animation?.cancel()\n      textarea.style.height = \"\"\n      textarea.style.overflowY = \"\"\n    }\n  }, [enabled, ref])\n\n  React.useLayoutEffect(() => {\n    fitRef.current?.(true)\n  }, [value])\n}\n\nexport { useAutosize }\n",
      "type": "registry:hook"
    }
  ],
  "type": "registry:hook"
}