useIsOverflow.ts 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import { useLayoutEffect, useState } from 'react'
  2. type CallbackArg = {
  3. hasOverflow: boolean
  4. clientWidth: number | undefined
  5. scrollWidth: number | undefined
  6. }
  7. type UseIsOverflowOpts = {
  8. ref: React.RefObject<HTMLElement | null>
  9. callback?: (arg: CallbackArg) => void
  10. disabled?: boolean
  11. }
  12. export const useIsOverflow = ({ ref, callback, disabled }: UseIsOverflowOpts) => {
  13. const [isOverflow, setIsOverflow] = useState<boolean>()
  14. const [clientWidth, setClientWidth] = useState<number>()
  15. const [scrollWidth, setScrollWidth] = useState<number>()
  16. const [domRect, setDomRect] = useState<DOMRect>()
  17. useLayoutEffect(() => {
  18. if (disabled) {
  19. return
  20. }
  21. const el = ref.current
  22. if (!el) {
  23. return
  24. }
  25. const trigger = () => {
  26. const hasOverflow = el.scrollWidth > el.clientWidth
  27. setClientWidth(el.clientWidth)
  28. setIsOverflow(hasOverflow)
  29. setScrollWidth(el.scrollWidth)
  30. setDomRect(el.getBoundingClientRect())
  31. if (callback) callback({ hasOverflow, clientWidth: el.clientWidth, scrollWidth: el.scrollWidth })
  32. }
  33. let resizeObserver: ResizeObserver
  34. if ('ResizeObserver' in window) {
  35. resizeObserver = new ResizeObserver(trigger)
  36. resizeObserver.observe(el)
  37. }
  38. trigger()
  39. return () => {
  40. if ('ResizeObserver' in window) {
  41. resizeObserver.unobserve(el)
  42. resizeObserver.disconnect()
  43. }
  44. }
  45. }, [callback, disabled, ref])
  46. return { isOverflow, clientWidth, scrollWidth, domRect }
  47. }