드롭다운

드롭다운 (Dropdown)

여러 옵션 중 하나를 선택할 때 사용하는 컴포넌트예요. 트리거를 호버하거나 클릭하면 메뉴가 나타나 옵션을 선택하고 관련 동작을 실행할 수 있어요.

출처: 문서

본문

언제 사용하나요 (When To Use)

선택할 옵션이 몇 개 이상일 때 Dropdown으로 감쌀 수 있어요. 트리거를 호버하거나 클릭하면 드롭다운 메뉴가 나타나고, 옵션을 선택해 관련 동작을 실행할 수 있어요.

예시 (Examples)

기본 (Basic)

가장 기본적인 드롭다운 메뉴예요.

import React from 'react';
import { DownOutlined, SmileOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    key: '1',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
        1st menu item
      </a>
    ),
  },
  {
    key: '2',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
        2nd menu item (disabled)
      </a>
    ),
    icon: <SmileOutlined />,
    disabled: true,
  },
  {
    key: '3',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
        3rd menu item (disabled)
      </a>
    ),
    disabled: true,
  },
  {
    key: '4',
    danger: true,
    label: 'a danger item',
  },
];

const App: React.FC = () => (
  <Dropdown menu={{ items }}>
    <a onClick={(e) => e.preventDefault()}>
      <Space>
        Hover me
        <DownOutlined />
      </Space>
    </a>
  </Dropdown>
);

export default App;

추가 노드 (Extra node)

단축키가 있는 드롭다운 메뉴예요.

import React from 'react';
import { DownOutlined, SettingOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    key: '1',
    label: 'My Account',
    disabled: true,
  },
  {
    type: 'divider',
  },
  {
    key: '2',
    label: 'Profile',
    extra: '⌘P',
  },
  {
    key: '3',
    label: 'Billing',
    extra: '⌘B',
  },
  {
    key: '4',
    label: 'Settings',
    icon: <SettingOutlined />,
    extra: '⌘S',
  },
];

const App: React.FC = () => (
  <Dropdown menu={{ items }}>
    <a onClick={(e) => e.preventDefault()}>
      <Space>
        Hover me
        <DownOutlined />
      </Space>
    </a>
  </Dropdown>
);

export default App;

위치 (Placement)

12가지 위치를 지원해요.

import React from 'react';
import type { MenuProps } from 'antd';
import { Button, Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    key: '1',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
        1st menu item
      </a>
    ),
  },
  {
    key: '2',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
        2nd menu item
      </a>
    ),
  },
  {
    key: '3',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
        3rd menu item
      </a>
    ),
  },
];

const App: React.FC = () => (
  <Space vertical>
    <Space wrap>
      <Dropdown menu={{ items }} placement="bottomLeft">
        <Button>bottomLeft</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="bottom">
        <Button>bottom</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="bottomRight">
        <Button>bottomRight</Button>
      </Dropdown>
    </Space>
    <Space wrap>
      <Dropdown menu={{ items }} placement="topLeft">
        <Button>topLeft</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="top">
        <Button>top</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="topRight">
        <Button>topRight</Button>
      </Dropdown>
    </Space>
    <Space wrap>
      <Dropdown menu={{ items }} placement="leftTop">
        <Button>leftTop</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="left">
        <Button>left</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="leftBottom">
        <Button>leftBottom</Button>
      </Dropdown>
    </Space>
    <Space wrap>
      <Dropdown menu={{ items }} placement="rightTop">
        <Button>rightTop</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="right">
        <Button>right</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="rightBottom">
        <Button>rightBottom</Button>
      </Dropdown>
    </Space>
  </Space>
);

export default App;

화살표 (Arrow)

화살표를 표시할 수 있어요.

import React from 'react';
import type { MenuProps } from 'antd';
import { Button, Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    key: '1',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
        1st menu item
      </a>
    ),
  },
  {
    key: '2',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
        2nd menu item
      </a>
    ),
  },
  {
    key: '3',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
        3rd menu item
      </a>
    ),
  },
];

