useResizableDrawer.tsx 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import {useCallback, useEffect, useRef, useState} from 'react';
  2. interface UseVerticallyResizableDrawerOptions {
  3. initialHeight: number;
  4. minHeight?: number;
  5. }
  6. export function useVerticallyResizableDrawer(
  7. options: UseVerticallyResizableDrawerOptions
  8. ) {
  9. const rafIdRef = useRef<number | null>(null);
  10. const startResizeVectorRef = useRef<[number, number] | null>(null);
  11. const [drawerHeight, setDrawerHeight] = useState(options.initialHeight);
  12. const onMouseMove = useCallback(
  13. (mvEvent: MouseEvent) => {
  14. const rafId = rafIdRef.current;
  15. if (rafId !== null) {
  16. window.cancelAnimationFrame(rafId);
  17. rafIdRef.current = null;
  18. }
  19. rafIdRef.current = window.requestAnimationFrame(() => {
  20. if (!startResizeVectorRef.current) {
  21. return;
  22. }
  23. const currentPositionVector: [number, number] = [
  24. mvEvent.clientX,
  25. mvEvent.clientY,
  26. ];
  27. const distance = [
  28. startResizeVectorRef.current[0] - currentPositionVector[0],
  29. startResizeVectorRef.current[1] - currentPositionVector[1],
  30. ];
  31. startResizeVectorRef.current = currentPositionVector;
  32. setDrawerHeight(h => Math.max(options.minHeight ?? 0, h + distance[1]));
  33. });
  34. },
  35. [options.minHeight]
  36. );
  37. const onMouseUp = useCallback(() => {
  38. document.removeEventListener('mousemove', onMouseMove);
  39. document.removeEventListener('mouseup', onMouseUp);
  40. }, [onMouseMove]);
  41. const onMouseDown = useCallback(
  42. (evt: React.MouseEvent<HTMLElement>) => {
  43. startResizeVectorRef.current = [evt.clientX, evt.clientY];
  44. document.addEventListener('mousemove', onMouseMove, {passive: true});
  45. document.addEventListener('mouseup', onMouseUp);
  46. },
  47. [onMouseMove, onMouseUp]
  48. );
  49. useEffect(() => {
  50. return () => {
  51. if (rafIdRef.current !== null) {
  52. window.cancelAnimationFrame(rafIdRef.current);
  53. }
  54. };
  55. });
  56. return {height: drawerHeight, onMouseDown};
  57. }