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
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
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 #ffffd',
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>;
}
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 #ffffd',
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.
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>
);
}