Merge pull request #313 from patrick-jones/sidenav-283
Make drawer compliant with material design guidelines.old
commit
0277719942
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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';
|
|
@ -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.
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 />;
|
|
@ -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;
|
|
@ -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 />
|
||||
|
|
Loading…
Reference in New Issue