use-expand-collapse.js 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. import { useRef, useState, useLayoutEffect } from 'react'
  2. import classNames from 'classnames'
  3. function useExpandCollapse({
  4. initiallyExpanded = false,
  5. collapsedSize = '0',
  6. dimension = 'height',
  7. classes = {}
  8. } = {}) {
  9. const ref = useRef()
  10. const [isExpanded, setIsExpanded] = useState(initiallyExpanded)
  11. const [sizing, setSizing] = useState({
  12. size: null,
  13. needsExpandCollapse: null
  14. })
  15. useLayoutEffect(() => {
  16. const expandCollapseEl = ref.current
  17. if (expandCollapseEl) {
  18. const expandedSize =
  19. dimension === 'height'
  20. ? expandCollapseEl.scrollHeight
  21. : expandCollapseEl.scrollWidth
  22. const needsExpandCollapse = expandedSize > collapsedSize
  23. if (isExpanded) {
  24. setSizing({ size: expandedSize, needsExpandCollapse })
  25. } else {
  26. setSizing({
  27. size: needsExpandCollapse ? collapsedSize : expandedSize,
  28. needsExpandCollapse
  29. })
  30. }
  31. }
  32. // eslint-disable-next-line react-hooks/exhaustive-deps
  33. }, [isExpanded])
  34. const expandableClasses = classNames(
  35. 'expand-collapse-container',
  36. classes.container,
  37. !isExpanded ? classes.containerCollapsed : null
  38. )
  39. function handleToggle() {
  40. setIsExpanded(!isExpanded)
  41. }
  42. return {
  43. isExpanded,
  44. needsExpandCollapse: sizing.needsExpandCollapse,
  45. expandableProps: {
  46. ref,
  47. style: {
  48. [dimension === 'height' ? 'height' : 'width']: `${sizing.size}px`
  49. },
  50. className: expandableClasses
  51. },
  52. toggleProps: {
  53. onClick: handleToggle
  54. }
  55. }
  56. }
  57. export default useExpandCollapse