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? |
boolean The 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 | null Where 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 | undefined Event handler called when the open state of the menu changes. |
— |
ariaLabel? |
string Defines a string value that labels the current element. |
Menu |
onSelect? |
(value: T) => void | undefined | boolean Event 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 | fixed The 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-end The initial placement of the floating element. |
"top" |
offset? |
Object The offset of the floating element. |
— |
gutter? |
number The main axis offset or gap between the reference and floating elements. |
5 |
overflowPadding? |
number The virtual padding around the viewport edges to check for overflow. |
8 |
flip? |
boolean Whether to flip the placement. |
true |
overlap? |
boolean Whether the floating element can overlap the reference element. |
false |
sameWidth? |
boolean Whether to make the floating element same width as the reference element. |
false |
fitViewport? |
boolean Whether the floating element should fit the viewport. |
false |
boundary? |
@floating-ui/dom.Boundary The overflow boundary of the reference element. |
— |