Raft 设计系统

组件 · 浮层

DropdownMenu

A list of actions on one thing, opened from a button, that can also hold a setting-style choice with a check mark on the current value.

稳定也叫 Menu
import { DropdownMenu } from "@botiverse/raft-ui";
本页尚未翻译
译文完成之前,这里显示英文原文。 阅读英文版

A dropdown menu is a list of actions on one thing (a message, a channel, a task, an agent), opened from a button such as ⋯. It can also hold a setting-style choice, such as sort order or status, with a check mark on the current value. Right-click on desktop and press and hold on a phone open the same menu as a ContextMenu.

dropdown-menu/compositions/sidebar-section-header-menu

When to use

SituationUse
Act on one thing, from a buttonDropdownMenu
The same actions, on right-click or press and holdContextMenu, with the same items
A setting-style choice that applies at once (sort, status)DropdownMenu with radio items
A value submitted with a formSelect
A little input on click (snooze, rename)Popover
A place to go, on a phoneA bottom sheet (Drawer)

Which overlay fits which job is in Choosing a component.

Anatomy

  • DropdownMenu: the root. Owns the open state. Required.
  • DropdownMenuTrigger: the button that opens the menu. Renders an outline Button unless you pass render. Required.
  • DropdownMenuContent: the menu, portaled and positioned next to the trigger, at least 240px wide. Required.
  • DropdownMenuItem: one action. A 16px icon, then the label. Wrap the label in DropdownMenuItemLabel to truncate it.
  • DropdownMenuShortcut: the keyboard shortcut at the end of an item, in text-mono-meta and foreground-hint.
  • DropdownMenuItemCount: a count at the end of an item.
  • DropdownMenuRadioGroup and DropdownMenuRadioItem: a set of values with one current value, marked with a check mark.
  • DropdownMenuCheckboxItem: an option that is on or off by itself.
  • DropdownMenuLabel: a heading over a group of items. Not interactive.
  • DropdownMenuSeparator: the line between sections.
  • DropdownMenuSubmenu and DropdownMenuSubmenuTrigger: a nested menu, opened from an item with a chevron.
  • DropdownMenuFooter: a line of quiet text at the bottom, such as a count.
  • DropdownMenuPortal and DropdownMenuPopup: lower-level parts. DropdownMenuPopup is the menu surface without positioning.

States

Hover, keyboard highlight, the current value and disabled items follow States. In a menu:

  • Keep the highlight and the current value apart. The brand-soft highlight shows where the pointer or keyboard is; the check mark and bold text show which value is chosen. Never use the highlight to mark the current value.
  • Show a disabled item with foreground-disabled text and icon, and nothing else.

Behavior

  • The menu opens below its trigger, aligned to the trigger's start edge, and moves or flips when it meets the window edge (Elevation).
  • Choosing an action closes the menu. Choosing a radio or checkbox item leaves it open, so people can see the result.
  • Esc and a click outside close the menu.
  • An item is as tall as its content: one line is 36px, and a second line, such as a description under the label, adds only its own height, with the same space above and below the text (Forms). This holds for radio and checkbox items too.

Content

  • Write item labels in Title Case: Copy Link, Reply in Thread (Writing).
  • Make the label of an item that opens a dialog the dialog's title (Writing).

Accessibility

  • DropdownMenu wraps the Base UI Menu. It provides the menu role, the item roles, arrow-key movement, typeahead, Esc, and focus return to the trigger.
  • Name the menu after the thing it acts on: put aria-label on DropdownMenuContent ("Message from Wug").
  • Give an icon-only trigger, such as ⋯, an aria-label ("More Actions").
  • Make every row in the menu a menu item, so arrow keys reach it. This includes each quick reaction: give it an aria-label ("React with thumbs up") and hide the drawing from assistive technology.
  • Leave item icons decorative; the label names the action.

