Merge pull request #313 from patrick-jones/sidenav-283

Make drawer compliant with material design guidelines.
old
Javi Velasco 2016-04-10 20:21:54 +02:00
commit 0277719942
15 changed files with 981 additions and 0 deletions

View File

@ -53,3 +53,31 @@ $z-index-high: 100 !default;
$z-index-normal: 1 !default;
$z-index-low: -100 !default;
$z-index-lower: -200 !default;
//-- Breakpoints
// height of app bar
// https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
$standard-increment-mobile: (5.6 * $unit) !default;
$standard-increment-desktop: (6.4 * $unit) !default;
// https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-baseline-grids
$baseline-grid: (0.8 * $unit) !default;
$layout-gutter-width-sm: ($baseline-grid * 2) !default;
$layout-gutter-width: ($baseline-grid * 3) !default;
// https://www.google.com/design/spec/layout/responsive-ui.html#responsive-ui-breakpoints
// 4 columns
$layout-breakpoint-xxs: 480px !default;
// 8 columns
$layout-breakpoint-xs: 600px !default;
// 12 columns
$layout-breakpoint-sm-tablet: 720px !default;
$layout-breakpoint-sm: 840px !default;
$layout-breakpoint-md: 960px !default;
$layout-breakpoint-lg-tablet: 1024px !default;
$layout-breakpoint-lg: 1280px !default;
$layout-breakpoint-xl: 1440px !default;
$layout-breakpoint-xxl: 1600px !default;
$layout-breakpoint-xxxl: 1920px !default;

View File

@ -13,6 +13,7 @@ export Dropdown from './dropdown';
export FontIcon from './font_icon';
export Form from './form';
export Input from './input';
export { Layout, NavDrawer, Panel, Sidebar } from './layout';
export Link from './link';
export List from './list/List';
export ListItem from './list/ListItem';

View File

@ -0,0 +1,60 @@
import React from 'react';
import ClassNames from 'classnames';
import style from './style';
const Layout = (props) => {
const className = ClassNames(style.root, props.className);
return (
<div data-react-toolbox='layout' className={className}>
{props.children}
</div>
);
};
const ALLOWED = [
'Panel',
'NavDrawer|Panel',
'NavDrawer|Panel|Sidebar',
'Panel|Sidebar'
];
function getChildName (child) {
if (child.type) {
return child.type.name || child.type;
}
if (!child.constructor || !child.constructor.name) {
return 'UNKNOWN';
}
return child.constructor.name;
}
Layout.propTypes = {
children (props, propName, componentName) {
// Accept only [NavDrawer]Pane[Sidebar]
const children = props[propName];
if (React.Children.count(children) > 3) {
return new Error(
'`' + componentName + '` ' +
'should have a Pane for a child, optionally preceded and/or followed by a Drawer.'
);
}
const names = React.Children.map(children, getChildName).join('|');
if (!(~ALLOWED.indexOf(names))) {
return new Error(
'`' + componentName + '` ' +
'should have a Panel for a child, optionally preceded by a NavDrawer and/or followed by a Sidebar.'
);
}
},
className: React.PropTypes.string
};
Layout.defaultProps = {
className: ''
};
export default Layout;

View File

@ -0,0 +1,47 @@
import React from 'react';
import ClassNames from 'classnames';
import style from './style';
const NavDrawer = (props) => {
const rootClasses = ClassNames([style.navDrawer], {
[style['permanent-' + props.permanentAt]]: props.permanentAt,
[style.wide]: (props.width === 'wide'),
[style.active]: props.active,
[style.pinned]: props.pinned
}, props.className);
const drawerClasses = ClassNames(style.drawerContent, {
[style.scrollY]: props.scrollY
});
return (
<div data-react-toolbox='nav-drawer' className={rootClasses} onClick={props.onOverlayClick}>
<div data-react-toolbox='nav-drawer-scrim' className={style.scrim}>
<aside data-react-toolbox='nav-drawer-content' className={drawerClasses}>
{props.children}
</aside>
</div>
</div>
);
};
NavDrawer.propTypes = {
active: React.PropTypes.bool,
children: React.PropTypes.any,
className: React.PropTypes.string,
onOverlayClick: React.PropTypes.func,
permanentAt: React.PropTypes.oneOf(['sm', 'md', 'lg', 'xl', 'xxl', 'xxxl']),
pinned: React.PropTypes.bool,
scrollY: React.PropTypes.bool,
width: React.PropTypes.oneOf(['normal', 'wide'])
};
NavDrawer.defaultProps = {
active: false,
className: '',
scrollY: false,
width: 'normal'
};
export default NavDrawer;

