shoelace/docs/pages/components/menu.md

156 wiersze
4.8 KiB
Markdown

2023-06-06 12:22:18 +00:00
---
meta:
title: Menu
description: Menus provide a list of options for the user to choose from.
layout: component
---
You can use [menu items](/components/menu-item), [menu labels](/components/menu-label), and [dividers](/components/divider) to compose a menu. Menus support keyboard interactions, including type-to-select an option.
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item value="undo">Undo</sl-menu-item>
<sl-menu-item value="redo">Redo</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
<sl-menu-item value="delete">Delete</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
2023-06-06 12:22:18 +00:00
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem value="undo">Undo</SlMenuItem>
<SlMenuItem value="redo">Redo</SlMenuItem>
<SlDivider />
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
<SlMenuItem value="delete">Delete</SlMenuItem>
</SlMenu>
);
```
{% endraw %}
:::tip
Menus are intended for system menus (dropdown menus, select menus, context menus, etc.). They should not be mistaken for navigation menus which serve a different purpose and have a different semantic meaning. If you're building navigation, use `<nav>` and `<a>` elements instead.
:::
Submenus (#1527) * [RFC] Proof-of-concept commit for submenu support This is a Request For Comments to seek directional guidance towards implementing the submenu slot of menu-item. Includes: - SubmenuController to manage event listeners on menu-item. - Example usage in menu-item documentation. - Trivial tests to check rendering. Outstanding questions include: - Accessibility concerns. E.g. where to handle 'ArrowRight', 'ArrowLeft'? - Should selection of menu-item denoting submenu be possible or customizable? - How to parameterize contained popup? - Implementation concerns: - Use of ref / id - delegation of some rendering to the controller - What to test Related to [#620](https://github.com/shoelace-style/shoelace/issues/620). * Update submenu-controller.ts Removed extraneous `console.log()`. * PoC working of ArrowRight to focus on submenu. * Revert "PoC working of ArrowRight to focus on submenu." (Didn't mean to publish this.) This reverts commit be04e9a221e7b9e38995297e70d4517b3fae6468. * [WIP] Submenu WIP continues. - Submenus now close on change-of-focus, not a timeout. - Keyboard navigation support added. - Skidding fix for better alignment. - Submenu documentation moved to Menu page. - Tests for accessibility, right and left arrow keys. * Cleanup: Removed dead code and dead code comments. * style: Eslint warnings and errors fixed. npm run verify now passes. * fix: 2 changes to menu / submenu on-click behavior: 1. Close submenu on click explicitly, so this occurs even if the menu is not inside of an sl-dropdown. 2. In menu, ignore clicks that do not explicitly target a menu-item. Clicks that were on (e.g. a menu-border) were emitting select events. * fix: Prevent menu's extraneous Enter / space key propagation. Menu's handleKeyDown calls item.click (to emit the selection). Propagating the keyboard event on Enter / space would the cause re-entry into a submenu, so prevent the needless propagation. * Submenu tweaks ... - 100 ms delay when opening submenus on mouseover - Shadows added - Distance added to popup to have submenus overlap menu slightly. * polish up submenu stuff * stay highlighted when submenu is open * update changelog * resolve feedback --------- Co-authored-by: Bryce Moore <bryce.moore@gmail.com>
2023-08-21 21:26:41 +00:00
## Examples
### In Dropdowns
Menus work really well when used inside [dropdowns](/components/dropdown).
```html:preview
<sl-dropdown>
<sl-button slot="trigger" caret>Edit</sl-button>
<sl-menu>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
</sl-menu>
</sl-dropdown>
```
```jsx:react
import SlButton from '@shoelace-style/shoelace/dist/react/button';
import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlDropdown>
<SlButton slot="trigger" caret>Edit</SlButton>
<SlMenu>
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
</SlMenu>
</SlDropdown>
);
```
### Submenus
To create a submenu, nest an `<sl-menu slot="submenu">` in any [menu item](/components/menu-item).
```html:preview
<sl-menu style="max-width: 200px;">
<sl-menu-item value="undo">Undo</sl-menu-item>
<sl-menu-item value="redo">Redo</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="cut">Cut</sl-menu-item>
<sl-menu-item value="copy">Copy</sl-menu-item>
<sl-menu-item value="paste">Paste</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
Find
<sl-menu slot="submenu">
<sl-menu-item value="find">Find…</sl-menu-item>
<sl-menu-item value="find-previous">Find Next</sl-menu-item>
<sl-menu-item value="find-next">Find Previous</sl-menu-item>
</sl-menu>
</sl-menu-item>
<sl-menu-item>
Transformations
<sl-menu slot="submenu">
<sl-menu-item value="uppercase">Make uppercase</sl-menu-item>
<sl-menu-item value="lowercase">Make lowercase</sl-menu-item>
<sl-menu-item value="capitalize">Capitalize</sl-menu-item>
</sl-menu>
</sl-menu-item>
</sl-menu>
```
{% raw %}
```jsx:react
import SlDivider from '@shoelace-style/shoelace/dist/react/divider';
import SlMenu from '@shoelace-style/shoelace/dist/react/menu';
import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem value="undo">Undo</SlMenuItem>
<SlMenuItem value="redo">Redo</SlMenuItem>
<SlDivider />
<SlMenuItem value="cut">Cut</SlMenuItem>
<SlMenuItem value="copy">Copy</SlMenuItem>
<SlMenuItem value="paste">Paste</SlMenuItem>
<SlDivider />
<SlMenuItem>
Find
<SlMenu slot="submenu">
<SlMenuItem value="find">Find…</SlMenuItem>
<SlMenuItem value="find-previous">Find Next</SlMenuItem>
<SlMenuItem value="find-next">Find Previous</SlMenuItem>
</SlMenu>
</SlMenuItem>
<SlMenuItem>
Transformations
<SlMenu slot="submenu">
<SlMenuItem value="uppercase">Make uppercase</SlMenuItem>
<SlMenuItem value="lowercase">Make lowercase</SlMenuItem>
<SlMenuItem value="capitalize">Capitalize</SlMenuItem>
</SlMenu>
</SlMenuItem>
</SlMenu>
);
```
:::warning
As a UX best practice, avoid using more than one level of submenus when possible.
:::
{% endraw %}