diff --git a/components/_globals.scss b/components/_globals.scss index 1759ac61..24d9906b 100644 --- a/components/_globals.scss +++ b/components/_globals.scss @@ -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; diff --git a/components/index.js b/components/index.js index ab0ca7f6..170bcb59 100644 --- a/components/index.js +++ b/components/index.js @@ -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'; diff --git a/components/layout/Layout.jsx b/components/layout/Layout.jsx new file mode 100644 index 00000000..24589e57 --- /dev/null +++ b/components/layout/Layout.jsx @@ -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 ( +
+ {props.children} +
+ ); +}; + +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; diff --git a/components/layout/NavDrawer.jsx b/components/layout/NavDrawer.jsx new file mode 100644 index 00000000..6c719122 --- /dev/null +++ b/components/layout/NavDrawer.jsx @@ -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 ( +
+
+ +
+
+ ); +}; + +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; diff --git a/components/layout/Panel.jsx b/components/layout/Panel.jsx new file mode 100644 index 00000000..4a8182b5 --- /dev/null +++ b/components/layout/Panel.jsx @@ -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 ( +
+ {props.children} +
+ ); +}; + +Panel.propTypes = { + children: React.PropTypes.any, + className: React.PropTypes.string, + scrollY: React.PropTypes.bool +}; + +Panel.defaultProps = { + className: '', + scrollY: false +}; + +export default Panel; diff --git a/components/layout/Sidebar.jsx b/components/layout/Sidebar.jsx new file mode 100644 index 00000000..570d601f --- /dev/null +++ b/components/layout/Sidebar.jsx @@ -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 ( +
+ +
+ ); +}; + +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; diff --git a/components/layout/_config.scss b/components/layout/_config.scss new file mode 100644 index 00000000..63c0ef25 --- /dev/null +++ b/components/layout/_config.scss @@ -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; diff --git a/components/layout/_mixins.scss b/components/layout/_mixins.scss new file mode 100644 index 00000000..5ea572ec --- /dev/null +++ b/components/layout/_mixins.scss @@ -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); + } + } +} diff --git a/components/layout/index.js b/components/layout/index.js new file mode 100644 index 00000000..9ece06a4 --- /dev/null +++ b/components/layout/index.js @@ -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'; diff --git a/components/layout/readme.md b/components/layout/readme.md new file mode 100644 index 00000000..2b74ed2e --- /dev/null +++ b/components/layout/readme.md @@ -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. + + +```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 ( + + +

+ Navigation, account switcher, etc. go here. +

+
+ + +
+

Main Content

+

Main content goes here.

+ + +
+
+ +
+
+

Supplemental content goes here.

+
+
+
+ ); + } +} +``` + + + +## 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 + + [navigation here] + + + + [main content here] + + + [supplemental info here] + + + + +``` + +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. diff --git a/components/layout/style.scss b/components/layout/style.scss new file mode 100644 index 00000000..06ac1c60 --- /dev/null +++ b/components/layout/style.scss @@ -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%; + } + } + } + } + } +} diff --git a/docs/app/components/layout/main/modules/components.js b/docs/app/components/layout/main/modules/components.js index 654f2de7..9b5a647b 100644 --- a/docs/app/components/layout/main/modules/components.js +++ b/docs/app/components/layout/main/modules/components.js @@ -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, diff --git a/docs/app/components/layout/main/modules/examples/layout_example_1.txt b/docs/app/components/layout/main/modules/examples/layout_example_1.txt new file mode 100644 index 00000000..0844289c --- /dev/null +++ b/docs/app/components/layout/main/modules/examples/layout_example_1.txt @@ -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 ( + + +

+ Navigation, account switcher, etc. go here. +

+
+ + +
+

Main Content

+

Main content goes here.

+ + +
+
+ +
+
+

Supplemental content goes here.

+
+
+
+ ); + } +} + +return ; diff --git a/spec/components/layout.jsx b/spec/components/layout.jsx new file mode 100644 index 00000000..f975c716 --- /dev/null +++ b/spec/components/layout.jsx @@ -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 (
  • {word}
  • ); +}); + +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 ( +
    +
    Layout
    +
    + + + +
    Drawer
    +
    +
      + {drawerItems} +
    +
    + + + + +
    +
    +
    +
    NavDrawer State
    +

    Drawer becomes permanent when window is....

    + + + + + + + + +
    + +
    +
    Sidebar State
    + + + + +
    Sidebar Width
    + +
    +
    + +
    +
    Scrollable Content
    +

    + The center pane should scroll independently from + the sides. Show + [-] + {`${this.state.loremIpsums}`} + [+] paragraph(s) below this one. +

    + {rng.map((x, i) =>

    {dummyText}

    )} +
    +
    +
    + +
    +
    +
    Sidebar
    +

    + Sidebar content should be secondary to the main content on a page. +

    +

    + The width of the sidebar can be set either in increments + (where 1 increment = height of the app bar) or in percentages. +

    +

    + As per the spec, the right sidebar expands to cover the entire + screen at small screen sizes. +

    +
    +
    +
    +
    +
    + ); + } +} + +export default LayoutTest; diff --git a/spec/root.jsx b/spec/root.jsx index e4f224c9..9757351d 100644 --- a/spec/root.jsx +++ b/spec/root.jsx @@ -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 = () => ( +