1
1
mirror of https://github.com/jxnblk/mdx-deck.git synced 2024-11-29 13:58:02 +03:00

Merge pull request #42 from jxnblk/speaker-notes

Speaker notes
This commit is contained in:
Brent Jackson 2018-08-02 22:26:52 -04:00 committed by GitHub
commit b6c0119243
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 229 additions and 61 deletions

View File

@ -262,6 +262,33 @@ To use presenter mode:
- Display the other window on the screen for the audience to see.
- Control the presentation from your window by using the left and right arrow keys; the other window should stay in sync
### Speaker Notes
Notes that only show in presenter mode can be added to any slide.
Speaker notes can be added in one of the following two ways:
**Markdown:** Use the `notes` language attribute in a fenced code block to add speaker notes.
````mdx
# Slide Content
```notes
These are only visible in presenter mode
```
````
**Notes Component:** Use the `Notes` component to create more complex speaker notes.
````mdx
import { Notes } from 'mdx-deck'
# Slide Content
<Notes>
Only visible in presenter mode
</Notes>
````
## Exporting
Add a `build` script to your `package.json` to export a presentation as HTML with a JS bundle.

View File

@ -1,5 +1,5 @@
export { future as theme } from '../src/themes'
import { Image } from '../src'
import { Image, Notes } from '../src'
import Layout from './Layout'
# mdx-deck
@ -33,14 +33,28 @@ import Box from 'superbox'
3. Present
---
```
<button>code example</button>
```
```notes
- These are speaker notes
- And they won't rendered in your slide
```
---
> “Blockquotes are essential to any good presentation”
Anonymous
<Notes>
<ul>
<li>Speaker notes can also be</li>
<li>Written in JSX</li>
</ul>
</Notes>
---
<Image src='https://images.unsplash.com/photo-1462331940025-496dfbfc7564?w=2048&q=20' />

View File

@ -1,5 +1,5 @@
import styled from 'styled-components'
import { space, color } from 'styled-system'
import { space, width, color } from 'styled-system'
const Flex = styled.div([], {
display: 'flex',
@ -7,6 +7,10 @@ const Flex = styled.div([], {
'@media print': {
display: 'none'
}
}, props => props.css, space, color)
}, props => props.css,
space,
width,
color
)
export default Flex

View File

@ -5,6 +5,7 @@ export default props =>
<Box
{...props}
css={{
fontFamily: 'Menlo, monospace'
fontFamily: 'Menlo, monospace',
whiteSpace: 'pre-wrap'
}}
/>

22
src/Notes.js Normal file
View File

@ -0,0 +1,22 @@
import React from 'react'
import { withDeck } from './context'
import { withSlide } from './Slide'
export default withDeck(withSlide(class extends React.Component {
setNotes = (props) => {
const { slide, deck, children } = props
if (!slide.index) return
deck.addNotes({
index: slide.index,
children
})
}
componentWillMount () {
this.setNotes(this.props)
}
render () {
return false
}
}))

View File

