score:19

Accepted answer

you can use indicator dragger with mousedown on it.

here for example

// styles
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'
}

...

state = {
  isresizing: false,
  lastdownx: 0,
  newwidth: {}
};

handlemousedown = e => {
  this.setstate({ isresizing: true, lastdownx: e.clientx });
};

handlemousemove = e => {
  // we don't want to do anything if we aren't resizing.
  if (!this.state.isresizing) {
    return;
  }

  let offsetright =
    document.body.offsetwidth - (e.clientx - document.body.offsetleft);
  let minwidth = 50;
  let maxwidth = 600;
  if (offsetright > minwidth && offsetright < maxwidth) {
    this.setstate({ newwidth: { width: offsetright } });
  }
};

handlemouseup = e => {
  this.setstate({ isresizing: false });
};

componentdidmount() {
  document.addeventlistener('mousemove', e => this.handlemousemove(e));
  document.addeventlistener('mouseup', e => this.handlemouseup(e));
}

...

<drawer
  variant="permanent"
  open
  anchor={'right'}
  classes={{
    paper: classes.drawerpaper
  }}
  paperprops={{ style: this.state.newwidth }}
>
  <div
    id="dragger"
    onmousedown={event => {
      this.handlemousedown(event);
    }}
    classname={classes.dragger}
  />
  {drawer}
</drawer>

the idea is, when click the dragger, it will resize width drawer followed mouse move.

play demo.

score:2

just use a synthetic event on your handle element. that way, you can avoid the messiness/performance costs of having a universal event listener. something like the following:

render() {
     return (
       <div onmousedown={this.yourresizefunc}>
       </div>
  );
}

score:2

you can do that with css only, if that fits your need. it's the simplest solution. look mom, no javascript.

.resizable {
  height: 150px;
  width: 150px;
  border: 1px solid #333;
  resize: horizontal;
  overflow: auto;
}
<div class="resizable"></div>

reference on mdn

score:4

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>
)

score:5

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>;
}

Related Query

More Query from same tag