A resizable `antd` Drawer?

Viewed 1947

I would like to provide a way to make an antd Drawer resizable ?

I read a popular answer specifically for material-ui/Drawer but I am looking to do something very similar with antd.

Does anyone have a similar antd example - or have a better idea how to handle info getting chopped off at side of the drawer.

2 Answers

You can extend the width of Drawer by specifying it on the width props. If you don't want to extend it but you want the content to be still fit, you can set the width on bodyStyle prop and use overflow: "auto":

<Drawer
    title="Basic Drawer"
    placement="right"
    closable={false}
    visible={isDrawerVisible}
    bodyStyle={{
      width: 400,
      overflow: "auto"
    }}
    onClose={toggleDrawerVisible}
>

I also made a resizable drawer based on the link that you provide in antd version (react hooks version answer).

ResizableDrawer.jsx

import React, { useState, useEffect } from "react";
import { Drawer } from "antd";

let isResizing = null;

const ResizableDrawer = ({ children, ...props }) => {
  const [drawerWidth, setDrawerWidth] = useState(undefined);

  const cbHandleMouseMove = React.useCallback(handleMousemove, []);
  const cbHandleMouseUp = React.useCallback(handleMouseup, []);

  useEffect(() => {
    setDrawerWidth(props.width);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [props.visible]);

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

  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) {
    let offsetRight =
      document.body.offsetWidth - (e.clientX - document.body.offsetLeft);
    let minWidth = 256;
    let maxWidth = 600;
    if (offsetRight > minWidth && offsetRight < maxWidth) {
      setDrawerWidth(offsetRight);
    }
  }

  return (
    <Drawer {...props} width={drawerWidth}>
      <div className="sidebar-dragger" onMouseDown={handleMousedown} />
      {children}
    </Drawer>
  );
};

export default ResizableDrawer;

and to use it:

import ResizableDrawer from "./ResizableDrawer";

<ResizableDrawer
    title="Resizable Drawer"
    placement="right"
    closable={false}
    visible={isResizableDrawerVisible}
    onClose={toggleResizableDrawerVisible}
>
    ...
</ResizableDrawer>

See working demo here:

Edit

  1. Have two states for tracking the width of the drawer and whether or not the drawer is being resized (isResizing).
  2. Add two event listeners on the global document where it will listen for mousemove and mouseup. The mousemove event will resize the drawer, only if isResizing is true. And the mouseup event will set isResizing to false.
  3. Add a div in your drawer that acts as the draggable border for making the drawer resizable. This div will listen for a mousedown event, which will set the state of isResizing to true.

Here's the code that has been improved upon from the basic drawer demo from antd's website.

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Drawer, Button } from "antd";

const App = () => {
  const [visible, setVisible] = useState(false);
  const [isResizing, setIsResizing] = useState(false);
  const [width, setWidth] = useState(256);

  const showDrawer = () => {
    setVisible(true);
  };

  const onClose = () => {
    setVisible(false);
  };

  const onMouseDown = e => {
    setIsResizing(true);
  };

  const onMouseUp = e => {
    setIsResizing(false);
  };

  const onMouseMove = e => {
    if (isResizing) {
      let offsetRight =
        document.body.offsetWidth - (e.clientX - document.body.offsetLeft);
      const minWidth = 50;
      const maxWidth = 600;
      if (offsetRight > minWidth && offsetRight < maxWidth) {
        setWidth(offsetRight);
      }
    }
  };

  useEffect(() => {
    document.addEventListener("mousemove", onMouseMove);
    document.addEventListener("mouseup", onMouseUp);

    return () => {
      document.removeEventListener("mousemove", onMouseMove);
      document.removeEventListener("mouseup", onMouseUp);
    };
  });

  return (
    <>
      <Button type="primary" onClick={showDrawer}>
        Open
      </Button>
      <Drawer
        title="Basic Drawer"
        placement="right"
        closable={false}
        onClose={onClose}
        visible={visible}
        width={width}
      >
        <div
          style={{
            position: "absolute",
            width: "5px",
            padding: "4px 0 0",
            top: 0,
            left: 0,
            bottom: 0,
            zIndex: 100,
            cursor: "ew-resize",
            backgroundColor: "#f4f7f9"
          }}
          onMouseDown={onMouseDown}
        />
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Drawer>
    </>
  );
};

ReactDOM.render(<App />, document.getElementById("container"));

And here's the demo of the code:

DEMO

Related