const App: React.FC = () => (
  <Space vertical>
    <Space wrap>
      <Dropdown menu={{ items }} placement="bottomLeft" arrow>
        <Button>bottomLeft</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="bottom" arrow>
        <Button>bottom</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="bottomRight" arrow>
        <Button>bottomRight</Button>
      </Dropdown>
    </Space>
    <Space wrap>
      <Dropdown menu={{ items }} placement="topLeft" arrow>
        <Button>topLeft</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="top" arrow>
        <Button>top</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="topRight" arrow>
        <Button>topRight</Button>
      </Dropdown>
    </Space>
  </Space>
);

export default App;

다른 요소 (Other elements)

구분선과 비활성화된 메뉴 항목이에요.

import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
        1st menu item
      </a>
    ),
    key: '0',
  },
  {
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
        2nd menu item
      </a>
    ),
    key: '1',
  },
  {
    type: 'divider',
  },
  {
    label: '3rd menu item(disabled)',
    key: '3',
    disabled: true,
  },
];

const App: React.FC = () => (
  <Dropdown menu={{ items }}>
    <a onClick={(e) => e.preventDefault()}>
      <Space>
        Hover me
        <DownOutlined />
      </Space>
    </a>
  </Dropdown>
);

export default App;

중앙을 가리키는 화살표 (Arrow pointing at the center)

arrow prop을 { pointAtCenter: true }로 지정하면 화살표가 타겟 요소의 중앙을 가리켜요.

import React from 'react';
import type { MenuProps } from 'antd';
import { Button, Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    key: '1',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
        1st menu item
      </a>
    ),
  },
  {
    key: '2',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
        2nd menu item
      </a>
    ),
  },
  {
    key: '3',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
        3rd menu item
      </a>
    ),
  },
];

const App: React.FC = () => (
  <Space vertical>
    <Space wrap>
      <Dropdown menu={{ items }} placement="bottomLeft" arrow={{ pointAtCenter: true }}>
        <Button>bottomLeft</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="bottom" arrow={{ pointAtCenter: true }}>
        <Button>bottom</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="bottomRight" arrow={{ pointAtCenter: true }}>
        <Button>bottomRight</Button>
      </Dropdown>
    </Space>
    <Space wrap>
      <Dropdown menu={{ items }} placement="topLeft" arrow={{ pointAtCenter: true }}>
        <Button>topLeft</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="top" arrow={{ pointAtCenter: true }}>
        <Button>top</Button>
      </Dropdown>
      <Dropdown menu={{ items }} placement="topRight" arrow={{ pointAtCenter: true }}>
        <Button>topRight</Button>
      </Dropdown>
    </Space>
  </Space>
);

export default App;

트리거 모드 (Trigger mode)

기본 트리거 모드는 hover이고, click으로 변경할 수 있어요.

import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    label: (
      <a href="https://www.antgroup.com" target="_blank" rel="noopener noreferrer">
        1st menu item
      </a>
    ),
    key: '0',
  },
  {
    label: (
      <a href="https://www.aliyun.com" target="_blank" rel="noopener noreferrer">
        2nd menu item
      </a>
    ),
    key: '1',
  },
  {
    type: 'divider',
  },
  {
    label: '3rd menu item',
    key: '3',
  },
];

const App: React.FC = () => (
  <Dropdown menu={{ items }} trigger={['click']}>
    <a onClick={(e) => e.preventDefault()}>
      <Space>
        Click me
        <DownOutlined />
      </Space>
    </a>
  </Dropdown>
);

export default App;

클릭 이벤트 (Click event)

메뉴 항목을 클릭하면 이벤트가 트리거되는데, 항목의 key에 따라 서로 다른 작업을 수행할 수 있어요.

import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, message, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    label: '1st menu item',
    key: '1',
  },
  {
    label: '2nd menu item',
    key: '2',
  },
  {
    label: '3rd menu item',
    key: '3',
  },
];

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const onClick: MenuProps['onClick'] = ({ key }) => {
    messageApi.info(`Click on item ${key}`);
  };

  return (
    <>
      {contextHolder}
      <Dropdown menu={{ items, onClick }}>
        <a onClick={(e) => e.preventDefault()}>
          <Space>
            Hover me, Click menu item
            <DownOutlined />
          </Space>
        </a>
      </Dropdown>
    </>
  );
};

