From 4556918cbdea6e5631ed5fdaeaada8869c23fe28 Mon Sep 17 00:00:00 2001 From: Brent Jackson Date: Wed, 20 Jun 2018 13:53:12 -0400 Subject: [PATCH] Add basic layout component --- README.md | 10 +- docs/_app.js | 13 +- package.json | 3 +- src/SidebarLayout.js | 319 +++++++++++++++++++++++++++++++++++++++++++ src/entry.js | 10 +- src/index.js | 3 + 6 files changed, 350 insertions(+), 8 deletions(-) create mode 100644 src/SidebarLayout.js diff --git a/README.md b/README.md index b8f7d67..ef90f46 100644 --- a/README.md +++ b/README.md @@ -368,9 +368,13 @@ See the [example](https://github.com/c8r/x0/tree/master/examples/webpack-config) **REMOVE BEFORE MERGING** -- [ ] pass RouterState props to view -- [ ] props.Component/children in custom apps - [ ] changelog/docs + +- [x] SidebarLayout +- [x] test getInitialProps +- [x] double check dynamic routing +- [x] pass RouterState props to view +- [x] props.children in custom app - [x] require.context `_app` - [x] peer deps - [x] props.ignore @@ -383,7 +387,9 @@ See the [example](https://github.com/c8r/x0/tree/master/examples/webpack-config) - [x] minimatch - [x] move client modules to src - [x] adjust resolve +- [x] props.Component in custom app? - [ ] ~~Head component with react helmet~~ doesn't work +- [ ] ~~scope loader shim for jsx front matter~~ ### Breaking diff --git a/docs/_app.js b/docs/_app.js index 705f6f3..bb936a4 100644 --- a/docs/_app.js +++ b/docs/_app.js @@ -1,7 +1,7 @@ import React from 'react' import * as scope from 'rebass' import { Link } from 'react-router-dom' -import { ScopeProvider } from '../components' +import { ScopeProvider, SidebarLayout } from '../components' import { Flex, Box, @@ -20,9 +20,20 @@ export default class App extends React.Component { route, children, // alternative to props.children + Component, render, } = this.props + // built-in layout test + if (false) { + return ( + + + + ) + } + + return ( {false ? ( diff --git a/package.json b/package.json index 9d591b4..7019089 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "x0": "cli.js" }, "scripts": { - "start": "./cli.js docs -op 8888", + "start": "./cli.js docs -p 8888", "build": "./cli.js build docs", "test": "nyc ava -T 20s", "cover": "nyc report --reporter=html --reporter=lcov" @@ -56,6 +56,7 @@ "mini-html-webpack-plugin": "^0.2.3", "minimatch": "^3.0.4", "pkg-conf": "^2.1.0", + "prop-types": "^15.6.2", "react": "^16.4.1", "react-dev-utils": "^5.0.1", "react-dom": "^16.4.1", diff --git a/src/SidebarLayout.js b/src/SidebarLayout.js new file mode 100644 index 0000000..d76f27c --- /dev/null +++ b/src/SidebarLayout.js @@ -0,0 +1,319 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { + Link as RouterLink, + NavLink as RouterNavLink +} from 'react-router-dom' +import styled from 'styled-components' +import { + Provider as RebassProvider, + Flex, + Box, + Fixed, + Container, + Text, + Close, + Toolbar, + Divider, + Heading, + NavLink, + BlockLink, + Button, + ButtonTransparent, +} from 'rebass' +import { borderColor, themeGet } from 'styled-system' + +const breakpoint = `@media screen and (min-width: 48em)` + +export const Root = styled(Flex)([], { + minHeight: '100vh' +}) + +export const Sidebar = styled('div')([], { + width: '256px', + height: '100vh', + flex: 'none', + overflowY: 'auto', + transition: 'transform .2s ease-out', + backgroundColor: '#fff', + borderRight: '1px solid', + position: 'fixed', + top: 0, + left: 0, + bottom: 0, + [breakpoint]: { + // transition: 'none' + } +}, props => ({ + transform: props.open ? 'translateX(0)' : 'translateX(-100%)', + [breakpoint]: { + transform: 'none' + } +}), borderColor) +Sidebar.defaultProps = { + borderColor: 'gray' +} + +export const Overlay = styled('div')([], { + position: 'fixed', + top: 0, + right: 0, + bottom: 0, + left: 0, +}) + +export const MobileOnly = styled.div([], { + [breakpoint]: { + display: 'none' + }, +}) + +export const MenuIcon = ({ size = 24, ...props }) => + + + + +export const FocusButton = styled(Button)([], { + width: '2em', + height: '2em', +}) +FocusButton.defaultProps = { + fontSize: 0, + px: '0.25em', + py: '0.25em', + m: 2, + color: 'black', + bg: 'gray' +} + +export const Main = props => + + +export const MaxWidth = props => + + +export const UL = styled('ul')([], { + listStyle: 'none', + margin: 0, + paddingLeft: 0, + paddingBottom: '48px', +}) + +export const LI = styled('li')([], { +}) + +const depthPad = ({ to = '' }) => + (1 + to.split('/') + .filter(s => s.length) + .slice(1).length) * 16 + +const Link = styled(props => ( + +))([], props => ({ + borderLeft: '4px solid', + borderColor: 'transparent', + '&.active, &:focus': { + color: themeGet('colors.blue', '#07c')(props), + outline: 'none', + }, + '&:focus': { + borderColor: 'inherit', + } +})) + +Link.defaultProps = { + to: '' +} + +const unhyphenate = str => str.replace(/(\w)(-)(\w)/g, '$1 $3') +const upperFirst = str => str.charAt(0).toUpperCase() + str.slice(1) +const format = str => upperFirst(unhyphenate(str)) + +const NavBar = ({ + title, + focus, + update, +}) => + + + {title} + + + {DEV && ( + update(toggle('focus'))}> + F + + )} + + +export const Nav = ({ + routes = [], + ...props +}) => + + + +
    + {/* rename to route.type='section' */} + {routes.map(route => route.exact ? ( +
  • + + {format(route.name)} (index) + +
  • + ) : ( +
  • + {/^https?:\/\//.test(route.path) ? ( + + {route.name} + + ) : ( + + {format(route.name)} + + )} +
  • + ))} +
+
+ +export const Pagination = ({ previous, next }) => + + {previous && ( + + Previous: + + {format(previous.name)} + + + )} + + {next && ( + + Next: + + {format(next.name)} + + + )} + + +// move to app +const toggle = key => state => ({ [key]: !state[key] }) +const close = state => ({ menu: false }) + +export default class Layout extends React.Component { + static propTypes = { + // content: PropTypes.node.isRequired, + routes: PropTypes.array.isRequired + } + + state = { + menu: false, + update: fn => this.setState(fn) + } + + render () { + const { + routes = [], + children, + route, + location, + title = 'x0', + // theme, + // color, + } = this.props + const { menu, update } = this.state + + const Wrapper = route && route.props && route.props.fullWidth + ? React.Fragment + : MaxWidth + + const index = routes.findIndex(r => r === route) + const pagination = { + previous: routes[index - 1], + next: routes[index + 1] + } + + return ( + + + + update(toggle('menu'))}> + + + + {title} + + + + + + + + {menu && update(close)} />} + update(close)}> +