Resize your layout with a draggable div in React
I have always wanted to make my layout more dynamic. WhatsApp web doesn't even have this but things like VSCode do, and I think it's super cool. It reminds me of the old days because frames in webpages had default support for it. Nonetheless it's a super useful feature for creating more flexibility for your layout for different preferences and screensizes: I'm talking about introducing a resizer!
I came across this piece of a guy that wrote a resizer in pure js; how amazing!
I pieced together his code, and then wrote it into a react hook (because I need it like that). I also improved his code a little bit more because the resize icon wasn't working perfectly.
This is what I ended up with: useHorizontalDraggableDiv.ts
import { useEffect } from "react";
/**
* To create a horizontal draggable div, put a div into your code in between two other divs like this:
*
`<Div className="w-1 bg-green-600" id="horizontalDraggable"></Div>`
Inspired by https://htmldom.dev/create-resizable-split-views/
\*/
export const useHorizontalDraggableDiv = (isEnabled?: boolean) =>
useEffect(() => {
if (!isEnabled) {
return;
}
// Query the element
const resizer = document.getElementById(
"horizontalDraggable"
) as HTMLDivElement | null;
if (!resizer) {
return;
}
const leftSide = resizer.previousElementSibling as HTMLDivElement | null;
const rightSide = resizer.nextElementSibling as HTMLDivElement | null;
if (!leftSide || !rightSide) {
return;
}
// The current position of mouse
let x = 0;
let y = 0;
// Width of left side
let leftWidth = 0;
const mouseMoveHandler = function (e: MouseEvent) {
// How far the mouse has been moved
const dx = e.clientX - x;
const dy = e.clientY - y;
leftSide.style.userSelect = "none";
leftSide.style.pointerEvents = "none";
rightSide.style.userSelect = "none";
rightSide.style.pointerEvents = "none";
if (!resizer.parentNode) {
return;
}
const newLeftWidth =
((leftWidth + dx) * 100) /
(resizer.parentNode as Element).getBoundingClientRect().width;
leftSide.style.width = `${newLeftWidth}%`;
};
const mouseUpHandler = function () {
leftSide.style.removeProperty("user-select");
leftSide.style.removeProperty("pointer-events");
rightSide.style.removeProperty("user-select");
rightSide.style.removeProperty("pointer-events");
// Remove the handlers of `mousemove` and `mouseup`
document.removeEventListener("mousemove", mouseMoveHandler);
document.removeEventListener("mouseup", mouseUpHandler);
};
// Handle the mousedown event
// that's triggered when user drags the resizer
const mouseDownHandler = function (e: MouseEvent) {
// Get the current mouse position
x = e.clientX;
y = e.clientY;
leftWidth = leftSide.getBoundingClientRect().width;
// Attach the listeners to `document`
document.addEventListener("mousemove", mouseMoveHandler);
document.addEventListener("mouseup", mouseUpHandler);
};
// Attach the handler
resizer.addEventListener("mouseenter", () => {
document.body.style.cursor = "col-resize";
});
resizer.addEventListener("mouseleave", () => {
document.body.style.removeProperty("cursor");
});
resizer.addEventListener("mousedown", mouseDownHandler);
}, [isEnabled]);
It can be used simply by hooking it into your component and adding your div like described in the doc-comment.
Super easy, super powerful!