View File

@ -0,0 +1,28 @@
import React from 'react';
import ClassNames from 'classnames';
import style from './style';
const Panel = (props) => {
const className = ClassNames(style.panel, {
[style.scrollY]: props.scrollY
}, props.className);
return (
<div data-react-toolbox='panel' className={className}>
{props.children}
</div>
);
};
Panel.propTypes = {
children: React.PropTypes.any,
className: React.PropTypes.string,
scrollY: React.PropTypes.bool
};
Panel.defaultProps = {
className: '',
scrollY: false
};
export default Panel;

View File

@ -0,0 +1,38 @@
import React from 'react';
import ClassNames from 'classnames';
import style from './style';
const Sidebar = (props) => {
const wrapperClasses = ClassNames(style.sidebar, style[`width-${props.width}`], {
[style.pinned]: props.pinned
}, props.className);
const innerClasses = ClassNames(style.sidebarContent, {
[style.scrollY]: props.scrollY
});
return (
<div data-react-toolbox='sidebar' className={wrapperClasses}>
<aside data-react-toolbox='sidebar-content' className={innerClasses}>
{props.children}
</aside>
</div>
);
};
Sidebar.propTypes = {
children: React.PropTypes.any,
className: React.PropTypes.string,
pinned: React.PropTypes.bool,
scrollY: React.PropTypes.bool,
width: React.PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 33, 50, 66, 75, 100])
};
Sidebar.defaultProps = {
className: '',
pinned: false,
scrollY: false,
width: 5
};
export default Sidebar;

View File

@ -0,0 +1,17 @@
$drawer-background-color: $palette-grey-50 !default;
$drawer-border-color: $palette-grey-300 !default;
$drawer-text-color: $palette-grey-800 !default;
$drawer-overlay-color: $color-black !default;
$drawer-overlay-opacity: .6 !default;
// from: https://www.google.com/design/spec/layout/structure.html#structure-side-nav
$navigation-drawer-desktop-width: 5 * $standard-increment-desktop !default;
$navigation-drawer-max-desktop-width: 40 * $unit !default;
// Mobile:
// Width = Screen width 56 dp
// Maximum width: 320dp
$navigation-drawer-mobile-width: 5 * $standard-increment-mobile !default;
// sass doesn't like use of variable here: calc(100% - $standard-increment-mobile);
$navigation-drawer-max-mobile-width: calc(100% - 5.6rem) !default;

View File

@ -0,0 +1,30 @@
@mixin open() {
transition-delay: $animation-delay;
& > .scrim {
& > .drawerContent {
pointer-events: all;
transition-delay: $animation-delay;
transform: translateX(0);
}
}
}
@mixin permanent() {
@include open();
width: $navigation-drawer-desktop-width;
max-width: $navigation-drawer-desktop-width;
&.wide {
width: $navigation-drawer-max-desktop-width;
max-width: $navigation-drawer-max-desktop-width;
}
&.active {
& > .scrim {
width: 0;
background-color: rgba($drawer-overlay-color, 0);
}
}
}

View File

@ -0,0 +1,4 @@
export { default as Layout } from './Layout.jsx';
export { default as Panel } from './Panel.jsx';
export { default as NavDrawer } from './NavDrawer.jsx';
export { default as Sidebar } from './Sidebar.jsx';

193
components/layout/readme.md Normal file
View File