export default App;

드롭다운 메뉴가 있는 버튼 (Button with dropdown menu)

왼쪽에 버튼이, 오른쪽에 관련 기능 메뉴가 있어요. icon 속성으로 오른쪽 아이콘을 변경할 수 있어요.

import React from 'react';
import { DownOutlined, EllipsisOutlined, UserOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Button, Dropdown, message, Space, Tooltip } from 'antd';

const items: MenuProps['items'] = [
  {
    label: '1st menu item',
    key: '1',
    icon: <UserOutlined />,
  },
  {
    label: '2nd menu item',
    key: '2',
    icon: <UserOutlined />,
  },
  {
    label: '3rd menu item',
    key: '3',
    icon: <UserOutlined />,
    danger: true,
  },
  {
    label: '4rd menu item',
    key: '4',
    icon: <UserOutlined />,
    danger: true,
    disabled: true,
  },
];

const App: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const handleButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    messageApi.info('Click on left button.');
    console.log('click left button', e);
  };

  const handleMenuClick: MenuProps['onClick'] = (e) => {
    messageApi.info('Click on menu item.');
    console.log('click', e);
  };

  const menuProps = {
    items,
    onClick: handleMenuClick,
  };

  return (
    <>
      {contextHolder}
      <Space wrap>
        <Space.Compact>
          <Button onClick={handleButtonClick}>Dropdown</Button>
          <Dropdown menu={menuProps} placement="bottomRight">
            <Button icon={<EllipsisOutlined />} />
          </Dropdown>
        </Space.Compact>
        <Space.Compact>
          <Button onClick={handleButtonClick}>Dropdown</Button>
          <Dropdown menu={menuProps} placement="bottomRight">
            <Button icon={<UserOutlined />} />
          </Dropdown>
        </Space.Compact>
        <Space.Compact>
          <Button onClick={handleButtonClick} disabled>
            Dropdown
          </Button>
          <Dropdown menu={menuProps} placement="bottomRight" disabled>
            <Button icon={<EllipsisOutlined />} disabled />
          </Dropdown>
        </Space.Compact>
        <Space.Compact>
          <Tooltip title="tooltip">
            <Button onClick={handleButtonClick}>With Tooltip</Button>
          </Tooltip>
          <Dropdown menu={menuProps} placement="bottomRight">
            <Button loading />
          </Dropdown>
        </Space.Compact>
        <Dropdown menu={menuProps}>
          <Button onClick={handleButtonClick} icon={<DownOutlined />} iconPlacement="end">
            Button
          </Button>
        </Dropdown>
        <Space.Compact>
          <Button onClick={handleButtonClick} danger>
            Danger
          </Button>
          <Dropdown menu={menuProps} placement="bottomRight">
            <Button icon={<EllipsisOutlined />} danger />
          </Dropdown>
        </Space.Compact>
      </Space>
    </>
  );
};

export default App;

커스텀 드롭다운 (Custom dropdown)

popupRender를 통해 드롭다운 메뉴를 커스터마이즈할 수 있어요. Menu 콘텐츠가 필요 없다면 Popover 컴포넌트를 직접 사용하세요.

import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import { Button, Divider, Dropdown, Space, theme } from 'antd';
import type { MenuProps } from 'antd';

const { useToken } = theme;

const items: MenuProps['items'] = [
  {
    key: '1',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
        1st menu item
      </a>
    ),
  },
  {
    key: '2',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
        2nd menu item (disabled)
      </a>
    ),
    disabled: true,
  },
  {
    key: '3',
    label: (
      <a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
        3rd menu item (disabled)
      </a>
    ),
    disabled: true,
  },
];

