From b0b00eeb5f624bb1cd7dff56a8a8be7bc3a02eb6 Mon Sep 17 00:00:00 2001 From: Brent Jackson Date: Sat, 28 Jul 2018 18:13:50 -0400 Subject: [PATCH] Split components and add support for inline modules --- docs/index.mdx | 7 +- docs/theme.js | 2 +- lib/SlideDeck.js | 163 ++++++++++++++++++++++++++++++++++++++++++++++ lib/components.js | 8 ++- lib/entry.js | 161 ++++++--------------------------------------- lib/loader.js | 11 +++- 6 files changed, 202 insertions(+), 150 deletions(-) create mode 100644 lib/SlideDeck.js diff --git a/docs/index.mdx b/docs/index.mdx index b23b616..a11d029 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -1,8 +1,4 @@ ---- -modules: - - import Box from 'superbox' ---- - `- export { default as theme } from './theme'` +export { default as theme } from './theme' # Hello @@ -17,6 +13,7 @@ modules: [MDX]: https://github.com/mdx-js/mdx --- +import Box from 'superbox' And you can import React components! diff --git a/docs/theme.js b/docs/theme.js index e652000..3a682d2 100644 --- a/docs/theme.js +++ b/docs/theme.js @@ -2,6 +2,6 @@ export default { colors: { text: '#fff', background: '#444', - blue: '#07c', + link: '#07c', }, } diff --git a/lib/SlideDeck.js b/lib/SlideDeck.js new file mode 100644 index 0000000..90a9d38 --- /dev/null +++ b/lib/SlideDeck.js @@ -0,0 +1,163 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { MDXProvider } from '@mdx-js/tag' +import styled, { ThemeProvider } from 'styled-components' +import { color } from 'styled-system' +import debounce from 'lodash.debounce' + +import defaultTheme from './theme' +import defaultComponents from './components' + +export const inc = state => ({ index: (state.index + 1) % state.length }) +export const dec = state => state.index > 0 + ? ({ index: (state.index - 1) % state.length }) + : null + +const CarouselRoot = styled.div([], { + display: 'flex', + overflowX: 'auto', + width: '100vw', + scrollSnapType: 'mandatory', + '::-webkit-scrollbar': { + display: 'none' + } +}) + +export class Carousel extends React.Component { + root = React.createRef() + isScroll = false + + handleScroll = debounce(e => { + if (this.isScroll) { + this.isScroll = false + return + } + const { scrollLeft } = e.target + const rect = e.target.getBoundingClientRect() + const n = Math.round(scrollLeft / rect.width) + this.props.onScroll(n) + }, 200) + + scrollTo = (index) => { + if (!this.root.current) return + const el = this.root.current.querySelector('#slide-' + index) + if (!el) return + el.scrollIntoView({ behavior: 'smooth' }) + } + + componentDidMount () { + this.root.current.addEventListener('scroll', this.handleScroll) + } + + componentWillUnmount () { + this.root.current.removeEventListener('scroll', this.handleScroll) + } + + componentDidUpdate (prev) { + if (prev.index === this.props.index) return + this.isScroll = true + this.scrollTo(this.props.index) + } + + render () { + const { onScroll, index, ...props } = this.props + + return ( + + ) + } +} + +export const Slide = styled.div([], { + flex: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'column', + overflow: 'hidden', + width: '100vw', + height: '90vh' +}, color) + +Slide.defaultProps = { + color: 'text', + bg: 'background' +} + +export default class SlideDeck extends React.Component { + static propTypes = { + slides: PropTypes.array.isRequired, + } + + static defaultProps = { + slides: [], + theme: defaultTheme, + components: defaultComponents + } + + state = { + length: this.props.slides.length, + index: 0 + } + + update = fn => this.setState(fn) + + handleKeyDown = e => { + console.log(e) + if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return + switch (e.key) { + case 'ArrowRight': + case ' ': + e.preventDefault() + this.update(inc) + break + case 'ArrowLeft': + e.preventDefault() + this.update(dec) + break + } + } + + componentDidMount () { + document.body.addEventListener('keydown', this.handleKeyDown) + } + + componentWillUnmount () { + document.body.removeEventListener('keydown', this.handleKeyDown) + } + + render () { + const { + slides, + theme, + components + } = this.props + const { index } = this.state + + return ( + + +
+ { + this.setState({ index }) + }}> + {slides.map((Component, i) => ( + + + + ))} + + {this.state.index + 1}/{this.state.length} + + +
+
+
+ ) + } +} diff --git a/lib/components.js b/lib/components.js index 78fcc38..ad91528 100644 --- a/lib/components.js +++ b/lib/components.js @@ -31,7 +31,13 @@ const h2 = props => fontSize={[ 4, 5, 6 ]} /> +const a = styled.a([], {}, color) +a.defaultProps = { + color: 'link' +} + export default { h1, - h2 + h2, + a } diff --git a/lib/entry.js b/lib/entry.js index caf1ec8..554a0ef 100644 --- a/lib/entry.js +++ b/lib/entry.js @@ -1,14 +1,7 @@ import React from 'react' import { render } from 'react-dom' import PropTypes from 'prop-types' -import { MDXProvider } from '@mdx-js/tag' -import styled, { ThemeProvider } from 'styled-components' -import { color } from 'styled-system' -import Box from 'superbox' -import throttle from 'lodash.throttle' -import debounce from 'lodash.debounce' -import defaultTheme from './theme' -import defaultComponents from './components' +import SlideDeck from './SlideDeck' // todo: dynamic import import slides, { @@ -17,148 +10,32 @@ import slides, { meta } from '../docs/index.mdx' -const CarouselRoot = styled.div([], { - display: 'flex', - overflowX: 'auto', - width: '100vw', - scrollSnapType: 'mandatory', - '::-webkit-scrollbar': { - display: 'none' - } -}) - -class Carousel extends React.Component { - root = React.createRef() - isScroll = false - - handleScroll = debounce(e => { - if (this.isScroll) { - this.isScroll = false - return - } - const { scrollLeft } = e.target - const rect = e.target.getBoundingClientRect() - const n = Math.round(scrollLeft / rect.width) - this.props.onScroll(n) - }, 200) - - scrollTo = (index) => { - if (!this.root.current) return - const el = this.root.current.querySelector('#slide-' + index) - if (!el) return - el.scrollIntoView({ behavior: 'smooth' }) - } - - componentDidMount () { - this.root.current.addEventListener('scroll', this.handleScroll) - } - - componentWillUnmount () { - this.root.current.removeEventListener('scroll', this.handleScroll) - } - - componentDidUpdate (prev) { - if (prev.index === this.props.index) return - this.isScroll = true - this.scrollTo(this.props.index) - } +class App extends React.Component { render () { - const { onScroll, index, ...props } = this.props + const { + slides, + theme, + components + } = this.props return ( - ) } } -const Slide = styled.div([], { - // outline: '2px solid tomato', - flex: 'none', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'column', - overflow: 'hidden', - width: '100vw', - height: '90vh' -}, color) - -Slide.defaultProps = { - color: 'text', - bg: 'background' -} - -const inc = state => ({ index: (state.index + 1) % state.length }) -const dec = state => state.index > 0 - ? ({ index: (state.index - 1) % state.length }) - : null - -class App extends React.Component { - static propTypes = { - slides: PropTypes.array.isRequired, - } - - state = { - length: this.props.slides.length, - index: 0 - } - - update = fn => this.setState(fn) - - handleKeyDown = e => { - switch (e.key) { - case 'ArrowRight': - e.preventDefault() - this.update(inc) - break - case 'ArrowLeft': - e.preventDefault() - this.update(dec) - break - } - } - - componentDidMount () { - document.body.addEventListener('keydown', this.handleKeyDown) - } - - componentWillUnmount () { - document.body.removeEventListener('keydown', this.handleKeyDown) - } - - render () { - const { slides } = this.props - const { index } = this.state - - return ( - - -
- { - this.setState({ index }) - }}> - {slides.map((Component, i) => ( - - - - ))} - - {this.state.index + 1}/{this.state.length} - - -
-
-
- ) - } -} - -render(, window.root) +render( + , + window.root +) if (module.hot) module.hot.accept() diff --git a/lib/loader.js b/lib/loader.js index 5d5f754..51085ed 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -3,18 +3,26 @@ const matter = require('gray-matter') const stringifyObject = require('stringify-object') const EXREG = /export\sdefault\s/g +const MODREG = /^(import|export)\s/ module.exports = async function (src) { const callback = this.async() const { data, content } = matter(src) + const inlineModules = [] const slides = content.split('---\n') .map(str => mdx.sync(str)) .map(str => str.trim()) .map(str => str.replace(EXREG, '')) + .map(str => { + const lines = str.split('\n\n') + inlineModules.push( + ...lines.filter(str => MODREG.test(str)) + ) + return lines.filter(str => !MODREG.test(str)) + }) - console.log(data) const { modules = [] } = data @@ -22,6 +30,7 @@ module.exports = async function (src) { const code = `import React from 'react' import { MDXTag } from '@mdx-js/tag' ${modules.join('\n')} +${inlineModules.join('\n')} export const meta = ${stringifyObject(data)}