Menu
Provide the behavior and accessibility implementation for a menu component.
# Features
- Customization options for side, alignment, offsets, and collision handling.
- Fully managed focus for seamless navigation.
- Full keyboard navigation support, including automatic focusing on the first or last item.
- Typeahead support for quickly focusing items by typing their text.
- Support for disabled items.
# Dependencies
This module depends on @floating-ui/dom.
# Anatomy
Typically, a menu consists of:
- Trigger: The element that toggles the menu.
- Menu: The container for menu items. The menu consists of:
- Item: An element used to trigger the selection.
- Separator: An optional element used to visually separate menu items.
<script>
import { createMenu } from '@grail-ui/svelte';
const { useTrigger, triggerAttrs, useMenu, menuAttrs, itemAttrs, open } = createMenu();
const options = [
{ id: "edit", label: "Edit" },
{ id: "delete", label: "Delete" },
{ id: "destroy", label: "Destroy" },
];
</script>
<button use:useTrigger {...$triggerAttrs}>Actions</button>
{#if $open}
<ul use:useMenu {...$menuAttrs}>
{#each options as option (option.id)}
<li><a href="/" {...$itemAttrs(option.id)}>{option.label}</a></li>
{/each}
</ul>
{/if}# Examples
# Listening to item selection
Pass onSelect to the createMenu function to perform some custom logic when
an item is selected. This callback is invoked with the id of the item.
createMenu({
onSelect(id) {
console.log(id);
},
})# Accessibility
# Keyboard Interactions
| Key | description |
|---|---|
Space
Enter
|
When focus is trigger element, toggles the menu. |
Esc
|
Closes the menu and moves focus to trigger element. |
▼
|
When focus is on trigger, opens the menu and focuses first available item. When focus is on an item, moves focus to the next one. |
▲
|
When focus is on trigger, opens the menu and focuses last available item. When focus is on an item, moves focus to the previous one. |
Home
|
Moves focus to the first element. |
End
|
Moves focus to the last element. |
# API
# MenuConfig
| Property | Description | Default |
|---|---|---|
open? |
booleanThe open state of the popover when it is initially rendered. |
false |
positioning? |
Partial<PositioningOptions>Where to place the floating element relative to its reference element. |
{ placement: 'bottom' } |
portal? |
string | HTMLElement | nullWhere to "portal" the floating element outside it's initial DOM position.
It can be a HTMLElement or a CSS selector that points to an already existing element. |
null |
onOpenChange? |
(value?: boolean) => void | undefinedEvent handler called when the open state of the menu changes. |
— |
ariaLabel? |
stringDefines a string value that labels the current element. |
Menu |
onSelect? |
(value: T) => void | undefined | booleanEvent handler called when an item is selected.
Return false to prevent closing of the menu. |
— |
# MenuReturn
| Property | Description |
|---|---|
useTrigger |
Action<HTMLElement, void>Action for the trigger element. |
triggerAttrs |
Readable<Record<string, string | undefined>>HTML attributes for the trigger element. |
useMenu |
Action<HTMLElement, void>Action for the menu element. |
menuAttrs |
Readable<Record<string, string>>HTML attributes for the menu element. |
itemAttrs |
Readable<Function>HTML attributes for the menu item element. |
separatorAttrs |
Readable<Record<string, string>>HTML attributes for the element that is used to visually separate menu items. |
open |
Writable<boolean>The controlled open state of the menu. |
# PositioningOptions
| Property | Description | Default |
|---|---|---|
strategy? |
absolute | fixedThe strategy to use for positioning. |
"absolute" |
placement? |
top | top-start | top-end | right | right-start | right-end | bottom | bottom-start | bottom-end | left | left-start | left-endThe initial placement of the floating element. |
"top" |
offset? |
ObjectThe offset of the floating element. |
— |
gutter? |
numberThe main axis offset or gap between the reference and floating elements. |
5 |
overflowPadding? |
numberThe virtual padding around the viewport edges to check for overflow. |
8 |
flip? |
booleanWhether to flip the placement. |
true |
overlap? |
booleanWhether the floating element can overlap the reference element. |
false |
sameWidth? |
booleanWhether to make the floating element same width as the reference element. |
false |
fitViewport? |
booleanWhether the floating element should fit the viewport. |
false |
boundary? |
@floating-ui/dom.BoundaryThe overflow boundary of the reference element. |
— |