const App: React.FC = () => {
  const { token } = useToken();

  const contentStyle: React.CSSProperties = {
    backgroundColor: token.colorBgElevated,
    borderRadius: token.borderRadiusLG,
    boxShadow: token.boxShadowSecondary,
  };

  const menuStyle: React.CSSProperties = {
    boxShadow: 'none',
  };

  return (
    <Dropdown
      menu={{ items }}
      popupRender={(menu) => (
        <div style={contentStyle}>
          {React.cloneElement(
            menu as React.ReactElement<{
              style: React.CSSProperties;
            }>,
            { style: menuStyle },
          )}
          <Divider style={{ margin: 0 }} />
          <Space style={{ padding: 8 }}>
            <Button type="primary">Click me!</Button>
          </Space>
        </div>
      )}
    >
      <a onClick={(e) => e.preventDefault()}>
        <Space>
          Hover me
          <DownOutlined />
        </Space>
      </a>
    </Dropdown>
  );
};

export default App;

캐스케이딩 메뉴 (Cascading menu)

메뉴가 여러 레벨로 구성된 경우예요.

import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    key: '1',
    type: 'group',
    label: 'Group title',
    children: [
      {
        key: '1-1',
        label: '1st menu item',
      },
      {
        key: '1-2',
        label: '2nd menu item',
      },
    ],
  },
  {
    key: '2',
    label: 'sub menu',
    children: [
      {
        key: '2-1',
        label: '3rd menu item',
      },
      {
        key: '2-2',
        label: '4th menu item',
      },
    ],
  },
  {
    key: '3',
    label: 'disabled sub menu',
    disabled: true,
    children: [
      {
        key: '3-1',
        label: '5d menu item',
      },
      {
        key: '3-2',
        label: '6th menu item',
      },
    ],
  },
];

const App: React.FC = () => (
  <Dropdown menu={{ items }}>
    <a onClick={(e) => e.preventDefault()}>
      <Space>
        Cascading menu
        <DownOutlined />
      </Space>
    </a>
  </Dropdown>
);

export default App;

메뉴 숨김 방식 (The way of hiding menu)

기본적으로 메뉴 항목을 클릭하면 메뉴가 닫혀요. 이 기능을 끌 수 있어요.

import React, { useState } from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { DropdownProps, MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);

  const handleMenuClick: MenuProps['onClick'] = (e) => {
    if (e.key === '3') {
      setOpen(false);
    }
  };

  const handleOpenChange: DropdownProps['onOpenChange'] = (nextOpen, info) => {
    if (info.source === 'trigger' || nextOpen) {
      setOpen(nextOpen);
    }
  };

  const items: MenuProps['items'] = [
    {
      label: 'Clicking me will not close the menu.',
      key: '1',
    },
    {
      label: 'Clicking me will not close the menu also.',
      key: '2',
    },
    {
      label: 'Clicking me will close the menu.',
      key: '3',
    },
  ];

  return (
    <Dropdown
      menu={{
        items,
        onClick: handleMenuClick,
      }}
      onOpenChange={handleOpenChange}
      open={open}
    >
      <a onClick={(e) => e.preventDefault()}>
        <Space>
          Hover me
          <DownOutlined />
        </Space>
      </a>
    </Dropdown>
  );
};

export default App;

컨텍스트 메뉴 (Context Menu)

기본 트리거 모드는 hover이고, contextMenu로 변경할 수 있어요. 팝업 메뉴 위치는 오른쪽 클릭 위치를 따라가요.

import React from 'react';
import type { MenuProps } from 'antd';
import { Dropdown, theme } from 'antd';

const items: MenuProps['items'] = [
  {
    label: '1st menu item',
    key: '1',
  },
  {
    label: '2nd menu item',
    key: '2',
  },
  {
    label: '3rd menu item',
    key: '3',
  },
];

const App: React.FC = () => {
  const {
    token: { colorBgLayout, colorTextTertiary },
  } = theme.useToken();

  return (
    <Dropdown menu={{ items }} trigger={['contextMenu']}>
      <div
        style={{
          color: colorTextTertiary,
          background: colorBgLayout,
          height: 200,
          textAlign: 'center',
          lineHeight: '200px',
        }}
      >
        Right Click on here
      </div>
    </Dropdown>
  );
};

