{"version":3,"file":"use-collapse.cjs","names":["mergeRefs"],"sources":["../../src/use-collapse/use-collapse.ts"],"sourcesContent":["import React, { CSSProperties, useEffectEvent, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\nimport { mergeRefs } from '../use-merged-ref/use-merged-ref';\n\nfunction getAutoHeightDuration(height: number | string) {\n  if (!height || typeof height === 'string') {\n    return 0;\n  }\n  const constant = height / 36;\n  return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);\n}\n\nexport function getElementHeight(elementRef: React.RefObject<HTMLElement | null>) {\n  return elementRef.current ? elementRef.current.scrollHeight : 'auto';\n}\n\nexport interface UseCollapseInput {\n  /** Expanded state  */\n  expanded: boolean;\n\n  /** Transition duration in milliseconds, by default calculated based on content height */\n  transitionDuration?: number;\n\n  /** Transition timing function, `ease` by default */\n  transitionTimingFunction?: string;\n\n  /** Called when transition ends */\n  onTransitionEnd?: () => void;\n\n  /** Called when transition starts */\n  onTransitionStart?: () => void;\n\n  /** If true, collapsed content is kept in the DOM and hidden with `display: none` styles */\n  keepMounted?: boolean;\n}\n\ninterface GetCollapsePropsInput {\n  style?: CSSProperties;\n  ref?: React.Ref<HTMLDivElement>;\n}\n\ninterface GetCollapsePropsReturnValue {\n  'aria-hidden': boolean;\n  inert: boolean;\n  ref: React.RefCallback<HTMLDivElement>;\n  onTransitionEnd: (event: React.TransitionEvent<Element>) => void;\n  style: React.CSSProperties;\n}\n\nexport type UseCollapseState = 'entering' | 'entered' | 'exiting' | 'exited';\n\nexport interface UseCollapseReturnValue {\n  /** Current transition state */\n  state: UseCollapseState;\n\n  /** Props to pass down to the collapsible element */\n  getCollapseProps: (input?: GetCollapsePropsInput) => GetCollapsePropsReturnValue;\n}\n\nexport function useCollapse({\n  transitionDuration,\n  transitionTimingFunction = 'ease',\n  onTransitionEnd,\n  onTransitionStart,\n  expanded,\n  keepMounted,\n}: UseCollapseInput): UseCollapseReturnValue {\n  const collapsedStyles = {\n    height: 0,\n    overflow: 'hidden',\n    ...(keepMounted ? {} : { display: 'none' }),\n  };\n\n  const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());\n\n  const elementRef = useRef<HTMLElement>(null);\n  const [styles, setStylesRaw] = useState<CSSProperties>(expanded ? {} : collapsedStyles);\n  const [state, setState] = useState<UseCollapseState>(expanded ? 'entered' : 'exited');\n  const setStyles = (newStyles: React.SetStateAction<CSSProperties>) => {\n    flushSync(() => setStylesRaw(newStyles));\n  };\n\n  const mergeStyles = (newStyles: CSSProperties) => {\n    setStyles((oldStyles) => ({ ...oldStyles, ...newStyles }));\n  };\n\n  const getTransitionStyles = (height: number | string) => {\n    const duration = transitionDuration || getAutoHeightDuration(height);\n    return {\n      transition: `height ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}`,\n    };\n  };\n\n  useDidUpdate(() => {\n    const shouldTransition = transitionDuration !== 0;\n\n    if (shouldTransition) {\n      onTransitionStartEvent();\n    }\n\n    if (expanded) {\n      window.requestAnimationFrame(() => {\n        flushSync(() => setState('entering'));\n        mergeStyles({ willChange: 'height', display: 'block', overflow: 'hidden' });\n        window.requestAnimationFrame(() => {\n          const height = getElementHeight(elementRef);\n          mergeStyles({ ...getTransitionStyles(height), height });\n        });\n      });\n    } else {\n      window.requestAnimationFrame(() => {\n        flushSync(() => setState('exiting'));\n        const height = getElementHeight(elementRef);\n        mergeStyles({ ...getTransitionStyles(height), willChange: 'height', height });\n        window.requestAnimationFrame(() => mergeStyles({ height: 0, overflow: 'hidden' }));\n      });\n    }\n  }, [expanded]);\n\n  const handleTransitionEnd = (event: React.TransitionEvent): void => {\n    if (event.target !== elementRef.current || event.propertyName !== 'height') {\n      return;\n    }\n\n    if (expanded) {\n      const height = getElementHeight(elementRef);\n\n      if (height === styles.height) {\n        setStyles({});\n      } else {\n        mergeStyles({ height });\n      }\n\n      setState('entered');\n      onTransitionEnd?.();\n    } else if (styles.height === 0) {\n      setStyles(collapsedStyles);\n      setState('exited');\n      onTransitionEnd?.();\n    }\n  };\n\n  return {\n    state,\n    getCollapseProps: (input) => ({\n      'aria-hidden': !expanded,\n      inert: !expanded,\n      ref: mergeRefs(elementRef, input?.ref),\n      onTransitionEnd: handleTransitionEnd,\n      style: { boxSizing: 'border-box', ...input?.style, ...styles },\n    }),\n  };\n}\n\nexport namespace useCollapse {\n  export type Input = UseCollapseInput;\n  export type ReturnValue = UseCollapseReturnValue;\n  export type State = UseCollapseState;\n}\n"],"mappings":";;;;;;AAKA,SAAS,sBAAsB,QAAyB;AACtD,KAAI,CAAC,UAAU,OAAO,WAAW,SAC/B,QAAO;CAET,MAAM,WAAW,SAAS;AAC1B,QAAO,KAAK,OAAO,IAAI,KAAK,YAAY,MAAO,WAAW,KAAK,GAAG;;AAGpE,SAAgB,iBAAiB,YAAiD;AAChF,QAAO,WAAW,UAAU,WAAW,QAAQ,eAAe;;AA8ChE,SAAgB,YAAY,EAC1B,oBACA,2BAA2B,QAC3B,iBACA,mBACA,UACA,eAC2C;CAC3C,MAAM,kBAAkB;EACtB,QAAQ;EACR,UAAU;EACV,GAAI,cAAc,EAAE,GAAG,EAAE,SAAS,QAAQ;EAC3C;CAED,MAAM,0BAAA,GAAA,MAAA,sBAA8C,qBAAqB,CAAC;CAE1E,MAAM,cAAA,GAAA,MAAA,QAAiC,KAAK;CAC5C,MAAM,CAAC,QAAQ,iBAAA,GAAA,MAAA,UAAwC,WAAW,EAAE,GAAG,gBAAgB;CACvF,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,UAAuC,WAAW,YAAY,SAAS;CACrF,MAAM,aAAa,cAAmD;AACpE,GAAA,GAAA,UAAA,iBAAgB,aAAa,UAAU,CAAC;;CAG1C,MAAM,eAAe,cAA6B;AAChD,aAAW,eAAe;GAAE,GAAG;GAAW,GAAG;GAAW,EAAE;;CAG5D,MAAM,uBAAuB,WAA4B;EACvD,MAAM,WAAW,sBAAsB,sBAAsB,OAAO;AACpE,SAAO,EACL,YAAY,UAAU,SAAS,KAAK,yBAAyB,YAAY,SAAS,KAAK,4BACxF;;AAGH,wBAAA,mBAAmB;AAGjB,MAFyB,uBAAuB,EAG9C,yBAAwB;AAG1B,MAAI,SACF,QAAO,4BAA4B;AACjC,IAAA,GAAA,UAAA,iBAAgB,SAAS,WAAW,CAAC;AACrC,eAAY;IAAE,YAAY;IAAU,SAAS;IAAS,UAAU;IAAU,CAAC;AAC3E,UAAO,4BAA4B;IACjC,MAAM,SAAS,iBAAiB,WAAW;AAC3C,gBAAY;KAAE,GAAG,oBAAoB,OAAO;KAAE;KAAQ,CAAC;KACvD;IACF;MAEF,QAAO,4BAA4B;AACjC,IAAA,GAAA,UAAA,iBAAgB,SAAS,UAAU,CAAC;GACpC,MAAM,SAAS,iBAAiB,WAAW;AAC3C,eAAY;IAAE,GAAG,oBAAoB,OAAO;IAAE,YAAY;IAAU;IAAQ,CAAC;AAC7E,UAAO,4BAA4B,YAAY;IAAE,QAAQ;IAAG,UAAU;IAAU,CAAC,CAAC;IAClF;IAEH,CAAC,SAAS,CAAC;CAEd,MAAM,uBAAuB,UAAuC;AAClE,MAAI,MAAM,WAAW,WAAW,WAAW,MAAM,iBAAiB,SAChE;AAGF,MAAI,UAAU;GACZ,MAAM,SAAS,iBAAiB,WAAW;AAE3C,OAAI,WAAW,OAAO,OACpB,WAAU,EAAE,CAAC;OAEb,aAAY,EAAE,QAAQ,CAAC;AAGzB,YAAS,UAAU;AACnB,sBAAmB;aACV,OAAO,WAAW,GAAG;AAC9B,aAAU,gBAAgB;AAC1B,YAAS,SAAS;AAClB,sBAAmB;;;AAIvB,QAAO;EACL;EACA,mBAAmB,WAAW;GAC5B,eAAe,CAAC;GAChB,OAAO,CAAC;GACR,KAAKA,uBAAAA,UAAU,YAAY,OAAO,IAAI;GACtC,iBAAiB;GACjB,OAAO;IAAE,WAAW;IAAc,GAAG,OAAO;IAAO,GAAG;IAAQ;GAC/D;EACF"}