Package Exports
- @material/menu
- @material/menu/dist/mdc.menu
- @material/menu/dist/mdc.menu.css
- @material/menu/index
- @material/menu/mdc-menu.scss
- @material/menu/simple/foundation
- @material/menu/util
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@material/menu) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
MDC Menu
The MDC Menu component is a spec-aligned menu component adhering to the Material Design menu specification. It implements simple menus. Menus require JavaScript to work correctly, but the open and closed states are correct on first render.
Installation
npm install --save @material/menu
Simple menu usage
A simple menu is usually closed, appearing when opened. It is appropriate for any display size.
<div class="mdc-simple-menu" tabindex="-1">
<ul class="mdc-simple-menu__items mdc-list" role="menu" aria-hidden="true">
<li class="mdc-list-item" role="menuitem" tabindex="0">
A Menu Item
</li>
<li class="mdc-list-item" role="menuitem" tabindex="0">
Another Menu Item
</li>
</ul>
</div>
Note: adding a
tabindex
of0
to the menu items places them in the tab order. Adding atabindex
of-1
to the root element makes it programmatically focusable, without placing it in the tab order. This allows the menu to be focused on open, so that the next Tab keypress moves to the first menu item. If you would like the first menu item to be automatically focused instead, removetabindex="-1"
from the root element.
let menu = new mdc.menu.MDCSimpleMenu(document.querySelector('.mdc-simple-menu'));
// Add event listener to some button to toggle the menu on and off.
document.querySelector('.some-button').addEventListener('click', () => menu.open = !menu.open);
You can start the menu in its open state by adding the mdc-simple-menu--open
class to your HTML:
<div class="mdc-simple-menu mdc-simple-menu--open">
...
</div>
Positioning the menu
The menu can either be positioned manually, or automatically, by anchoring it to an element.
Automatic Positioning
The menu understands the concept of an anchor, which it can use to determine how to position itself, and which corner to open from.
The anchor can either be a visible element that the menu is a child of:
<div class="toolbar mdc-menu-anchor">
<div class="mdc-simple-menu">
...
</div>
</div>
or a wrapper element that contains the actual visible element to attach to:
<div class="mdc-menu-anchor">
<button>Open Menu</button>
<div class="mdc-simple-menu">
...
</div>
</div>
Note:
overflow: visible
andposition: relative
will be set on the element withmdc-menu-anchor
to ensure that the menu is positioned and displayed correctly.
The menu will check if its parent element has the mdc-menu-anchor
class set, and if so, it will automatically position
itself relative to this anchor element. It will open from the top left (top right in RTL) corner of the anchor by
default, but will choose an appropriate different corner if close to the edge of the screen.
Manual Positioning
The menu is position: absolute
by default, and must be positioned by the user when doing manual positioning.
<div class="container">
<div class="mdc-simple-menu" style="top:0; left: 0;">
...
</div>
</div>
The menu will open from the top left by default (top right in RTL). Depending on how you've positioned your button, you
may want to change the point it opens from.
To override the opening point, you can style transform-origin
directly, or use one of the following convenience
classes:
class name | description |
---|---|
mdc-simple-menu--open-from-top-left |
Open the menu from the top left. |
mdc-simple-menu--open-from-top-right |
Open the menu from the top right. |
mdc-simple-menu--open-from-bottom-left |
Open the menu from the bottom left. |
mdc-simple-menu--open-from-bottom-right |
Open the menu from the bottom right. |
Using the JS Component
MDC Simple Menu ships with a Component / Foundation combo which allows for frameworks to richly integrate the correct menu behaviors into idiomatic components.
The component has a read-write property, open
, which keeps track of the visual state of the component.
// Analyse current state.
console.log('The menu is ' + (menu.open ? 'open' : 'closed'));
// Open menu.
menu.open = true;
// Close menu.
menu.open = false;
It also has two lower level methods, which control the menu directly, by showing (opening) and hiding (closing) it:
// Show (open) menu.
menu.show();
// Hide (close) menu.
menu.hide();
// Show (open) menu, and focus the menu item at index 1.
menu.show({focusIndex: 1});
You can still use the open
getter property even if showing and hiding directly:
menu.show();
console.log(`Menu is ${menu.open ? 'open' : 'closed'}.`);
Including in code
ES2015
import {MDCSimpleMenu, MDCSimpleMenuFoundation} from 'mdc-menu';
CommonJS
const mdcMenu = require('mdc-menu');
const MDCSimpleMenu = mdcMenu.MDCSimpleMenu;
const MDCSimpleMenuFoundation = mdcMenu.MDCSimpleMenuFoundation;
AMD
require(['path/to/mdc-menu'], mdcMenu => {
const MDCSimpleMenu = mdcMenu.MDCSimpleMenu;
const MDCSimpleMenuFoundation = mdcMenu.MDCSimpleMenuFoundation;
});
Global
const MDCSimpleMenu = mdc.Menu.MDCSimpleMenu;
const MDCSimpleMenuFoundation = mdc.Menu.MDCSimpleMenuFoundation;
Automatic Instantiation
If you do not care about retaining the component instance for the simple menu, simply call attachTo()
and pass it a
DOM element.
mdc.MDCSimpleMenu.attachTo(document.querySelector('.mdc-simple-menu'));
Manual Instantiation
Simple menus can easily be initialized using their default constructors as well, similar to attachTo
.
import MDCSimpleMenu from 'mdc-menu';
const menu = new MDCSimpleMenu(document.querySelector('.mdc-simple-menu'));
Handling selection events
When a menu item is selected, the menu component will emit a MDCSimpleMenu:selected
custom event
with the following detail
data:
property name | type | description |
---|---|---|
item |
HTMLElement |
The DOM element for the selected item |
index |
number |
The index of the selected item |
If the menu is closed with no selection made (for example, if the user hits Escape
while it's open), a MDCSimpleMenu:cancel
custom event is emitted instead, with no data attached.
Using the Foundation Class
MDC Simple Menu ships with an MDCSimpleMenuFoundation
class that external frameworks and libraries can use to
integrate the component. As with all foundation classes, an adapter object must be provided.
The adapter for temporary drawers must provide the following functions, with correct signatures:
Method Signature | Description |
---|---|
addClass(className: string) => void |
Adds a class to the root element. |
removeClass(className: string) => void |
Removes a class from the root element. |
hasClass(className: string) => boolean |
Returns boolean indicating whether element has a given class. |
hasNecessaryDom() => boolean |
Returns boolean indicating whether the necessary DOM is present (namely, the mdc-simple-menu__items container). |
getInnerDimensions() => {width: number, height: number} |
Returns an object with the items container width and height |
hasAnchor: () => boolean |
Returns whether the menu has an anchor for positioning. |
getAnchorDimensions() => { width: number, height: number, top: number, right: number, bottom: number, left: number } |
Returns an object with the dimensions and position of the anchor (same semantics as DOMRect ). |
getWindowDimensions() => {width: number, height: number} |
Returns an object with width and height of the page, in pixels. |
setScale(x: string, y: string) => void |
Sets the transform on the root element to the provided (x, y) scale. |
setInnerScale(x: string, y: string) => void |
Sets the transform on the items container to the provided (x, y) scale. |
getNumberOfItems() => numbers |
Returns the number of item elements inside the items container. In our vanilla component, we determine this by counting the number of list items whose role attribute corresponds to the correct child role of the role present on the menu list element. For example, if the list element has a role of menu this queries for all elements that have a role of menuitem . |
registerInteractionHandler(type: string, handler: EventListener) => void |
Adds an event listener handler for event type type . |
deregisterInteractionHandler(type: string, handler: EventListener) => void |
Removes an event listener handler for event type type . |
registerDocumentClickHandler(handler: EventListener) => void |
Adds an event listener handler for event type 'click'. |
deregisterDocumentClickHandler(handler: EventListener) => void |
Removes an event listener handler for event type 'click'. |
getYParamsForItemAtIndex(index: number) => {top: number, height: number} |
Returns an object with the offset top and offset height values for the item element inside the items container at the provided index. Note that this is an index into the list of item elements, and not necessarily every child element of the list. |
setTransitionDelayForItemAtIndex(index: number, value: string) => void |
Sets the transition delay on the element inside the items container at the provided index to the provided value. The same notice for index applies here as above. |
getIndexForEventTarget(target: EventTarget) => number |
Checks to see if the target of an event pertains to one of the menu items, and if so returns the index of that item. Returns -1 if the target is not one of the menu items. The same notice for index applies here as above. |
notifySelected(evtData: {index: number}) => void |
Dispatches an event notifying listeners that a menu item has been selected. The function should accept an evtData parameter containing the an object with an index property representing the index of the selected item. Implementations may choose to supplement this data with additional data, such as the item itself. |
notifyCancel() => void |
Dispatches an event notifying listeners that the menu has been closed with no selection made. |
saveFocus() => void |
Stores the currently focused element on the document, for restoring with restoreFocus . |
restoreFocus() => void |
Restores the previously saved focus state, by making the previously focused element the active focus again. |
isFocused() => boolean |
Returns a boolean value indicating whether the root element of the simple menu is focused. |
focus() => void |
Focuses the root element of the simple menu. |
getFocusedItemIndex() => number |
Returns the index of the currently focused menu item (-1 if none). |
focusItemAtIndex(index: number) => void |
Focuses the menu item with the provided index. |
isRtl() => boolean |
Returns boolean indicating whether the current environment is RTL. |
setTransformOrigin(value: string) => void |
Sets the transform origin for the menu element. |
setPosition(position: { top: string, right: string, bottom: string, left: string }) => void |
Sets the position of the menu element. |
getAccurateTime() => number |
Returns a number representing the number of milliseconds (and fractional milliseconds, as the decimal part) since a given point in time, which should remain constant during the component's lifecycle. Ideally, this should be provided by window.performance.now() , which has enough precision for high frequency animation calculations. Using Date.now() or equivalent may result in some aliasing in animations. |
The full foundation API
MDCSimpleMenuFoundation.open({focusIndex: number} = {}) => void
Opens the menu. Takes an options object containing a focusIndex
property that specifies the index of the menu item to be focused. If the options object or focusIndex
is omitted, no menu item will be focused.
MDCSimpleMenuFoundation.close() => void
Closes the menu.
MDCSimpleMenuFoundation.isOpen() => boolean
Returns whether or not the menu is open.