{"version":3,"file":"useAutoGrow.mjs","names":[],"sources":["../../../../package/common/hooks/useAutoGrow.ts"],"sourcesContent":["import { useLayoutEffect, useRef, type RefObject } from 'react';\n\ninterface AutoGrowOptions {\n  enabled?: boolean;\n  /** Whether the text needs more than the field's `rows`, after each change of `value` or width. */\n  onGrow?: (isGrown: boolean) => void;\n}\n\n/** Sizes the field to its text, and says whether that took it past its `rows`. */\nfunction grow(field: HTMLElement): boolean {\n  // Collapsing the field to measure it would shrink its parent for that moment. A scroller that\n  // gains the room clamps its position and keeps it when the field grows back: a chat's message\n  // list lost its bottom on every keystroke. Holding the parent's height keeps the collapse inside;\n  // offsetHeight, as a transform (a modal scaling in) would make the rect too small to hold it.\n  const parent = field.parentElement;\n  const { minHeight } = parent?.style ?? {};\n  const { alignSelf } = field.style;\n  if (parent) parent.style.minHeight = `${parent.offsetHeight}px`;\n  // A stretched field would grow back to the held height and never measure shorter (Input's is).\n  field.style.alignSelf = 'flex-start';\n  field.style.height = 'auto';\n  // Collapsed, the field is as tall as its `rows`. The 1px absorbs rounding: a line is far more.\n  const isGrown = field.scrollHeight > field.clientHeight + 1;\n  field.style.height = `${field.scrollHeight}px`;\n  field.style.alignSelf = alignSelf;\n  if (parent) parent.style.minHeight = minHeight ?? '';\n  return isGrown;\n}\n\n/**\n * Keeps a textarea as tall as its text, up to the `max-height` its stylesheet sets. It measures\n * again when `value` changes, so the field must be controlled, and when its width changes.\n */\nexport function useAutoGrow(\n  ref: RefObject<HTMLElement | null>,\n  value: unknown,\n  { enabled = true, onGrow }: AutoGrowOptions = {},\n): void {\n  // Kept in a ref so a fresh callback each render does not resubscribe the observer.\n  const onGrowRef = useRef(onGrow);\n  useLayoutEffect(() => {\n    onGrowRef.current = onGrow;\n  });\n\n  useLayoutEffect(() => {\n    const field = ref.current;\n    if (!enabled || !field) return undefined;\n    let width = field.offsetWidth;\n    let frame = 0;\n    // A new width rewraps the same text onto more or fewer lines. Growing in the callback itself\n    // would resize what is observed, which browsers report as a ResizeObserver loop error, so it\n    // waits a frame. The height it then changes is skipped, as the width is the same.\n    const observer = new ResizeObserver(() => {\n      if (field.offsetWidth === width) return;\n      width = field.offsetWidth;\n      cancelAnimationFrame(frame);\n      frame = requestAnimationFrame(() => {\n        const isGrown = grow(field);\n        onGrowRef.current?.(isGrown);\n      });\n    });\n    observer.observe(field);\n    return () => {\n      observer.disconnect();\n      cancelAnimationFrame(frame);\n      // Only clears a height this set, so an explicit one from a style prop survives while\n      // disabled. Here rather than below, where it would collapse the field on every keystroke.\n      field.style.height = '';\n    };\n  }, [ref, enabled]);\n\n  useLayoutEffect(() => {\n    if (!enabled || !ref.current) return;\n    // Measured first: an optional call skips evaluating its argument when there is no callback.\n    const isGrown = grow(ref.current);\n    onGrowRef.current?.(isGrown);\n  }, [ref, value, enabled]);\n}\n"],"mappings":"iEASA,SAAS,EAAK,EAA6B,CAKzC,IAAM,EAAS,EAAM,cACf,CAAE,aAAc,GAAQ,OAAS,CAAC,EAClC,CAAE,aAAc,EAAM,MACxB,IAAQ,EAAO,MAAM,UAAY,GAAG,EAAO,aAAa,KAE5D,EAAM,MAAM,UAAY,aACxB,EAAM,MAAM,OAAS,OAErB,IAAM,EAAU,EAAM,aAAe,EAAM,aAAe,EAI1D,MAHA,GAAM,MAAM,OAAS,GAAG,EAAM,aAAa,IAC3C,EAAM,MAAM,UAAY,EACpB,IAAQ,EAAO,MAAM,UAAY,GAAa,IAC3C,CACT,CAMA,SAAgB,EACd,EACA,EACA,CAAE,UAAU,GAAM,UAA4B,CAAC,EACzC,CAEN,IAAM,EAAY,EAAO,CAAM,EAC/B,MAAsB,CACpB,EAAU,QAAU,CACtB,CAAC,EAED,MAAsB,CACpB,IAAM,EAAQ,EAAI,QAClB,GAAI,CAAC,GAAW,CAAC,EAAO,OACxB,IAAI,EAAQ,EAAM,YACd,EAAQ,EAIN,EAAW,IAAI,mBAAqB,CACpC,EAAM,cAAgB,IAC1B,EAAQ,EAAM,YACd,qBAAqB,CAAK,EAC1B,EAAQ,0BAA4B,CAClC,IAAM,EAAU,EAAK,CAAK,EAC1B,EAAU,UAAU,CAAO,CAC7B,CAAC,EACH,CAAC,EAED,OADA,EAAS,QAAQ,CAAK,MACT,CACX,EAAS,WAAW,EACpB,qBAAqB,CAAK,EAG1B,EAAM,MAAM,OAAS,EACvB,CACF,EAAG,CAAC,EAAK,CAAO,CAAC,EAEjB,MAAsB,CACpB,GAAI,CAAC,GAAW,CAAC,EAAI,QAAS,OAE9B,IAAM,EAAU,EAAK,EAAI,OAAO,EAChC,EAAU,UAAU,CAAO,CAC7B,EAAG,CAAC,EAAK,EAAO,CAAO,CAAC,CAC1B"}