export default App;

로딩 (Loading)

loading 속성을 설정해 버튼에 로딩 표시를 추가할 수 있어요.

import React, { useState } from 'react';
import { DownOutlined, EllipsisOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Button, Dropdown, Space } from 'antd';

const items: MenuProps['items'] = [
  {
    label: 'Submit and continue',
    key: '1',
  },
];

const App: React.FC = () => {
  const [loadings, setLoadings] = useState<boolean[]>([]);

  const enterLoading = (index: number) => {
    setLoadings((state) => {
      const newLoadings = [...state];
      newLoadings[index] = true;
      return newLoadings;
    });

    setTimeout(() => {
      setLoadings((state) => {
        const newLoadings = [...state];
        newLoadings[index] = false;
        return newLoadings;
      });
    }, 6000);
  };

  return (
    <Space vertical>
      <Space.Compact>
        <Button type="primary" loading>
          Submit
        </Button>
        <Dropdown menu={{ items }}>
          <Button type="primary" icon={<EllipsisOutlined />} />
        </Dropdown>
      </Space.Compact>
      <Space.Compact size="small">
        <Button type="primary" loading>
          Submit
        </Button>
        <Dropdown menu={{ items }}>
          <Button type="primary" icon={<EllipsisOutlined />} />
        </Dropdown>
      </Space.Compact>
      <Space.Compact>
        <Button type="primary" loading={loadings[0]} onClick={() => enterLoading(0)}>
          Submit
        </Button>
        <Dropdown menu={{ items }}>
          <Button type="primary" icon={<EllipsisOutlined />} />
        </Dropdown>
      </Space.Compact>
      <Space.Compact>
        <Button loading={loadings[1]} onClick={() => enterLoading(1)}>
          Submit
        </Button>
        <Dropdown menu={{ items }}>
          <Button icon={<DownOutlined />} />
        </Dropdown>
      </Space.Compact>
    </Space>
  );
};

export default App;

선택 가능한 메뉴 (Selectable Menu)

menu에서 selectable 속성을 설정해 선택 기능을 활성화할 수 있어요.

import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space, Typography } from 'antd';

const items: MenuProps['items'] = [
  {
    key: '1',
    label: 'Item 1',
  },
  {
    key: '2',
    label: 'Item 2',
  },
  {
    key: '3',
    label: 'Item 3',
  },
];

const App: React.FC = () => (
  <Dropdown
    menu={{
      items,
      selectable: true,
      defaultSelectedKeys: ['3'],
    }}
  >
    <Typography.Link>
      <Space>
        Selectable
        <DownOutlined />
      </Space>
    </Typography.Link>
  </Dropdown>
);

export default App;

선택 동작 (Selection actions)

브라우저의 Selection API와 함께 Dropdown을 사용해 텍스트를 선택한 뒤 커스텀 동작을 보여 줄 수 있어요.

import React, { useRef, useState } from 'react';
import type { MenuProps } from 'antd';
import { Dropdown, message } from 'antd';
import { createStyles } from 'antd-style';
import type { ItemType } from 'antd/es/menu/interface';

interface SelectionInfo {
  text: string;
  x: number;
  y: number;
}

const labels: Record<string, string> = {
  mask: 'Mask keyword',
  mark: 'Mark keyword',
  search: 'Search keyword',
};

const useStyle = createStyles(({ cssVar, css }) => {
  const { colorText, colorBgLayout, borderRadiusLG, paddingLG } = cssVar;
  return {
    wrapper: css`
      padding: ${paddingLG};
      user-select: text;
      color: ${colorText};
      background-color: ${colorBgLayout};
      border-radius: ${borderRadiusLG};
    `,
    trigger: css`
      position: fixed;
      display: block;
      width: 1px;
      height: 1px;
      margin: 0;
      padding: 0;
      pointer-events: none;
    `,
  };
});

