diff --git a/CHANGELOG.md b/CHANGELOG.md index 177079e..97a176a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ - Refactor localStorage to use hooks #334 - Refactor keyboard shortcuts #335 - Refactor query string to use hooks #336 +- Refactor to use hooks #337 + - Adds `MDXDeckState` provider component + - Fixes an issue with rerenders in Gatsby theme + - Adjusts styles in grid mode + - Refactors `useSteps` to use effect hook ## v2.2.3 2019-04-20 diff --git a/docs/demo.mdx b/docs/demo.mdx index 1b75f75..89f2815 100644 --- a/docs/demo.mdx +++ b/docs/demo.mdx @@ -57,8 +57,10 @@ import { Box } from '@rebass/emotion' ``` -These are speaker notes + +- These are speaker notes - And they won't be rendered in your slide + --- diff --git a/package.json b/package.json index 51d2f81..332855d 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,9 @@ "start": "yarn workspace @mdx-deck/docs start", "analyze-bundle": "yarn workspace @mdx-deck/docs start --webpack bundle-analyzer.config.js", "build": "yarn workspace @mdx-deck/docs build", + "start-theme": "yarn workspace @mdx-deck/gatsby-theme start", + "build-theme": "yarn workspace @mdx-deck/gatsby-theme build", + "export": "yarn workspace @mdx-deck/export pdf", "test": "jest" }, "devDependencies": { diff --git a/packages/components/package.json b/packages/components/package.json index 4a612a9..0c73576 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -21,6 +21,7 @@ "devDependencies": { "react": "^16.8.3", "react-dom": "^16.8.3", - "react-test-renderer": "^16.8.4" + "react-test-renderer": "^16.8.4", + "react-testing-library": "^6.1.2" } } diff --git a/packages/components/src/Grid.js b/packages/components/src/Grid.js index 5875759..c19dd4d 100644 --- a/packages/components/src/Grid.js +++ b/packages/components/src/Grid.js @@ -1,22 +1,9 @@ import React, { useEffect } from 'react' -import { Location, navigate } from '@reach/router' import Zoom from './Zoom' import Slide from './Slide' -const getIndex = ({ pathname }) => { - return Number(pathname.split('/')[1] || 0) -} - -const withLocation = Component => props => ( - ( - - )} - /> -) - -export const Grid = withLocation(props => { - const { index, slides, modes, update, basepath } = props +export const Grid = props => { + const { index, slides, modes, update, goto } = props const activeThumb = React.createRef() useEffect(() => { @@ -49,18 +36,19 @@ export const Grid = withLocation(props => { key={i} role="link" onClick={e => { - navigate(basepath + '/' + i) + goto(i) update({ mode: modes.NORMAL }) }} style={{ display: 'block', - width: '25vw', - height: '25vh', - padding: '2px', + width: 'calc(25vw - 4px)', + height: 'calc(25vh - 4px)', + margin: '2px', overflow: 'hidden', color: 'inherit', textDecoration: 'none', cursor: 'pointer', + outline: i === index ? '4px solid #0cf' : null, }} > @@ -73,6 +61,6 @@ export const Grid = withLocation(props => { ) -}) +} export default Grid diff --git a/packages/components/src/Head.js b/packages/components/src/Head.js index 236ae54..94c58cd 100644 --- a/packages/components/src/Head.js +++ b/packages/components/src/Head.js @@ -20,6 +20,7 @@ export const HeadProvider = ({ tags = [], children }) => { // get head for all slides export const UserHead = ({ mdx }) => + !!mdx && React.createElement(mdx, { components: { wrapper: props => { diff --git a/packages/components/src/Keyboard.js b/packages/components/src/Keyboard.js index 5658fc0..543ddb8 100644 --- a/packages/components/src/Keyboard.js +++ b/packages/components/src/Keyboard.js @@ -69,21 +69,13 @@ const handleKeyDown = props => e => { } } -export const Keyboard = props => { +export default props => { useEffect(() => { const handler = handleKeyDown(props) window.addEventListener('keydown', handler) return () => { window.removeEventListener('keydown', handler) } - }, []) + }, [props.metadata]) return false } - -const noop = () => {} - -Keyboard.defaultProps = { - setState: noop, -} - -export default Keyboard diff --git a/packages/components/src/MDXDeck.js b/packages/components/src/MDXDeck.js index efe669b..d3f76d8 100644 --- a/packages/components/src/MDXDeck.js +++ b/packages/components/src/MDXDeck.js @@ -1,8 +1,10 @@ -import React from 'react' +import React, { useContext, useReducer, useMemo } from 'react' import PropTypes from 'prop-types' import { Router, globalHistory, navigate } from '@reach/router' import { Global } from '@emotion/core' import { Swipeable } from 'react-swipeable' +import merge from 'lodash.merge' + import Provider from './Provider' import Slide from './Slide' import Presenter from './Presenter' @@ -11,9 +13,10 @@ import Grid from './Grid' import Print from './Print' import GoogleFonts from './GoogleFonts' import Catch from './Catch' -import QueryString from './QueryString' import Keyboard from './Keyboard' import Storage from './Storage' +import QueryString from './QueryString' +import Style from './Style' const NORMAL = 'normal' const PRESENTER = 'presenter' @@ -30,156 +33,152 @@ const modes = { const BaseWrapper = props => <>{props.children} -export class MDXDeck extends React.Component { - constructor(props) { - super(props) +const getIndex = ({ basepath }) => { + const { pathname } = globalHistory.location + const root = pathname.replace(basepath, '') + const n = Number(root.split('/')[1]) + const index = isNaN(n) ? 0 : n + return index +} - this.state = { - slides: props.slides, - step: 0, - mode: NORMAL, - update: fn => this.setState(fn), - } +const mergeState = (state, next) => + merge({}, state, typeof next === 'function' ? next(state) : next) +const useState = init => useReducer(mergeState, init) + +const getWrapper = mode => { + switch (mode) { + case PRESENTER: + return Presenter + case OVERVIEW: + return Overview + case GRID: + return Grid + default: + return BaseWrapper + break + } +} + +export const MDXDeckContext = React.createContext() + +const useDeckState = () => { + const context = useContext(MDXDeckContext) + if (context) return context + + const [state, setState] = useState({ + metadata: {}, + step: 0, + mode: NORMAL, + }) + + return { + state, + setState, + } +} + +export const MDXDeckState = ({ children }) => { + const context = useDeckState() + return +} + +export const MDXDeck = props => { + const { slides, basepath } = props + const { state, setState } = useDeckState(MDXDeckContext) + + const index = getIndex(props) + + const getMeta = i => { + return state.metadata[i] || {} } - getIndex = () => { - const { basepath } = this.props - const { pathname } = globalHistory.location - const pagepath = pathname.replace(basepath, '') - const n = Number(pagepath.split('/')[1]) - const index = isNaN(n) ? 0 : n - return index + const register = (index, meta) => { + setState({ + metadata: { + ...state.metadata, + [index]: { + ...state.metadata[index], + ...meta, + }, + }, + }) } - getMeta = i => { - const { slides } = this.state - const { meta = {} } = slides[i] || {} - return meta - } - - goto = i => { - const { basepath } = this.props - const current = this.getIndex() - const reverse = i < current - navigate(basepath + '/' + i) - const meta = this.getMeta(i) - this.setState({ + const goto = nextIndex => { + const current = getIndex(props) + const reverse = nextIndex < current + const { search } = globalHistory.location + navigate(basepath + '/' + nextIndex + search) + const meta = getMeta(nextIndex) + setState({ step: reverse ? meta.steps || 0 : 0, }) } - previous = () => { - const { step } = this.state - const index = this.getIndex() - const meta = this.getMeta(index) - if (meta.steps && step > 0) { - this.setState(state => ({ - step: state.step - 1, - })) + const previous = () => { + const current = getIndex(props) + const meta = getMeta(current) + if (meta.steps && state.step > 0) { + setState({ step: state.step - 1 }) } else { - const previous = index - 1 - if (previous < 0) return - this.goto(previous) + const p = current - 1 + if (p < 0) return + goto(p) } } - next = () => { - const { slides, step } = this.state - const index = this.getIndex() - const meta = this.getMeta(index) - if (meta.steps && step < meta.steps) { - this.setState(state => ({ - step: state.step + 1, - })) + const next = () => { + const current = getIndex(props) + const meta = getMeta(current) + if (meta.steps && state.step < meta.steps) { + setState({ step: state.step + 1 }) } else { - const next = index + 1 - if (next > slides.length - 1) return - this.goto(next) + const n = current + 1 + if (n > slides.length - 1) return + goto(n) } } - register = (index, meta) => { - const { slides } = this.state - const initialMeta = slides[index].meta || {} - slides[index].meta = { ...initialMeta, ...meta } - this.setState({ slides }) + const context = { + ...state, + update: setState, + register, + modes, + index, + goto, + previous, + next, } - componentDidCatch(err) { - console.error('componentDidCatch') - console.error(err) - } + const [First] = slides + const Wrapper = getWrapper(state.mode) - render() { - const { basepath } = this.props - const { pathname } = globalHistory.location - const { slides } = this.state - const pagepath = pathname.replace(basepath, '') - const mode = pagepath === '/print' ? PRINT : this.state.mode - const index = this.getIndex() - const context = { - ...this.state, - register: this.register, - modes, - previous: this.previous, - next: this.next, - } - - const [FirstSlide] = slides - - let Wrapper = BaseWrapper - switch (mode) { - case PRESENTER: - Wrapper = Presenter - break - case OVERVIEW: - Wrapper = Overview - break - case GRID: - Wrapper = Grid - break - default: - break - } - - const style = - mode !== modes.PRINT ? ( - - ) : null - - return ( - - {style} - - - - - - - - - - + return ( + + +