Rules

  • Don't color destructive items such as Delete or Stop. They open an AlertDialog, whose orange or red button carries the consequence (Feedback).
  • Use the same separator between every two sections of a menu.
  • Don't put a control that submits a form value in a menu; use a Select.

The message menu

The message menu starts with the quick reactions (Conversation; sizes in Brand).

  • End the row of quick reactions with a smile-plus icon button that opens every emoji.
  • Put one separator between the reactions and the first action, then group the actions into sections.

API

Prop类型默认值
actionsRefA ref to imperative actions. - `unmount`: Manually unmounts the menu. Call this after any externally controlled closing animation finishes. - `close`: When specified, the menu can be closed imperatively.RefObject<MenuRootActions | null>
childrenThe content of the menu. This can be a regular React node or a render function that receives the `payload` of the active trigger.ReactNode | PayloadChildRenderFunction<unknown>
closeParentOnEscWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu.booleanfalse
defaultOpenWhether the menu is initially open.booleanfalse
defaultTriggerIdID of the trigger that the menu is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open menu.string
disabledWhether the component should ignore user interaction.booleanfalse
handleA handle to associate the menu with a trigger. If specified, allows external triggers to control the menu's open state.BaseMenu.Handle<unknown>
highlightItemOnHoverWhether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.booleantrue
loopFocusWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys.booleantrue
modalDetermines if the menu enters a modal state when open. - `true`: user interaction is limited to the menu: document page scroll is locked and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed.booleantrue
onOpenChangeEvent handler called when the menu is opened or closed.(open: boolean, eventDetails: BaseMenu.Root.ChangeEventDetails) => void
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed.(open: boolean) => void
openWhether the menu is currently open.boolean
orientationThe visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys.MenuRootOrientation'vertical'
triggerIdID of the trigger that the menu is associated with. This is useful in conjunction with the `open` prop to create a controlled menu. There's no need to specify this prop when the menu is uncontrolled (that is, when the `open` prop is not set).string
Prop类型默认值
checkedWhether the checkbox item is currently ticked.boolean
closeOnClickWhether to close the menu when the item is clicked.booleanfalse
defaultCheckedWhether the checkbox item is initially ticked.booleanfalse
disabledWhether the component should ignore user interaction.booleanfalse
idstring
labelOverrides the text label to use when the item is matched during keyboard text navigation.string
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button.booleanfalse
onCheckedChangeEvent handler called when the checkbox item is ticked or unticked.(checked: boolean, eventDetails: BaseMenu.CheckboxItem.ChangeEventDetails) => void
onClickThe click handler for the menu item.(event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

Prop类型默认值
keepMountedWhether to keep the HTML element in the DOM when the checkbox item is not checked.booleanfalse
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

Prop类型默认值
portalPropsContextMenuPortalProps
alignHow to align the popup relative to the specified side.Align"start"
alignOffsetAdditional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment.number | OffsetFunction0
anchorAn element to position the popup against. By default, the popup will be positioned against the trigger.Element | VirtualElement | RefObject<Element | null> | (() => Element | VirtualElement | null)
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup.number5
childrenReactNode
collisionAvoidanceDetermines how to handle collisions when positioning the popup.CollisionAvoidance
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to.Boundary'clipping-ancestors'
collisionPaddingAdditional space to maintain from the edge of the collision boundary.Padding5
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor.booleanfalse
finalFocusDetermines the element to focus when the menu is closed.boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | HTMLElement | null | void)
idstring
positionMethodDetermines which CSS `position` property to use."fixed" | "absolute""fixed"
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)
sideWhich side of the anchor element to align the popup against. May automatically change to avoid collisions.Side"bottom"
sideOffsetDistance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment.number | OffsetFunction0
stickyWhether to maintain the popup in the viewport after the anchor element was scrolled out of view.booleanfalse

Also takes the attributes of its element.

接受其元素的全部属性。

