mirror of
https://github.com/jxnblk/mdx-deck.git
synced 2024-11-29 05:46:00 +03:00
Reorganize modules
This commit is contained in:
parent
e97e7a1df7
commit
a838705222
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
dist
|
dist
|
||||||
site
|
public
|
||||||
coverage
|
coverage
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
34
packages/components/notes.md
Normal file
34
packages/components/notes.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# notes
|
||||||
|
|
||||||
|
mdx-deck v2 prototype
|
||||||
|
|
||||||
|
todo:
|
||||||
|
|
||||||
|
- [x] Head
|
||||||
|
- [x] Image
|
||||||
|
- [x] Notes
|
||||||
|
- [x] Appear
|
||||||
|
- [ ] Code (may handle this with theming)
|
||||||
|
- [ ] normalize slide styles with v1
|
||||||
|
- [x] history api fallback
|
||||||
|
- [x] mdx components
|
||||||
|
- [x] themes
|
||||||
|
- [ ] layouts
|
||||||
|
- [ ] presenter mode
|
||||||
|
- [ ] timer/clock
|
||||||
|
- [ ] open new tab button
|
||||||
|
- [ ] look at google slides behavior
|
||||||
|
- [ ] overview mode
|
||||||
|
- [ ] highlighted state bug
|
||||||
|
- [ ] slide numbers
|
||||||
|
- [ ] localStorage
|
||||||
|
- [ ] keyboard shortcuts
|
||||||
|
- [ ] docs for customizing any component
|
||||||
|
- [x] swipeable
|
||||||
|
|
||||||
|
extras
|
||||||
|
|
||||||
|
- [ ] docs for syntax highlighting
|
||||||
|
- [ ] Print view
|
||||||
|
- [ ] PDF export?
|
||||||
|
- [ ] dots??
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@mdx-deck/components",
|
"name": "@mdx-deck/components",
|
||||||
"version": "2.0.0-0",
|
"version": "2.0.0-0",
|
||||||
"main": "index.js",
|
"main": "src/index.js",
|
||||||
"author": "Brent Jackson <jxnblk@gmail.com>",
|
"author": "Brent Jackson <jxnblk@gmail.com>",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
|
26
packages/components/src/Appear.js
Normal file
26
packages/components/src/Appear.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Steps from './Steps'
|
||||||
|
|
||||||
|
export const Appear = props => {
|
||||||
|
const arr = React.Children.toArray(props.children)
|
||||||
|
return (
|
||||||
|
<Steps
|
||||||
|
length={arr.length}
|
||||||
|
render={({ step }) => {
|
||||||
|
const children = arr.map((child, i) =>
|
||||||
|
i < step
|
||||||
|
? child
|
||||||
|
: React.cloneElement(child, {
|
||||||
|
style: {
|
||||||
|
...child.props.style,
|
||||||
|
visibility: 'hidden',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return <>{children}</>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Appear
|
68
packages/components/src/Head.js
Normal file
68
packages/components/src/Head.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React, { useContext } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
|
export const HeadContext = React.createContext({
|
||||||
|
tags: [],
|
||||||
|
push: () => {
|
||||||
|
console.warn('Missing HeadProvider')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const HeadProvider = ({ tags = [], children }) => {
|
||||||
|
const push = elements => {
|
||||||
|
tags.push(...elements)
|
||||||
|
}
|
||||||
|
const context = { push }
|
||||||
|
return <HeadContext.Provider value={context}>{children}</HeadContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Head extends React.Component {
|
||||||
|
state = {
|
||||||
|
didMount: false,
|
||||||
|
}
|
||||||
|
rehydrate = () => {
|
||||||
|
const children = React.Children.toArray(this.props.children)
|
||||||
|
const nodes = [...document.head.querySelectorAll('[data-head]')]
|
||||||
|
nodes.forEach(node => {
|
||||||
|
node.remove()
|
||||||
|
})
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child.type === 'title') {
|
||||||
|
const title = document.head.querySelector('title')
|
||||||
|
if (title) title.remove()
|
||||||
|
}
|
||||||
|
if (child.type === 'meta') {
|
||||||
|
const { name } = child.props
|
||||||
|
let meta
|
||||||
|
if (name) meta = document.head.querySelector(`meta[name="${name}"]`)
|
||||||
|
if (meta) meta.remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.setState({ didMount: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.rehydrate()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const children = React.Children.toArray(this.props.children).map(child =>
|
||||||
|
React.cloneElement(child, {
|
||||||
|
'data-head': true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
if (!this.state.didMount) {
|
||||||
|
return (
|
||||||
|
<HeadContext.Consumer
|
||||||
|
children={({ push }) => {
|
||||||
|
push(children)
|
||||||
|
return false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return createPortal(children, document.head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Head
|
22
packages/components/src/Image.js
Normal file
22
packages/components/src/Image.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
export const Image = styled.div(
|
||||||
|
{
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
},
|
||||||
|
props => ({
|
||||||
|
backgroundSize: props.size,
|
||||||
|
width: props.width,
|
||||||
|
height: props.height,
|
||||||
|
backgroundImage: `url(${props.src})`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
Image.defaultProps = {
|
||||||
|
size: 'cover',
|
||||||
|
width: '100vw',
|
||||||
|
height: '100vh',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Image
|
196
packages/components/src/MDXDeck.js
Normal file
196
packages/components/src/MDXDeck.js
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Router, globalHistory, navigate, Link } from '@reach/router'
|
||||||
|
import { Swipeable } from 'react-swipeable'
|
||||||
|
import Provider from './Provider'
|
||||||
|
import Root from './Root'
|
||||||
|
import Slide from './Slide'
|
||||||
|
import { default as defaultTheme } from '@mdx-deck/themes'
|
||||||
|
|
||||||
|
const NORMAL = 'NORMAL'
|
||||||
|
const PRESENTER = 'PRESENTER'
|
||||||
|
const OVERVIEW = 'OVERVIEW'
|
||||||
|
const PRINT = 'PRINT'
|
||||||
|
|
||||||
|
const keys = {
|
||||||
|
right: 39,
|
||||||
|
left: 37,
|
||||||
|
space: 32,
|
||||||
|
p: 80,
|
||||||
|
o: 79,
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleMode = key => state => ({
|
||||||
|
mode: state.mode === key ? NORMAL : key,
|
||||||
|
})
|
||||||
|
|
||||||
|
const BaseWrapper = props => <>{props.children}</>
|
||||||
|
|
||||||
|
export class MDXDeck extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
slides: props.slides,
|
||||||
|
step: 0,
|
||||||
|
mode: NORMAL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown = e => {
|
||||||
|
const { key, keyCode, metaKey, ctrlKey, altKey, shiftKey } = e
|
||||||
|
const { activeElement } = document
|
||||||
|
if (activeElement.tagName !== 'BODY' && activeElement.tagName !== 'DIV')
|
||||||
|
return
|
||||||
|
if (metaKey || ctrlKey) return
|
||||||
|
const alt = altKey && !shiftKey
|
||||||
|
const shift = shiftKey && !altKey
|
||||||
|
|
||||||
|
if (alt) {
|
||||||
|
switch (keyCode) {
|
||||||
|
case keys.p:
|
||||||
|
this.setState(toggleMode(PRESENTER))
|
||||||
|
break
|
||||||
|
case keys.o:
|
||||||
|
this.setState(toggleMode(OVERVIEW))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (keyCode) {
|
||||||
|
case keys.left:
|
||||||
|
e.preventDefault()
|
||||||
|
this.previous()
|
||||||
|
break
|
||||||
|
case keys.right:
|
||||||
|
case keys.space:
|
||||||
|
e.preventDefault()
|
||||||
|
this.next()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndex = () => {
|
||||||
|
const { pathname } = globalHistory.location
|
||||||
|
return Number(pathname.split('/')[1] || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
getMeta = i => {
|
||||||
|
const { slides } = this.state
|
||||||
|
const { meta = {} } = slides[i] || {}
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
||||||
|
goto = i => {
|
||||||
|
const current = this.getIndex()
|
||||||
|
const reverse = i < current
|
||||||
|
navigate('/' + i)
|
||||||
|
const meta = this.getMeta(i)
|
||||||
|
this.setState({
|
||||||
|
step: reverse ? meta.steps || 0 : 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = () => {
|
||||||
|
const { slides, step } = this.state
|
||||||
|
const index = this.getIndex()
|
||||||
|
const meta = this.getMeta(index)
|
||||||
|
if (meta.steps && step > 0) {
|
||||||
|
this.setState(state => ({
|
||||||
|
step: state.step - 1,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
const previous = index - 1
|
||||||
|
if (previous < 0) return
|
||||||
|
this.goto(previous)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
const next = index + 1
|
||||||
|
if (next > slides.length - 1) return
|
||||||
|
this.goto(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register = (index, meta) => {
|
||||||
|
const { slides } = this.state
|
||||||
|
const initialMeta = slides[index].meta || {}
|
||||||
|
slides[index].meta = { ...initialMeta, ...meta }
|
||||||
|
this.setState({ slides })
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.body.addEventListener('keydown', this.handleKeyDown)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.body.removeEventListener('keydown', this.handleKeyDown)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { slides, mode } = this.state
|
||||||
|
const index = this.getIndex()
|
||||||
|
const meta = this.getMeta(index)
|
||||||
|
const context = {
|
||||||
|
...this.state,
|
||||||
|
register: this.register,
|
||||||
|
}
|
||||||
|
|
||||||
|
const [FirstSlide] = slides
|
||||||
|
|
||||||
|
let Wrapper = BaseWrapper
|
||||||
|
switch (mode) {
|
||||||
|
case PRESENTER:
|
||||||
|
Wrapper = Presenter
|
||||||
|
break
|
||||||
|
case OVERVIEW:
|
||||||
|
Wrapper = Overview
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Provider {...this.props} {...this.state} index={index}>
|
||||||
|
<Wrapper {...this.state} index={index}>
|
||||||
|
<Swipeable onSwipedRight={this.previous} onSwipedLeft={this.next}>
|
||||||
|
<Root>
|
||||||
|
{/*<GoogleFonts />*/}
|
||||||
|
<Router>
|
||||||
|
<Slide path="/" index={0} {...context}>
|
||||||
|
<FirstSlide path="/" />
|
||||||
|
</Slide>
|
||||||
|
{slides.map((Component, i) => (
|
||||||
|
<Slide key={i} path={i + '/*'} index={i} {...context}>
|
||||||
|
<Component path={i + '/*'} />
|
||||||
|
</Slide>
|
||||||
|
))}
|
||||||
|
</Router>
|
||||||
|
</Root>
|
||||||
|
</Swipeable>
|
||||||
|
</Wrapper>
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MDXDeck.propTypes = {
|
||||||
|
slides: PropTypes.array.isRequired,
|
||||||
|
theme: PropTypes.object.isRequired,
|
||||||
|
headTags: PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
MDXDeck.defaultProps = {
|
||||||
|
slides: [],
|
||||||
|
theme: defaultTheme,
|
||||||
|
headTags: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MDXDeck
|
19
packages/components/src/Notes.js
Normal file
19
packages/components/src/Notes.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { withContext } from './context'
|
||||||
|
|
||||||
|
export const Notes = withContext(
|
||||||
|
class extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
const { context, children } = props
|
||||||
|
if (!context || typeof context.index === 'undefined') return
|
||||||
|
context.register(context.index, { notes: children })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Notes
|
@ -1,92 +1,65 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import { Link } from '@reach/router'
|
||||||
import get from 'lodash.get'
|
|
||||||
import Box from './Box'
|
|
||||||
import Flex from './Flex'
|
|
||||||
import Zoom from './Zoom'
|
import Zoom from './Zoom'
|
||||||
import Slide from './Slide'
|
import Slide from './Slide'
|
||||||
import Root from './Root'
|
|
||||||
import Mono from './Mono'
|
|
||||||
|
|
||||||
export const Overview = ({
|
const noop = () => {}
|
||||||
index,
|
|
||||||
length,
|
export const Overview = props => {
|
||||||
slides = [],
|
const { index, slides } = props
|
||||||
mode,
|
|
||||||
metadata = {},
|
|
||||||
update,
|
|
||||||
step,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const notes = get(metadata, index + '.notes')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<div
|
||||||
color="white"
|
style={{
|
||||||
bg="black"
|
display: 'flex',
|
||||||
css={{
|
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
|
backgroundColor: 'black',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<div
|
||||||
mr="auto"
|
style={{
|
||||||
px={2}
|
|
||||||
py={3}
|
|
||||||
css={{
|
|
||||||
flex: 'none',
|
flex: 'none',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
|
paddingLeft: 4,
|
||||||
|
paddingRight: 4,
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{slides.map((Component, i) => (
|
{slides.map((Component, i) => (
|
||||||
<Box
|
<Link
|
||||||
key={i}
|
key={i}
|
||||||
role="link"
|
to={'/' + i}
|
||||||
p={1}
|
|
||||||
style={{
|
style={{
|
||||||
outline: i === index ? '1px solid #07c' : null,
|
display: 'block',
|
||||||
}}
|
color: 'inherit',
|
||||||
css={{
|
textDecoration: 'none',
|
||||||
|
padding: 0,
|
||||||
|
marginTop: 4,
|
||||||
|
marginBottom: 4,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
}}
|
outline: i === index ? '4px solid #0cf' : null,
|
||||||
onClick={e => {
|
|
||||||
update({ index: i })
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Zoom zoom={1 / 6}>
|
<Zoom zoom={1 / 6}>
|
||||||
<Root {...props}>
|
<Slide register={noop}>
|
||||||
<Slide>
|
|
||||||
<Component />
|
<Component />
|
||||||
</Slide>
|
</Slide>
|
||||||
</Root>
|
|
||||||
</Zoom>
|
</Zoom>
|
||||||
</Box>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</div>
|
||||||
<Box mx="auto" py={4} width={2 / 3}>
|
<div
|
||||||
<Zoom zoom={2 / 3}>
|
style={{
|
||||||
<Root {...props}>{props.children}</Root>
|
width: 200 / 3 + '%',
|
||||||
</Zoom>
|
margin: 'auto',
|
||||||
<Flex>
|
}}
|
||||||
<Box ml="auto" py={2}>
|
>
|
||||||
{index + 1}/{length}
|
<Zoom zoom={2 / 3}>{props.children}</Zoom>
|
||||||
</Box>
|
</div>
|
||||||
</Flex>
|
</div>
|
||||||
<Box mt={3}>{notes}</Box>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Overview.propTypes = {
|
|
||||||
index: PropTypes.number.isRequired,
|
|
||||||
length: PropTypes.number.isRequired,
|
|
||||||
update: PropTypes.func.isRequired,
|
|
||||||
step: PropTypes.number.isRequired,
|
|
||||||
slides: PropTypes.array,
|
|
||||||
mode: PropTypes.string,
|
|
||||||
notes: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Overview
|
export default Overview
|
||||||
|
@ -1,112 +1,68 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import get from 'lodash.get'
|
|
||||||
import Box from './Box'
|
|
||||||
import Flex from './Flex'
|
|
||||||
import Zoom from './Zoom'
|
import Zoom from './Zoom'
|
||||||
import Slide from './Slide'
|
import Slide from './Slide'
|
||||||
import Root from './Root'
|
|
||||||
import Timer from './Timer'
|
|
||||||
import Mono from './Mono'
|
|
||||||
import Button from './Button'
|
|
||||||
|
|
||||||
const Anchor = Button.withComponent('a')
|
const noop = () => {}
|
||||||
|
|
||||||
export const Presenter = ({
|
export const Presenter = props => {
|
||||||
index,
|
const { slides, index } = props
|
||||||
length,
|
const Current = slides[index]
|
||||||
slides = [],
|
|
||||||
mode,
|
|
||||||
metadata = {},
|
|
||||||
update,
|
|
||||||
step,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const Next = slides[index + 1]
|
const Next = slides[index + 1]
|
||||||
const notes = get(metadata, index + '.notes')
|
const { notes } = Current.meta || {}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<div
|
||||||
color="white"
|
style={{
|
||||||
bg="black"
|
backgroundColor: 'black',
|
||||||
css={{
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex my="auto">
|
<div
|
||||||
<Box
|
style={{
|
||||||
mx="auto"
|
marginTop: 'auto',
|
||||||
width={5 / 8}
|
marginBottom: 'auto',
|
||||||
css={{
|
display: 'flex',
|
||||||
border: '1px solid rgba(128, 128, 128, 0.25)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Zoom zoom={5 / 8}>
|
<div
|
||||||
<Root {...props}>{props.children}</Root>
|
style={{
|
||||||
</Zoom>
|
width: 500 / 8 + '%',
|
||||||
</Box>
|
marginLeft: 'auto',
|
||||||
<Flex
|
marginRight: 'auto',
|
||||||
width={1 / 4}
|
|
||||||
mx="auto"
|
|
||||||
css={{
|
|
||||||
flex: 'none',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Zoom zoom={5 / 8}>{props.children}</Zoom>
|
||||||
mx="auto"
|
</div>
|
||||||
css={{
|
<div
|
||||||
border: '1px solid rgba(128, 128, 128, 0.25)',
|
style={{
|
||||||
|
width: 100 / 4 + '%',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Zoom zoom={1 / 4}>
|
<Zoom zoom={1 / 4}>
|
||||||
<Root {...props}>
|
|
||||||
{Next && (
|
{Next && (
|
||||||
<Slide>
|
<Slide register={noop}>
|
||||||
<Next />
|
<Next />
|
||||||
</Slide>
|
</Slide>
|
||||||
)}
|
)}
|
||||||
</Root>
|
|
||||||
</Zoom>
|
</Zoom>
|
||||||
</Box>
|
{notes}
|
||||||
<Box
|
</div>
|
||||||
py={3}
|
</div>
|
||||||
css={{
|
<div
|
||||||
flex: 'auto',
|
style={{
|
||||||
|
color: 'white',
|
||||||
|
padding: 16,
|
||||||
|
fontSize: 20,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{notes}
|
<pre style={{ fontFamily: 'Menlo, monospace' }}>
|
||||||
</Box>
|
{index + 1} of {slides.length}
|
||||||
</Flex>
|
</pre>
|
||||||
</Flex>
|
</div>
|
||||||
<Flex mt="auto" px={3} py={3}>
|
</div>
|
||||||
<Mono>
|
|
||||||
Slide {index + 1} of {length}
|
|
||||||
</Mono>
|
|
||||||
<Box mx="auto" />
|
|
||||||
<Anchor
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={`${window.location.origin}/${window.location.hash}`}
|
|
||||||
>
|
|
||||||
Open in Normal mode
|
|
||||||
</Anchor>
|
|
||||||
<Box mx="auto" />
|
|
||||||
<Timer />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Presenter.propTypes = {
|
|
||||||
index: PropTypes.number.isRequired,
|
|
||||||
length: PropTypes.number.isRequired,
|
|
||||||
update: PropTypes.func.isRequired,
|
|
||||||
step: PropTypes.number.isRequired,
|
|
||||||
slides: PropTypes.array,
|
|
||||||
mode: PropTypes.string,
|
|
||||||
metadata: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Presenter
|
|
||||||
|
32
packages/components/src/Provider.js
Normal file
32
packages/components/src/Provider.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { HeadProvider } from './Head'
|
||||||
|
import { ThemeProvider } from 'styled-components'
|
||||||
|
import { MDXProvider } from '@mdx-js/tag'
|
||||||
|
import mdxComponents from './mdx-components'
|
||||||
|
|
||||||
|
const DefaultProvider = props => <>{props.children}</>
|
||||||
|
|
||||||
|
export const Provider = props => {
|
||||||
|
const { headTags, theme, components } = props
|
||||||
|
const {
|
||||||
|
Provider: UserProvider = DefaultProvider,
|
||||||
|
components: themeComponents = {},
|
||||||
|
} = theme
|
||||||
|
|
||||||
|
const allComponents = {
|
||||||
|
...mdxComponents,
|
||||||
|
...themeComponents,
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeadProvider tags={headTags}>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<MDXProvider components={allComponents}>
|
||||||
|
<UserProvider {...props} />
|
||||||
|
</MDXProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</HeadProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Provider
|
31
packages/components/src/Root.js
Normal file
31
packages/components/src/Root.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const themed = (...tags) => props =>
|
||||||
|
tags.map(tag => props.theme[tag] && { ['& ' + tag]: props.theme[tag] })
|
||||||
|
|
||||||
|
export const Root = styled.div(
|
||||||
|
props => ({
|
||||||
|
fontFamily: props.theme.font,
|
||||||
|
color: props.theme.colors.text,
|
||||||
|
backgroundColor: props.theme.colors.background,
|
||||||
|
}),
|
||||||
|
props => props.theme.css,
|
||||||
|
themed(
|
||||||
|
'h1',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6',
|
||||||
|
'a',
|
||||||
|
'ul',
|
||||||
|
'ol',
|
||||||
|
'li',
|
||||||
|
'p',
|
||||||
|
'blockquote',
|
||||||
|
'img',
|
||||||
|
'table'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Root
|
23
packages/components/src/Slide.js
Normal file
23
packages/components/src/Slide.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Context } from './context'
|
||||||
|
|
||||||
|
const SlideRoot = styled.div(
|
||||||
|
{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: '100vw',
|
||||||
|
height: '100vh',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
props => props.theme.Slide
|
||||||
|
)
|
||||||
|
|
||||||
|
export const Slide = ({ children, ...props }) => (
|
||||||
|
<Context.Provider value={props}>
|
||||||
|
<SlideRoot>{children}</SlideRoot>
|
||||||
|
</Context.Provider>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Slide
|
20
packages/components/src/Steps.js
Normal file
20
packages/components/src/Steps.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { withContext } from './context'
|
||||||
|
|
||||||
|
export const Steps = withContext(
|
||||||
|
class extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
const { register, index } = props.context
|
||||||
|
const { length } = props
|
||||||
|
register(index, { steps: length })
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { context, render } = this.props
|
||||||
|
const { step } = context
|
||||||
|
return render({ step })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Steps
|
@ -1,41 +1,23 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
const ZoomRoot = styled.div(
|
const ZoomRoot = styled.div(props => ({
|
||||||
[],
|
backgroundColor: props.theme.colors.background,
|
||||||
{
|
|
||||||
backgroundColor: 'white',
|
|
||||||
},
|
|
||||||
props => ({
|
|
||||||
width: 100 * props.zoom + 'vw',
|
width: 100 * props.zoom + 'vw',
|
||||||
height: 100 * props.zoom + 'vh',
|
height: 100 * props.zoom + 'vh',
|
||||||
})
|
}))
|
||||||
)
|
|
||||||
|
|
||||||
ZoomRoot.propTypes = {
|
|
||||||
zoom: PropTypes.number.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
const ZoomInner = styled.div([], props => ({
|
const ZoomInner = styled.div([], props => ({
|
||||||
transformOrigin: '0 0',
|
transformOrigin: '0 0',
|
||||||
transform: `scale(${props.zoom})`,
|
transform: `scale(${props.zoom})`,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
ZoomInner.propTypes = {
|
export const Zoom = ({ zoom, ...props }) => (
|
||||||
zoom: PropTypes.number.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Zoom = ({ zoom, ...props }) => (
|
|
||||||
<ZoomRoot zoom={zoom}>
|
<ZoomRoot zoom={zoom}>
|
||||||
<ZoomInner zoom={zoom} {...props} />
|
<ZoomInner zoom={zoom} {...props} />
|
||||||
</ZoomRoot>
|
</ZoomRoot>
|
||||||
)
|
)
|
||||||
|
|
||||||
Zoom.propTypes = {
|
|
||||||
zoom: PropTypes.number,
|
|
||||||
}
|
|
||||||
|
|
||||||
Zoom.defaultProps = {
|
Zoom.defaultProps = {
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
}
|
}
|
||||||
|
92
packages/components/src/_ref/Overview.js
Normal file
92
packages/components/src/_ref/Overview.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import get from 'lodash.get'
|
||||||
|
import Box from './Box'
|
||||||
|
import Flex from './Flex'
|
||||||
|
import Zoom from './Zoom'
|
||||||
|
import Slide from './Slide'
|
||||||
|
import Root from './Root'
|
||||||
|
import Mono from './Mono'
|
||||||
|
|
||||||
|
export const Overview = ({
|
||||||
|
index,
|
||||||
|
length,
|
||||||
|
slides = [],
|
||||||
|
mode,
|
||||||
|
metadata = {},
|
||||||
|
update,
|
||||||
|
step,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
const notes = get(metadata, index + '.notes')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
color="white"
|
||||||
|
bg="black"
|
||||||
|
css={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
height: '100vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
mr="auto"
|
||||||
|
px={2}
|
||||||
|
py={3}
|
||||||
|
css={{
|
||||||
|
flex: 'none',
|
||||||
|
height: '100vh',
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{slides.map((Component, i) => (
|
||||||
|
<Box
|
||||||
|
key={i}
|
||||||
|
role="link"
|
||||||
|
p={1}
|
||||||
|
style={{
|
||||||
|
outline: i === index ? '1px solid #07c' : null,
|
||||||
|
}}
|
||||||
|
css={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
onClick={e => {
|
||||||
|
update({ index: i })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Zoom zoom={1 / 6}>
|
||||||
|
<Root {...props}>
|
||||||
|
<Slide>
|
||||||
|
<Component />
|
||||||
|
</Slide>
|
||||||
|
</Root>
|
||||||
|
</Zoom>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
<Box mx="auto" py={4} width={2 / 3}>
|
||||||
|
<Zoom zoom={2 / 3}>
|
||||||
|
<Root {...props}>{props.children}</Root>
|
||||||
|
</Zoom>
|
||||||
|
<Flex>
|
||||||
|
<Box ml="auto" py={2}>
|
||||||
|
{index + 1}/{length}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<Box mt={3}>{notes}</Box>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Overview.propTypes = {
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
length: PropTypes.number.isRequired,
|
||||||
|
update: PropTypes.func.isRequired,
|
||||||
|
step: PropTypes.number.isRequired,
|
||||||
|
slides: PropTypes.array,
|
||||||
|
mode: PropTypes.string,
|
||||||
|
notes: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Overview
|
112
packages/components/src/_ref/Presenter.js
Normal file
112
packages/components/src/_ref/Presenter.js
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import get from 'lodash.get'
|
||||||
|
import Box from './Box'
|
||||||
|
import Flex from './Flex'
|
||||||
|
import Zoom from './Zoom'
|
||||||
|
import Slide from './Slide'
|
||||||
|
import Root from './Root'
|
||||||
|
import Timer from './Timer'
|
||||||
|
import Mono from './Mono'
|
||||||
|
import Button from './Button'
|
||||||
|
|
||||||
|
const Anchor = Button.withComponent('a')
|
||||||
|
|
||||||
|
export const Presenter = ({
|
||||||
|
index,
|
||||||
|
length,
|
||||||
|
slides = [],
|
||||||
|
mode,
|
||||||
|
metadata = {},
|
||||||
|
update,
|
||||||
|
step,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
const Next = slides[index + 1]
|
||||||
|
const notes = get(metadata, index + '.notes')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
color="white"
|
||||||
|
bg="black"
|
||||||
|
css={{
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex my="auto">
|
||||||
|
<Box
|
||||||
|
mx="auto"
|
||||||
|
width={5 / 8}
|
||||||
|
css={{
|
||||||
|
border: '1px solid rgba(128, 128, 128, 0.25)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Zoom zoom={5 / 8}>
|
||||||
|
<Root {...props}>{props.children}</Root>
|
||||||
|
</Zoom>
|
||||||
|
</Box>
|
||||||
|
<Flex
|
||||||
|
width={1 / 4}
|
||||||
|
mx="auto"
|
||||||
|
css={{
|
||||||
|
flex: 'none',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
mx="auto"
|
||||||
|
css={{
|
||||||
|
border: '1px solid rgba(128, 128, 128, 0.25)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Zoom zoom={1 / 4}>
|
||||||
|
<Root {...props}>
|
||||||
|
{Next && (
|
||||||
|
<Slide>
|
||||||
|
<Next />
|
||||||
|
</Slide>
|
||||||
|
)}
|
||||||
|
</Root>
|
||||||
|
</Zoom>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
py={3}
|
||||||
|
css={{
|
||||||
|
flex: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{notes}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<Flex mt="auto" px={3} py={3}>
|
||||||
|
<Mono>
|
||||||
|
Slide {index + 1} of {length}
|
||||||
|
</Mono>
|
||||||
|
<Box mx="auto" />
|
||||||
|
<Anchor
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={`${window.location.origin}/${window.location.hash}`}
|
||||||
|
>
|
||||||
|
Open in Normal mode
|
||||||
|
</Anchor>
|
||||||
|
<Box mx="auto" />
|
||||||
|
<Timer />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Presenter.propTypes = {
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
length: PropTypes.number.isRequired,
|
||||||
|
update: PropTypes.func.isRequired,
|
||||||
|
step: PropTypes.number.isRequired,
|
||||||
|
slides: PropTypes.array,
|
||||||
|
mode: PropTypes.string,
|
||||||
|
metadata: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Presenter
|
8
packages/components/src/_temp.js
Normal file
8
packages/components/src/_temp.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// may cut some of this
|
||||||
|
|
||||||
|
const GoogleFonts = withTheme(
|
||||||
|
props =>
|
||||||
|
!!props.theme.googleFont && (
|
||||||
|
<link href={props.theme.googleFont} rel="stylesheet" />
|
||||||
|
)
|
||||||
|
)
|
11
packages/components/src/context.js
Normal file
11
packages/components/src/context.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { useContext } from 'react'
|
||||||
|
|
||||||
|
export const Context = React.createContext(null)
|
||||||
|
|
||||||
|
export const withContext = Component => props => (
|
||||||
|
<Context.Consumer
|
||||||
|
children={context => <Component {...props} context={context} />}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const useDeck = () => useContext(Context)
|
@ -1,641 +1,7 @@
|
|||||||
/*
|
export { MDXDeck } from './MDXDeck'
|
||||||
* mdx-deck v2 prototype
|
export { Head } from './Head'
|
||||||
*
|
export { Image } from './Image'
|
||||||
* todo:
|
export { Notes } from './Notes'
|
||||||
* - [x] Head
|
export { Steps } from './Steps'
|
||||||
* - [x] Image
|
export { Appear } from './Appear'
|
||||||
* - [x] Notes
|
export { withContext, useDeck } from './context'
|
||||||
* - [x] Appear
|
|
||||||
* - [ ] Code (may handle this with theming)
|
|
||||||
* - [x] history api fallback
|
|
||||||
* - [x] mdx components
|
|
||||||
* - [x] themes
|
|
||||||
* - [ ] layouts
|
|
||||||
* - [ ] presenter mode
|
|
||||||
* - [ ] timer/clock
|
|
||||||
* - [ ] open new tab button
|
|
||||||
* - [ ] look at google slides behavior
|
|
||||||
* - [ ] overview mode
|
|
||||||
* - [ ] highlighted state bug
|
|
||||||
* - [ ] slide numbers
|
|
||||||
* - [ ] localStorage
|
|
||||||
* - [ ] keyboard shortcuts
|
|
||||||
* - [ ] docs for customizing any component
|
|
||||||
* - [x] swipeable
|
|
||||||
*
|
|
||||||
* extras
|
|
||||||
* - [ ] docs for syntax highlighting
|
|
||||||
* - [ ] Print view
|
|
||||||
* - [ ] PDF export?
|
|
||||||
* - [ ] dots??
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import { createPortal } from 'react-dom'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { Router, globalHistory, navigate, Link } from '@reach/router'
|
|
||||||
import styled, { ThemeProvider, withTheme } from 'styled-components'
|
|
||||||
import { MDXProvider } from '@mdx-js/tag'
|
|
||||||
import { Swipeable } from 'react-swipeable'
|
|
||||||
import { default as defaultTheme } from './themes'
|
|
||||||
|
|
||||||
const NORMAL = 'NORMAL'
|
|
||||||
const PRESENTER = 'PRESENTER'
|
|
||||||
const OVERVIEW = 'OVERVIEW'
|
|
||||||
const PRINT = 'PRINT'
|
|
||||||
|
|
||||||
export const Context = React.createContext(null)
|
|
||||||
|
|
||||||
export const withContext = Component => props => (
|
|
||||||
<Context.Consumer
|
|
||||||
children={context => <Component {...props} context={context} />}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO check against v1 styles
|
|
||||||
const SlideRoot = styled.div(
|
|
||||||
{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
width: '100vw',
|
|
||||||
height: '100vh',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
props => props.theme.Slide
|
|
||||||
)
|
|
||||||
|
|
||||||
const Slide = ({ children, ...props }) => (
|
|
||||||
<Context.Provider value={props}>
|
|
||||||
<SlideRoot>{children}</SlideRoot>
|
|
||||||
</Context.Provider>
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultProvider = props => <>{props.children}</>
|
|
||||||
|
|
||||||
// MDX components
|
|
||||||
const Heading = styled.h1({ margin: 0 })
|
|
||||||
|
|
||||||
const inlineCode = styled.code(
|
|
||||||
props => ({
|
|
||||||
fontFamily: props.theme.monospace,
|
|
||||||
}),
|
|
||||||
props => props.theme.code
|
|
||||||
)
|
|
||||||
|
|
||||||
const code = styled.pre(
|
|
||||||
props => ({
|
|
||||||
fontFamily: props.theme.monospace,
|
|
||||||
fontSize: '.75em',
|
|
||||||
}),
|
|
||||||
props => props.theme.pre
|
|
||||||
)
|
|
||||||
|
|
||||||
const img = styled.img({
|
|
||||||
maxWidth: '100%',
|
|
||||||
height: 'auto',
|
|
||||||
objectFit: 'cover',
|
|
||||||
})
|
|
||||||
|
|
||||||
const TableWrap = styled.div({
|
|
||||||
overflowX: 'auto',
|
|
||||||
})
|
|
||||||
const Table = styled.table({
|
|
||||||
width: '100%',
|
|
||||||
borderCollapse: 'separate',
|
|
||||||
borderSpacing: 0,
|
|
||||||
'& td, & th': {
|
|
||||||
textAlign: 'left',
|
|
||||||
paddingRight: '.5em',
|
|
||||||
paddingTop: '.25em',
|
|
||||||
paddingBottom: '.25em',
|
|
||||||
borderBottom: '1px solid',
|
|
||||||
verticalAlign: 'top',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const table = props => (
|
|
||||||
<TableWrap>
|
|
||||||
<Table {...props} />
|
|
||||||
</TableWrap>
|
|
||||||
)
|
|
||||||
|
|
||||||
const components = {
|
|
||||||
pre: props => props.children,
|
|
||||||
code,
|
|
||||||
inlineCode,
|
|
||||||
img,
|
|
||||||
table,
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = {
|
|
||||||
right: 39,
|
|
||||||
left: 37,
|
|
||||||
space: 32,
|
|
||||||
p: 80,
|
|
||||||
o: 79,
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleMode = key => state => ({
|
|
||||||
mode: state.mode === key ? NORMAL : key,
|
|
||||||
})
|
|
||||||
|
|
||||||
const ZoomOuter = styled.div(props => ({
|
|
||||||
backgroundColor: props.theme.colors.background,
|
|
||||||
width: 100 * props.zoom + 'vw',
|
|
||||||
height: 100 * props.zoom + 'vh',
|
|
||||||
}))
|
|
||||||
const ZoomInner = styled.div(props => ({
|
|
||||||
transformOrigin: '0 0',
|
|
||||||
transform: `scale(${props.zoom})`,
|
|
||||||
}))
|
|
||||||
const Zoom = props => (
|
|
||||||
<ZoomOuter zoom={props.zoom}>
|
|
||||||
<ZoomInner {...props} />
|
|
||||||
</ZoomOuter>
|
|
||||||
)
|
|
||||||
Zoom.defaultProps = {
|
|
||||||
zoom: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
const noop = () => {}
|
|
||||||
|
|
||||||
const Presenter = props => {
|
|
||||||
const { slides, index } = props
|
|
||||||
const Current = slides[index]
|
|
||||||
const Next = slides[index + 1]
|
|
||||||
const { notes } = Current.meta || {}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
backgroundColor: 'black',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
height: '100vh',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
display: 'flex',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 500 / 8 + '%',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Zoom zoom={5 / 8}>{props.children}</Zoom>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 100 / 4 + '%',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Zoom zoom={1 / 4}>
|
|
||||||
{Next && (
|
|
||||||
<Slide register={noop}>
|
|
||||||
<Next />
|
|
||||||
</Slide>
|
|
||||||
)}
|
|
||||||
</Zoom>
|
|
||||||
{notes}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
color: 'white',
|
|
||||||
padding: 16,
|
|
||||||
fontSize: 20,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<pre style={{ fontFamily: 'Menlo, monospace' }}>
|
|
||||||
{index + 1} of {slides.length}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Overview = props => {
|
|
||||||
const { index, slides } = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
height: '100vh',
|
|
||||||
backgroundColor: 'black',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
flex: 'none',
|
|
||||||
height: '100vh',
|
|
||||||
paddingLeft: 4,
|
|
||||||
paddingRight: 4,
|
|
||||||
overflowY: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{slides.map((Component, i) => (
|
|
||||||
<Link
|
|
||||||
key={i}
|
|
||||||
to={'/' + i}
|
|
||||||
style={{
|
|
||||||
display: 'block',
|
|
||||||
color: 'inherit',
|
|
||||||
textDecoration: 'none',
|
|
||||||
padding: 0,
|
|
||||||
marginTop: 4,
|
|
||||||
marginBottom: 4,
|
|
||||||
cursor: 'pointer',
|
|
||||||
outline: i === index ? '4px solid #0cf' : null,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Zoom zoom={1 / 6}>
|
|
||||||
<Slide register={noop}>
|
|
||||||
<Component />
|
|
||||||
</Slide>
|
|
||||||
</Zoom>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 200 / 3 + '%',
|
|
||||||
margin: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Zoom zoom={2 / 3}>{props.children}</Zoom>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = props => <>{props.children}</>
|
|
||||||
|
|
||||||
const themed = (...tags) => props =>
|
|
||||||
tags.map(tag => props.theme[tag] && { ['& ' + tag]: props.theme[tag] })
|
|
||||||
|
|
||||||
const RootStyles = styled.div(
|
|
||||||
props => ({
|
|
||||||
fontFamily: props.theme.font,
|
|
||||||
color: props.theme.colors.text,
|
|
||||||
backgroundColor: props.theme.colors.background,
|
|
||||||
}),
|
|
||||||
props => props.theme.css,
|
|
||||||
themed(
|
|
||||||
'h1',
|
|
||||||
'h2',
|
|
||||||
'h3',
|
|
||||||
'h4',
|
|
||||||
'h5',
|
|
||||||
'h6',
|
|
||||||
'a',
|
|
||||||
'ul',
|
|
||||||
'ol',
|
|
||||||
'li',
|
|
||||||
'p',
|
|
||||||
'blockquote',
|
|
||||||
'img',
|
|
||||||
'table'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const GoogleFonts = withTheme(
|
|
||||||
props =>
|
|
||||||
!!props.theme.googleFont && (
|
|
||||||
<link href={props.theme.googleFont} rel="stylesheet" />
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
export class MDXDeck extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
slides: props.slides,
|
|
||||||
step: 0,
|
|
||||||
mode: NORMAL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyDown = e => {
|
|
||||||
const { key, keyCode, metaKey, ctrlKey, altKey, shiftKey } = e
|
|
||||||
const { activeElement } = document
|
|
||||||
if (activeElement.tagName !== 'BODY' && activeElement.tagName !== 'DIV')
|
|
||||||
return
|
|
||||||
if (metaKey || ctrlKey) return
|
|
||||||
const alt = altKey && !shiftKey
|
|
||||||
const shift = shiftKey && !altKey
|
|
||||||
|
|
||||||
if (alt) {
|
|
||||||
switch (keyCode) {
|
|
||||||
case keys.p:
|
|
||||||
this.setState(toggleMode(PRESENTER))
|
|
||||||
break
|
|
||||||
case keys.o:
|
|
||||||
this.setState(toggleMode(OVERVIEW))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (keyCode) {
|
|
||||||
case keys.left:
|
|
||||||
e.preventDefault()
|
|
||||||
this.previous()
|
|
||||||
break
|
|
||||||
case keys.right:
|
|
||||||
case keys.space:
|
|
||||||
e.preventDefault()
|
|
||||||
this.next()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getIndex = () => {
|
|
||||||
const { pathname } = globalHistory.location
|
|
||||||
return Number(pathname.split('/')[1] || 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
getMeta = i => {
|
|
||||||
const { slides } = this.state
|
|
||||||
const { meta = {} } = slides[i] || {}
|
|
||||||
return meta
|
|
||||||
}
|
|
||||||
|
|
||||||
goto = i => {
|
|
||||||
const current = this.getIndex()
|
|
||||||
const reverse = i < current
|
|
||||||
navigate('/' + i)
|
|
||||||
const meta = this.getMeta(i)
|
|
||||||
this.setState({
|
|
||||||
step: reverse ? meta.steps || 0 : 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
previous = () => {
|
|
||||||
const { slides, step } = this.state
|
|
||||||
const index = this.getIndex()
|
|
||||||
const meta = this.getMeta(index)
|
|
||||||
if (meta.steps && step > 0) {
|
|
||||||
this.setState(state => ({
|
|
||||||
step: state.step - 1,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
const previous = index - 1
|
|
||||||
if (previous < 0) return
|
|
||||||
this.goto(previous)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
const next = index + 1
|
|
||||||
if (next > slides.length - 1) return
|
|
||||||
this.goto(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register = (index, meta) => {
|
|
||||||
const { slides } = this.state
|
|
||||||
const initialMeta = slides[index].meta || {}
|
|
||||||
slides[index].meta = { ...initialMeta, ...meta }
|
|
||||||
this.setState({ slides })
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.body.addEventListener('keydown', this.handleKeyDown)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.body.removeEventListener('keydown', this.handleKeyDown)
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { headTags, theme, components } = this.props
|
|
||||||
const { slides, mode } = this.state
|
|
||||||
const index = this.getIndex()
|
|
||||||
const meta = this.getMeta(index)
|
|
||||||
const context = {
|
|
||||||
...this.state,
|
|
||||||
register: this.register,
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
Provider = DefaultProvider,
|
|
||||||
components: themeComponents = {},
|
|
||||||
} = theme
|
|
||||||
|
|
||||||
const [FirstSlide] = slides
|
|
||||||
|
|
||||||
const mdxComponents = {
|
|
||||||
...components,
|
|
||||||
...themeComponents,
|
|
||||||
}
|
|
||||||
|
|
||||||
let Wrapper = Root
|
|
||||||
switch (mode) {
|
|
||||||
case PRESENTER:
|
|
||||||
Wrapper = Presenter
|
|
||||||
break
|
|
||||||
case OVERVIEW:
|
|
||||||
Wrapper = Overview
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<HeadProvider tags={headTags}>
|
|
||||||
<ThemeProvider theme={theme}>
|
|
||||||
<MDXProvider components={mdxComponents}>
|
|
||||||
<Provider {...this.state} index={index}>
|
|
||||||
<Wrapper {...this.state} index={index}>
|
|
||||||
<Swipeable
|
|
||||||
onSwipedRight={this.previous}
|
|
||||||
onSwipedLeft={this.next}
|
|
||||||
>
|
|
||||||
<RootStyles>
|
|
||||||
<GoogleFonts />
|
|
||||||
<Router>
|
|
||||||
<Slide path="/" index={0} {...context}>
|
|
||||||
<FirstSlide path="/" />
|
|
||||||
</Slide>
|
|
||||||
{slides.map((Component, i) => (
|
|
||||||
<Slide key={i} path={i + '/*'} index={i} {...context}>
|
|
||||||
<Component path={i + '/*'} />
|
|
||||||
</Slide>
|
|
||||||
))}
|
|
||||||
</Router>
|
|
||||||
</RootStyles>
|
|
||||||
</Swipeable>
|
|
||||||
</Wrapper>
|
|
||||||
</Provider>
|
|
||||||
</MDXProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</HeadProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MDXDeck.propTypes = {
|
|
||||||
slides: PropTypes.array.isRequired,
|
|
||||||
theme: PropTypes.object.isRequired,
|
|
||||||
components: PropTypes.object,
|
|
||||||
// Provider: PropTypes.func,
|
|
||||||
headTags: PropTypes.array.isRequired,
|
|
||||||
}
|
|
||||||
MDXDeck.defaultProps = {
|
|
||||||
slides: [],
|
|
||||||
theme: defaultTheme,
|
|
||||||
headTags: [],
|
|
||||||
components,
|
|
||||||
}
|
|
||||||
|
|
||||||
const HeadContext = React.createContext({
|
|
||||||
tags: [],
|
|
||||||
push: () => {
|
|
||||||
console.warn('Missing HeadProvider')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const HeadProvider = ({ tags = [], children }) => {
|
|
||||||
const push = elements => {
|
|
||||||
tags.push(...elements)
|
|
||||||
}
|
|
||||||
const context = { push }
|
|
||||||
return <HeadContext.Provider value={context}>{children}</HeadContext.Provider>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Head extends React.Component {
|
|
||||||
state = {
|
|
||||||
didMount: false,
|
|
||||||
}
|
|
||||||
rehydrate = () => {
|
|
||||||
const children = React.Children.toArray(this.props.children)
|
|
||||||
const nodes = [...document.head.querySelectorAll('[data-head]')]
|
|
||||||
nodes.forEach(node => {
|
|
||||||
node.remove()
|
|
||||||
})
|
|
||||||
children.forEach(child => {
|
|
||||||
if (child.type === 'title') {
|
|
||||||
const title = document.head.querySelector('title')
|
|
||||||
if (title) title.remove()
|
|
||||||
}
|
|
||||||
if (child.type === 'meta') {
|
|
||||||
const { name } = child.props
|
|
||||||
let meta
|
|
||||||
if (name) meta = document.head.querySelector(`meta[name="${name}"]`)
|
|
||||||
if (meta) meta.remove()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.setState({ didMount: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.rehydrate()
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const children = React.Children.toArray(this.props.children).map(child =>
|
|
||||||
React.cloneElement(child, {
|
|
||||||
'data-head': true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
if (!this.state.didMount) {
|
|
||||||
return (
|
|
||||||
<HeadContext.Consumer
|
|
||||||
children={({ push }) => {
|
|
||||||
push(children)
|
|
||||||
return false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return createPortal(children, document.head)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Image = styled.div(
|
|
||||||
{
|
|
||||||
backgroundPosition: 'center',
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
},
|
|
||||||
props => ({
|
|
||||||
backgroundSize: props.size,
|
|
||||||
width: props.width,
|
|
||||||
height: props.height,
|
|
||||||
backgroundImage: `url(${props.src})`,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
Image.defaultProps = {
|
|
||||||
size: 'cover',
|
|
||||||
width: '100vw',
|
|
||||||
height: '100vh',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Notes = withContext(
|
|
||||||
class extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
const { context, children } = props
|
|
||||||
if (!context || typeof context.index === 'undefined') return
|
|
||||||
context.register(context.index, { notes: children })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export const Steps = withContext(
|
|
||||||
class extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
const { register, index } = props.context
|
|
||||||
const { length } = props
|
|
||||||
register(index, { steps: length })
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
const { context, render } = this.props
|
|
||||||
const { step } = context
|
|
||||||
return render({ step })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export const Appear = props => {
|
|
||||||
const arr = React.Children.toArray(props.children)
|
|
||||||
return (
|
|
||||||
<Steps
|
|
||||||
length={arr.length}
|
|
||||||
render={({ step }) => {
|
|
||||||
const children = arr.map((child, i) =>
|
|
||||||
i < step
|
|
||||||
? child
|
|
||||||
: React.cloneElement(child, {
|
|
||||||
style: {
|
|
||||||
...child.props.style,
|
|
||||||
visibility: 'hidden',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return <>{children}</>
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
58
packages/components/src/mdx-components.js
Normal file
58
packages/components/src/mdx-components.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
export const Heading = styled.h1({ margin: 0 })
|
||||||
|
|
||||||
|
export const inlineCode = styled.code(
|
||||||
|
props => ({
|
||||||
|
fontFamily: props.theme.monospace,
|
||||||
|
}),
|
||||||
|
props => props.theme.code
|
||||||
|
)
|
||||||
|
|
||||||
|
export const code = styled.pre(
|
||||||
|
props => ({
|
||||||
|
fontFamily: props.theme.monospace,
|
||||||
|
fontSize: '.75em',
|
||||||
|
}),
|
||||||
|
props => props.theme.pre
|
||||||
|
)
|
||||||
|
|
||||||
|
export const img = styled.img({
|
||||||
|
maxWidth: '100%',
|
||||||
|
height: 'auto',
|
||||||
|
objectFit: 'cover',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const TableWrap = styled.div({
|
||||||
|
overflowX: 'auto',
|
||||||
|
})
|
||||||
|
export const Table = styled.table({
|
||||||
|
width: '100%',
|
||||||
|
borderCollapse: 'separate',
|
||||||
|
borderSpacing: 0,
|
||||||
|
'& td, & th': {
|
||||||
|
textAlign: 'left',
|
||||||
|
paddingRight: '.5em',
|
||||||
|
paddingTop: '.25em',
|
||||||
|
paddingBottom: '.25em',
|
||||||
|
borderBottom: '1px solid',
|
||||||
|
verticalAlign: 'top',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const table = props => (
|
||||||
|
<TableWrap>
|
||||||
|
<Table {...props} />
|
||||||
|
</TableWrap>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const components = {
|
||||||
|
pre: props => props.children,
|
||||||
|
code,
|
||||||
|
inlineCode,
|
||||||
|
img,
|
||||||
|
table,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default components
|
16
packages/html-plugin/README.md
Normal file
16
packages/html-plugin/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# @mdx-deck/webpack-html-plugin
|
||||||
|
|
||||||
|
Webpack plugin for generating HTML
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm i @mdx-deck/webpack-html-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// webpack.config.js
|
||||||
|
const HTMLPlugin = require('@mdx-deck/webpack-html-plugin')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [new HTMLPlugin()],
|
||||||
|
}
|
||||||
|
```
|
@ -3,5 +3,8 @@
|
|||||||
"version": "2.0.0-0",
|
"version": "2.0.0-0",
|
||||||
"author": "Brent Jackson <jxnblk@gmail.com>",
|
"author": "Brent Jackson <jxnblk@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "github:jxnblk/mdx-deck"
|
"repository": "github:jxnblk/mdx-deck",
|
||||||
|
"dependencies": {
|
||||||
|
"webpack-sources": "^1.3.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
7
packages/layouts/package.json
Normal file
7
packages/layouts/package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@mdx-deck/layouts",
|
||||||
|
"version": "2.0.0-0",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "Brent Jackson <jxnblk@gmail.com>",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
@ -1,14 +1,12 @@
|
|||||||
const { getOptions } = require('loader-utils')
|
const { getOptions } = require('loader-utils')
|
||||||
const mdx = require('@mdx-js/mdx')
|
const mdx = require('@mdx-js/mdx')
|
||||||
// const normalizeNewline = require('normalize-newline')
|
const mdxPlugin = require('@mdx-deck/mdx-plugin')
|
||||||
const mdxPluginSplit = require('mdx-plugin-split')
|
|
||||||
|
|
||||||
module.exports = async function(src) {
|
module.exports = async function(src) {
|
||||||
const callback = this.async()
|
const callback = this.async()
|
||||||
const options = getOptions(this) || {}
|
const options = getOptions(this) || {}
|
||||||
// options.skipExport = true
|
|
||||||
options.mdPlugins = options.mdPlugins || []
|
options.mdPlugins = options.mdPlugins || []
|
||||||
options.mdPlugins.push(mdxPluginSplit)
|
options.mdPlugins.push(mdxPlugin)
|
||||||
|
|
||||||
const result = mdx.sync(src, options)
|
const result = mdx.sync(src, options)
|
||||||
|
|
||||||
@ -16,7 +14,5 @@ module.exports = async function(src) {
|
|||||||
import { MDXTag } from '@mdx-js/tag'
|
import { MDXTag } from '@mdx-js/tag'
|
||||||
${result}`
|
${result}`
|
||||||
|
|
||||||
console.log(code)
|
|
||||||
|
|
||||||
return callback(null, code)
|
return callback(null, code)
|
||||||
}
|
}
|
12
packages/loader/package.json
Normal file
12
packages/loader/package.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "@mdx-deck/loader",
|
||||||
|
"version": "2.0.0-0",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "Brent Jackson <jxnblk@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@mdx-deck/mdx-plugin": "^2.0.0-0",
|
||||||
|
"@mdx-js/mdx": "^0.20.0",
|
||||||
|
"loader-utils": "^1.2.3"
|
||||||
|
}
|
||||||
|
}
|
94
packages/mdx-deck/demo.mdx
Normal file
94
packages/mdx-deck/demo.mdx
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { Head, Image, Notes, Appear, Steps } from '@mdx-deck/components'
|
||||||
|
|
||||||
|
<Head>
|
||||||
|
<title>MDX Deck Demo</title>
|
||||||
|
</Head>
|
||||||
|
|
||||||
|
# Hello MDX Deck
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## This is v2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What's New
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<Appear>
|
||||||
|
<li>Reach Router</li>
|
||||||
|
<li>Less opinionated styles</li>
|
||||||
|
<li>more stuff</li>
|
||||||
|
</Appear>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<Image
|
||||||
|
src='https://source.unsplash.com/random/1024x768'
|
||||||
|
size='contain'
|
||||||
|
/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## This slide has notes
|
||||||
|
|
||||||
|
<Notes>
|
||||||
|
Hello, secret speaker notes
|
||||||
|
</Notes>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```js
|
||||||
|
const codeExample = require('./code-example')
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## More Appear
|
||||||
|
|
||||||
|
<Appear>
|
||||||
|
<div>One</div>
|
||||||
|
<div>Two</div>
|
||||||
|
<div>Three</div>
|
||||||
|
<div>Four</div>
|
||||||
|
<div>Five</div>
|
||||||
|
<div>Six</div>
|
||||||
|
</Appear>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Steps Components
|
||||||
|
|
||||||
|
<Steps
|
||||||
|
length={3}
|
||||||
|
render={({ step }) => (
|
||||||
|
<pre>
|
||||||
|
Step: {step}
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
export default props =>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100vw',
|
||||||
|
height: '100vh',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: 'tomato'
|
||||||
|
}}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## With a (tomato) layout
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Last Slide
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@ const remark = {
|
|||||||
emoji: require('remark-emoji'),
|
emoji: require('remark-emoji'),
|
||||||
unwrapImages: require('remark-unwrap-images'),
|
unwrapImages: require('remark-unwrap-images'),
|
||||||
}
|
}
|
||||||
const HTMLPlugin = require('./html-plugin')
|
const HTMLPlugin = require('@mdx-deck/webpack-html-plugin')
|
||||||
const babel = require('../babel.config')
|
const babel = require('../babel.config')
|
||||||
|
|
||||||
const rules = [
|
const rules = [
|
||||||
@ -35,7 +35,7 @@ const rules = [
|
|||||||
options: babel,
|
options: babel,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loader: require.resolve('./loader.js'),
|
loader: require.resolve('@mdx-deck/loader'),
|
||||||
options: {
|
options: {
|
||||||
mdPlugins: [remark.emoji, remark.unwrapImages],
|
mdPlugins: [remark.emoji, remark.unwrapImages],
|
||||||
},
|
},
|
||||||
@ -80,7 +80,7 @@ const createConfig = (opts = {}) => {
|
|||||||
path.join(opts.dirname, 'node_modules')
|
path.join(opts.dirname, 'node_modules')
|
||||||
)
|
)
|
||||||
|
|
||||||
config.entry = [path.join(__dirname, '../src/entry.js')]
|
config.entry = [path.join(__dirname, './entry.js')]
|
||||||
|
|
||||||
const defs = Object.assign({}, opts.globals, {
|
const defs = Object.assign({}, opts.globals, {
|
||||||
OPTIONS: JSON.stringify(opts),
|
OPTIONS: JSON.stringify(opts),
|
||||||
|
@ -1,23 +1,13 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render } from 'react-dom'
|
import { render } from 'react-dom'
|
||||||
import { MDXDeck } from './index'
|
import { MDXDeck } from '@mdx-deck/components'
|
||||||
|
|
||||||
const mod = require(FILENAME)
|
const mod = require(FILENAME)
|
||||||
const { slides } = mod
|
const { slides, theme } = mod
|
||||||
const { theme, components, Provider } = mod
|
|
||||||
|
|
||||||
console.log(slides)
|
|
||||||
|
|
||||||
export default class App extends React.Component {
|
export default class App extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <MDXDeck slides={slides} theme={theme} />
|
||||||
<MDXDeck
|
|
||||||
slides={slides}
|
|
||||||
theme={theme}
|
|
||||||
components={components}
|
|
||||||
Provider={Provider}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,13 +2,12 @@
|
|||||||
"name": "mdx-deck",
|
"name": "mdx-deck",
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"description": "MDX-based presentation decks",
|
"description": "MDX-based presentation decks",
|
||||||
"main": "src/index.js",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"mdx-deck": "./cli.js"
|
"mdx-deck": "./cli.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "./cli.js docs/index.mdx -p 8080",
|
"start": "./cli.js demo.mdx -p 8080",
|
||||||
"build": "./cli.js build docs/index.mdx -d site",
|
"build": "./cli.js build demo.mdx -d public",
|
||||||
"help": "./cli.js",
|
"help": "./cli.js",
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
@ -24,6 +23,9 @@
|
|||||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||||
"@babel/preset-env": "^7.3.4",
|
"@babel/preset-env": "^7.3.4",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
|
"@mdx-deck/components": "^2.0.0-0",
|
||||||
|
"@mdx-deck/loader": "^2.0.0-0",
|
||||||
|
"@mdx-deck/webpack-html-plugin": "^2.0.0-0",
|
||||||
"@mdx-js/mdx": "^0.20.0",
|
"@mdx-js/mdx": "^0.20.0",
|
||||||
"@mdx-js/tag": "^0.18.0",
|
"@mdx-js/tag": "^0.18.0",
|
||||||
"@reach/router": "^1.2.1",
|
"@reach/router": "^1.2.1",
|
||||||
@ -60,7 +62,6 @@
|
|||||||
"webpack-hot-middleware": "^2.24.3",
|
"webpack-hot-middleware": "^2.24.3",
|
||||||
"webpack-merge": "^4.2.1",
|
"webpack-merge": "^4.2.1",
|
||||||
"webpack-node-externals": "^1.7.2",
|
"webpack-node-externals": "^1.7.2",
|
||||||
"webpack-sources": "^1.3.0",
|
|
||||||
"webpackbar": "^3.1.5"
|
"webpackbar": "^3.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mdx-plugin-split",
|
"name": "@mdx-deck/mdx-plugin",
|
||||||
"version": "0.0.2",
|
"version": "2.0.0-0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
7
packages/themes/package.json
Normal file
7
packages/themes/package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@mdx-deck/themes",
|
||||||
|
"version": "2.0.0-0",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"author": "Brent Jackson <jxnblk@gmail.com>",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
@ -5362,6 +5362,14 @@ mdx-deck-code-surfer@^0.5.5:
|
|||||||
code-surfer "^0.5.5"
|
code-surfer "^0.5.5"
|
||||||
memoize-one "^4.0.2"
|
memoize-one "^4.0.2"
|
||||||
|
|
||||||
|
mdx-plugin-split@0.0.2:
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdx-plugin-split/-/mdx-plugin-split-0.0.2.tgz#608d90f12108593badfc54ce24533ef82fd41d47"
|
||||||
|
integrity sha512-Kl8H0CVO4QPJ1AZ+Weaf7uHMB+px4uffZ2h3tl7ItcvU4VJkASRjeFYJN5GtQbmM8RpXTgxyp+xfaQqHOClNXQ==
|
||||||
|
dependencies:
|
||||||
|
unist-util-is "^2.1.2"
|
||||||
|
unist-util-visit "^1.4.0"
|
||||||
|
|
||||||
mem@^4.0.0:
|
mem@^4.0.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a"
|
resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a"
|
||||||
|
Loading…
Reference in New Issue
Block a user