@ -0,0 +1,193 @@
# Layout
A Layout is a container that can hold a main content area with an optional
navigation drawer (on the left) and/or sidebar (on the right). According to
the [material design spec](https://www.google.com/design/spec/layout/structure.html#structure-side-nav),
the left drawer is typically used for navigation or identity-based content,
while the right sidebar is secondary content related to the main content.
<!-- example -->
```jsx
import { AppBar, Checkbox, IconButton } from 'react-toolbox';
import { Layout, NavDrawer, Panel, Sidebar } from 'react-toolbox';
class LayoutTest extends React.Component {
state = {
drawerActive: false,
drawerPinned: false,
sidebarPinned: false
};
toggleDrawerActive = () => {
this.setState({ drawerActive: !this.state.drawerActive });
};
toggleDrawerPinned = () => {
this.setState({ drawerPinned: !this.state.drawerPinned });
}
toggleSidebar = () => {
this.setState({ sidebarPinned: !this.state.sidebarPinned });
};
render() {
return (
<Layout>
<NavDrawer active={this.state.drawerActive}
pinned={this.state.drawerPinned} permanentAt='xxxl'
onOverlayClick={ this.toggleDrawerActive }>
<p>
Navigation, account switcher, etc. go here.
</p>
</NavDrawer>
<Panel>
<AppBar><IconButton icon='menu' inverse={ true } onClick={ this.toggleDrawerActive }/></AppBar>
<div style={{ flex: 1, overflowY: 'auto', padding: '1.8rem' }}>
<h1>Main Content</h1>
<p>Main content goes here.</p>
<Checkbox label='Pin drawer' checked={this.state.drawerPinned} onChange={this.toggleDrawerPinned} />
<Checkbox label='Show sidebar' checked={this.state.sidebarPinned} onChange={this.toggleSidebar} />
</div>
</Panel>
<Sidebar pinned={ this.state.sidebarPinned } width={ 5 }>
<div><IconButton icon='close' onClick={ this.toggleSidebar }/></div>
<div style={{ flex: 1 }}>
<p>Supplemental content goes here.</p>
</div>
</Sidebar>
</Layout>
);
}
}
```
<!--component-docgen-start-->
## Layout
The primary layout component. This acts as the main container
that all subcomponents are placed within. The layout is typically placed
so as to fill the entire screen, although it does not have to be.
### Breakpoints and Increments
The Layout's subcomponents can alter their appearance and behavior based
on the current screen size. The layout uses the screen breakpoints described
in the [material design spec](https://www.google.com/design/spec/layout/responsive-ui.html#responsive-ui-breakpoints),
namely:
| Width | Abreviation | Typical Device |
|:-----|:-----|:-----|
| 480px | `xxs` | Phone (portrait) |
| 600px | `xs` | Small tablet, phone (landscape) |
| 720px | `sm-tablet` | Small tablet (portrait) |
| 840px | `sm` | Large tablet (portrait) |
| 960px | `md` | Small tablet (landscape) |
| 1024px | `lg-tablet` | Large tablet (landscape) |
| 1280px | `lg` | Large tablet (landscape), desktop |
| 1440px | `xl` | desktop |
| 1600px | `xxl` | desktop |
| 1920px | `xxxl` | desktop |
The components also make use of [standard increments](https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-sizing-by-increments),
which is a unit equal to the height of the action bar. At mobile sizes (< `xs`) the increment is
56px. On larger screens, it is 64px.
### Content Area Layout
The content areas of all three of the subcomponents (`NavDrawer`, `Panel`, and `Sidebar`)
use flexbox column layouts set to fill the entire height of the containing `Layout`.
The column layout lends itself well to the fixed header/scrolling content that will frequently
inhabit these components. By default, these components also do not scroll content vertically
so that you can control where scrolling occurs. (For example, see the content of the `Panel`
in the sample.)
If the column layout does not suit your needs, simply fill the content area with an element
with `flex` set to 1, and use whatever layout you like within it.
### Properties
| Name | Type | Default | Description |
|:-----|:-----|:-----|:-----|
| `children` | `Nodes` | | A `Panel`, optionally preceded by a `NavDrawer` and/or followed by a `Sidebar` |
| `className` | `string` | | Additional class(es) for custom styling. |
## NavDrawer
The [navigation drawer](https://www.google.com/design/spec/patterns/navigation-drawer.html) slides
in from the left and usually holds [the application's main navigation](https://www.google.com/design/spec/layout/structure.html#structure-side-nav).
The drawer's width is based on the screen size:
| Breakpoint | Drawer Width | Notes |
|:-----|:-----|:-----|
| < `xs` | 280px or (Screen width - 85px) | whichever is smaller |
| > `xs` | 320px | |
| > `xs` | 400px | If property `width` is set to `wide` |
The drawer can be docked to the left side of the screen or can float temporarily
as an overlay. You can control the drawer's display manually `active` and `pinned` properties,
and can also specify a breakpoint at which the drawer automatically becomes permanently docked.
### Properties
| Name | Type | Default | Description |
|:-----|:-----|:-----|:-----|
| `width` | `enum`(`'normal'`,`'wide'`) | `normal` | 320px or 400px. Only applicable above the `sm` breakpoint. |
| `active` | `bool` | `false` | If true, the drawer will be shown as an overlay. |
| `pinned` | `bool` | `false` | If true, the drawer will be pinned open. `pinned` takes precedence over `active`. |
| `permanentAt` | `enum`(`'sm'`,`'md'`,`'lg'`,`'xl'`,`'xxl'`,`'xxxl'`) | | The breakpoint at which the drawer is automatically pinned. |
| `scrollY` | `bool` | `false` | If true, the drawer will vertically scroll all content. |
| `onOverlayClick` | `Function` | | Callback function to be invoked when the overlay is clicked.|
| `className` | `string` | | Additional class(es) for custom styling. |
## Panel
The `Panel` is the main content area within a `Layout`. It is a full-height
flexbox column that takes up all remaining horizontal space after the `NavDrawer`
and `Sidebar` are laid out.
### Properties
| Name | Type | Default | Description |
|:-----|:-----|:-----|:-----|
| `scrollY` | `bool` | `false` | If true, the panel will vertically scroll all content. |
| `className` | `string` | | Additional class(es) for custom styling. |
## Sidebar
The `Sidebar` is an extra drawer that docks to the right side of the `Layout`.
The sidebar's width can be set either to a multiple of the "standard increment"
(1 - 12 increments) or as a percentage of the parent `Layout` width (25%, 33%, 50%, 66%, 75%, 100%).
Regardless of the width set, at mobile screen sizes the sidebar acts like a full-screen dialog that
covers the entire screen (see [examples](https://www.google.com/design/spec/layout/structure.html#structure-side-nav)).
### Properties
| Name | Type | Default | Description |
|:-----|:-----|:-----|:-----|
| `width` | `enum`(`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`,`9`,`10`,`11`,`12`,`25`,`33`,`50`,`66`,`75`,`100`) | `5` | Width in standard increments (1-12) or percentage (25, 33, 50, 66, 75, 100) |
| `pinned` | `bool` | `false` | If true, the sidebar will be pinned open. |
| `scrollY` | `bool` | `false` | If true, the sidebar will vertically scroll all content. |
| `className` | `string` | | Additional class(es) for custom styling. |
## Nesting Layouts
The `Layout` is meant to be used near the top level of your application,
so that it occupies the entire screen. However, it is possible to nest one
layout inside another:
```jsx
<Layout>
<NavDrawer>[navigation here]<NavDrawer>
<Panel>
<Layout>
<Panel>
[main content here]
</Panel>
<Sidebar>
[supplemental info here]
</Sidebar>
</Layout>
</Panel>
</Layout>
```
The main reason you would want to do something like this would be so that
the navigation could be rendered at a high level, while the contents of the
inner `Layout` would be controlled by react-router or something like that.

View File

@ -0,0 +1,318 @@
@import "../base";
@import "./config";
@import "./mixins";
.root {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: stretch;
overflow-y: hidden;
position: relative;
width: 100%;
height: 100%;
.navDrawer {
height: 100%;
width: 0px;
min-width: 0px;
overflow-y: hidden;
overflow-x: hidden;
transition-timing-function: $animation-curve-default;
transition-duration: $animation-duration;
transition-property: width, min-width;
.scrim {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 0;
height: 100%;
background-color: rgba($drawer-overlay-color, 0);
transition: background-color $animation-duration $animation-curve-default,
width 10ms linear $animation-duration;
z-index: $z-index-highest;
}
.drawerContent {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
overflow-x: hidden;
overflow-y: hidden;
@include shadow-2dp();
background-color: $drawer-background-color;
border-right: 1px solid $drawer-border-color;
color: $drawer-text-color;
width: $navigation-drawer-mobile-width;
max-width: $navigation-drawer-max-mobile-width;
pointer-events: none;
transform: translateX(-100%);
transition: transform $animation-duration $animation-curve-default;
&.scrollY {
overflow-y: auto;
}
}
&.pinned {
width: $navigation-drawer-mobile-width;
max-width: $navigation-drawer-max-mobile-width;
@include open();
}
&.active {
&:not(.pinned) {
.scrim {
width: 100%;
background-color: rgba($drawer-overlay-color, $drawer-overlay-opacity);
transition: background-color $animation-duration $animation-curve-default;
}
@include open();
}
}
// larger than mobile width drawer
@media screen and (min-width: $layout-breakpoint-xs) {
&.pinned {
width: $navigation-drawer-desktop-width;
max-width: $navigation-drawer-desktop-width;
}
.drawerContent {
width: $navigation-drawer-desktop-width;
max-width: $navigation-drawer-desktop-width;
}
&.wide {
&.pinned {
width: $navigation-drawer-max-desktop-width;
max-width: $navigation-drawer-max-desktop-width;
}
.drawerContent {
width: $navigation-drawer-max-desktop-width;
max-width: $navigation-drawer-max-desktop-width;
}
}
}
// permanent screen, ignore .active and make part of layout
@media screen and (min-width: $layout-breakpoint-sm) {
&.permanent-sm {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-md) {
&.permanent-md {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-lg) {
&.permanent-lg {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-xl) {
&.permanent-xl {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-xxl) {
&.permanent-xxl {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-xxxl) {
&.permanent-xxxl {
@include permanent();
}
}
}
& .root {
.scrim {
z-index: $z-index-highest - 1;
}
& .root {
.scrim {
z-index: $z-index-highest - 2;
}
}
}
.panel {
flex: 1;
height: 100%;
overflow-y: hidden;
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
&.scrollY {
overflow-y: auto;
}
}
.sidebar {
height: 100%;
overflow-y: hidden;
overflow-x: hidden;
width: 0;
transition-timing-function: $animation-curve-default;
transition-duration: $animation-duration;
transition-property: width;
position: absolute;
top: 0;
right: 0;
bottom: 0;
z-index: $z-index-highest - 1;
background-color: $drawer-background-color;
color: $drawer-text-color;
.sidebarContent {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
height: 100%;
overflow-y: hidden;
&.scrollY {
overflow-y: auto;
}
}
$increments: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
@each $increment in $increments {
&.width-#{$increment} {
$mobile: $standard-increment-mobile * $increment;
$desktop: $standard-increment-desktop * $increment;
.sidebarContent {
min-width: 100%;
}
&.pinned {
width: 100%;
}
@if $increment < 10 {
@media screen and (min-width: $layout-breakpoint-xs) and (orientation: landscape) {
position: relative;
.sidebarContent {
min-width: $mobile;
}
&.pinned {
width: $mobile;
}
}
@media screen and (min-width: $layout-breakpoint-xs) and (orientation: portrait) {
position: relative;
.sidebarContent {
min-width: $desktop;
}
&.pinned {
width: $desktop;
}
}
}
@if $increment < 11 {
@media screen and (min-width: $layout-breakpoint-sm-tablet) {
position: relative;
.sidebarContent {
min-width: $desktop;
}
&.pinned {
width: $desktop;
}
}
}
@media screen and (min-width: $layout-breakpoint-sm) {
position: relative;
.sidebarContent {
min-width: $desktop;
}
&.pinned {
width: $desktop;
}
}
}
}
$percentages: (25, 33, 50, 66, 75);
&.width-100 {
position: absolute;
.sidebarContent {
min-width: 100%;
}
&.pinned {
width: 100%;
}
}
@each $pct in $percentages {
&.width-#{$pct} {
position: absolute;
.sidebarContent {
min-width: 100%;
}
&.pinned {
width: 100%;
}
}
}
@media screen and (min-width: $layout-breakpoint-sm-tablet) {
@each $pct in $percentages {
&.width-#{$pct} {
position: relative;
.sidebarContent {
min-width: $pct * 1%;
}
&.pinned {
width: $pct * 1%;
}
}
}
}
}
}

View File

@ -11,6 +11,7 @@ import Drawer from 'react-toolbox/drawer/readme';
import Dropdown from 'react-toolbox/dropdown/readme';
import FontIcon from 'react-toolbox/font_icon/readme';
import Input from 'react-toolbox/input/readme';
import Layout from 'react-toolbox/layout/readme';
import Link from 'react-toolbox/link/readme';
import List from 'react-toolbox/list/readme';
import Menu from 'react-toolbox/menu/readme';
@ -38,6 +39,7 @@ import DrawerExample1 from './examples/drawer_example_1.txt';
import DrodpownExample1 from './examples/dropdown_example_1.txt';
import FontIconExample1 from './examples/font_icon_example_1.txt';
import InputExample1 from './examples/input_example_1.txt';
import LayoutExample1 from './examples/layout_example_1.txt';
import LinkExample1 from './examples/link_example_1.txt';
import ListExample1 from './examples/list_example_1.txt';
import MenuExample1 from './examples/menu_example_1.txt';
@ -125,6 +127,12 @@ export default {
path: '/components/input',
examples: [InputExample1]
},
layout: {
name: 'Layout',
docs: Layout,
path: '/components/layout',
examples: [LayoutExample1]
},
link: {
name: 'Link',
docs: Link,

View File

@ -0,0 +1,50 @@
class LayoutTest extends React.Component {
state = {
drawerActive: false,
drawerPinned: false,
sidebarPinned: false
};
toggleDrawerActive = () => {
this.setState({ drawerActive: !this.state.drawerActive });
};
toggleDrawerPinned = () => {
this.setState({ drawerPinned: !this.state.drawerPinned });
}
toggleSidebar = () => {
this.setState({ sidebarPinned: !this.state.sidebarPinned });
};
render() {
return (
<Layout>
<NavDrawer active={this.state.drawerActive}
pinned={this.state.drawerPinned} permanentAt='xxxl'
onOverlayClick={ this.toggleDrawerActive }>
<p>
Navigation, account switcher, etc. go here.
</p>
</NavDrawer>
<Panel>
<AppBar><IconButton icon='menu' inverse={ true } onClick={ this.toggleDrawerActive }/></AppBar>
<div style={{ flex: 1, overflowY: 'auto', padding: '1.8rem' }}>
<h1>Main Content</h1>
<p>Main content goes here.</p>
<Checkbox label='Pin drawer' checked={this.state.drawerPinned} onChange={this.toggleDrawerPinned} />
<Checkbox label='Show sidebar' checked={this.state.sidebarPinned} onChange={this.toggleSidebar} />
</div>
</Panel>
<Sidebar pinned={ this.state.sidebarPinned } width={ 5 }>
<div><IconButton icon='close' onClick={ this.toggleSidebar }/></div>
<div style={{ flex: 1 }}>
<p>Supplemental content goes here.</p>
</div>
</Sidebar>
</Layout>
);
}
}
return <LayoutTest />;

157
spec/components/layout.jsx Normal file
View File

@ -0,0 +1,157 @@
import React from 'react';
import { AppBar, Checkbox, Dropdown, IconButton, RadioGroup, RadioButton } from '../../components';
import { Layout, NavDrawer, Panel, Sidebar } from '../../components';
const dummyText = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.';
const drawerItems = dummyText.split(/\s/).map(function (word, index) {
return (<li key={index}>{word}</li>);
});
const sidebarWidths = [
{ value: 4, label: '4 incr' },
{ value: 5, label: '5 incr' },
{ value: 6, label: '6 incr' },
{ value: 7, label: '7 incr' },
{ value: 8, label: '8 incr' },
{ value: 9, label: '9 incr' },
{ value: 10, label: '10 incr' },
{ value: 25, label: '25%'},
{ value: 33, label: '33%'},
{ value: 50, label: '50%'},
{ value: 66, label: '66%'},
{ value: 75, label: '75%'}
];
class LayoutTest extends React.Component {
state = {
permanentAt: 'lg',
drawerOpen: false,
drawerPinned: false,
sidebarPinned: false,
sidebarWidth: 5,
loremIpsums: 1
};
toggleDrawer = (event) => {
event.stopPropagation();
this.setState({ drawerOpen: !this.state.drawerOpen });
};
toggleDrawerPinned = () => {
this.setState({ drawerPinned: !this.state.drawerPinned });
};
changeDrawerPermanentAt = (value) => {
this.setState({ permanentAt: value });
};
toggleSidebar = (value) => {
this.setState({ sidebarPinned: (value === true) });
};
changeSidebarWidth = (value) => {
this.setState({ sidebarWidth: value });
};
fewer = (event) => {
event.preventDefault();
this.setState({ loremIpsums: Math.max(0, this.state.loremIpsums - 1) });
};
more = (event) => {
event.preventDefault();
this.setState({ loremIpsums: this.state.loremIpsums + 1 });
};
render () {
const rng = Array.from(new Array(this.state.loremIpsums), (x, i) => i);
return (
<section>
<h5>Layout</h5>
<div style={{ width: '100%', height: '60rem', margin: '1.8rem 0' }}>
<Layout>
<NavDrawer active={this.state.drawerOpen} pinned={this.state.drawerPinned} permanentAt={this.state.permanentAt} onOverlayClick={this.toggleDrawer}>
<AppBar>
<h5>Drawer</h5>
</AppBar>
<ul style={{ listStyle: 'none', overflowY: 'auto', flex: 1, padding: '1.6rem' }}>
{drawerItems}
</ul>
</NavDrawer>
<Panel>
<AppBar>
<IconButton icon='menu' inverse onClick={this.toggleDrawer}/>
</AppBar>
<div style={{ flex: 1, overflowY: 'auto' }}>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<section style={{ margin: '1.8rem'}}>
<h5>NavDrawer State</h5>
<p>Drawer becomes permanent when window is....</p>
<RadioGroup name='permanentAt' value={this.state.permanentAt} onChange={this.changeDrawerPermanentAt}>
<RadioButton label='Small' value='sm'/>
<RadioButton label='Medium' value='md' />
<RadioButton label='Large' value='lg' />
<RadioButton label='Extra Large' value='xl' />
<RadioButton label='Never' value={null} />
</RadioGroup>
<Checkbox label='Pin drawer' checked={this.state.drawerPinned} onChange={this.toggleDrawerPinned} />
</section>
<section style={{ margin: '1.8rem'}}>
<h5>Sidebar State</h5>
<RadioGroup name='sidebarPinned' value={this.state.sidebarPinned} onChange={this.toggleSidebar}>
<RadioButton label='Pinned' value />
<RadioButton label='Unpinned' value={false} />
</RadioGroup>
<h5>Sidebar Width</h5>
<Dropdown
auto
onChange={this.changeSidebarWidth}
source={sidebarWidths}
value={this.state.sidebarWidth}
/>
</section>
</div>
<section style={{ margin: '1.8rem' }}>
<h5>Scrollable Content</h5>
<p>
The center pane should scroll independently from
the sides. Show
[<a href='#' onClick={this.fewer}>-</a>]
{`${this.state.loremIpsums}`}
[<a href='#' onClick={this.more}>+</a>] paragraph(s) below this one.
</p>
{rng.map((x, i) => <p key={i}>{dummyText}</p>)}
</section>
</div>
</Panel>
<Sidebar pinned={this.state.sidebarPinned} width={Number(this.state.sidebarWidth)}>
<div><IconButton icon='close' onClick={this.toggleSidebar}/></div>
<div style={{ flex: 1, margin: '1.8rem' }}>
<h5>Sidebar</h5>
<p>
Sidebar content should be secondary to the main content on a page.
</p>
<p>
The width of the sidebar can be set either in <em>increments</em>
(where 1 increment = height of the app bar) or in percentages.
</p>
<p>
As per the spec, the right sidebar expands to cover the entire
screen at small screen sizes.
</p>
</div>
</Sidebar>
</Layout>
</div>
</section>
);
}
}
export default LayoutTest;

View File

@ -13,6 +13,7 @@ import Drawer from './components/drawer';
import Dropdown from './components/dropdown';
import IconMenu from './components/icon_menu';
import Input from './components/input';
import Layout from './components/layout';
import List from './components/list';
import Menu from './components/menu';
import Pickers from './components/pickers';
@ -54,6 +55,7 @@ const Root = () => (
<Dropdown />
<IconMenu />
<Input />
<Layout />
<List />
<Menu />
<Pickers />