Prop类型默认值
closeOnClickWhether to close the menu when the item is clicked.booleantrue
disabledWhether the component should ignore user interaction.booleanfalse
idstring
labelOverrides the text label to use when the item is matched during keyboard text navigation.string
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button.booleanfalse
onClickThe click handler for the menu item.(event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

Prop类型默认值
appearance"outline" | "solid" | "soft"
variant"primary" | "accent" | "warning" | "danger" | "default" | "information" | "muted" | "success"
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

接受其元素的全部属性。

Prop类型默认值
containerA parent element to render the portal element into.HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null>
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden.booleanfalse
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

Prop类型默认值
childrenThe content of the component.ReactNode
defaultValueThe uncontrolled value of the radio item that should be initially selected.any
disabledWhether the component should ignore user interaction.booleanfalse
onValueChangeFunction called when the selected value changes.(value: any, eventDetails: BaseMenu.RadioGroup.ChangeEventDetails) => void
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)
valueThe controlled value of the radio item that should be currently selected.any

Also takes the attributes of its element.

Prop类型默认值
closeOnClickWhether to close the menu when the item is clicked.booleanfalse
disabledWhether the component should ignore user interaction.booleanfalse
idstring
labelOverrides the text label to use when the item is matched during keyboard text navigation.string
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button.booleanfalse
onClickThe click handler for the menu item.(event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)
value (required)Value of the radio item. This is the value that will be set in the MenuRadioGroup when the item is selected.any

Also takes the attributes of its element.

Prop类型默认值
keepMountedWhether to keep the HTML element in the DOM when the radio item is inactive.booleanfalse
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

Prop类型默认值
orientationThe orientation of the separator.Orientation'horizontal'
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

接受其元素的全部属性。

Prop类型默认值
actionsRefA ref to imperative actions. - `unmount`: Manually unmounts the menu. Call this after any externally controlled closing animation finishes. - `close`: When specified, the menu can be closed imperatively.RefObject<MenuRootActions | null>
childrenThe content of the submenu.ReactNode
closeParentOnEscWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu.booleanfalse
defaultOpenWhether the menu is initially open.booleanfalse
disabledWhether the component should ignore user interaction.booleanfalse
highlightItemOnHoverWhether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.booleantrue
loopFocusWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys.booleantrue
onOpenChangeEvent handler called when the menu is opened or closed.(open: boolean, eventDetails: BaseMenu.SubmenuRoot.ChangeEventDetails) => void
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed.(open: boolean) => void
openWhether the menu is currently open.boolean
orientationThe visual orientation of the menu. Controls whether roving focus uses up/down or left/right arrow keys.MenuRootOrientation'vertical'
Prop类型默认值
closeDelayHow long to wait before closing the menu that was opened on hover. Specified in milliseconds.number0
delayHow long to wait before the menu may be opened on hover. Specified in milliseconds.number100
disabledWhether the component should ignore user interaction.booleanfalse
idstring
labelOverrides the text label to use when the item is matched during keyboard text navigation.string
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button.booleanfalse
onClick(event: BaseUIEvent<MouseEvent<HTMLDivElement, MouseEvent>>) => void
openOnHoverWhether the menu should also open when the trigger is hovered.booleantrue
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)

Also takes the attributes of its element.

Prop类型默认值
childrenReactNode
closeDelayHow long to wait before closing the menu that was opened on hover. Specified in milliseconds.number0
delayHow long to wait before the menu may be opened on hover. Specified in milliseconds.number100
disabledWhether the component should ignore user interaction.booleanfalse
handleA handle to associate the trigger with a menu.BaseMenu.Handle<unknown>
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).booleantrue
openOnHoverWhether the menu should also open when the trigger is hovered.boolean
payloadA payload to pass to the menu when it is opened.{}
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.ReactElement | ((props, state) => ReactElement)<Button variant="outline" size="md" />

Also takes the attributes of its element.

改动

  • 0.0.20 · AddedAdded ContextMenuItemLabel, ContextMenuFooter, DropdownMenuItemLabel, and DropdownMenuFooter for truncatable menu labels and structured footer content.