ResizableArea.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { type JSX, memo, useCallback, useRef } from 'react';
  2. import type { ResizableAreaProps } from './props';
  3. import styles from './ResizableArea.module.scss';
  4. export const ResizableArea = memo((props: ResizableAreaProps): JSX.Element => {
  5. const {
  6. className,
  7. width,
  8. minWidth = 0,
  9. disabled,
  10. children,
  11. onResize,
  12. onResizeDone,
  13. onCollapsed,
  14. } = props;
  15. const resizableContainer = useRef<HTMLDivElement>(null);
  16. const draggableAreaMoveHandler = useCallback(
  17. (event: MouseEvent) => {
  18. event.preventDefault();
  19. const widthByMousePos = event.pageX;
  20. const newWidth = Math.max(widthByMousePos, minWidth);
  21. onResize?.(newWidth);
  22. resizableContainer.current?.classList.add('dragging');
  23. },
  24. [minWidth, onResize],
  25. );
  26. const dragableAreaMouseUpHandler = useCallback(
  27. (event: MouseEvent) => {
  28. if (resizableContainer.current == null) {
  29. return;
  30. }
  31. const widthByMousePos = event.pageX;
  32. if (widthByMousePos < minWidth / 2) {
  33. // force collapsed
  34. onCollapsed?.();
  35. } else {
  36. const newWidth = resizableContainer.current.clientWidth;
  37. onResizeDone?.(newWidth);
  38. }
  39. resizableContainer.current.classList.remove('dragging');
  40. },
  41. [minWidth, onCollapsed, onResizeDone],
  42. );
  43. const dragableAreaMouseDownHandler = useCallback(
  44. (event: React.MouseEvent) => {
  45. if (disabled) {
  46. return;
  47. }
  48. event.preventDefault();
  49. const removeEventListeners = () => {
  50. document.removeEventListener('mousemove', draggableAreaMoveHandler);
  51. document.removeEventListener('mouseup', dragableAreaMouseUpHandler);
  52. document.removeEventListener('mouseup', removeEventListeners);
  53. };
  54. document.addEventListener('mousemove', draggableAreaMoveHandler);
  55. document.addEventListener('mouseup', dragableAreaMouseUpHandler);
  56. document.addEventListener('mouseup', removeEventListeners);
  57. },
  58. [dragableAreaMouseUpHandler, draggableAreaMoveHandler, disabled],
  59. );
  60. return (
  61. <>
  62. <div
  63. ref={resizableContainer}
  64. className={`${styles['grw-resizable-area']} ${className}`}
  65. style={{ width }}
  66. >
  67. {children}
  68. </div>
  69. <div className={styles['grw-navigation-draggable']}>
  70. {!disabled && (
  71. <>
  72. <button
  73. type="button"
  74. className="grw-navigation-draggable-hitarea"
  75. aria-label="Resize sidebar"
  76. onMouseDown={dragableAreaMouseDownHandler}
  77. />
  78. <div className="grw-navigation-draggable-line"></div>
  79. </>
  80. )}
  81. </div>
  82. </>
  83. );
  84. });