Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recommended way to have drawer resizable?

I would like to have the material ui drawer's width resizable through a draggable handle. My current approach is to have a mousevent listener on the whole app which checks if handle was pressed and updates the width according to mouse position on every mouse move.

This however requires a constant mouseevent listener on the whole app which seems to be overkill for a simple resize feature.

Are there better/ recommended ways of doing the resize?

like image 308
nauti Avatar asked Mar 24 '18 20:03

nauti


2 Answers

It might be a useResize hook with API to enable resizing and providing current width.

import { useCallback, useEffect, useState } from 'react'

type UseResizeProps = {
  minWidth: number
}

type UseResizeReturn = {
  width: number
  enableResize: () => void
}

const useResize = ({
  minWidth,
}: UseResizeProps): UseResizeReturn => {
  const [isResizing, setIsResizing] = useState(false)
  const [width, setWidth] = useState(minWidth)

  const enableResize = useCallback(() => {
    setIsResizing(true)
  }, [setIsResizing])

  const disableResize = useCallback(() => {
    setIsResizing(false)
  }, [setIsResizing])

  const resize = useCallback(
    (e: MouseEvent) => {
      if (isResizing) {
        const newWidth = e.clientX // You may want to add some offset here from props
        if (newWidth >= minWidth) {
          setWidth(newWidth)
        }
      }
    },
    [minWidth, isResizing, setWidth],
  )

  useEffect(() => {
    document.addEventListener('mousemove', resize)
    document.addEventListener('mouseup', disableResize)

    return () => {
      document.removeEventListener('mousemove', resize)
      document.removeEventListener('mouseup', disableResize)
    }
  }, [disableResize, resize])

  return { width, enableResize }
}

export default useResize

Then you could decouple resizing logic from your layout component like this:

const Layout = () => {
  const { width, enableResize } = useResize(200);

  return (
    <Drawer
      variant="permanent"
      open
      PaperProps={{ style: { width } }}
    >
      {drawer}
      <div
        style={{ 
          position: absolute,
          width: '2px',
          top: '0',
          right: '-1px',
          bottom: '0',
          cursor: 'col-resize'
        }}
        onMouseDown={enableResize}
      />
    </Drawer>
)
like image 197
Vadim Shvetsov Avatar answered Oct 19 '22 21:10

Vadim Shvetsov


I would like to add an answer that is more up to date using React Hooks.

You can do it like this, then:

CSS:

sidebar-dragger: {
  width: '5px',
  cursor: 'ew-resize',
  padding: '4px 0 0',
  borderTop: '1px solid #ddd',
  position: 'absolute',
  top: 0,
  left: 0,
  bottom: 0,
  zIndex: '100',
  backgroundColor: '#f4f7f9'
}

React (using hooks with refs and states)

let isResizing = null;

function ResizeableSidebar (props) {
  const sidebarPanel = React.useRef('sidebarPanel');
  const cbHandleMouseMove = React.useCallback(handleMousemove, []);
  const cbHandleMouseUp = React.useCallback(handleMouseup, []);

  function handleMousedown (e) {
    e.stopPropagation();
    e.preventDefault();
    // we will only add listeners when needed, and remove them afterward
    document.addEventListener('mousemove', cbHandleMouseMove);
    document.addEventListener('mouseup', cbHandleMouseUp);
    isResizing = true;
  };

  function handleMousemove (e) {
    if (!isResizing) {
      return;
    }

    let offsetRight =
      document.body.offsetWidth - (e.clientX - document.body.offsetLeft);
    let minWidth = 50;
    if (offsetRight > minWidth) {
      let curSize = offsetRight - 60;
      // using a ref instead of state will be way faster
      sidebarPanel.current.style.width = curSize + 'px';
    }
  };

  function handleMouseup (e) {
    if (!isResizing) {
      return;
    }
    isResizing = false;
    document.removeEventListener('mousemove', cbHandleMouseMove);
    document.removeEventListener('mouseup', cbHandleMouseUp);
  };

  return <div className="sidebar-container">
    <div
      className="sidebar-dragger"
      onMouseDown={handleMousedown}
    />
    <div>
      Your stuff goes here
    </div>
  </div>;
}
like image 22
Felipe N Moura Avatar answered Oct 19 '22 21:10

Felipe N Moura