diff --git a/docs/components.js b/docs/components.js index 152e334b..65f6d56a 100644 --- a/docs/components.js +++ b/docs/components.js @@ -68,6 +68,14 @@ export default [ 'Elevations create a sense of depth by applying a drop shadow to an element.', component: 'src/components/component-doc', }, + { + name: 'Menu', + url: 'menu', + componentKey: 'menu', + description: + 'Menus represent a list of items. They can be used within selects or dropdowns.', + component: 'src/components/component-doc', + }, { name: 'Chips', url: 'chips', diff --git a/docs/examples/menu.js b/docs/examples/menu.js new file mode 100644 index 00000000..7ae1e32f --- /dev/null +++ b/docs/examples/menu.js @@ -0,0 +1,16 @@ +class MenuExample extends Component { + render() { + return ( + + Option one + Option two + Option three + Option four + Option five + Option six + + ); + } +} + +return ; diff --git a/docs/readmes/menu.md b/docs/readmes/menu.md new file mode 100644 index 00000000..65e58d66 --- /dev/null +++ b/docs/readmes/menu.md @@ -0,0 +1,36 @@ +## Usage + +```jsx +import { Menu } from 'materialish'; +import 'materialish/menu.css'; +``` + +# `Menu` + +## Props + +| Prop Name | Default Value | Required | Description | +| --------- | ------------- | -------- | ----------------------------------------------------------------- | +| className | | No | Additional class name(s) to add to the menu | +| ...rest | | No | Other props are placed on the underlying `ul` element of the menu | + +## CSS Variables + +| Variable | Default Value | Description | +| ---------------------- | ------------- | ----------------------------------------- | +| --mt-baseFontSize | 1rem | The size of text within the menu | +| --mt-fontFamily | 'Roboto' | The font family to use for text | +| --mt-menuItemMinHeight | | The minimum allowed height for menu items | + +# `Menu.Item` + +## Props + +| Prop Name | Default Value | Required | Description | +| --------- | ------------- | -------- | --------------------------------------------------------------------------- | +| className | | No | Additional class name(s) to add to the menu item | +| separator | false | No | Pass `true` to include a border-bottom to the menu item | +| selected | false | No | Whether or not this menu item is currently selected | +| ripple | true | No | Whether or not to display the "ripple" effect when the menu item is clicked | +| children | | No | The contents to render within the menu item | +| ...rest | | No | Other props are placed on the root element of the menu item | diff --git a/docs/static.config.js b/docs/static.config.js index 3787c163..e46a87b0 100644 --- a/docs/static.config.js +++ b/docs/static.config.js @@ -18,6 +18,7 @@ const readmes = { radio: fs.readFileSync('./readmes/radio.md', fsOptions), dialog: fs.readFileSync('./readmes/dialog.md', fsOptions), elevation: fs.readFileSync('./readmes/elevation.md', fsOptions), + menu: fs.readFileSync('./readmes/menu.md', fsOptions), 'action-chip': fs.readFileSync('./readmes/action-chip.md', fsOptions), 'filter-chip': fs.readFileSync('./readmes/filter-chip.md', fsOptions), 'choice-chip': fs.readFileSync('./readmes/choice-chip.md', fsOptions), @@ -33,6 +34,7 @@ const examples = { radio: fs.readFileSync('./examples/radio.js', fsOptions), dialog: fs.readFileSync('./examples/dialog.js', fsOptions), elevation: fs.readFileSync('./examples/elevation.js', fsOptions), + menu: fs.readFileSync('./examples/menu.js', fsOptions), 'action-chip': fs.readFileSync('./examples/action-chip.js', fsOptions), 'filter-chip': fs.readFileSync('./examples/filter-chip.js', fsOptions), 'choice-chip': fs.readFileSync('./examples/choice-chip.js', fsOptions), diff --git a/src/index.js b/src/index.js index 9f308346..bc6eac28 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ import Elevation from './elevation/elevation'; import ActionChip from './action-chip/action-chip'; import ChoiceChip from './choice-chip/choice-chip'; import FilterChip from './filter-chip/filter-chip'; +import Menu from './menu/menu'; export { Avatar, @@ -24,4 +25,5 @@ export { ActionChip, ChoiceChip, FilterChip, + Menu, }; diff --git a/src/menu/menu.css b/src/menu/menu.css new file mode 100644 index 00000000..60b4dc22 --- /dev/null +++ b/src/menu/menu.css @@ -0,0 +1,55 @@ +.mt-menu { + --mt-ripple-color: #666; + all: initial; + font-size: calc(var(--mt-baseFontSize, 1rem) * 0.875); + font-family: var(--mt-fontFamily, 'Roboto'); + position: relative; + box-sizing: border-box; + border-radius: 2px; + text-align: left; + margin: 0; + padding: 0.55em 0; + display: inline-block; + background-color: #fff; + min-width: 10em; + overflow: hidden; +} + +.mt-menu_item { + position: relative; + box-sizing: border-box; + display: block; + min-height: var(--mt-menuItemMinHeight); + margin: 0; + padding: 1.1em 1.7em; + z-index: 0; + cursor: pointer; +} + +.mt-menu_item:after { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #eee; + z-index: -1; + opacity: 0; + will-change: opacity; + transition: opacity 0.1s ease-in-out; +} + +.mt-menu_item:hover:after { + opacity: 1; +} + +.mt-menu_item-separator { + border-bottom: 1px solid #e3e2e2; +} + +.mt-menu_item-selected:after { + background-color: #e1e0e0; + opacity: 1; +} diff --git a/src/menu/menu.js b/src/menu/menu.js new file mode 100644 index 00000000..2369cc47 --- /dev/null +++ b/src/menu/menu.js @@ -0,0 +1,68 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Ripple from '../ripple/ripple'; +import Elevation from '../elevation/elevation'; + +export default class Menu extends Component { + render() { + const { className = '', ...props } = this.props; + return ( + +