useVirtualScrolling.tsx 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import {RefObject, useCallback, useEffect, useState} from 'react';
  2. import clamp from 'sentry/utils/number/clamp';
  3. interface Props<Element extends HTMLElement> {
  4. contentRef: RefObject<Element>;
  5. windowRef: RefObject<Element>;
  6. }
  7. export default function useVirtualScrolling<Element extends HTMLElement>({
  8. contentRef,
  9. windowRef,
  10. }: Props<Element>) {
  11. const window = windowRef.current;
  12. const content = contentRef.current;
  13. const [scrollPosition, setScrollPosition] = useState({
  14. offsetX: 0,
  15. offsetY: 0,
  16. });
  17. const reclamp = useCallback(() => {
  18. if (!window) {
  19. return;
  20. }
  21. setScrollPosition(prev => {
  22. const minX =
  23. (content?.clientWidth ?? Number.MAX_SAFE_INTEGER) * -1 + window.clientWidth;
  24. const minY =
  25. (content?.clientHeight ?? Number.MAX_SAFE_INTEGER) * -1 + window.clientHeight;
  26. const offsetX = clamp(prev.offsetX, minX, 0);
  27. const offsetY = clamp(prev.offsetY, minY, 0);
  28. return {offsetX, offsetY};
  29. });
  30. }, [content, window]);
  31. useEffect(() => {
  32. if (!window) {
  33. return () => {};
  34. }
  35. const handleWheel = (e: WheelEvent) => {
  36. const {deltaX, deltaY} = e;
  37. setScrollPosition(prev => {
  38. const minX =
  39. (content?.clientWidth ?? Number.MAX_SAFE_INTEGER) * -1 + window.clientWidth;
  40. const minY =
  41. (content?.clientHeight ?? Number.MAX_SAFE_INTEGER) * -1 + window.clientHeight;
  42. const offsetX = clamp(prev.offsetX - deltaX, minX, 0);
  43. const offsetY = clamp(prev.offsetY - deltaY, minY, 0);
  44. return {offsetX, offsetY};
  45. });
  46. };
  47. window.addEventListener('wheel', handleWheel);
  48. return () => {
  49. window.removeEventListener('wheel', handleWheel);
  50. };
  51. }, [content, window]);
  52. return {
  53. ...scrollPosition,
  54. reclamp,
  55. };
  56. }