Menu component for React
July 31, 2019 ยท View on GitHub
Back to Polythene Menu main page
Menu component for React
Options
Options
Types of menu
- Dropdown menu
- The placement varies with the element that opens it; often the menu covers the clicked item. Usage
- Exposing dropdown menu
- The menu is placed below the element, which display the currently selected menu item. Usage
- Dialog menu
- This variant was introduced in the first version of Material Design specs. When a menu contains elements that don't fit on single lines, the guidelines suggested to use a Dialog instead. This type of menu behaves more global as it doesn't scroll with the page; it could even be made a modal dialog.
- Examples:
Usage
A bare bones menu (made permanently visible to start simple):
import React from "react"
import { Menu, List, ListTile } from "polythene-react"
<Menu permanent>
<List>
<ListTile title="Yes" />
<ListTile title="No" />
</List>
</Menu>
Showing
In real life we want to show a menu after user interaction.
A menu floats on top of other things, so it acts a bit similar to a Dialog. But the behavior of a menu is entirely different - instead of being displayed globally, its context is close to the caller (a button or clickable list item). The local nature of the menu can be seen when scrolling a page: an open menu will scroll along with the page.
Four things are involved in creating a menu:
- The Menu component
- The menu visibility state (
show: trueorshow: false) - A button (or link or clickable list item) to set the menu state; and to act as Menu's target for positioning
- A container that holds both menu and button (or list)
- Because the menu is positioned
absolute, the container must have styleposition: relative - To use the maximum available space, the container must have a height
- Because the menu is positioned
Menu state is best stored locally, in the container component:
import React, { Component } from "react"
import { Menu, List, ListTile, Button } from "polythene-react"
export default class extends Component {
constructor(props) {
super(props)
this.state = {
isOpen: false
}
}
render() {
const isOpen = this.state.isOpen
const target = "simple-menu"
return (
<div style={{ position: "relative" }}>
<Button
raised
label="Open menu"
id={target}
events={{
onClick: () => this.setState({ isOpen: true })
}}
/>
<Menu
target={`#${target}`}
show={isOpen}
didHide={() => this.setState({ isOpen: false })}
>
<List>
<ListTile title="Yes" ink hoverable />
<ListTile title="No" ink hoverable />
</List>
</Menu>
</div>
)
}
}
Hiding the menu
A menu is closed by tapping outside of the menu, or by pressing ESCAPE.
The option didHide is used to reset the local visibility variable.
Positioning
To position a menu to another element, pass parameters target (set to the selector of the element) and optionally origin to relatively position the menu.
To be able to shift the menu vertically to the selected menu item, that menu item (List Tile) must have the class pe-list-tile--selected (set with option selected: true).
Long lists: menu height and scrolling
Menu lists with a small number of items will fit on any screen. Longer lists need to be constrained to a certain size, especially on mobile.
Set the menu height with option height:
- Use a Number with or without pixels or percentage, for example:
160,"160px"or"75%"- When using percentage the parent element must have a height
- Use "max" to use the maximum available height within the parent element (the top position and bottom margin will be subtracted automatically)
Content that does not fit the menu frame will be scrollable.
- To scroll a selected item into view when the menu appears, use
scrollTargetto pass a selector, for example:scrollTarget: ".list-item-12"
Callbacks
Two optional callbacks are used after the transition: didShow and didHide. As shown in the example above, didHide is used to update the Menu state.
You can add different behavior, for instance route to another page:
didHide: id => (
this.setState({ isOpen: false }),
history.push("/")
)
Dropdown menu
The placement varies with the element that opens it; often the menu covers the clicked item.
- Use option
offsetVwith value0to cover the target element. UseoffsetHto tweak the horizontal position. - The menu appears with a fade-in animation, unless option
originis set, in which case it will appear from a corner or a side. - Use
reposition: trueto align the menu to the selected value (when the clicked element is a List Tiles). - Use
topMenu: trueto make the menu appear full width and fixed to the top of the page
Exposing dropdown menu
The menu exposes the clicked element above it. This is the default menu behavior: a menu has a top position that takes the clicked element into account.
The appearance will look more natural when origin is set to "top" - it will look as if the menu is appearing out of the clicked element.
Dialog menu
A dialog can be used as menu by passing param menu to the dialog component. This will show a dialog with menu contents, centered on the screen:
import React from "react";
import { Dialog, Button, List, ListTile } from "polythene-react";
const Tile = ({ title, selected, disabled }) =>
<ListTile
title={title}
selected={selected}
disabled={disabled}
ink
events={{
onClick: () => {
if (!disabled) {
Dialog.hide()
}
}
}}
/>;
const dialogOptions = {
menu: <List
hoverable
tiles={[
<Tile title="Item one" selected={true} disabled={false} />,
<Tile title="Item two" selected={false} disabled={false} />,
<Tile title="Item three" selected={false} disabled={true} />
]}
/>,
hideDelay: .240
};
export default () =>
<Button
raised
label="Open Menu Dialog"
events={{
onClick: () => Dialog.show(dialogOptions)
}}
/>;
Settings menu (position to selected value)
A settings menu shows the selected value, and when opening the menu, highlights the selected value in the menu.
Similar to the simple menu, we keep track of the "open" state. Here we're adding the state for the selected index.
<Menu
target={`#${target}`}
show={isOpen}
size={5}
offsetH={16}
offsetV={0}
reposition
didHide={() => this.setState({ isOpen: false })
>
<List>
{
menuOptions.map((setting, index) =>
<ListTile
title={setting}
selected={index === this.state.selectedIndex}
ink
events={{
onClick: () => this.setState({ selectedIndex: index })
}}
/>
)
}
</List>
</Menu>
Appearance
Variations
- Set the menu width with option
width(choose 1, 1.5, 2, 3, 4, 5, 6, 7, or "auto") - Set the menu height with
height; use "max" to use the maximum available height within the parent element (the top position and bottom margin will be subtracted automatically) - To make the menu stand out more from the surroundings, create a backdrop with
backdrop: true
Styling
Below are examples how to change the Menu appearance, either with a theme or with CSS.
You can find more information about theming in Theming.
Themed component
import { MenuCSS } from "polythene-css"
MenuCSS.addStyle(".themed-menu", {
color_light_background: "#2196F3",
border_radius: 0
})
<Menu className="themed-menu" />
CSS
Change CSS using the Menu CSS classes.
Class names can be imported with:
import classes from "polythene-css-classes/menu"
Style
Some style attributes can be set using option style. Because a Menu is created with a List, the style must also be passed to its List Tiles:
const backgroundColor = "#2196F3"
const color = "#fff"
const style = {
backgroundColor,
color
}
<Menu style={style}>
<List>
<ListTile title="Yes" style={style} />
<ListTile title="No" style={style} />
</List>
</Menu>
Dark or light tone
If the component - or a component's parent - has option tone set to "dark", the component will be rendered with light colors on dark.
- Use
tone: "dark"to render light on dark - Use
tone: "light"to locally render normally when dark tone is set
Transitions
See Transitions