const items = Object.entries(labels).map<ItemType>(([key, label]) => ({ key, label }));

const Demo: React.FC = () => {
  const [messageApi, contextHolder] = message.useMessage();

  const { styles } = useStyle();

  const wrapperRef = useRef<HTMLDivElement>(null);

  const [selection, setSelection] = useState<SelectionInfo | null>(null);

  const handleSelect = () => {
    const selectionInstance = window.getSelection();
    const selectedText = selectionInstance?.toString().trim();

    if (!selectionInstance || !selectedText || selectionInstance.rangeCount === 0) {
      setSelection(null);
      return;
    }

    const range = selectionInstance.getRangeAt(0);

    if (!wrapperRef.current?.contains(range.commonAncestorContainer)) {
      setSelection(null);
      return;
    }

    const rect = range.getBoundingClientRect();

    if (!rect.width || !rect.height) {
      setSelection(null);
      return;
    }

    setSelection({
      text: selectedText,
      x: rect.left + rect.width / 2,
      y: rect.bottom + 4,
    });
  };

  const handleMouseUp: React.MouseEventHandler<HTMLDivElement> = () => {
    setTimeout(handleSelect);
  };

  const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
    if (!selection) {
      return;
    }
    messageApi.info(`${labels[key]}: ${selection.text}`);
    window.getSelection()?.removeAllRanges();
    setSelection(null);
  };

  return (
    <>
      {contextHolder}
      <Dropdown
        menu={{ items, onClick: handleMenuClick }}
        open={Boolean(selection)}
        placement="bottom"
        trigger={[]}
        onOpenChange={(nextOpen) => {
          if (!nextOpen) {
            setSelection(null);
          }
        }}
      >
        <span
          aria-hidden
          className={styles.trigger}
          style={{
            left: selection?.x ?? -9999,
            top: selection?.y ?? -9999,
          }}
        />
      </Dropdown>
      <div
        ref={wrapperRef}
        onMouseDown={() => setSelection(null)}
        onMouseUp={handleMouseUp}
        className={styles.wrapper}
      >
        Select any text in this paragraph to open a Dropdown menu near the selection. This is useful
        for actions such as masking sensitive words, marking entities, or searching the selected
        keyword. Example data: Alice, phone 13800138000, ID 110101199001011234.
      </div>
    </>
  );
};

export default Demo;

커스텀 시맨틱 DOM 스타일 (Custom semantic dom styling)

classNames와 styles로 객체/함수를 넘겨 Dropdown의 시맨틱 DOM 스타일을 커스터마이즈할 수 있어요.

import React from 'react';
import { DownOutlined, LogoutOutlined, SettingOutlined } from '@ant-design/icons';
import { Button, Dropdown, Flex, Space } from 'antd';
import type { DropdownProps, GetProp, MenuProps } from 'antd';
import { createStyles } from 'antd-style';