@ -12,6 +12,7 @@ export const Presenter = ({
length,
slides = [],
mode,
notes = {},
...props
}) => {
const Next = slides[index + 1]
@ -24,11 +25,10 @@ export const Presenter = ({
height: '100vh'
}}
>
<Flex
my='auto'
css={{ alignItems: 'flex-start' }}>
<Flex my='auto'>
<Box
mx='auto'
width={5/8}
css={{
border: '1px solid rgba(128, 128, 128, 0.25)'
}}>
@ -38,21 +38,36 @@ export const Presenter = ({
</Root>
</Zoom>
</Box>
<Box
<Flex
width={1/4}
mx='auto'
css={{
border: '1px solid rgba(128, 128, 128, 0.25)'
flex: 'none',
flexDirection: 'column'
}}>
<Zoom zoom={1/4}>
<Root {...props}>
{Next && (
<Slide>
<Next />
</Slide>
)}
</Root>
</Zoom>
</Box>
<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[index]}
</Box>
</Flex>
</Flex>
<Flex mt='auto' px={3} py={3}>
<Mono>Slide {index} of {length}</Mono>

View File

@ -1,7 +1,20 @@
import React from 'react'
import styled from 'styled-components'
import { space, color } from 'styled-system'
export const Slide = styled.div([], {
const Context = React.createContext(null)
export const withSlide = Component => props =>
<Context.Consumer>
{slide => (
<Component
{...props}
slide={slide}
/>
)}
</Context.Consumer>
const Root = styled.div([], {
flex: 'none',
display: 'flex',
alignItems: 'center',
@ -19,6 +32,20 @@ export const Slide = styled.div([], {
}
}, space, color)
class Slide extends React.Component {
render () {
const {
index,
...props
} = this.props
return (
<Context.Provider value={{ index }}>
<Root {...props} />
</Context.Provider>
)
}
}
Slide.defaultProps = {
px: [ 4, 5, 6 ]
}

View File

@ -5,6 +5,8 @@ import {
space,
color
} from 'styled-system'
import Notes from './Notes'
import Mono from './Mono'
const css = key => props => props.theme[key]
@ -89,15 +91,16 @@ blockquote.defaultProps = {
color: 'quote'
}
const pre = styled.pre([], props => ({
fontFamily: props.theme.monospace
const Pre = styled.pre([], props => ({
fontFamily: props.theme.monospace,
whiteSpace: 'pre-wrap'
}),
fontSize,
space,
color,
css('pre')
)
pre.defaultProps = {
Pre.defaultProps = {
fontSize: 1,
m: 0,
p: 2,
@ -105,10 +108,23 @@ pre.defaultProps = {
bg: 'preBackground'
}
const code = styled.code([], props => ({
const code = props => {
switch (props.className) {
case 'language-notes':
return (
<Notes>
<Mono {...props} color='white' />
</Notes>
)
default:
return <Pre {...props} />
}
}
const inlineCode = styled.code([], props => ({
fontFamily: props.theme.monospace
}), fontSize, space, color, css('code'))
code.defaultProps = {
inlineCode.defaultProps = {
color: 'code',
bg: 'codeBackground'
}
@ -132,7 +148,7 @@ export default {
ol,
li,
pre: props => props.children,
code: pre,
inlineCode: code,
code,
inlineCode,
img
}

16
src/context.js Normal file
View File

@ -0,0 +1,16 @@
import React from 'react'
export const Context = React.createContext(null)
export const { Provider, Consumer } = Context
export const withDeck = Component => props =>
<Consumer>
{deck => (
<Component
{...props}
deck={deck}
/>
)}
</Consumer>
export default Context

View File

@ -4,6 +4,7 @@ import { MDXProvider } from '@mdx-js/tag'
import { ThemeProvider } from 'styled-components'
import debounce from 'lodash.debounce'
import { Provider } from './context'
import Carousel from './Carousel'
import Slide from './Slide'
import Dots from './Dots'
@ -15,6 +16,7 @@ import defaultTheme from './themes'
import defaultComponents from './components'
export { default as Image } from './Image'
export { default as Notes } from './Notes'
export { default as components } from './components'
// themes
@ -49,7 +51,8 @@ export class SlideDeck extends React.Component {
state = {
length: this.props.slides.length,
index: 0,
mode: modes.normal
mode: modes.normal,
notes: {}
}
update = fn => this.setState(fn)
@ -104,6 +107,15 @@ export class SlideDeck extends React.Component {
this.setState({ index })
}
addNotes = ({ index, children }) => {
this.setState(state => ({
notes: {
...state.notes,
[index]: children
}
}))
}
componentDidMount () {
document.body.addEventListener('keydown', this.handleKeyDown)
window.addEventListener('hashchange', this.handleHashChange)
@ -123,8 +135,10 @@ export class SlideDeck extends React.Component {
this.isHashChange = false
return
}
const { index } = this.state
history.pushState(null, null, '#' + index)
const { index, mode } = this.state
let query = '?'
if (mode === modes.presenter) query += 'presenter'
history.pushState(null, null, query + '#' + index)
localStorage.setItem('mdx-slide', index)
}
@ -142,38 +156,49 @@ export class SlideDeck extends React.Component {
? Presenter
: Root
const context = {
...this.state,
slides,
addNotes: this.addNotes
}
return (
<ThemeProvider theme={theme}>
<MDXProvider
components={{
...defaultComponents,
...components
}}>
<Wrapper
{...this.state}
slides={slides}
width={width}
height={height}>
<GoogleFonts />
<Carousel index={index}>
{slides.map((Component, i) => (
<Slide key={i} id={'slide-' + i}>
<Component />
</Slide>
))}
</Carousel>
<Dots
mt={-32}
mx='auto'
index={index}
length={length}
onClick={index => {
this.setState({ index })
}}
/>
</Wrapper>
</MDXProvider>
</ThemeProvider>
<Provider value={context}>
<ThemeProvider theme={theme}>
<MDXProvider
components={{
...defaultComponents,
...components
}}>
<Wrapper
{...this.state}
slides={slides}
width={width}
height={height}>
<GoogleFonts />
<Carousel index={index}>
{slides.map((Component, i) => (
<Slide
key={i}
id={'slide-' + i}
index={i}>
<Component />
</Slide>
))}
</Carousel>
<Dots
mt={-32}
mx='auto'
index={index}
length={length}
onClick={index => {
this.setState({ index })
}}
/>
</Wrapper>
</MDXProvider>
</ThemeProvider>
</Provider>
)
}
}

View File

@ -33,6 +33,7 @@ exports[`renders blockquote 1`] = `
exports[`renders code 1`] = `
.c0 {
white-space: pre-wrap;
font-size: 14px;
margin: 0px;
padding: 8px;