Menus are built with Popovers, with added features for keyboard navigation and accessibility.
The placement of the menu content is handled by <Popover>, so <Menu> accepts the same arguments for positioning the dropdown.
Like <Popover>, the <Menu> component uses the Popover API for layering, which totally solves z-index and overflow clipping issues, no portals needed.
import { Menu } from "ember-primitives";
<template>
<Menu @offsetOptions={{8}} as |m|>
<m.Trigger class="trigger" aria-label="Options">
<EllipsisVertical />
</m.Trigger>
<m.Content class="content" as |c|>
<c.Item>Item 1</c.Item>
<c.Item>Item 2</c.Item>
<c.Separator />
<c.LinkItem class="not-prose" @href="/">Item 3</c.LinkItem>
</m.Content>
</Menu>
<style>
.content {
all: unset;
min-width: 180px;
background: #fff;
color: #111827;
padding: 8px 0;
border-radius: 6px;
border: none;
font-size: 14px;
z-index: 10;
box-shadow:
0 0 #0000,
0 0 #0000,
0 10px 15px -3px rgb(0 0 0 / 0.1),
0 4px 6px -4px rgb(0 0 0 / 0.1);
display: flex;
flex-direction: column;
}
.content [role="menuitem"] {
all: unset;
display: block;
padding: 4px 12px;
cursor: pointer;
}
.content [role="menuitem"]:focus,
.trigger:hover {
background-color: #f9fafb;
}
.content [role="separator"] {
border-bottom: 1px solid rgb(17 24 39 / 0.1);
}
.trigger {
display: inline-block;
border-radius: 4px;
border-width: 0;
background-color: #fff;
color: #111827;
border-radius: 100%;
padding: 10px;
box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px;
cursor: pointer;
}
.trigger svg {
width: 15px;
height: 15px;
display: block;
}
</style>
</template>
const EllipsisVertical = <template>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 128 512"
fill="currentColor"
><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path
d="M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z"
/></svg>
</template>;Sometimes, you need to use an existing component as the trigger. <Menu> also yields a trigger modifier that you can use anywhere, even on your own components (e.g a custom button):
Keep in mind that for the modifier to do its work, your custom component must use ...attributes in some HTML element.
import { ComponentSignature } from "kolay";
<template>
<ComponentSignature
@package="ember-primitives"
@module="declarations/components/menu"
@name="Signature"
/>
</template>Adheres to the Menu Button WAI-ARIA design pattern.
| key | description |
|---|---|
| Space Enter | When focus is on Trigger, opens the menu and focuses the first item. When focus is on an Item, activates the focused item. |
| ArrowDown ArrowRight | When Content is open, moves to the next item. |
| ArrowUp ArrowLeft | When Content is open, moves to the previous item. |
| Esc | Closes the menu and moves focus to Trigger. |
<Menu> now uses the browser's Popover API for layering. You no longer need <PortalTargets /> in your templates.
- import { PortalTargets, Menu } from 'ember-primitives';
+ import { Menu } from 'ember-primitives/components/menu';
<template>
- <PortalTargets />
<Menu as |m|>
...
</Menu>
</template>The @inline argument has been removed. Menu content now renders inline in the DOM and uses the Popover API for layering.
The popover HTML attribute has a browser UA stylesheet that adds default border and overflow styles to elements with [popover]. You may need to set border: none on your menu content if you don't want the browser's default [popover] border. The component resets overflow: visible automatically.