const useStyles = createStyles(({ token }) => ({
  root: {
    backgroundColor: token.colorFillAlter,
    border: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`,
    borderRadius: token.borderRadius,
  },
}));

const items: MenuProps['items'] = [
  {
    key: '1',
    label: 'Profile',
  },
  {
    key: '2',
    label: 'Settings',
    icon: <SettingOutlined />,
  },
  {
    type: 'divider',
  },
  {
    key: '3',
    label: 'Logout',
    icon: <LogoutOutlined />,
    danger: true,
  },
];

const objectStyles: DropdownProps['styles'] = {
  root: {
    backgroundColor: '#fff',
    border: '1px solid #d9d9d9',
    borderRadius: '4px',
  },
  item: {
    padding: '8px 12px',
    fontSize: '14px',
  },
  itemTitle: {
    fontWeight: '500',
  },
  itemIcon: {
    color: '#1890ff',
    marginInlineEnd: '8px',
  },
  itemContent: {
    backgroundColor: 'transparent',
  },
};

const functionStyles: DropdownProps['styles'] = (
  info,
): GetProp<DropdownProps, 'styles', 'Return'> => {
  const { props } = info;
  const isClick = props.trigger?.includes('click');
  if (isClick) {
    return {
      root: {
        borderColor: '#1890ff',
        borderRadius: '8px',
      },
    };
  }
  return {};
};

const App: React.FC = () => {
  const { styles } = useStyles();

  const sharedProps: DropdownProps = {
    menu: { items },
    placement: 'bottomLeft',
    classNames: { root: styles.root },
  };

  return (
    <Flex gap="medium" wrap="wrap">
      <Space vertical size="large">
        <Dropdown {...sharedProps} styles={objectStyles}>
          <Button>
            <Space>
              Object Style
              <DownOutlined />
            </Space>
          </Button>
        </Dropdown>

        <Dropdown {...sharedProps} styles={functionStyles} trigger={['click']}>
          <Button type="primary">
            <Space>
              Function Style
              <DownOutlined />
            </Space>
          </Button>
        </Dropdown>
      </Space>
    </Flex>
  );
};

export default App;

API

공통 props 참조: Common props

속성 설명 타입 기본값 버전 전역 설정
arrow 드롭다운 화살표 표시 여부 boolean | { pointAtCenter: boolean } false ×
autoAdjustOverflow 드롭다운이 화면 밖일 때 위치를 자동으로 조정할지 여부 boolean true 5.2.0 ×
classNames Dropdown 컴포넌트 내 각 시맨틱 구조에 대한 클래스를 커스터마이즈. 객체 또는 함수를 지원 Record<SemanticDOM, string> | (info: { props }) => Record<SemanticDOM, string> - 6.0.0
disabled 드롭다운 메뉴 비활성화 여부 boolean - ×
destroyPopupOnHide 숨길 때 드롭다운을 파괴할지 여부, destroyOnHidden을 대신 사용 boolean false ×
destroyOnHidden 숨길 때 드롭다운을 파괴할지 여부 boolean false 5.25.0 ×
dropdownRender 드롭다운 콘텐츠 커스터마이즈, popupRender를 대신 사용 (menus: ReactNode) => ReactNode - 4.24.0 ×
popupRender 팝업 콘텐츠 커스터마이즈 (menus: ReactNode) => ReactNode - 5.25.0 ×
getPopupContainer 드롭다운 메뉴의 컨테이너 설정. 기본은 body에 div를 만들지만, 스크롤 영역으로 재설정하고 상대 재배치할 수 있음. CodePen 예시 (triggerNode: HTMLElement) => HTMLElement () => document.body ×
menu 메뉴 props MenuProps - ×
overlayClassName 드롭다운 루트 요소의 클래스 이름, classNames.root를 대신 사용 string - ×
overlayStyle 드롭다운 루트 요소의 스타일, styles.root를 대신 사용 CSSProperties - ×
placement 팝업 메뉴 위치: top topLeft topRight bottom bottomLeft bottomRight left leftTop leftBottom right rightTop rightBottom string bottomLeft left leftTop leftBottom right rightTop rightBottom: 6.5.0 ×
styles Dropdown 컴포넌트 내 각 시맨틱 구조에 대한 인라인 스타일을 커스터마이즈. 객체 또는 함수를 지원 Record<SemanticDOM, CSSProperties> | (info: { props }) => Record<SemanticDOM, CSSProperties> - 6.0.0
trigger 드롭다운 동작을 실행하는 트리거 모드. 참고로 호버는 터치스크린에서 사용할 수 없음 Array<click|hover|contextMenu> [hover] ×
open 드롭다운 메뉴가 현재 열려 있는지 여부 boolean - ×
onOpenChange 열림 상태가 변경될 때 호출됨. 항목 클릭으로 숨길 때는 트리거되지 않음 (open: boolean, info: { source: 'trigger' | 'menu' }) => void - info.source: 5.11.0 ×

참고 (Note)

Dropdown의 하위 노드가 onMouseEnter, onMouseLeave, onFocus, onClick 이벤트를 받을 수 있도록 해 주세요.

Semantic DOM

https://ant.design/components/dropdown/semantic.md

Design Token

Component Token (Dropdown)

토큰 이름 설명 타입 기본값
paddingBlock dropdown의 세로 패딩 PaddingBlock<string | number> | undefined 5
zIndexPopup dropdown의 z-index number 1050

Global Token

토큰 이름 설명 타입 기본값
borderRadiusLG LG 크기의 테두리 반경. Card, Modal 등 큰 테두리 반경 컴포넌트에서 사용 number
borderRadiusSM SM 크기의 테두리 반경. 작은 크기 입력 컴포넌트(Button, Input, Select 등)에서 사용 number
borderRadiusXS XS 크기의 테두리 반경. Segmented, Arrow 등 작은 테두리 반경 컴포넌트에서 사용 number
boxShadowSecondary 요소의 2차 박스 섀도 스타일 제어 string
colorBgElevated 팝업 레이어의 컨테이너 배경색. 다크 모드에서는 colorBgContainer보다 밝음. 예: modal, pop-up, menu 등 string
colorError 작업 실패를 나타내는 시각 요소. 예: error Button, error Result 컴포넌트 등 string
colorIcon 약한 동작. allowClear나 Alert 닫기 버튼 등 string
colorPrimary 브랜드 색상은 제품의 특성과 커뮤니케이션을 반영하는 가장 직접적인 시각 요소 중 하나 string
colorPrimaryBorder 메인 색 그라데이션의 획 색상. Slider 등 컴포넌트의 획에 사용 string
colorSplit 구분선의 색상으로 사용. colorBorderSecondary와 같지만 투명도를 가짐 string
colorText W3C 표준을 따르는 기본 텍스트 색상. 가장 어두운 중립 색 string
colorTextDescription 텍스트 설명의 글꼴 색상 제어 string
colorTextDisabled 비활성 상태 텍스트의 색상 제어 string
colorTextLightSolid 배경색이 있는 텍스트의 하이라이트 색상 제어. 예: Primary Button 컴포넌트의 텍스트 string
controlHeightLG LG 컴포넌트 높이 number
controlItemBgActive 활성 상태일 때 제어 컴포넌트 항목의 배경색 제어 string
controlItemBgActiveHover 호버하면서 활성 상태일 때 제어 컴포넌트 항목의 배경색 제어 string
controlItemBgHover 호버 시 제어 컴포넌트 항목의 배경색 제어 string
controlPaddingHorizontal 요소의 가로 패딩 제어 number
fontFamily Ant Design의 글꼴 패밀리는 시스템 기본 인터페이스 글꼴을 우선하며, 화면 표시에 적합한 대체 글꼴 라이브러리를 제공 string
fontSize 디자인 시스템에서 가장 널리 쓰이는 글꼴 크기. 텍스트 그라데이션이 여기서 파생됨 number
fontSizeIcon Select, Cascader 등의 동작 아이콘 글꼴 크기 제어. 보통 fontSizeSM과 같음 number
fontSizeSM 작은 글꼴 크기 number
lineHeight 텍스트의 줄 높이 number
lineWidthFocus 컴포넌트가 포커스 상태일 때 라인의 너비 제어 number
marginXS 요소의 여백을 작은 크기로 제어 number
marginXXS 요소의 여백을 가장 작은 크기로 제어 number
motionDurationMid 중간 속도 모션. 중간 요소 애니메이션 상호작용에 사용 string
motionEaseInOutCirc 사전 설정된 모션 커브 string
motionEaseInQuint 사전 설정된 모션 커브 string
motionEaseOutCirc 사전 설정된 모션 커브 string
motionEaseOutQuint 사전 설정된 모션 커브 string
padding 요소의 패딩 제어 number
paddingXS 요소의 매우 작은 패딩 제어 number
paddingXXS 요소의 아주 아주 작은 패딩 제어 number
sizePopupArrow 컴포넌트 화살표의 크기 number

FAQ

width: max-content 스타일을 사용해 처리할 수 있어요. #43025 참고.

더 알아보기 (Learn more)