Evan93
Evan93

Reputation: 352

Prevent close when clicking Ant Design Dropdown submenu item

I have an Ant Design Dropdown component in a React application. The dropdown includes submenus. Right now, the submenu items close when they're clicked on. I would like them to instead remain open on click.

The Ant Design docs have a solution for this for the base level dropdown menu items, however, this doesn't work for sub menu items.

I also came across this Github issue where the problem is supposedly solved. However, the solution uses the old overlay prop for the Dropdown component, which is deprecated in favor of the menu prop.

Finally, I tried adding a div around the label of the submenu items with onClick={(e) => e.stopPropagation()}. This mostly works, except the menu item has some padding around itself, so the label does not take up the full space that the menu item takes up. Because of this, the submenu does not close when you click in the center of it (on the label), but it still closes if you click around the edge (on the padding).

Upvotes: 1

Views: 30

Answers (2)

Evan93
Evan93

Reputation: 352

I was able to find a pretty hacky way of accomplishing this without using the deprecated overlay prop. I had to manually manage the dropdown's state, as well as the menu state. I also had to use a ref to tell the menu when not to close.

import type { DropdownProps } from 'antd';
import { Dropdown } from 'antd';
import React, { useRef, useState } from 'react';

export const PersistentDropdown: React.FC<DropdownProps> = ({ children, ...props }) => {
  const [open, setOpen] = useState(false);
  const closeItem = useRef(true);
  const [openKeys, setOpenKeys] = useState<string[]>([]);

  return (
    <Dropdown
      {...props}
      open={open}
      onOpenChange={(nextOpen, info) => {
        if (info.source === 'trigger' || nextOpen) {
          setTimeout(() => setOpen(nextOpen), 0);
        }
      }}
      menu={
        props.menu && {
          ...props.menu,
          openKeys,
          onOpenChange: (keys: string[]) => {
            if (closeItem.current) {
              setOpenKeys(keys);
            } else {
              closeItem.current = true;
            }
          },
          onClick: () => (closeItem.current = false),
        }
      }
    >
      {children}
    </Dropdown>
  );
};

Upvotes: 1

Shashika Silva
Shashika Silva

Reputation: 228

The issue arises because Ant Design automatically closes the submenu when an item inside it is clicked. However, we can work around this using onTitleClick on the SubMenu and handling event propagation properly.

import React, { useState } from "react";
import { Dropdown, Menu } from "antd";
import { DownOutlined } from "@ant-design/icons";

const { SubMenu } = Menu;

const DropdownMenu = () => {
  const [openKeys, setOpenKeys] = useState<string[]>([]);

  const handleSubmenuClick = (key: string) => {
    setOpenKeys((prevKeys) =>
      prevKeys.includes(key) ? prevKeys.filter((k) => k !== key) : [...prevKeys, key]
    );
  };

  const menu = (
    <Menu
      openKeys={openKeys}
      onOpenChange={setOpenKeys}
      mode="vertical"
      triggerSubMenuAction="click" // Keeps submenus open on click
    >
      <Menu.Item key="1">Option 1</Menu.Item>
      <Menu.Item key="2">Option 2</Menu.Item>

      <SubMenu
        key="submenu"
        title="More Options"
        onTitleClick={() => handleSubmenuClick("submenu")} // Manages submenu open state
      >
        <Menu.Item key="3">Submenu Item 1</Menu.Item>
        <Menu.Item key="4">Submenu Item 2</Menu.Item>
      </SubMenu>
    </Menu>
  );

  return (
    <Dropdown overlay={menu} trigger={["click"]}>
      <a onClick={(e) => e.preventDefault()}>
        Click me <DownOutlined />
      </a>
    </Dropdown>
  );
};

export default DropdownMenu;

If above did not work try this: If you prefer, you can wrap submenu items in a div and stop event propagation to prevent closing:

<Menu.Item key="3">
  <div onClick={(e) => e.stopPropagation()}>Submenu Item 1</div>
</Menu.Item>

Upvotes: 1

Related Questions