Menu
A menu is a list of things to do. That is the whole difference from a ListBox, which holds a choice: picking a menu item fires it and the menu goes away. Most of the time a menu lives inside a Dropdown, which supplies the trigger and the popover around it.
import { Menu, MenuItem, MenuItemIndicator, MenuSection } from "ropav";Each MenuItem needs an id, which is what the action event carries, and a text-value for typeahead where the label is not plain text. MenuSection groups items; a Separator between sections draws the line.
A Description beside the Label makes the item two lines, with a leading icon and a trailing Kbd staying on the label's own line — wrap the pair yourself if you need a trailing element of another kind.
menu-basic.vue
<script setup lang="ts">
import { Label, Menu, MenuItem, Separator, Surface } from "ropav";
</script>
<template>
<Surface class="surface">
<Menu aria-label="File actions">
<MenuItem id="new" text-value="New file">
<Label>New file</Label>
</MenuItem>
<MenuItem id="copy" text-value="Copy link">
<Label>Copy link</Label>
</MenuItem>
<Separator />
<MenuItem id="delete" text-value="Delete file" variant="danger">
<Label>Delete file</Label>
</MenuItem>
</Menu>
</Surface>
</template>
<style scoped>
.surface {
width: calc(var(--rp-spacing) * 56);
padding: 0;
}
</style>variant="danger" on an item is the one place colour does work in a menu — and it still needs the word "Delete" beside it. The Label in the demo is for the type, not the colour.
When a menu holds a choice
selection-mode turns items into a checked set, for the view-style and sort-order menus that are really settings. MenuItemIndicator is the tick, and should-close-on-select is worth setting to false there: a menu of checkboxes that closes on the first tick makes the second one a chore.
MenuItemSubmenuIndicator is the arrow on an item that opens a submenu.
Accessibility
- The menu is a
role="menu"and each item arole="menuitem", so a reader is told they have entered a menu and how many things are in it. - Inside a dropdown the trigger names the menu through
aria-labelledby. A menu standing on its own needsaria-labelinstead. auto-focusdecides where focus lands when the menu opens: a selected item wins over the end you asked for, and with neither the menu takes focus itself.- Typeahead matches on the item's text. Where the label is an icon and a word,
text-valueis what makes the item reachable by typing.
API
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | Names the menu when it has no trigger to point ariaLabelledby at. |
ariaLabelledby | string | — | The id of the element naming the menu, normally its trigger. |
autoFocus | boolean | FocusStrategy | — | Where focus lands when the menu appears. A selected item wins over the end asked for, and with neither the menu itself takes focus. |
defaultSelectedKeys | "all" | Iterable<CollectionKey> | — | |
disabledBehavior | "all" | "selection" | "all" | |
disabledKeys | Iterable<CollectionKey> | — | |
disallowEmptySelection | boolean | — | |
id | string | — | Overrides the menu's id, which a trigger's aria-controls points at. |
selectedKeys | "all" | Iterable<CollectionKey> | — | |
selectionBehavior | "replace" | "toggle" | "toggle" | |
selectionMode | "none" | "single" | "multiple" | "none" | |
shouldCloseOnSelect | boolean | true | Whether choosing an item closes the menu. |
size | "xs" | "sm" | "md" | "lg" | "md" | How tall a row stands, matching a field of the same size. |
Slots: default, empty.
| Event | Payload |
|---|---|
action | CollectionKey |
close | — |
selectionChange | CollectionSelection |
update:selectedKeys | CollectionSelection |
Items and their groups are separate components.
MenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
id | CollectionKey | — | The item's key, which selection, disabling and the collection all address it by. Falls back to a generated id, so an item that is only ever acted on still has a stable identity. |
isDisabled | boolean | — | Disables the item: it cannot be focused, selected or acted on. |
textValue | string | — | The text typeahead matches against. Read from the item's own content when absent, which skips the description, the indicators and the keyboard shortcut. |
variant | "danger" | "default" | "default" | Item variant. |
The default slot receives:
| Slot prop | Type |
|---|---|
hasSubmenu | boolean |
isDisabled | boolean |
isFocused | boolean |
isFocusVisible | boolean |
isHovered | boolean |
isOpen | boolean |
isPressed | boolean |
isSelected | boolean |
selectionMode | SelectionMode |
MenuItemIndicator
| Prop | Type | Default | Description |
|---|---|---|---|
type | "checkmark" | "dot" | "checkmark" | Which mark the indicator draws. A checkmark reads as "one of several", a dot as "one of these" — the stylesheet does not enforce the pairing, so it is a choice. |
The default slot receives:
| Slot prop | Type |
|---|---|
isSelected | boolean |
MenuItemSubmenuIndicator
Slots: default.
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | Names the section when it has no Header to point at. |
defaultSelectedKeys | "all" | Iterable<CollectionKey> | — | |
disallowEmptySelection | boolean | — | |
selectedKeys | "all" | Iterable<CollectionKey> | — | |
selectionBehavior | "replace" | "toggle" | "toggle" | |
selectionMode | "none" | "single" | "multiple" | — | Gives the section a selection of its own, separate from the menu's and from any other section's — one section of text styles, another of alignments. Without this the section is presentational and its items select through the menu. |
shouldCloseOnSelect | boolean | — | Whether choosing an item in this section closes the menu. Inherited when absent. |
Slots: default.
| Event | Payload |
|---|---|
selectionChange | CollectionSelection |
update:selectedKeys | CollectionSelection |