├── .circleci └── config.yml ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── MIGRATION.md ├── README.md ├── babel.config.js ├── cypress.json ├── cypress ├── integration │ └── hello.js ├── plugins │ └── index.js └── support │ ├── commands.js │ └── index.js ├── docs ├── Counter.js ├── ace.png ├── api.md ├── card.png ├── components.md ├── demo.mdx ├── exporting.md ├── gatsby.md ├── images │ ├── big.png │ ├── book.png │ ├── code.png │ ├── comic.png │ ├── condensed.png │ ├── dark.png │ ├── default.png │ ├── future.png │ ├── hack.png │ ├── lobster.png │ ├── notes.png │ ├── overview-mode.png │ ├── presenter-mode.png │ ├── rye.png │ ├── script.png │ ├── swiss.png │ └── yellow.png ├── layouts.md ├── package.json ├── presenting.md ├── themes.md └── theming.md ├── examples ├── README.md ├── aspect-ratio │ ├── deck.mdx │ └── package.json ├── basic │ ├── deck.mdx │ └── package.json ├── gatsby │ ├── decks │ │ ├── beep.mdx │ │ └── hello.mdx │ ├── gatsby-config.js │ ├── package.json │ └── src │ │ └── pages │ │ └── index.mdx ├── head │ ├── deck.mdx │ └── package.json ├── header-footer │ ├── deck.mdx │ └── package.json ├── images │ ├── deck.mdx │ └── package.json ├── layouts │ ├── deck.mdx │ └── package.json ├── multiple │ ├── deck.js │ ├── one.mdx │ ├── package.json │ └── two.mdx ├── prism │ ├── deck.mdx │ └── package.json ├── provider │ ├── deck.mdx │ ├── package.json │ └── theme.js ├── steps │ ├── deck.mdx │ └── package.json ├── syntax-highlighting │ ├── deck.mdx │ └── package.json └── themes │ ├── deck.mdx │ ├── package.json │ └── theme.js ├── lerna.json ├── netlify.toml ├── package.json ├── packages ├── create-deck │ ├── README.md │ ├── cli.js │ └── package.json ├── gatsby-plugin │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── gatsby-browser.js │ ├── gatsby-config.js │ ├── gatsby-node.js │ ├── gatsby-ssr.js │ ├── index.js │ ├── package.json │ ├── src │ │ ├── clock.js │ │ ├── components.js │ │ ├── container.js │ │ ├── context.js │ │ ├── deck.js │ │ ├── footer.js │ │ ├── header.js │ │ ├── index.js │ │ ├── keyboard.js │ │ ├── modes.js │ │ ├── slide.js │ │ ├── split-slides.js │ │ ├── storage.js │ │ ├── theme.js │ │ ├── timer.js │ │ └── use-steps.js │ └── test │ │ ├── __snapshots__ │ │ └── components.js.snap │ │ ├── clock.js │ │ ├── components.js │ │ └── deck.js ├── gatsby-theme │ ├── .gitignore │ ├── README.md │ ├── decks │ │ ├── beep.mdx │ │ └── hello.mdx │ ├── gatsby-browser.js │ ├── gatsby-config.js │ ├── gatsby-node.js │ ├── gatsby-ssr.js │ ├── index.js │ ├── package.json │ └── src │ │ ├── components │ │ ├── app.js │ │ ├── appear.js │ │ ├── clock.js │ │ ├── deck.js │ │ ├── decks.js │ │ ├── embed.js │ │ ├── full-screen-code.js │ │ ├── grid.js │ │ ├── head.js │ │ ├── horizontal.js │ │ ├── image.js │ │ ├── invert.js │ │ ├── notes.js │ │ ├── overview.js │ │ ├── presenter-footer.js │ │ ├── presenter.js │ │ ├── slide-list.js │ │ ├── slide.js │ │ ├── split-right.js │ │ ├── split.js │ │ ├── timer.js │ │ ├── wrapper.js │ │ └── zoom.js │ │ ├── constants.js │ │ ├── context.js │ │ ├── convert-legacy-theme.js │ │ ├── gatsby-plugin-theme-ui │ │ ├── components.js │ │ └── index.js │ │ ├── hooks │ │ ├── use-deck.js │ │ ├── use-keyboard.js │ │ ├── use-steps.js │ │ ├── use-storage.js │ │ └── use-swipe.js │ │ ├── index.js │ │ ├── navigate.js │ │ ├── split-slides.js │ │ └── templates │ │ ├── deck.js │ │ └── decks.js ├── mdx-deck │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── cli.js │ ├── gatsby-config.js │ ├── hello.mdx │ ├── index.js │ └── package.json ├── starter │ ├── decks │ │ └── .gitkeep │ ├── gatsby-config.js │ └── package.json ├── themes │ ├── README.md │ ├── base.js │ ├── big.js │ ├── book.js │ ├── code.js │ ├── comic.js │ ├── condensed.js │ ├── dark.js │ ├── future.js │ ├── index.js │ ├── lobster.js │ ├── notes.js │ ├── package.json │ ├── poppins.js │ ├── script.js │ ├── swiss.js │ ├── syntax-highlighter-prism.js │ ├── syntax-highlighter.js │ └── yellow.js └── website-pdf │ ├── .gitignore │ ├── README.md │ ├── cli.js │ ├── index.js │ └── package.json ├── templates └── basic │ ├── .gitignore │ ├── README.md │ ├── deck.mdx │ ├── package.json │ └── theme.js └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | # - image: circleci/node:10 11 | - image: cypress/base:10 12 | 13 | # Specify service dependencies here if necessary 14 | # CircleCI maintains a library of pre-built images 15 | # documented at https://circleci.com/docs/2.0/circleci-images/ 16 | # - image: circleci/mongo:3.4.4 17 | 18 | working_directory: ~/repo 19 | 20 | steps: 21 | - checkout 22 | 23 | # Download and cache dependencies 24 | - restore_cache: 25 | keys: 26 | - v1-dependencies-{{ checksum "package.json" }} 27 | # fallback to using the latest cache if no exact match is found 28 | - v1-dependencies- 29 | 30 | - run: yarn 31 | 32 | - save_cache: 33 | paths: 34 | - node_modules 35 | key: v1-dependencies-{{ checksum "package.json" }} 36 | 37 | # run tests! 38 | - run: yarn test 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | public 3 | coverage 4 | node_modules 5 | package-lock.json 6 | public 7 | .cache 8 | cypress/videos 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5", 5 | "jsxBracketSameLine": true 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | - Add missing dependency 6 | 7 | ## v4.1.0 8 | 9 | - Update colors on notes theme 10 | - Add support for static asset directory 11 | 12 | ## v4.0.0 13 | 14 | - Refactored implementation for `mdx-deck` CLI 15 | - New `Header` and `Footer` components for adding persistent header and footer content 16 | - **Deprecated:** CLI `eject` command 17 | - **Deprecated:** Swipe gestures - to be replaced with new UI 18 | - **Deprecated:** Title is no longer inferred from first heading 19 | - **Deprecated:** `export const themes` has been removed - merge themes in a separate module if needed 20 | - **Deprecated:** Functional themes are no longer supported, merge themes in a separate module if needed 21 | - **Deprecated:** `theme.Provider` - use `Header` and `Footer` components instead 22 | - **Deprecated:** Fixed aspect ratio has been removed 23 | - Bug fixes 24 | - Update dependencies 25 | - **Deprecated:** `gatsby-theme-mdx-deck`: no longer resolves title from first heading 26 | 27 | ## v3.1.0 2020-02-01 28 | 29 | - Update dependencies 30 | - Adjust schema customization in Gatsby theme 31 | 32 | ## v3.0.13 2019-09-23 33 | 34 | - Adjust Gatsby content digest 35 | 36 | ## v3.0.12 2019-09-23 37 | 38 | - Update dependencies 39 | 40 | ## v3.0.11 2019-09-10 41 | 42 | - Add support for up and down keys #467 43 | - Fix for double slash in print route #473 44 | 45 | ## v3.0.10 2019-09-05 46 | 47 | - Add remark-import-code #457 48 | - Fix pathname in windows #465 49 | 50 | ## v3.0.9 2019-08-05 51 | 52 | - Fix for remounting component #428 53 | 54 | ## v3.0.8 2019-07-28 55 | 56 | - Add support for Gatsby `pathPrefix` option 57 | 58 | ## v3.0.0 2019-07-16 59 | 60 | - Refactored to leverage Gatsby 61 | - Rewritten CLI based on Gatsby 62 | - Updated Gatsby theme to allow for component shadowing 63 | - Gatsby theme has been renamed to `gatsby-theme-mdx-deck` 64 | - Now uses [Theme UI](https:/theme-ui.com) for theming 65 | - Improved touchscreen and mobile views 66 | - Deprecated: 67 | - `@mdx-deck/components` 68 | - `@mdx-deck/layouts` 69 | - `@mdx-deck/loader` 70 | - `@mdx-deck/mdx-plugin` 71 | - `@mdx-deck/webpack-html-plugin` 72 | 73 | See the [Migration](MIGRATION.md) docs for more 74 | 75 | ## v2.5.1 2019-07-16 76 | 77 | - Fix loader #357 78 | 79 | ## v2.5.0 2019-07-07 80 | 81 | - Update Gatsby theme to official API #389 #387 #385 82 | - Update docs #382 83 | 84 | ## v2.4.0 2019-06-19 85 | 86 | - Add `useTheme` hook to API #359 87 | - Makes presenter mode themeable #366 88 | - Add support for `--webpack` flag #369 89 | 90 | ## v2.3.2 2019-04-21 91 | 92 | - Fixed issue when Head only had one element #345 93 | 94 | ## v2.3.1 2019-04-21 95 | 96 | - Add experimental support for fluid aspect ratios #342 97 | 98 | ## v2.3.0 2019-04-20 99 | 100 | - Refactor localStorage to use hooks #334 101 | - Refactor keyboard shortcuts #335 102 | - Refactor query string to use hooks #336 103 | - Refactor to use hooks #337 104 | - Adds `MDXDeckState` provider component 105 | - Fixes an issue with rerenders in Gatsby theme 106 | - Adjusts styles in grid mode 107 | - Refactors `useSteps` to use effect hook 108 | 109 | ## v2.2.3 2019-04-20 110 | 111 | - Refactor Head component #329 112 | 113 | ## v2.2.2 2019-04-20 114 | 115 | - Fix typos #333 116 | - Refactor themes for better bundle sizes #328 117 | 118 | ## v2.2.1 2019-04-15 119 | 120 | - Add support for page up/down keys #319 121 | - Fix: remove global styles from Embed component #331 122 | 123 | ## v2.2.0 2019-04-13 124 | 125 | - Add Embed component #323 126 | - Adjust context passed to Slide component 127 | - Add default props to Slide to show all Appear steps 128 | - Adds header and footer components for shadowing in Gatsby theme 129 | - Refactor and clean up code 130 | 131 | ## v2.1.4 2019-04-12 132 | 133 | - Add `mdx` option to Gatsby theme #325 134 | 135 | ## v2.1.3 2019-04-12 136 | 137 | - Update docs for Gatsby theme #324 138 | 139 | ## v2.1.2 2019-04-12 140 | 141 | - Bump dependencies to MDX 1.0.0 #322 142 | 143 | ## v2.1.1 2019-04-11 144 | 145 | - Add support for single deck mode in Gatsby theme #320 146 | 147 | ## v2.1.0 2019-04-11 148 | 149 | - Added Gatsby theme #318 150 | 151 | ## v2.0.9 2019-04-05 152 | 153 | - Rename internal const #312 154 | 155 | ## v2.0.8 2019-04-05 156 | 157 | - Update MDX #311 158 | 159 | ## v2.0.7 2019-04-05 160 | 161 | - Add `--no-html` flag back #295 162 | 163 | ## v2.0.6 2019-03-28 164 | 165 | - Pin alpha version of MDX #302 166 | 167 | ## v2.0.5 2019-03-23 168 | 169 | - Update remark-unwrap-images #289 170 | - Update webpack config merging #290 171 | 172 | ## v2.0.4 2019-03-23 173 | 174 | - Fix for css-loader #288 175 | 176 | ## v2.0.3 2019-03-23 177 | 178 | - Fix for building decks with Google Fonts #287 179 | 180 | ## v2.0.2 2019-03-23 181 | 182 | - Fix syntax error in theme #286 183 | 184 | ## v2.0.1 2019-03-23 185 | 186 | - Add language support to syntax highlighter themes #278 187 | 188 | ## v2.0.0 2019-03-16 189 | 190 | - Simplified custom mdx loader, removing unused front-matter support 191 | - Simplified theming and default styles 192 | - Removes default Provider component with dot indicator 193 | - Uses Reach Router - resolves issues with focus trapping 194 | - Removed PDF export and screenshots from core CLI - now available with the `@mdx-deck/export` package 195 | - Removed built-in syntax highlighting 196 | - Removed `notes` language attribute for fenced code blocks 197 | - Refactored dev server 198 | 199 | ## v1.10.2 2019-03-10 200 | 201 | - Fix bad release 202 | 203 | ## v1.10.1 2019-03-10 204 | 205 | - Prevent Appear children from disappearing during slide transition #253 206 | 207 | ## v1.10.0 2019-02-18 208 | 209 | - Update to Babel 7 210 | 211 | ## v1.9.0 2019-02-18 212 | 213 | - Fix for font size in nested lists #204 214 | - Add `--hot-port` option to CLI #206 215 | - Add support for `.jsx` file extensions #239 216 | - Fix typos in syntax highlighting component #250 217 | - Add context to grid view #187 218 | - Add `--no-sandbox` option to CLI #200 219 | - Surface compilation errors from webpack #252 220 | 221 | ## v1.8.2 2018-12-04 222 | 223 | - Bugfix for window check 224 | 225 | ## v1.8.1 2018-11-27 226 | 227 | - Show Appear children in PDF export 228 | 229 | ## v1.8.0 2018-11-27 230 | 231 | - Adds button to open new window for presenting in presenter mode 232 | 233 | ## v1.7.14 2018-11-18 234 | 235 | - Fix typo in SlideDeck 236 | 237 | ## v1.7.13 2018-11-18 238 | 239 | - Add overflow auto to FullScreenCode 240 | 241 | ## v1.7.12 2018-11-18 242 | 243 | - Keep styles intact for Appear children 244 | - Fix prop types for Appear component 245 | - Add missing CLI option to docs 246 | 247 | ## v1.7.11 2018-11-18 248 | 249 | - Update remark-unwrap-images 250 | 251 | ## v1.7.10 2018-11-12 252 | 253 | - Update dependencies 254 | 255 | ## v1.7.9 2018-11-12 256 | 257 | - Update dependencies 258 | 259 | ## v1.7.8 2018-11-12 260 | 261 | - Fix typo in Root prop types 262 | - Edit docs 263 | 264 | ## v1.7.7 2018-09-22 265 | 266 | - Remove overflow hidden styles from body 267 | - Adds prettier 268 | 269 | ## v1.7.6 2018-09-22 270 | 271 | - Changes styles to use `translate3d` 272 | - Add support for page up and page down keys 273 | 274 | ## v1.7.5 2018-09-22 275 | 276 | - Add `Horizontal` layout component 277 | 278 | ## v1.7.4 2018-09-15 279 | 280 | - Add `--host` option 281 | 282 | ## v1.7.3 2018-09-05 283 | 284 | - Fix swipe direction on touchscreens 285 | 286 | ## v1.7.1 2018-08-30 287 | 288 | - Fix for localStorage updater 289 | 290 | ## v1.7.0 2018-08-29 291 | 292 | - Adds support for stepping through Appear component with left and right arrows #144 293 | - Refactor internal context 294 | 295 | ## v1.6.9 2018-08-27 296 | 297 | - Adds support for custom webpack configs #136 298 | 299 | ## v1.6.8 2018-08-27 300 | 301 | - Fixes `build` when using Notes or Appear components #138 302 | - Fixes slide number in presenter mode #142 303 | 304 | ## v1.6.7 2018-08-25 305 | 306 | - Use `mkdirp` for build and export 307 | - Adds ability to change slide transition timing function and duration via themes 308 | 309 | ## v1.6.6 2018-08-25 310 | 311 | - Left align text in code blocks #130 312 | - Extract static CSS on build #129 313 | - Adds `--no-html` option for client-side only builds 314 | 315 | ## v1.6.5 2018-08-25 316 | 317 | - Adjust slide number in overview mode #122 318 | 319 | ## v1.6.4 2018-08-18 320 | 321 | - Add respository field to package.json #117 322 | - Remove trailing comma in function arguments #115 323 | 324 | ## v1.6.3 2018-08-16 325 | 326 | - Disable swiping with mouse #113 327 | 328 | ## v1.6.2 2018-08-15 329 | 330 | - Adjust import/export parsing in loader #110 331 | 332 | ## v1.6.1 2018-08-15 333 | 334 | - Add missing `babel-core` dependency 335 | 336 | ## v1.6.0 2018-08-14 337 | 338 | - Adds `Head` component for setting document head 339 | - Adds screenshot command to create a screenshot of the first slide 340 | - Removes the `--title` option in favor of using the `Head` component 341 | 342 | ## v1.5.15 2018-08-11 343 | 344 | - Adds swipe gesture support for touchscreen devices 345 | - Fixes URL bug when initializing mode 346 | - Fixes bug previous/next buttons are not rendered 347 | - Prevents last slide from cycling back to the beginning 348 | 349 | ## v1.5.14 2018-08-10 350 | 351 | - Adds `size` prop to Image component 352 | 353 | ## v1.5.13 2018-08-10 354 | 355 | - Fixes an issue where speaker notes would incorrectly show on the wrong slide 356 | 357 | ## v1.5.12 2018-08-10 358 | 359 | - Add FullScreenCode layout component 360 | 361 | ## v1.5.11 2018-08-10 362 | 363 | - Adjust querystring updater to fix mode showing as undefined 364 | 365 | ## v1.5.10 2018-08-05 366 | 367 | - Update overview mode styles 368 | - Add grid view mode 369 | - Update docs 370 | 371 | ## v1.5.9 2018-08-05 372 | 373 | - Update docs 374 | 375 | ## v1.5.8 2018-08-05 376 | 377 | - Add support for `components` and `Provider` in themes 378 | 379 | ## v1.5.7 2018-08-05 380 | 381 | - Add more built-in themes 382 | 383 | ## v1.5.6 2018-08-05 384 | 385 | - Add invisible buttons to left and right for use on mobile devices 386 | 387 | ## v1.5.5 2018-08-05 388 | 389 | - Update docs 390 | 391 | ## v1.5.4 2018-08-04 392 | 393 | - Add docs for syntax highlighting 394 | 395 | ## v1.5.3 2018-08-04 396 | 397 | - Add overview mode to see multiple slides at once 398 | - Add default layouts for inverting colors and creating a split layout slide 399 | 400 | ## v1.5.2 2018-08-04 401 | 402 | - Add default styles for tables 403 | 404 | ## v1.5.1 2018-08-04 405 | 406 | - Use remark-unwrap-images plugin 407 | 408 | ## v1.5.0 2018-08-04 409 | 410 | - Add syntax highlighting option for fenced code blocks 411 | 412 | ## v1.4.4 2018-08-04 413 | 414 | - Fix for how Appear children display on slide change 415 | 416 | ## v1.4.3 2018-08-04 417 | 418 | - Update build setup for smaller package 419 | - Adjust keyboard shortcuts 420 | 421 | ## v1.4.2 2018-08-03 422 | 423 | - Update ok-cli for better HTML output 424 | 425 | ## v1.4.1 2018-08-03 426 | 427 | - Update docs 428 | - Add `yellow` theme 429 | 430 | ## v1.4.0 2018-08-03 431 | 432 | - Adds Appear component 433 | - Adds propTypes to components 434 | - Update README 435 | 436 | ## v1.3.2 2018-08-02 437 | 438 | - Remove default `target="_blank"` from links 439 | - Move custom Provider component into app 440 | 441 | ## v1.3.1 2018-08-02 442 | 443 | - Add speaker notes markdown syntax and component 444 | 445 | ## v1.3.0 2018-08-02 446 | 447 | - Add presenter mode with preview of next slide and timer 448 | 449 | ## v1.2.3 2018-08-01 450 | 451 | - Fix `history.pushState` hash 452 | 453 | ## v1.2.2 2018-07-31 454 | 455 | - Update dev server for static file server 456 | 457 | ## v1.2.1 2018-07-31 458 | 459 | - Merge custom components with defaults 460 | 461 | ## v1.2.0 2018-07-31 462 | 463 | - Add PDF export to CLI 464 | 465 | ## v1.1.3 2018-07-31 466 | 467 | - Add emoji support 468 | - Update `.npmignore` 469 | 470 | ## v1.1.2 2018-07-31 471 | 472 | - Fix `--no-open` option 473 | - Add ability to ignore key events 474 | - Normalize newlines for cross-platform compatibility 475 | 476 | ## v1.1.1 2018-07-31 477 | 478 | - Fix for supporting markdown tables 479 | 480 | ## v1.1.0 2018-07-30 481 | 482 | - Updated styles and theming 483 | - Updated docs 484 | 485 | ## v1.0.3 2018-07-29 486 | 487 | - Updated docs 488 | 489 | ## v1.0.2 2018-07-29 490 | 491 | - Add hashchange listeners 492 | 493 | ## v1.0.1 2018-07-29 494 | 495 | - Fix for `--out-dir` CLI flag 496 | 497 | ## v1.0.0 2018-07-29 498 | 499 | Initial release 500 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for contributing! 4 | 5 | Please take a look at the issues and PRs to see if there's already some discussion around any contribution you'd like to make. 6 | If you'd like to make a significant change to the code base, please open an issue for discussion first, 7 | otherwise we'd love to have your help! 8 | 9 | ## Development 10 | 11 | This project is set up as a monorepo using Yarn Workspaces. 12 | 13 | 1. Clone the repo 14 | 2. Install dependencies with `yarn` 15 | 3. Run `yarn start` to see the demo `docs/demo.mdx` 16 | 17 | ### Watch mode 18 | 19 | To watch files for changes during development, run `npm run watch` 20 | 21 | ## Testing 22 | 23 | Tests are located in each package. 24 | 25 | Run `yarn test` 26 | 27 | - Watch Mode: `yarn test --watch` 28 | - Coverage: `yarn test --coverage` 29 | 30 | --- 31 | 32 | # Contributor Covenant Code of Conduct 33 | 34 | ## Our Pledge 35 | 36 | In the interest of fostering an open and welcoming environment, we as 37 | contributors and maintainers pledge to making participation in our project and 38 | our community a harassment-free experience for everyone, regardless of age, body 39 | size, disability, ethnicity, sex characteristics, gender identity and expression, 40 | level of experience, education, socio-economic status, nationality, personal 41 | appearance, race, religion, or sexual identity and orientation. 42 | 43 | ## Our Standards 44 | 45 | Examples of behavior that contributes to creating a positive environment 46 | include: 47 | 48 | - Using welcoming and inclusive language 49 | - Being respectful of differing viewpoints and experiences 50 | - Gracefully accepting constructive criticism 51 | - Focusing on what is best for the community 52 | - Showing empathy towards other community members 53 | 54 | Examples of unacceptable behavior by participants include: 55 | 56 | - The use of sexualized language or imagery and unwelcome sexual attention or 57 | - advances 58 | - Trolling, insulting/derogatory comments, and personal or political attacks 59 | - Public or private harassment 60 | - Publishing others' private information, such as a physical or electronic 61 | - address, without explicit permission 62 | - Other conduct which could reasonably be considered inappropriate in a 63 | professional setting 64 | 65 | ## Our Responsibilities 66 | 67 | Project maintainers are responsible for clarifying the standards of acceptable 68 | behavior and are expected to take appropriate and fair corrective action in 69 | response to any instances of unacceptable behavior. 70 | 71 | Project maintainers have the right and responsibility to remove, edit, or 72 | reject comments, commits, code, wiki edits, issues, and other contributions 73 | that are not aligned to this Code of Conduct, or to ban temporarily or 74 | permanently any contributor for other behaviors that they deem inappropriate, 75 | threatening, offensive, or harmful. 76 | 77 | ## Scope 78 | 79 | This Code of Conduct applies both within project spaces and in public spaces 80 | when an individual is representing the project or its community. Examples of 81 | representing a project or community include using an official project e-mail 82 | address, posting via an official social media account, or acting as an appointed 83 | representative at an online or offline event. Representation of a project may be 84 | further defined and clarified by project maintainers. 85 | 86 | ## Enforcement 87 | 88 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 89 | reported by contacting the project team at jxnblk@gmail.com. All 90 | complaints will be reviewed and investigated and will result in a response that 91 | is deemed necessary and appropriate to the circumstances. The project team is 92 | obligated to maintain confidentiality with regard to the reporter of an incident. 93 | Further details of specific enforcement policies may be posted separately. 94 | 95 | Project maintainers who do not follow or enforce the Code of Conduct in good 96 | faith may face temporary or permanent repercussions as determined by other 97 | members of the project's leadership. 98 | 99 | ## Attribution 100 | 101 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 102 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 103 | 104 | [homepage]: https://www.contributor-covenant.org 105 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | Copyright (c) 2018 Brent Jackson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migration 2 | 3 | ## Upgrading to MDX Deck v4 4 | 5 | For simple decks, upgrading to v4 should not require any major changes and should help resolve an issue when making changes to a file while the development server is running. 6 | 7 | - If you are using a custom `Provider` component, replace this component with the new `Header` and `Footer` components 8 | - Note that some functionality previously available in the Provider component is no longer possible 9 | - Replace any instance of the `Appear` component with the new `Steps` component. This should be a 1:1 change. 10 | - The document title is no longer inferred from the first heading. Use the `Head` component with a `
` and ``
111 | - `text.heading`: styles for all headings
112 | - `styles`: Theme UI styles for MDX elements
113 | - `styles.Slide`: styles for the wrapping Slide component
114 | - `styles.Header`: styles for the Header component
115 | - `styles.Footer`: styles for the Footer component
116 | - `components`: object of MDX components
117 | - `googleFont`: Stylesheet URL for adding a Google Font
118 |
119 | [emotion]: https://emotion.sh
120 | [theme ui]: https://theme-ui.com
121 | [mdx]: https://github.com/mdx-js/mdx
122 | [react-syntax-highlighter]: https://github.com/conorhastings/react-syntax-highlighter
123 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Examples
3 |
4 | - [Basic Example](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/basic)
5 | - [Multiple Decks](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/multiple)
6 | - [Syntax Highlighting](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/syntax-highlighting)
7 | - [Prism Syntax Highlighting](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/prism)
8 | - [Aspect Ratio](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/aspect-ratio)
9 | - [Layouts](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/layouts)
10 | - [Images](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/images)
11 | - [Appear](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/appear)
12 | - [Head](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/head)
13 | - [Provider](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/provider)
14 | - [Themes](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/themes)
15 |
16 |
20 |
--------------------------------------------------------------------------------
/examples/aspect-ratio/deck.mdx:
--------------------------------------------------------------------------------
1 | import { future, aspect } from 'mdx-deck/themes'
2 |
3 | export const themes = [
4 | future,
5 | aspect,
6 | ]
7 |
8 | # Hello!
9 |
10 | ---
11 |
12 | This deck is fluid to a 16:9 aspect ratio
13 |
14 | ---
15 |
16 | Images will still render full bleed at other aspect ratios
17 |
18 |
--------------------------------------------------------------------------------
/examples/aspect-ratio/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/aspect-ratio-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/basic/deck.mdx:
--------------------------------------------------------------------------------
1 |
2 | # Hello!
3 |
4 | ---
5 |
6 | This is MDX Deck
7 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/basic-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/gatsby/decks/beep.mdx:
--------------------------------------------------------------------------------
1 |
2 | # Beep
3 |
4 | ---
5 |
6 | ## Boop
7 |
8 | ---
9 |
10 | Bop
11 |
--------------------------------------------------------------------------------
/examples/gatsby/decks/hello.mdx:
--------------------------------------------------------------------------------
1 |
2 | # Hello
3 |
4 | ---
5 |
6 | This is built with gatsby-theme-mdx-deck
7 |
--------------------------------------------------------------------------------
/examples/gatsby/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pathPrefix: '/mdx-deck',
3 | plugins: [
4 | 'gatsby-plugin-catch-links',
5 | {
6 | resolve: 'gatsby-theme-mdx-deck',
7 | options: {
8 | basePath: '/slides',
9 | },
10 | },
11 | ],
12 | }
13 |
--------------------------------------------------------------------------------
/examples/gatsby/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/gatsby-example",
4 | "version": "4.1.0",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "scripts": {
8 | "start": "gatsby develop",
9 | "clean": "gatsby clean",
10 | "build": "gatsby build --prefix-paths",
11 | "serve": "gatsby serve --prefix-paths",
12 | "gh-pages": "npx gh-pages -d public"
13 | },
14 | "dependencies": {
15 | "gatsby": "*",
16 | "gatsby-plugin-catch-links": "^2.1.2",
17 | "gatsby-theme-mdx-deck": "^4.1.0",
18 | "react": "^16.8.6",
19 | "react-dom": "^16.8.6"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/gatsby/src/pages/index.mdx:
--------------------------------------------------------------------------------
1 |
2 | # MDX Deck Gatsby Example
3 |
4 | - [Deck](/slides)
5 |
--------------------------------------------------------------------------------
/examples/head/deck.mdx:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello
4 |
5 |
6 | # Hello!
7 |
8 | ---
9 |
10 | This deck has a custom title
11 |
12 |
--------------------------------------------------------------------------------
/examples/head/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/head-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/header-footer/deck.mdx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # Header
5 |
6 |
7 |
8 |
13 |
14 | # Hello!
15 |
16 | ---
17 |
18 | This deck has a header and footer
19 |
20 |
--------------------------------------------------------------------------------
/examples/header-footer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/header-footer-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/images/deck.mdx:
--------------------------------------------------------------------------------
1 | import {
2 | Image,
3 | } from 'mdx-deck'
4 |
5 | # Hello!
6 |
7 | ---
8 |
9 |
18 |
19 | ## Background Image
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/images/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/images-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/layouts/deck.mdx:
--------------------------------------------------------------------------------
1 |
2 | # Hello!
3 |
4 | ---
5 |
6 |
7 |
8 | ## Invert Layout
9 |
10 |
11 |
12 | ---
13 |
14 |
15 |
16 | 
17 |
18 | ## Split Layout
19 |
20 |
21 |
22 | ---
23 |
24 |
25 |
26 | 
27 |
28 | ## SplitRight Layout
29 |
30 |
31 |
32 | ---
33 |
34 |
35 |
36 | ## Horizontal Layout
37 |
38 | 
39 |
40 | 
41 |
42 |
43 |
44 | ---
45 |
46 |
47 |
48 | ```jsx
49 |
50 | ```
51 |
52 |
53 |
--------------------------------------------------------------------------------
/examples/layouts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/layouts-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/multiple/deck.js:
--------------------------------------------------------------------------------
1 | import { slides as one } from './one.mdx'
2 | import { slides as two } from './two.mdx'
3 |
4 | export const slides = [...one, ...two]
5 |
--------------------------------------------------------------------------------
/examples/multiple/one.mdx:
--------------------------------------------------------------------------------
1 |
2 | # Hello!
3 |
4 | ---
5 |
6 | This is MDX Deck #1
7 |
--------------------------------------------------------------------------------
/examples/multiple/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/multiple-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.js",
7 | "build": "mdx-deck build deck.js",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/multiple/two.mdx:
--------------------------------------------------------------------------------
1 |
2 | # This is Deck # 2
3 |
4 | ---
5 |
6 | :sunglasses:
7 |
--------------------------------------------------------------------------------
/examples/prism/deck.mdx:
--------------------------------------------------------------------------------
1 | import { future, prism } from 'mdx-deck/themes'
2 |
3 | export const themes = [
4 | future,
5 | prism
6 | ]
7 |
8 | # Hello!
9 |
10 | ---
11 |
12 | ```jsx
13 | import React from 'react'
14 |
15 | export default props =>
16 | Highlighting
17 | ```
18 |
--------------------------------------------------------------------------------
/examples/prism/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/prism-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/provider/deck.mdx:
--------------------------------------------------------------------------------
1 | import { comic } from 'mdx-deck/themes'
2 | import theme from './theme'
3 |
4 | export const themes = [
5 | comic,
6 | theme,
7 | ]
8 |
9 | # Hello!
10 |
11 | ---
12 |
13 | This deck has a custom Provider component
14 |
15 |
--------------------------------------------------------------------------------
/examples/provider/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/provider-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/provider/theme.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Provider = props => (
4 |
5 | {props.children}
6 |
14 | Put your name here
15 |
16 |
17 | )
18 |
19 | export default {
20 | Provider,
21 | }
22 |
--------------------------------------------------------------------------------
/examples/steps/deck.mdx:
--------------------------------------------------------------------------------
1 |
2 | # Hello!
3 |
4 | ---
5 |
6 |
7 | - One
8 |
9 | - Two
10 | - Three
11 | - Four
12 |
13 |
14 |
15 | ---
16 |
17 | ## One
18 |
19 |
20 |
21 | ## Two
22 | ## Three
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/examples/steps/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/appear-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/syntax-highlighting/deck.mdx:
--------------------------------------------------------------------------------
1 | import { future, highlight } from '@mdx-deck/themes'
2 |
3 | export const theme = {
4 | ...future,
5 | ...highlight
6 | }
7 |
8 | # Hello!
9 |
10 | ---
11 |
12 | ```jsx
13 | import React from 'react'
14 |
15 | export default props =>
16 | Highlighting
17 | ```
18 |
--------------------------------------------------------------------------------
/examples/syntax-highlighting/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/syntax-highlighting-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/themes/deck.mdx:
--------------------------------------------------------------------------------
1 | import { ThemeName } from './theme'
2 |
3 | export { theme } from './theme'
4 |
5 | # Hello !
6 |
7 | ---
8 |
9 | This deck has a custom Provider component
10 | that lets you switch between all the different themes.
11 |
12 | ---
13 |
14 | ```jsx
15 | import React from 'react'
16 |
17 | export default ({ children }) => {
18 | return (
19 | <>
20 | {children}
21 | >
22 | )
23 | }
24 | ```
25 |
--------------------------------------------------------------------------------
/examples/themes/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/themes-example",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/themes/theme.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react'
2 | import { ThemeProvider } from 'emotion-theming'
3 | import { MDXProvider } from '@mdx-js/react'
4 | import * as themes from 'mdx-deck/themes'
5 |
6 | const names = Object.keys(themes)
7 |
8 | const DefaultProvider = props => <>{props.children}>
9 |
10 | const Context = React.createContext()
11 |
12 | const Provider = props => {
13 | const [name, setTheme] = useState(names[0])
14 | const cycle = e => {
15 | const i = (names.indexOf(name) + 1) % names.length
16 | setTheme(names[i])
17 | }
18 |
19 | const baseTheme = themes[name]
20 | const theme = typeof baseTheme === 'function' ? baseTheme({}) : baseTheme
21 | const Root = theme.Provider || DefaultProvider
22 |
23 | return (
24 |
25 |
26 |
27 |
28 | {props.children}
29 |
30 |
31 |
32 |
39 |
52 |
53 |
54 | )
55 | }
56 |
57 | export const ThemeName = props => {
58 | const context = useContext(Context)
59 |
60 | return <>{context}>
61 | }
62 |
63 | export const theme = {
64 | Provider,
65 | }
66 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/*"
4 | ],
5 | "npmClient": "yarn",
6 | "useWorkspaces": true,
7 | "version": "4.1.1"
8 | }
9 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "npm i yarn@latest && yarn && yarn build"
3 | publish = "docs/public"
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "workspaces": [
4 | "packages/*",
5 | "templates/*",
6 | "examples/*",
7 | "docs"
8 | ],
9 | "scripts": {
10 | "start": "yarn workspace @mdx-deck/docs start",
11 | "build": "yarn workspace @mdx-deck/docs build",
12 | "export": "./packages/export/cli.js http://localhost:8000/print -o docs/public/deck.pdf",
13 | "cypress:open": "cypress open",
14 | "cypress:run": "cypress run",
15 | "test:dev": "start-server-and-test start http://localhost:8000 cypress:open",
16 | "jest": "jest",
17 | "test": "jest && start-server-and-test start http://localhost:8000 cypress:run"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.8.4",
21 | "@babel/preset-env": "^7.8.4",
22 | "@babel/preset-react": "^7.8.3",
23 | "@testing-library/react": "^10.0.2",
24 | "cypress": "^4.3.0",
25 | "husky": "^4.2.1",
26 | "jest": "^25.1.0",
27 | "jest-emotion": "^10.0.27",
28 | "lerna": "^3.13.1",
29 | "lint-staged": "^10.0.4",
30 | "prettier": "^1.16.4",
31 | "react-test-renderer": "^16.12.0",
32 | "start-server-and-test": "^1.9.1"
33 | },
34 | "jest": {
35 | "testMatch": [
36 | "**/packages/**/test/*.js"
37 | ],
38 | "testPathIgnorePatterns": [
39 | "/node_modules/",
40 | "/.cache/",
41 | "/public/"
42 | ],
43 | "coverageReporters": [
44 | "lcov",
45 | "text",
46 | "html"
47 | ],
48 | "collectCoverageFrom": [
49 | "packages/**/src/**/*.js"
50 | ],
51 | "coverageThreshold": {
52 | "global": {
53 | "branches": 80,
54 | "functions": 80,
55 | "lines": 80,
56 | "statements": 80
57 | }
58 | },
59 | "snapshotSerializers": [
60 | "jest-emotion"
61 | ]
62 | },
63 | "husky": {
64 | "hooks": {
65 | "pre-commit": "lint-staged"
66 | }
67 | },
68 | "lint-staged": {
69 | "*.{js,json}": [
70 | "prettier --write",
71 | "git add"
72 | ]
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/packages/create-deck/README.md:
--------------------------------------------------------------------------------
1 | # npm init deck
2 |
3 | Create mdx-deck presentations
4 |
5 | ```sh
6 | npm init deck my-presentation
7 | ```
8 |
--------------------------------------------------------------------------------
/packages/create-deck/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const fs = require('fs')
3 | const path = require('path')
4 | const meow = require('meow')
5 | const chalk = require('chalk')
6 | const initit = require('initit')
7 |
8 | const logo = chalk.magenta('[mdx-deck]')
9 | const log = (...args) => {
10 | console.log(logo, ...args)
11 | }
12 | log.error = (...args) => {
13 | console.log(chalk.red('[ERROR]'), ...args)
14 | }
15 |
16 | const template = 'jxnblk/mdx-deck/templates/basic'
17 |
18 | const cli = meow(
19 | `
20 | Usage
21 |
22 | $ npm init deck my-presentation
23 |
24 | $ npx create-deck my-presentation
25 |
26 | `,
27 | {
28 | booleanDefault: undefined,
29 | flags: {
30 | help: {
31 | type: 'boolean',
32 | alias: 'h',
33 | },
34 | version: {
35 | type: 'boolean',
36 | alias: 'v',
37 | },
38 | },
39 | }
40 | )
41 |
42 | const [name] = cli.input
43 |
44 | if (!name) {
45 | cli.showHelp(0)
46 | }
47 |
48 | // todo: ensure directory doesn't exist
49 | initit({ name, template })
50 | .then(res => {
51 | log('created mdx-deck')
52 | process.exit(0)
53 | })
54 | .catch(err => {
55 | log.error('failed to create mdx-deck')
56 | log.error(err)
57 | process.exit(1)
58 | })
59 |
--------------------------------------------------------------------------------
/packages/create-deck/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-deck",
3 | "version": "4.0.0",
4 | "description": "Create mdx-deck presentations",
5 | "bin": {
6 | "create-deck": "cli.js"
7 | },
8 | "author": "Brent Jackson",
9 | "license": "MIT",
10 | "dependencies": {
11 | "chalk": "^3.0.0",
12 | "initit": "^1.0.0-2",
13 | "meow": "^6.0.0"
14 | },
15 | "gitHead": "13d00b47780424cc3b52d38ad6f19e99d007c06a"
16 | }
17 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | .cache
3 | public
4 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/.npmignore:
--------------------------------------------------------------------------------
1 | .cache
2 | coverage
3 | public
4 | test
5 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/README.md:
--------------------------------------------------------------------------------
1 |
2 | # @mdx-deck/gatsby-plugin
3 |
4 | Plugin used internally by mdx-deck core package -- **not intended for standalone usage**
5 |
6 | See [`gatsby-theme-mdx-deck`](https://github.com/jxnblk/mdx-deck/tree/master/packages/gatsby-theme) for custom usage with Gatsby
7 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | export { wrapPageElement } from './src'
2 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | 'gatsby-plugin-react-helmet',
4 | ],
5 | }
6 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const { createPath, validatePath } = require('gatsby-page-utils')
4 | const remarkPlugins = [
5 | require('remark-images'),
6 | require('remark-unwrap-images'),
7 | require('remark-emoji'),
8 | ]
9 |
10 | exports.onPreBootstrap = ({}, opts = {}) => {
11 | opts.dirname = opts.dirname || __dirname
12 | const staticSourceDir = path.join(opts.dirname, 'static')
13 | const hasStaticDir = fs.existsSync(staticSourceDir)
14 |
15 | if (fs.existsSync('./static')) {
16 | // remove temp directory
17 | fs.unlinkSync('./static')
18 | }
19 |
20 | if (hasStaticDir) {
21 | // link to source static directory
22 | fs.symlinkSync(staticSourceDir, './static')
23 | }
24 | }
25 |
26 | exports.onCreateWebpackConfig = ({
27 | stage,
28 | rules,
29 | loaders,
30 | plugins,
31 | actions,
32 | }) => {
33 | actions.setWebpackConfig({
34 | module: {
35 | rules: [
36 | {
37 | test: /\.mdx$/,
38 | use: [
39 | loaders.js(),
40 | {
41 | loader: '@mdx-js/loader',
42 | options: {
43 | remarkPlugins,
44 | }
45 | },
46 | ]
47 | }
48 | ]
49 | }
50 | })
51 | }
52 |
53 | exports.resolvableExtensions = () => ['.mdx']
54 |
55 | exports.createPages = ({
56 | actions,
57 | }, {
58 | path: source,
59 | } = {}) => {
60 | if (!source) return
61 |
62 | actions.createPage({
63 | path: '/',
64 | matchPath: '/*',
65 | component: source,
66 | })
67 | }
68 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | export { wrapPageElement } from './src'
2 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/index.js:
--------------------------------------------------------------------------------
1 | // noop
2 | export * from './src'
3 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mdx-deck/gatsby-plugin",
3 | "version": "4.1.1",
4 | "main": "index.js",
5 | "author": "Brent Jackson",
6 | "license": "MIT",
7 | "repository": "github:jxnblk/mdx-deck",
8 | "scripts": {
9 | "start": "gatsby develop --open"
10 | },
11 | "peerDependencies": {
12 | "gatsby": "^2.14.0",
13 | "react": "^16.10.0",
14 | "react-dom": "^16.10.0"
15 | },
16 | "devDependencies": {
17 | "gatsby": "^2.14.0",
18 | "react": "^16.10.0",
19 | "react-dom": "^16.10.0"
20 | },
21 | "dependencies": {
22 | "@mdx-js/loader": "^1.5.3",
23 | "@mdx-js/mdx": "^1.5.3",
24 | "gatsby-page-utils": "^0.0.39",
25 | "gatsby-plugin-react-helmet": "^3.1.18",
26 | "hhmmss": "^1.0.0",
27 | "react-helmet": "^5.2.1",
28 | "remark-emoji": "^2.0.2",
29 | "remark-images": "^2.0.0",
30 | "remark-unwrap-images": "^2.0.0",
31 | "theme-ui": "^0.3.0-alpha.6"
32 | },
33 | "gitHead": "13d00b47780424cc3b52d38ad6f19e99d007c06a",
34 | "publishConfig": {
35 | "access": "public"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/clock.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default props => {
4 | const [time, setTime] = React.useState(new Date().toLocaleTimeString())
5 |
6 | React.useEffect(() => {
7 | const timer = setInterval(() => {
8 | const now = new Date()
9 | setTime(now.toLocaleTimeString())
10 | }, 1000)
11 | return () => {
12 | clearInterval(timer)
13 | }
14 | }, [])
15 |
16 | return time
17 | }
18 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/components.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React from 'react'
4 | import useSteps from './use-steps'
5 |
6 | const createComponent = key => {
7 | const Component = () => false
8 | Component.__mdxDeck = true
9 | Component[`__mdxDeck_${key}`] = true
10 | return Component
11 | }
12 |
13 | export const Notes = createComponent('notes')
14 | export const Head = createComponent('head')
15 | export const Header = createComponent('header')
16 | export const Footer = createComponent('footer')
17 |
18 | export const Color = ({
19 | color,
20 | bg,
21 | ...props
22 | }) =>
23 |
39 |
40 | export const Invert = props =>
41 |
46 |
47 | export const StepList = props => {
48 | const list = React.Children.toArray(props.children)
49 | .find(child => /^(ul|ol)$/.test(child.props.originalType))
50 |
51 | // ensure this works
52 | const items = React.Children.toArray(list && list.props.children)
53 |
54 | const step = useSteps(items.length)
55 |
56 | const children = items.map((item, i) => React.cloneElement(item, {
57 | style: {
58 | visibility: i < step ? 'visible' : 'hidden'
59 | }
60 | }))
61 |
62 | return React.cloneElement(list, { children })
63 | }
64 |
65 | export const Appear = props => {
66 | const children = React.Children.toArray(props.children)
67 | const step = useSteps(children.length)
68 | const styled = children.map((child, i) =>
69 | React.cloneElement(child, {
70 | style: {
71 | visibility: i < step ? 'visible' : 'hidden',
72 | }
73 | })
74 | )
75 | return {styled}
76 | }
77 |
78 | export const Steps = props => {
79 | const list = React.Children.toArray(props.children)
80 | .find(child => /^(ul|ol)$/.test(child.props.originalType))
81 |
82 | if (!list) return
83 | return
84 | }
85 |
86 | export const Image = ({
87 | src,
88 | width = '100%',
89 | height = '100%',
90 | size = 'cover',
91 | ...props
92 | }) =>
93 |
104 |
105 | export const Horizontal = ({
106 | ...props
107 | }) => {
108 | const children = React.Children.toArray(props.children)
109 | return (
110 |
118 | {children.map((child, i) => (
119 |
127 | {child}
128 |
129 | ))}
130 |
131 | )
132 | }
133 |
134 | const Half = props =>
140 |
141 | export const Split = ({ reverse, ...props }) => {
142 | const [first, ...rest] = React.Children.toArray(props.children)
143 | const children = reverse
144 | ? [ {rest} , {first} ]
145 | : [ {first} , {rest} ]
146 |
147 | return (
148 |
156 | {children}
157 |
158 | )
159 | }
160 |
161 | export const SplitRight = props =>
162 |
166 |
167 | export const FullScreenCode = ({ ...props }) => (
168 |
182 | )
183 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/container.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx, Box, Flex } from 'theme-ui'
3 | import React from 'react'
4 | import { Context, useDeck } from './context'
5 | import modes from './modes'
6 | import Header from './header'
7 | import Footer from './footer'
8 | import Slide from './slide'
9 | import Clock from './clock'
10 | import Timer from './timer'
11 |
12 | const Main = ({
13 | width = '100vw',
14 | height = '100vh',
15 | preview = false,
16 | ...props
17 | }) => {
18 | const outer = useDeck()
19 | const context = {
20 | ...outer,
21 | main: !preview,
22 | }
23 |
24 | return (
25 |
26 |
33 | {props.header && (
34 |
35 | {props.header}
36 |
37 | )}
38 | {props.children}
39 | {props.footer && (
40 |
43 | )}
44 |
45 |
46 | )
47 | }
48 |
49 | const Presenter = props => {
50 | const next = props.slides[props.index + 1]
51 |
52 | return (
53 |
60 |
66 |
70 |
71 | {props.slide}
72 |
73 |
74 |
75 |
84 |
90 | {next}
91 |
92 |
97 | {props.notes}
98 |
99 |
104 |
105 | {props.index} / {props.slides.length - 1}
106 |
107 |
117 | ⬈
118 |
119 |
120 |
121 |
122 | {' '}
123 |
124 |
125 |
126 |
127 |
128 | )
129 | }
130 |
131 | const Overview = props => {
132 | const ref = React.useRef(null)
133 |
134 | React.useEffect(() => {
135 | if (!ref.current) return
136 | if (typeof ref.current.scrollIntoViewIfNeeded !== 'function') return
137 | ref.current.scrollIntoViewIfNeeded()
138 | }, [ref.current])
139 |
140 | return (
141 |
148 |
155 | {props.slides.map((slide, i) => (
156 | {
162 | props.setIndex(i)
163 | props.setStep(0)
164 | props.setSteps(0)
165 | }}
166 | sx={{
167 | p: 2,
168 | height: '30%'
169 | }}>
170 |
175 | {slide}
176 |
177 |
178 | ))}
179 |
180 |
187 |
191 |
192 | {props.slide}
193 |
194 |
195 |
196 |
197 | )
198 | }
199 |
200 | const Grid = props => {
201 | const ref = React.useRef(null)
202 |
203 | React.useEffect(() => {
204 | if (!ref.current) return
205 | if (typeof ref.current.scrollIntoViewIfNeeded !== 'function') return
206 | ref.current.scrollIntoViewIfNeeded()
207 | }, [ref.current])
208 |
209 | return (
210 |
216 |
222 | {props.slides.map((slide, i) => (
223 | {
229 | props.setIndex(i)
230 | props.setStep(0)
231 | props.setSteps(0)
232 | props.setMode(modes.default)
233 | }}
234 | sx={{
235 | p: 2,
236 | width: '25%',
237 | height: '23vh',
238 | }}>
239 |
244 | {slide}
245 |
246 |
247 | ))}
248 |
249 |
250 | )
251 | }
252 |
253 | const Print = props => {
254 | return (
255 |
256 | {props.slides.map((slide, i) => (
257 |
258 |
259 | {slide}
260 |
261 |
262 | ))}
263 |
264 | )
265 | }
266 |
267 | export default props => {
268 | const context = useDeck()
269 |
270 | switch (context.mode) {
271 | case modes.presenter:
272 | return
273 | case modes.overview:
274 | return
275 | case modes.grid:
276 | return
277 | case modes.print:
278 | return
279 | case modes.default:
280 | default:
281 | return
282 | }
283 | }
284 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/context.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export const Context = React.createContext({})
4 |
5 | export const useDeck = () => React.useContext(Context)
6 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/deck.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Helmet } from 'react-helmet'
3 | import { ThemeProvider, merge } from 'theme-ui'
4 | import split from './split-slides'
5 | import { Context } from './context'
6 | import Keyboard from './keyboard'
7 | import modes from './modes'
8 | import Storage from './storage'
9 | import Container from './container'
10 | import Slide from './slide'
11 | import baseTheme from './theme'
12 |
13 | const getIndex = props => {
14 | if (!props.location) return 0
15 | const n = Number(props.location.hash.replace(/^#/, ''))
16 | return n
17 | }
18 |
19 | export default props => {
20 | const slides = split(props)
21 | const [index, setIndex] = React.useState(getIndex(props))
22 | const { slug } = props.pageContext || {}
23 | const slide = slides[index]
24 |
25 | const [mode, setMode] = React.useState(modes.default)
26 | const toggleMode = next => setMode(current =>
27 | current === next ? modes.default : next
28 | )
29 |
30 | const [step, setStep] = React.useState(0)
31 | const [steps, setSteps] = React.useState(0)
32 |
33 | const lastIndex = React.useRef(0)
34 | const direction = index - lastIndex.current
35 |
36 | React.useEffect(() => {
37 | lastIndex.current = index
38 | }, [index])
39 |
40 | React.useEffect(() => {
41 | if (props.location.pathname === '/print') return
42 | props.navigate('/#' + index, {
43 | replace: true,
44 | })
45 | }, [index])
46 |
47 | React.useEffect(() => {
48 | if (props.location.pathname === '/print') {
49 | setMode(modes.print)
50 | }
51 | if (!slide) {
52 | props.navigate('/')
53 | setIndex(0)
54 | }
55 | }, [])
56 |
57 | if (!slide) return false
58 |
59 | const context = {
60 | slides,
61 | slug,
62 | index,
63 | setIndex,
64 | direction,
65 | length: slides.length,
66 | slide,
67 | mode,
68 | setMode,
69 | toggleMode,
70 | notes: slide.notes,
71 | header: slides.header,
72 | footer: slides.footer,
73 | step,
74 | setStep,
75 | steps,
76 | setSteps,
77 | }
78 |
79 | context.previous = () => {
80 | if (steps && step > 0) {
81 | setStep(n => n - 1)
82 | } else {
83 | setIndex(n => n > 0 ? n - 1 : n)
84 | setStep(0)
85 | setSteps(0)
86 | }
87 | }
88 |
89 | context.next = () => {
90 | if (step < steps) {
91 | setStep(n => n + 1)
92 | } else {
93 | setIndex(n => n < slides.length - 1 ? n + 1 : n)
94 | setStep(0)
95 | setSteps(0)
96 | }
97 | }
98 |
99 | const theme = merge(baseTheme, props.theme || {})
100 |
101 | return (
102 |
103 |
104 |
105 |
106 | {slides.head.children}
107 | {theme.googleFont && }
108 |
109 |
112 |
113 |
114 | {slide}
115 |
116 |
117 |
118 |
119 | )
120 | }
121 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/footer.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 |
4 | export default props =>
5 |
17 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/header.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 |
4 | export default props =>
5 |
17 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { MDXProvider } from '@mdx-js/react'
3 | import wrapper from './deck'
4 | import * as mdxComponents from './components'
5 |
6 | const components = {
7 | wrapper,
8 | ...mdxComponents,
9 | }
10 |
11 | const Page = props =>
12 |
13 | {props.children}
14 |
15 |
16 | export const wrapPageElement = ({ element, props }) =>
17 |
18 | {element}
19 |
20 |
21 | export { useDeck } from './context'
22 | export { useSteps } from './use-steps'
23 | export * from './components'
24 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/keyboard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useDeck } from './context'
3 | import modes from './modes'
4 |
5 | const keys = {
6 | right: 39,
7 | left: 37,
8 | up: 38,
9 | down: 40,
10 | space: 32,
11 | p: 80,
12 | o: 79,
13 | g: 71,
14 | esc: 27,
15 | pageUp: 33,
16 | pageDown: 34,
17 | }
18 |
19 | export const useKeyboard = () => {
20 | const context = useDeck()
21 |
22 | React.useEffect(() => {
23 | const handleKeyDown = e => {
24 | if (e.metaKey) return
25 | if (e.ctrlKey) return
26 |
27 | if (e.altKey) {
28 | switch (e.keyCode) {
29 | case keys.p:
30 | if (e.shiftKey) {
31 | context.toggleMode(modes.print)
32 | } else {
33 | context.toggleMode(modes.presenter)
34 | }
35 | break
36 | case keys.o:
37 | context.toggleMode(modes.overview)
38 | break
39 | case keys.g:
40 | context.toggleMode(modes.grid)
41 | break
42 | default:
43 | break
44 | }
45 | } else if (e.shiftKey) {
46 | switch (e.keyCode) {
47 | case keys.space:
48 | e.preventDefault()
49 | context.previous()
50 | break
51 | default:
52 | break
53 | }
54 | } else {
55 | switch (e.keyCode) {
56 | case keys.right:
57 | case keys.down:
58 | case keys.pageDown:
59 | case keys.space:
60 | e.preventDefault()
61 | context.next()
62 | break
63 | case keys.left:
64 | case keys.up:
65 | case keys.pageUp:
66 | e.preventDefault()
67 | context.previous()
68 | break
69 | case keys.esc:
70 | context.setMode(modes.default)
71 | break
72 | default:
73 | break
74 | }
75 | }
76 | }
77 |
78 | document.addEventListener('keydown', handleKeyDown)
79 |
80 | return () => {
81 | document.removeEventListener('keydown', handleKeyDown)
82 | }
83 | }, [context])
84 | }
85 |
86 | export default () => {
87 | useKeyboard()
88 | return false
89 | }
90 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/modes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | default: 'default',
3 | presenter: 'presenter',
4 | overview: 'overview',
5 | grid: 'grid',
6 | print: 'print',
7 | }
8 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/slide.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 |
4 | export default ({
5 | zoom,
6 | width = '100%',
7 | height = '100%',
8 | children,
9 | ...props
10 | }) =>
11 |
28 | {children}
29 |
30 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/split-slides.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default props => {
4 | const arr = React.Children.toArray(props.children)
5 | const splits = []
6 | const slides = []
7 | slides.head = {
8 | props: {},
9 | children: [],
10 | }
11 | const notes = {}
12 |
13 | arr.forEach((child, i) => {
14 | const {
15 | originalType,
16 | mdxType,
17 | parentName,
18 | children,
19 | ...childProps
20 | } = child.props
21 |
22 | // todo: figure out nested decks
23 | // if (originalType.isMDXComponent) {}
24 |
25 | // get notes
26 | if (originalType.__mdxDeck_notes || mdxType === 'Notes') {
27 | notes[splits.length] = children
28 | } else if (originalType.__mdxDeck_header || mdxType === 'Header') {
29 | slides.header = children
30 | } else if (originalType.__mdxDeck_footer || mdxType === 'Footer') {
31 | slides.footer = children
32 | // get head content
33 | } else if (originalType.__mdxDeck_head || mdxType === 'Head') {
34 | slides.head.children.push(children)
35 | Object.assign(slides.head.props, childProps)
36 | }
37 | if (mdxType === 'hr') {
38 | splits.push(i)
39 | }
40 | })
41 |
42 | let previousSplit = 0
43 | splits.forEach((split, i) => {
44 | const children = [...arr.slice(previousSplit, split)]
45 | if (notes[i]) children.notes = notes[i]
46 | slides.push(children)
47 | previousSplit = split + 1
48 | })
49 | const last = [...arr.slice(previousSplit)]
50 | if (notes[slides.length]) last.notes = notes[slides.length]
51 | slides.push(last)
52 |
53 | slides.head.children = React.Children.toArray(slides.head.children).map(
54 | (child, i) => {
55 | const { originalType, mdxType, parentName, ...childProps } = child.props
56 | return React.createElement(originalType, {
57 | key: i,
58 | ...childProps,
59 | })
60 | }
61 | )
62 |
63 | return slides
64 | }
65 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/storage.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useDeck } from './context'
3 |
4 | const keys = {
5 | slide: 'mdx-deck-slide',
6 | step: 'mdx-deck-step',
7 | }
8 |
9 | export const useStorage = () => {
10 | const context = useDeck()
11 | const [focused, setFocused] = React.useState(false)
12 |
13 | const handleFocus = () => setFocused(true)
14 | const handleBlur = () => setFocused(false)
15 |
16 | const handleStorageChange = e => {
17 | const n = parseInt(e.newValue, 10)
18 | if (isNaN(n)) return
19 | switch (e.key) {
20 | case keys.slide:
21 | context.setIndex(n)
22 | break
23 | case keys.step:
24 | context.setStep(n)
25 | break
26 | }
27 | }
28 |
29 | React.useEffect(() => {
30 | setFocused(document.hasFocus())
31 | }, [])
32 |
33 | React.useEffect(() => {
34 | if (!focused) window.addEventListener('storage', handleStorageChange)
35 | window.addEventListener('focus', handleFocus)
36 | window.addEventListener('blur', handleBlur)
37 | return () => {
38 | if (!focused) window.removeEventListener('storage', handleStorageChange)
39 | window.removeEventListener('focus', handleFocus)
40 | window.removeEventListener('blur', handleBlur)
41 | }
42 | }, [focused])
43 |
44 | // store changes
45 | React.useEffect(() => {
46 | if (!focused) return
47 | localStorage.setItem(keys.slide, context.index)
48 | }, [focused, context.index])
49 | }
50 |
51 | export default () => {
52 | useStorage()
53 | return false
54 | }
55 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/theme.js:
--------------------------------------------------------------------------------
1 | // base theme
2 | export default {
3 | colors: {
4 | text: '#fff',
5 | background: '#000',
6 | backdrop: '#111',
7 | },
8 | fonts: {
9 | body: 'system-ui, sans-serif',
10 | heading: 'inherit',
11 | monospace: 'Menlo, monospace',
12 | },
13 | fontWeights: {
14 | body: 400,
15 | heading: 700,
16 | },
17 | lineHeights: {
18 | body: 1.5,
19 | heading: 1.125,
20 | },
21 | text: {
22 | heading: {
23 | fontFamily: 'heading',
24 | fontWeight: 'heading',
25 | lineHeight: 'heading',
26 | },
27 | },
28 | styles: {
29 | root: {
30 | fontFamily: 'system-ui, sans-serif',
31 | },
32 | img: {
33 | width: '100vw',
34 | maxWidth: '100%',
35 | height: '100vh',
36 | objectFit: 'contain',
37 | },
38 | h1: {
39 | variant: 'text.heading',
40 | },
41 | h2: {
42 | variant: 'text.heading',
43 | },
44 | h3: {
45 | variant: 'text.heading',
46 | },
47 | h4: {
48 | variant: 'text.heading',
49 | },
50 | h5: {
51 | variant: 'text.heading',
52 | },
53 | h6: {
54 | variant: 'text.heading',
55 | },
56 | code: {
57 | fontFamily: 'monospace',
58 | },
59 | pre: {
60 | fontFamily: 'monospace',
61 | },
62 | Slide: {
63 | fontFamily: 'body',
64 | fontSize: '2em',
65 | },
66 | Header: {
67 | px: 3,
68 | },
69 | Footer: {
70 | px: 3,
71 | },
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/timer.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React from 'react'
4 | import hhmmss from 'hhmmss'
5 |
6 | export default props => {
7 | const [seconds, setSeconds] = React.useState(0)
8 | const [active, setActive] = React.useState(false)
9 |
10 | React.useEffect(() => {
11 | const timer = setInterval(() => {
12 | if (!active) return
13 | setSeconds(n => n + 1)
14 | }, 1000)
15 | return () => {
16 | clearInterval(timer)
17 | }
18 | }, [active])
19 |
20 | return (
21 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/src/use-steps.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useDeck } from './context'
3 |
4 | export const useSteps = length => {
5 | const context = useDeck()
6 |
7 | React.useEffect(() => {
8 | if (!context.main) return
9 | context.setSteps(length)
10 | if (context.direction < 0) context.setStep(length)
11 | }, [length, context])
12 |
13 | if (!context.main) return length
14 | return context.step
15 | }
16 |
17 | export default useSteps
18 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/test/__snapshots__/components.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Color renders 1`] = `
4 | .emotion-0 {
5 | display: -webkit-box;
6 | display: -webkit-flex;
7 | display: -ms-flexbox;
8 | display: flex;
9 | -webkit-flex-direction: column;
10 | -ms-flex-direction: column;
11 | flex-direction: column;
12 | -webkit-align-items: center;
13 | -webkit-box-align: center;
14 | -ms-flex-align: center;
15 | align-items: center;
16 | -webkit-box-pack: center;
17 | -webkit-justify-content: center;
18 | -ms-flex-pack: center;
19 | justify-content: center;
20 | width: 100%;
21 | height: 100%;
22 | background-color: tomato;
23 | }
24 |
25 | .emotion-0 a {
26 | color: inherit;
27 | }
28 |
29 |
32 | `;
33 |
34 | exports[`FullScreenCode renders 1`] = `
35 | .emotion-0 {
36 | width: 100%;
37 | height: 100%;
38 | }
39 |
40 | .emotion-0 pre {
41 | margin: 0 !important;
42 | width: 100%;
43 | height: 100%;
44 | overflow: auto;
45 | }
46 |
47 |
50 | Hi
51 |
52 | `;
53 |
54 | exports[`Horizontal renders 1`] = `
55 | .emotion-2 {
56 | display: -webkit-box;
57 | display: -webkit-flex;
58 | display: -ms-flexbox;
59 | display: flex;
60 | -webkit-align-items: center;
61 | -webkit-box-align: center;
62 | -ms-flex-align: center;
63 | align-items: center;
64 | height: 100%;
65 | text-align: center;
66 | }
67 |
68 | .emotion-0 {
69 | width: 50%;
70 | }
71 |
72 | .emotion-0 img {
73 | height: auto;
74 | }
75 |
76 |
79 |
82 |
83 | A
84 |
85 |
86 |
89 |
90 | B
91 |
92 |
93 |
94 | `;
95 |
96 | exports[`Image renders 1`] = `
97 | .emotion-0 {
98 | width: 100%;
99 | height: 100%;
100 | background-size: cover;
101 | background-image: url(kittens.png);
102 | background-position: center;
103 | background-repeat: no-repeat;
104 | }
105 |
106 |
109 | `;
110 |
111 | exports[`Invert renders 1`] = `
112 | .emotion-0 {
113 | display: -webkit-box;
114 | display: -webkit-flex;
115 | display: -ms-flexbox;
116 | display: flex;
117 | -webkit-flex-direction: column;
118 | -ms-flex-direction: column;
119 | flex-direction: column;
120 | -webkit-align-items: center;
121 | -webkit-box-align: center;
122 | -ms-flex-align: center;
123 | align-items: center;
124 | -webkit-box-pack: center;
125 | -webkit-justify-content: center;
126 | -ms-flex-pack: center;
127 | justify-content: center;
128 | width: 100%;
129 | height: 100%;
130 | color: background;
131 | background-color: text;
132 | }
133 |
134 | .emotion-0 a {
135 | color: inherit;
136 | }
137 |
138 |
141 | `;
142 |
143 | exports[`Split renders 1`] = `
144 | .emotion-2 {
145 | display: -webkit-box;
146 | display: -webkit-flex;
147 | display: -ms-flexbox;
148 | display: flex;
149 | -webkit-align-items: center;
150 | -webkit-box-align: center;
151 | -ms-flex-align: center;
152 | align-items: center;
153 | height: 100%;
154 | text-align: center;
155 | }
156 |
157 | .emotion-0 {
158 | width: 50%;
159 | }
160 |
161 | .emotion-0 img {
162 | height: auto;
163 | }
164 |
165 |
168 |
171 |
172 | A
173 |
174 |
175 |
178 |
179 | B
180 |
181 |
182 |
183 | `;
184 |
185 | exports[`SplitRight renders 1`] = `
186 | .emotion-2 {
187 | display: -webkit-box;
188 | display: -webkit-flex;
189 | display: -ms-flexbox;
190 | display: flex;
191 | -webkit-align-items: center;
192 | -webkit-box-align: center;
193 | -ms-flex-align: center;
194 | align-items: center;
195 | height: 100%;
196 | text-align: center;
197 | }
198 |
199 | .emotion-0 {
200 | width: 50%;
201 | }
202 |
203 | .emotion-0 img {
204 | height: auto;
205 | }
206 |
207 |
210 |
213 |
214 | B
215 |
216 |
217 |
220 |
221 | A
222 |
223 |
224 |
225 | `;
226 |
227 | exports[`Steps renders 1`] = `
228 | Array [
229 |
236 | One
237 | ,
238 |
245 | Two
246 | ,
247 | ]
248 | `;
249 |
250 | exports[`Steps renders with list 1`] = `
251 |
254 | -
261 | One
262 |
263 | -
270 | Two
271 |
272 |
273 | `;
274 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/test/clock.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import renderer from 'react-test-renderer'
3 | import Clock from '../src/clock'
4 |
5 | const render = el => renderer.create(el).toJSON()
6 |
7 | test('renders time', () => {
8 | const tree = render( )
9 | expect(tree).toMatch(/\d\d:\d\d/)
10 | })
11 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/test/components.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import renderer from 'react-test-renderer'
3 | import {
4 | Notes,
5 | Head,
6 | Header,
7 | Footer,
8 | Color,
9 | Invert,
10 | Steps,
11 | Image,
12 | Horizontal,
13 | Split,
14 | SplitRight,
15 | FullScreenCode,
16 | } from '../src/components'
17 |
18 | const render = el => renderer.create(el).toJSON()
19 |
20 | test('Color renders', () => {
21 | const json = render(
22 |
23 | )
24 | expect(json).toMatchSnapshot()
25 | })
26 |
27 | test('Invert renders', () => {
28 | const json = render(
29 |
30 | )
31 | expect(json).toMatchSnapshot()
32 | })
33 |
34 | test('Steps renders', () => {
35 | const json = render(
36 |
37 | One
38 | Two
39 |
40 | )
41 | expect(json).toMatchSnapshot()
42 | })
43 |
44 | test('Steps renders with list', () => {
45 | const json = render(
46 |
47 |
48 | - One
49 | - Two
50 |
51 |
52 | )
53 | expect(json).toMatchSnapshot()
54 | })
55 |
56 | test('Image renders', () => {
57 | const json = render(
58 |
59 | )
60 | expect(json).toMatchSnapshot()
61 | })
62 |
63 | test('Horizontal renders', () => {
64 | const json = render(
65 |
66 | A
67 | B
68 |
69 | )
70 | expect(json).toMatchSnapshot()
71 | })
72 |
73 | test('Split renders', () => {
74 | const json = render(
75 |
76 | A
77 | B
78 |
79 | )
80 | expect(json).toMatchSnapshot()
81 | })
82 |
83 | test('SplitRight renders', () => {
84 | const json = render(
85 |
86 | A
87 | B
88 |
89 | )
90 | expect(json).toMatchSnapshot()
91 | })
92 |
93 | test('FullScreenCode renders', () => {
94 | const json = render(
95 |
96 | Hi
97 |
98 | )
99 | expect(json).toMatchSnapshot()
100 | })
101 |
102 |
--------------------------------------------------------------------------------
/packages/gatsby-plugin/test/deck.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | render,
4 | fireEvent,
5 | cleanup
6 | } from '@testing-library/react'
7 | import Deck from '../src/deck'
8 | import {
9 | Steps,
10 | Header,
11 | Footer,
12 | } from '../src'
13 |
14 | afterEach(cleanup)
15 |
16 | let __key = 0
17 | const Comp = ({
18 | originalType,
19 | mdxType,
20 | ...props
21 | }) => React.createElement(originalType, props)
22 |
23 | const x = (tag, props, children) =>
24 | React.createElement(Comp, {
25 | key: __key++,
26 | tag,
27 | originalType: tag,
28 | mdxType: tag,
29 | ...props
30 | }, children)
31 |
32 | const mdx = [
33 | x('div', null, 'One'),
34 | x('hr', null),
35 | x('div', null, 'Two'),
36 | ]
37 |
38 | const deckProps = {
39 | children: mdx,
40 | location: {
41 | hash: '',
42 | pathname: '/',
43 | },
44 | navigate: jest.fn()
45 | }
46 |
47 | test('renders', () => {
48 | const tree = render(
49 |
50 | )
51 | const text = tree.getByText('One')
52 | expect(text).toBeTruthy()
53 | })
54 |
55 | test('advances one slide with right arrow key', () => {
56 | const tree =render (
57 |
58 | )
59 | fireEvent.keyDown(document.body, {
60 | keyCode: 39,
61 | })
62 | const text = tree.getByText('Two')
63 | expect(text).toBeTruthy()
64 | })
65 |
66 | test('advances one slide with down arrow key', () => {
67 | const tree =render (
68 |
69 | )
70 | fireEvent.keyDown(document.body, {
71 | keyCode: 40,
72 | })
73 | const text = tree.getByText('Two')
74 | expect(text).toBeTruthy()
75 | })
76 |
77 | test('advances one slide with spacebar key', () => {
78 | const tree =render (
79 |
80 | )
81 | fireEvent.keyDown(document.body, {
82 | keyCode: 32,
83 | })
84 | const text = tree.getByText('Two')
85 | expect(text).toBeTruthy()
86 | })
87 |
88 | test('advances one slide with page down key', () => {
89 | const tree =render (
90 |
91 | )
92 | fireEvent.keyDown(document.body, {
93 | keyCode: 34,
94 | })
95 | const text = tree.getByText('Two')
96 | expect(text).toBeTruthy()
97 | })
98 |
99 | test('goes back one slide with left arrow key', () => {
100 | const tree =render (
101 |
107 | )
108 | fireEvent.keyDown(document.body, {
109 | keyCode: 37,
110 | })
111 | const text = tree.getByText('One')
112 | expect(text).toBeTruthy()
113 | })
114 |
115 | test('goes back one slide with up arrow key', () => {
116 | const tree =render (
117 |
123 | )
124 | fireEvent.keyDown(document.body, {
125 | keyCode: 38,
126 | })
127 | const text = tree.getByText('One')
128 | expect(text).toBeTruthy()
129 | })
130 |
131 | test('goes back one slide with page up key', () => {
132 | const tree =render (
133 |
139 | )
140 | fireEvent.keyDown(document.body, {
141 | keyCode: 33,
142 | })
143 | const text = tree.getByText('One')
144 | expect(text).toBeTruthy()
145 | })
146 |
147 | test('goes back one slide with shift + space bar', () => {
148 | const tree =render (
149 |
155 | )
156 | fireEvent.keyDown(document.body, {
157 | shiftKey: true,
158 | keyCode: 32,
159 | })
160 | const text = tree.getByText('One')
161 | expect(text).toBeTruthy()
162 | })
163 |
164 | test('ignores meta keys', () => {
165 | const tree =render (
166 |
167 | )
168 | fireEvent.keyDown(document.body, {
169 | metaKey: true,
170 | keyCode: 39,
171 | })
172 | const text = tree.getByText('One')
173 | expect(text).toBeTruthy()
174 | })
175 |
176 | test('ignores ctrl keys', () => {
177 | const tree =render (
178 |
179 | )
180 | fireEvent.keyDown(document.body, {
181 | ctrlKey: true,
182 | keyCode: 39,
183 | })
184 | const text = tree.getByText('One')
185 | expect(text).toBeTruthy()
186 | })
187 |
188 | test('initializes print mode', () => {
189 | const tree =render (
190 |
197 | )
198 | const one = tree.getByText('One')
199 | const two = tree.getByText('Two')
200 | expect(one).toBeTruthy()
201 | expect(two).toBeTruthy()
202 | })
203 |
204 | describe('steps', () => {
205 | const children = [
206 | x('div', null,
207 |
208 | A
209 | B
210 | C
211 |
212 | ),
213 | x('hr', null),
214 | x('div', null, 'Two'),
215 | ]
216 |
217 | test('increments step', () => {
218 | const tree =render (
219 |
223 | )
224 | fireEvent.keyDown(document.body, {
225 | keyCode: 39,
226 | })
227 | const a = tree.getByText('A')
228 | const b = tree.getByText('B')
229 | expect(a.style.visibility).toBe('visible')
230 | expect(b.style.visibility).toBe('hidden')
231 | })
232 |
233 | test('decrements step', () => {
234 | const tree =render (
235 |
239 | )
240 | fireEvent.keyDown(document.body, { keyCode: 39 })
241 | fireEvent.keyDown(document.body, { keyCode: 39 })
242 | fireEvent.keyDown(document.body, { keyCode: 37 })
243 | const a = tree.getByText('A')
244 | const b = tree.getByText('B')
245 | expect(a.style.visibility).toBe('visible')
246 | expect(b.style.visibility).toBe('hidden')
247 | })
248 | })
249 |
250 | test('renders with Header and Footer', () => {
251 | const children = [
252 | x(Header, null, 'Header'),
253 | x(Footer, null, 'Footer'),
254 | x('div', null, 'Beep'),
255 | x('hr', null),
256 | x('div', null, 'Two'),
257 | ]
258 | const tree =render (
259 |
263 | )
264 | const header = tree.getByText('Header')
265 | const footer = tree.getByText('Footer')
266 | expect(header).toBeTruthy()
267 | expect(footer).toBeTruthy()
268 | })
269 |
270 | test('option + p toggles presenter mode', () => {
271 | let context
272 | })
273 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | public
3 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/README.md:
--------------------------------------------------------------------------------
1 |
2 | # gatsby-theme-mdx-deck
3 |
4 | Add MDX Deck presentations to any Gatsby site
5 |
6 | ```sh
7 | npm i gatsby-theme-mdx-deck
8 | ```
9 |
10 | ```js
11 | // gatsby-config.js
12 | module.exports = {
13 | plugins: [
14 | 'gatsby-theme-mdx-deck',
15 | ]
16 | }
17 | ```
18 |
19 | Add one or more MDX presentation files to the `decks/` directory.
20 | The filenames will be used for creating routes to each deck.
21 |
22 | Example `decks/hello.mdx`
23 |
24 | ```mdx
25 | # Hello!
26 |
27 | ---
28 |
29 | ## Beep boop
30 | ```
31 |
32 | ## Layouts
33 |
34 | Individual slides can be wrapped with layout components,
35 | which work similarly to slide templates found in other presentation software.
36 |
37 | Example `decks/hello.mdx`
38 |
39 | ```mdx
40 | import Layout from './my-layout'
41 |
42 |
43 |
44 | # Hello
45 |
46 |
47 |
48 | ---
49 |
50 | ## Beep boop
51 | ```
52 |
53 | ## Configuration Options
54 |
55 | The Gatsby theme accepts the following options.
56 |
57 | ```js
58 | // gatsby-config.js
59 | module.exports = {
60 | plugins: [
61 | {
62 | resolve: 'gatsby-theme-mdx-deck',
63 | options: {
64 | // enable or disable gatsby-plugin-mdx
65 | mdx: false,
66 | // source directory
67 | contentPath: 'decks',
68 | // base path for routes generate by this theme
69 | basePath: ''
70 | }
71 | }
72 | ]
73 | }
74 | ```
75 |
76 | MIT License
77 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/decks/beep.mdx:
--------------------------------------------------------------------------------
1 |
2 | # Beep
3 |
4 | ---
5 |
6 | ## Boop bop
7 |
8 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/decks/hello.mdx:
--------------------------------------------------------------------------------
1 | import { Head, Appear, Notes } from '../src'
2 |
3 |
4 |
8 |
9 |
10 | # Hello
11 |
12 | ---
13 |
14 | `@mdx-deck/gatsby-theme-next`
15 |
16 |
17 |
18 | Hi
19 | Hello, my secret notes...
20 |
21 |
22 |
23 | ---
24 |
25 | ## Beep
26 |
27 | ---
28 |
29 | ## Appear:
30 |
31 |
32 |
33 | - One
34 | - Two
35 | - Three
36 |
37 |
38 |
39 | ---
40 |
41 | ## Hi
42 |
43 | ---
44 |
45 | ```jsx
46 | import React from 'react'
47 | import { Embed } from 'gatsby-theme-mdx-deck'
48 | import Hello from './hello.mdx'
49 |
50 | export default props =>
51 |
55 | ```
56 |
57 | ---
58 |
59 | More steps
60 |
61 |
62 | beep
63 | boop
64 |
65 |
66 | ---
67 |
68 | ## The End
69 |
70 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | export { wrapPageElement } from './src'
2 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/gatsby-config.js:
--------------------------------------------------------------------------------
1 | const IS_LOCAL = process.cwd() === __dirname
2 |
3 | const remarkPlugins = [require('remark-unwrap-images'), require('remark-emoji')]
4 | const gatsbyRemarkPlugins = [`gatsby-remark-import-code`]
5 |
6 | const config = (opts = {}) => {
7 | const { mdx = true, contentPath: name = 'decks' } = opts
8 |
9 | return {
10 | plugins: [
11 | {
12 | resolve: 'gatsby-source-filesystem',
13 | options: {
14 | name,
15 | path: name,
16 | },
17 | },
18 | mdx && {
19 | resolve: 'gatsby-plugin-mdx',
20 | options: {
21 | gatsbyRemarkPlugins,
22 | remarkPlugins,
23 | },
24 | },
25 | 'gatsby-plugin-react-helmet',
26 | 'gatsby-plugin-emotion',
27 | 'gatsby-plugin-catch-links',
28 | 'gatsby-plugin-theme-ui',
29 | {
30 | resolve: 'gatsby-plugin-compile-es6-packages',
31 | options: {
32 | modules: ['@mdx-deck/themes'],
33 | },
34 | },
35 | ].filter(Boolean),
36 | }
37 | }
38 |
39 | module.exports = IS_LOCAL ? config() : config
40 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/gatsby-node.js:
--------------------------------------------------------------------------------
1 | // based on gatsby-theme-blog
2 | const fs = require(`fs`)
3 | const path = require(`path`)
4 | const mkdirp = require(`mkdirp`)
5 | const Debug = require(`debug`)
6 | const pkg = require('./package.json')
7 |
8 | const debug = Debug(pkg.name)
9 |
10 | let basePath
11 | let contentPath
12 |
13 | const DeckTemplate = require.resolve(`./src/templates/deck`)
14 | const DecksTemplate = require.resolve(`./src/templates/decks`)
15 |
16 | exports.onPreBootstrap = ({ store }, opts = {}) => {
17 | const { program } = store.getState()
18 |
19 | basePath = opts.basePath || `/`
20 | contentPath = opts.contentPath || `decks`
21 |
22 | if (opts.cli) return
23 | const dirname = path.join(program.directory, contentPath)
24 | mkdirp.sync(dirname)
25 |
26 | debug(`Initializing ${dirname} directory`)
27 | }
28 |
29 | const mdxResolverPassthrough = fieldName => async (
30 | source,
31 | args,
32 | context,
33 | info
34 | ) => {
35 | const type = info.schema.getType(`Mdx`)
36 | const mdxNode = context.nodeModel.getNodeById({
37 | id: source.parent,
38 | })
39 | const resolver = type.getFields()[fieldName].resolve
40 | const result = await resolver(mdxNode, args, context, {
41 | fieldName,
42 | })
43 | return result
44 | }
45 |
46 | const resolveTitle = async (...args) => {
47 | const headings = await mdxResolverPassthrough('headings')(...args)
48 | const [first = {}] = headings
49 | return first.value || ''
50 | }
51 |
52 | exports.createSchemaCustomization = ({ actions, schema }) => {
53 | actions.createTypes(
54 | schema.buildObjectType({
55 | name: `Deck`,
56 | fields: {
57 | id: { type: `ID!` },
58 | slug: {
59 | type: `String!`,
60 | },
61 | title: {
62 | type: 'String!',
63 | resolve: resolveTitle,
64 | },
65 | body: {
66 | type: `String!`,
67 | resolve: mdxResolverPassthrough(`body`),
68 | },
69 | },
70 | interfaces: [`Node`],
71 | })
72 | )
73 | }
74 |
75 | exports.createPages = async ({ graphql, actions, reporter, pathPrefix }) => {
76 | const { createPage } = actions
77 |
78 | const result = await graphql(`
79 | {
80 | allDeck {
81 | edges {
82 | node {
83 | id
84 | slug
85 | title
86 | }
87 | }
88 | }
89 | }
90 | `)
91 |
92 | if (result.errors) {
93 | reporter.panic(result.errors)
94 | }
95 |
96 | const { allDeck } = result.data
97 | const decks = allDeck.edges
98 |
99 | // single deck mode
100 | if (decks.length === 1) {
101 | const [deck] = decks
102 | const base = basePath === '/' ? '' : basePath
103 | const matchPath = [base, '*'].join('/')
104 |
105 | const slug = [pathPrefix, base].filter(Boolean).join('')
106 |
107 | createPage({
108 | path: basePath,
109 | matchPath,
110 | component: DeckTemplate,
111 | context: {
112 | ...deck.node,
113 | slug,
114 | },
115 | })
116 | createPage({
117 | path: base + '/print',
118 | component: DeckTemplate,
119 | context: {
120 | ...deck.node,
121 | slug,
122 | },
123 | })
124 | return
125 | }
126 |
127 | decks.forEach(({ node }, index) => {
128 | const matchPath = [node.slug, '*'].join('/')
129 | const slug = [pathPrefix, node.slug].filter(Boolean).join('')
130 |
131 | createPage({
132 | path: node.slug,
133 | matchPath,
134 | component: DeckTemplate,
135 | context: {
136 | ...node,
137 | slug,
138 | },
139 | })
140 | createPage({
141 | path: slug + '/print',
142 | component: DeckTemplate,
143 | context: {
144 | ...node,
145 | slug,
146 | },
147 | })
148 | })
149 |
150 | // index page
151 | createPage({
152 | path: basePath,
153 | component: DecksTemplate,
154 | context: {
155 | decks,
156 | },
157 | })
158 | }
159 |
160 | exports.onCreateNode = ({
161 | node,
162 | actions,
163 | getNode,
164 | createNodeId,
165 | createContentDigest,
166 | }) => {
167 | const { createNode, createParentChildLink } = actions
168 |
169 | const toPath = node => {
170 | const { dir } = path.posix.parse(node.relativePath)
171 | return path.posix.join(basePath, dir, node.name)
172 | }
173 |
174 | if (node.internal.type !== `Mdx`) return
175 |
176 | const fileNode = getNode(node.parent)
177 | const source = fileNode.sourceInstanceName
178 |
179 | if (node.internal.type !== `Mdx` || source !== contentPath) return
180 |
181 | const slug = toPath(fileNode)
182 | const id = createNodeId(`${node.id} >>> Deck`)
183 |
184 | createNode({
185 | slug,
186 | // Required fields.
187 | id,
188 | parent: node.id,
189 | children: [],
190 | internal: {
191 | type: `Deck`,
192 | contentDigest: createContentDigest(node.rawBody),
193 | content: node.rawBody,
194 | description: `Slide Decks`,
195 | },
196 | })
197 | createParentChildLink({ parent: fileNode, child: getNode(id) })
198 | }
199 |
200 | exports.onCreateDevServer = ({ app }) => {
201 | if (typeof process.send !== 'function') return
202 | process.send({
203 | mdxDeck: true,
204 | })
205 | }
206 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | export { wrapPageElement } from './src'
2 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/index.js:
--------------------------------------------------------------------------------
1 | export * from './src'
2 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-theme-mdx-deck",
3 | "version": "4.1.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "start": "gatsby develop",
8 | "build": "gatsby build",
9 | "clean": "gatsby clean"
10 | },
11 | "devDependencies": {
12 | "gatsby": "^2.13.6",
13 | "react": "^16.8.6",
14 | "react-dom": "^16.8.6"
15 | },
16 | "peerDependencies": {
17 | "gatsby": "^2.13.6",
18 | "react": "^16.8.6",
19 | "react-dom": "^16.8.6"
20 | },
21 | "dependencies": {
22 | "@emotion/core": "^10.0.14",
23 | "@mdx-deck/themes": "^4.1.0",
24 | "@mdx-js/mdx": "^1.0.21",
25 | "@mdx-js/react": "^1.0.21",
26 | "@reach/router": "^1.2.1",
27 | "debug": "^4.1.1",
28 | "gatsby": "^2.13.24",
29 | "gatsby-plugin-catch-links": "^2.1.0",
30 | "gatsby-plugin-compile-es6-packages": "^2.0.0",
31 | "gatsby-plugin-emotion": "^4.1.0",
32 | "gatsby-plugin-mdx": "^1.0.13",
33 | "gatsby-plugin-react-helmet": "^3.1.0",
34 | "gatsby-plugin-theme-ui": "^0.3.0",
35 | "gatsby-remark-import-code": "^0.1.1",
36 | "gatsby-source-filesystem": "^2.1.3",
37 | "hhmmss": "^1.0.0",
38 | "lodash.get": "^4.4.2",
39 | "lodash.merge": "^4.6.1",
40 | "mkdirp": "^1.0.3",
41 | "react-helmet": "^6.0.0-beta",
42 | "react-swipeable": "^5.3.0",
43 | "remark-emoji": "^2.0.2",
44 | "remark-unwrap-images": "^2.0.0",
45 | "theme-ui": "^0.3.1"
46 | },
47 | "gitHead": "13d00b47780424cc3b52d38ad6f19e99d007c06a"
48 | }
49 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/app.js:
--------------------------------------------------------------------------------
1 | import React, { useReducer } from 'react'
2 | import merge from 'lodash.merge'
3 | import Context from '../context'
4 | import { modes } from '../constants'
5 |
6 | const reducer = (state, next) =>
7 | typeof next === 'function'
8 | ? merge({}, state, next(state))
9 | : merge({}, state, next)
10 |
11 | export default props => {
12 | const [state, setState] = useReducer(reducer, {
13 | mode: modes.normal,
14 | step: 0,
15 | metadata: {},
16 | })
17 |
18 | const register = (index, key, value) => {
19 | if (state.metadata[index] && state.metadata[index][key]) return
20 | setState({
21 | metadata: {
22 | [index]: {
23 | [key]: value,
24 | },
25 | },
26 | })
27 | }
28 |
29 | const context = {
30 | ...state,
31 | setState,
32 | register,
33 | }
34 |
35 | return {props.children}
36 | }
37 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/appear.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import useSteps from '../hooks/use-steps'
3 |
4 | export const Appear = props => {
5 | const children = React.Children.toArray(props.children)
6 | const step = useSteps(children.length)
7 | const styled = children.map((child, i) =>
8 | React.cloneElement(child, {
9 | style: {
10 | visibility: i < step ? 'visible' : 'hidden',
11 | },
12 | })
13 | )
14 |
15 | return <>{styled}>
16 | }
17 |
18 | export default Appear
19 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/clock.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | export const Clock = props => {
4 | const [time, setTime] = useState(new Date().toLocaleTimeString())
5 | useEffect(() => {
6 | const tick = () => {
7 | const now = new Date()
8 | setTime(now.toLocaleTimeString())
9 | }
10 | const timer = setInterval(tick, 1000)
11 | return () => {
12 | clearInterval(timer)
13 | }
14 | }, [])
15 |
16 | return time
17 | }
18 |
19 | export default Clock
20 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/deck.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Router, globalHistory } from '@reach/router'
3 | import { Global } from '@emotion/core'
4 | import { ThemeProvider } from 'theme-ui'
5 | import { Helmet } from 'react-helmet'
6 | import get from 'lodash.get'
7 | import merge from 'lodash.merge'
8 | import useKeyboard from '../hooks/use-keyboard'
9 | import useStorage from '../hooks/use-storage'
10 | import useDeck from '../hooks/use-deck'
11 | import Context from '../context'
12 | import Wrapper from './wrapper'
13 | import Slide from './slide'
14 | import { modes } from '../constants'
15 |
16 | import Presenter from './presenter'
17 | import Overview from './overview'
18 | import Grid from './grid'
19 |
20 | const Keyboard = () => {
21 | useKeyboard()
22 | return false
23 | }
24 |
25 | const Storage = () => {
26 | useStorage()
27 | return false
28 | }
29 |
30 | const Print = ({ slides }) => {
31 | const outer = useDeck()
32 | const context = {
33 | ...outer,
34 | mode: modes.print,
35 | }
36 |
37 | return (
38 |
39 | {slides.map((slide, i) => (
40 |
41 | ))}
42 |
43 | )
44 | }
45 |
46 | const getIndex = () => {
47 | const { pathname } = globalHistory.location
48 | const paths = pathname.split('/')
49 | const n = Number(paths[paths.length - 1])
50 | const index = isNaN(n) ? 0 : n
51 | return index
52 | }
53 |
54 | const GoogleFont = ({ theme }) => {
55 | if (!theme.googleFont) return false
56 | return (
57 |
58 |
59 |
60 | )
61 | }
62 |
63 | const mergeThemes = (...themes) =>
64 | themes.reduce(
65 | (acc, theme) =>
66 | typeof theme === 'function' ? theme(acc) : merge(acc, theme),
67 | {}
68 | )
69 |
70 | const DefaultMode = ({ children }) =>
71 |
72 | export default ({
73 | slides = [],
74 | pageContext: { title, slug },
75 | theme = {},
76 | themes = [],
77 | ...props
78 | }) => {
79 | const outer = useDeck()
80 | const index = getIndex()
81 |
82 | const head = slides.head.children
83 |
84 | const { components, ...mergedTheme } = mergeThemes(theme, ...themes)
85 |
86 | const context = {
87 | ...outer,
88 | slug,
89 | length: slides.length,
90 | index,
91 | steps: get(outer, `metadata.${index}.steps`),
92 | notes: get(outer, `metadata.${index}.notes`),
93 | theme: mergedTheme,
94 | }
95 |
96 | let Mode = DefaultMode
97 |
98 | switch (context.mode) {
99 | case modes.presenter:
100 | Mode = Presenter
101 | break
102 | case modes.overview:
103 | Mode = Overview
104 | break
105 | case modes.grid:
106 | Mode = Grid
107 | break
108 | default:
109 | break
110 | }
111 |
112 | return (
113 | <>
114 |
115 | {title && {title} }
116 | {head}
117 |
118 |
119 |
120 |
121 |
128 |
129 |
130 |
131 |
132 |
137 |
138 | {slides.map((slide, i) => (
139 |
140 | ))}
141 |
142 |
143 |
144 |
145 |
146 |
147 | >
148 | )
149 | }
150 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/decks.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import { Link } from 'gatsby'
4 |
5 | export default ({ decks }) => {
6 | return (
7 |
14 | MDX Deck
15 |
19 | {decks.map(d => (
20 | -
25 |
34 | {d.slug}
35 |
36 |
37 | ))}
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/embed.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import splitSlides from '../split-slides'
4 | import Slide from './slide'
5 | import Zoom from './zoom'
6 |
7 | const wrapper = ({ slide: i, ratio, zoom, ...props }) => {
8 | const slides = splitSlides(props)
9 | const slide = slides[i - 1]
10 |
11 | if (!slide) {
12 | return No slide found (slide {i})
13 | }
14 |
15 | return (
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | const components = {
23 | wrapper,
24 | }
25 |
26 | export const Embed = ({
27 | src: Deck,
28 | slide = 1,
29 | ratio = 16 / 9,
30 | zoom = 1,
31 | ...props
32 | }) => (
33 |
40 | )
41 |
42 | export default Embed
43 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/full-screen-code.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 |
4 | export const FullScreenCode = ({ ...props }) => (
5 |
19 | )
20 |
21 | export default FullScreenCode
22 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/grid.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import { navigate } from '@reach/router'
4 | import useDeck from '../hooks/use-deck'
5 | import { modes } from '../constants'
6 | import SlideList from './slide-list'
7 |
8 | export default ({ slides }) => {
9 | const { slug, setState } = useDeck()
10 | return (
11 |
17 |
22 | {
25 | navigate([slug, i].join('/'))
26 | setState({ mode: modes.normal })
27 | }}
28 | sx={{
29 | width: '25%',
30 | m: 0,
31 | }}
32 | />
33 |
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/head.js:
--------------------------------------------------------------------------------
1 | export const Head = props => false
2 |
3 | Head.mdxDeckHead = true
4 |
5 | export default Head
6 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/horizontal.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React from 'react'
4 |
5 | export const Horizontal = ({ ...props }) => {
6 | const children = React.Children.toArray(props.children)
7 |
8 | return (
9 |
17 | {children.map((child, i) => (
18 |
19 | {child}
20 |
21 | ))}
22 |
23 | )
24 | }
25 |
26 | export default Horizontal
27 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/image.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 |
4 | export const Image = ({
5 | width = '100%',
6 | height = '100%',
7 | size = 'cover',
8 | src,
9 | css,
10 | ...props
11 | }) => (
12 |
24 | )
25 |
26 | export default Image
27 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/invert.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 |
4 | export const Invert = ({ ...props }) => (
5 |
21 | )
22 |
23 | export default Invert
24 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/notes.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import useDeck from '../hooks/use-deck'
3 |
4 | export const Notes = props => {
5 | const context = useDeck()
6 | useEffect(() => {
7 | context.register(context.index, 'notes', props.children)
8 | }, [props.children])
9 |
10 | return false
11 | }
12 |
13 | export default Notes
14 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/overview.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import { navigate } from '@reach/router'
4 | import useDeck from '../hooks/use-deck'
5 | import Zoom from './zoom'
6 | import SlideList from './slide-list'
7 |
8 | export default ({ slides, children }) => {
9 | const { slug, index, length } = useDeck()
10 |
11 | return (
12 |
20 |
30 | {
34 | navigate([slug, i].join('/'))
35 | }}
36 | />
37 |
38 |
47 |
51 | {children}
52 |
53 |
57 | {index} / {length - 1}
58 |
59 |
60 |
61 | )
62 | }
63 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/presenter-footer.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React from 'react'
4 | import { globalHistory } from '@reach/router'
5 | import useDeck from '../hooks/use-deck'
6 | import Clock from './clock'
7 | import Timer from './timer'
8 |
9 | export default props => {
10 | const context = useDeck()
11 | const { index, length } = context
12 |
13 | return (
14 |
15 |
16 | {index} / {length - 1}
17 |
18 |
22 |
30 | Open in New Window ↗︎
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/presenter.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React from 'react'
4 | import Zoom from './zoom'
5 | import Slide from './slide'
6 | import useDeck from '../hooks/use-deck'
7 | import Footer from './presenter-footer'
8 |
9 | export const Presenter = ({ slides, children }) => {
10 | const context = useDeck()
11 | const next = slides[context.index + 1]
12 | const notes = context.notes ? React.Children.toArray(context.notes) : false
13 |
14 | return (
15 |
25 |
31 |
36 | {children}
37 |
38 |
43 |
44 |
45 |
46 | {notes && (
47 |
51 | {notes}
52 |
53 | )}
54 |
55 |
56 |
66 |
67 |
68 |
69 | )
70 | }
71 |
72 | export default Presenter
73 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/slide-list.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React, { useEffect, useRef } from 'react'
4 | import Zoom from './zoom'
5 | import Slide from './slide'
6 | import useDeck from '../hooks/use-deck'
7 |
8 | const noop = () => {}
9 |
10 | export const SlideList = ({
11 | slides = [],
12 | ratio = 16 / 9,
13 | zoom = 1 / 4,
14 | onClick = noop,
15 | ...props
16 | }) => {
17 | const { index } = useDeck()
18 | const thumb = useRef(null)
19 |
20 | useEffect(() => {
21 | const el = thumb.current
22 | if (!el) return
23 | if (typeof el.scrollIntoViewIfNeeded === 'function') {
24 | el.scrollIntoViewIfNeeded()
25 | }
26 | })
27 |
28 | return (
29 |
30 | {slides.map((slide, i) => (
31 | {
37 | onClick(i)
38 | }}
39 | style={
40 | index === i
41 | ? {
42 | position: 'relative',
43 | zIndex: 1,
44 | }
45 | : null
46 | }
47 | sx={{
48 | m: 2,
49 | cursor: 'pointer',
50 | outline: index === i ? `4px solid cyan` : null,
51 | }}>
52 |
53 |
54 |
55 |
56 | ))}
57 |
58 | )
59 | }
60 |
61 | export default SlideList
62 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/slide.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React, { Fragment } from 'react'
4 | import Context from '../context'
5 | import useDeck from '../hooks/use-deck'
6 | import useSwipe from '../hooks/use-swipe'
7 | import { modes } from '../constants'
8 |
9 | export const Slide = ({ slide, index, preview, ...props }) => {
10 | const outer = useDeck()
11 | const swipeProps = useSwipe()
12 | const context = {
13 | ...outer,
14 | index,
15 | preview,
16 | }
17 |
18 | return (
19 |
20 |
36 | {slide}
37 |
38 |
39 | )
40 | }
41 |
42 | export default Slide
43 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/split-right.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React from 'react'
4 |
5 | export const SplitRight = ({ children, ...props }) => {
6 | const [first, ...rest] = React.Children.toArray(children)
7 | return (
8 |
16 | {rest}
17 | {first}
18 |
19 | )
20 | }
21 |
22 | export default SplitRight
23 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/split.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React from 'react'
4 |
5 | export const Split = ({ children, ...props }) => {
6 | const [first, ...rest] = React.Children.toArray(children)
7 | return (
8 |
16 | {first}
17 | {rest}
18 |
19 | )
20 | }
21 |
22 | export default Split
23 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/timer.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React, { useEffect } from 'react'
4 | import hhmmss from 'hhmmss'
5 | import useDeck from '../hooks/use-deck'
6 |
7 | let ticker
8 |
9 | export const Timer = props => {
10 | const { setState, timer = false, seconds = 0 } = useDeck()
11 |
12 | useEffect(() => {
13 | const tick = () => {
14 | if (!timer) return
15 | setState({
16 | seconds: seconds + 1,
17 | })
18 | }
19 | ticker = setInterval(tick, 1000)
20 | return () => {
21 | clearInterval(ticker)
22 | }
23 | }, [timer, seconds])
24 |
25 | const toggle = () => {
26 | setState({
27 | timer: !timer,
28 | })
29 | }
30 |
31 | const reset = () => {
32 | setState({ seconds: 0 })
33 | }
34 |
35 | return (
36 |
37 | {' '}
46 | {' '}
54 | {hhmmss(seconds)}
55 |
56 | )
57 | }
58 |
59 | export default Timer
60 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/wrapper.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 | import React, { Fragment, useState, useEffect } from 'react'
4 | import useDeck from '../hooks/use-deck'
5 | import { modes } from '../constants'
6 |
7 | const DefaultProvider = props =>
8 | React.createElement(Fragment, null, props.children)
9 |
10 | export default props => {
11 | const [height, setHeight] = useState('100vh')
12 | const { mode, theme } = useDeck()
13 |
14 | useEffect(() => {
15 | // handle mobile safari height
16 | setHeight(window.innerHeight)
17 | const handleResize = e => {
18 | setHeight(window.innerHeight)
19 | }
20 | const stopTouch = e => {
21 | if (mode !== modes.normal) return
22 | e.preventDefault()
23 | }
24 | window.addEventListener('resize', handleResize)
25 | document.body.addEventListener('touchstart', stopTouch)
26 | return () => {
27 | window.removeEventListener('resize', handleResize)
28 | document.body.removeEventListener('touchstart', stopTouch)
29 | }
30 | }, [mode])
31 |
32 | const { Provider = DefaultProvider } = theme
33 |
34 | return (
35 |
36 |
47 |
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/components/zoom.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from 'theme-ui'
3 |
4 | export const Zoom = ({ ratio, zoom = 1, ...props }) => (
5 |
13 |
29 |
30 | )
31 |
32 | export default Zoom
33 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/constants.js:
--------------------------------------------------------------------------------
1 | export const modes = {
2 | normal: 'NORMAL',
3 | presenter: 'PRESENTER',
4 | overview: 'OVERVIEW',
5 | grid: 'GRID',
6 | print: 'PRINT',
7 | }
8 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/context.js:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | export default createContext({})
4 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/convert-legacy-theme.js:
--------------------------------------------------------------------------------
1 | import merge from 'lodash.merge'
2 |
3 | export const convertLegacyTheme = (legacyTheme = {}) => {
4 | const {
5 | components,
6 | colors = {},
7 | font,
8 | monospace,
9 | // UI
10 | Provider,
11 | Presenter,
12 | googleFont,
13 | // styles
14 | css,
15 | heading,
16 | ...styles
17 | } = legacyTheme
18 |
19 | const theme = {
20 | googleFont,
21 | colors: {
22 | ...colors,
23 | primary: colors.link,
24 | muted: colors.codeBackground,
25 | },
26 | fonts: {
27 | body: font,
28 | heading: font,
29 | monospace,
30 | },
31 | text: {
32 | heading,
33 | },
34 | styles: merge(
35 | {
36 | root: css,
37 | h1: {
38 | variant: 'text.heading',
39 | },
40 | h2: {
41 | variant: 'text.heading',
42 | },
43 | h3: {
44 | variant: 'text.heading',
45 | },
46 | h4: {
47 | variant: 'text.heading',
48 | },
49 | h5: {
50 | variant: 'text.heading',
51 | },
52 | h6: {
53 | variant: 'text.heading',
54 | },
55 | code: {
56 | fontFamily: 'monospace',
57 | color: 'code',
58 | bg: 'codeBackground',
59 | },
60 | pre: {
61 | fontFamily: 'monospace',
62 | color: 'code',
63 | bg: 'codeBackground',
64 | },
65 | },
66 | styles
67 | ),
68 | }
69 |
70 | return {
71 | components,
72 | theme,
73 | }
74 | }
75 |
76 | export default convertLegacyTheme
77 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/gatsby-plugin-theme-ui/components.js:
--------------------------------------------------------------------------------
1 | import {
2 | Appear,
3 | Notes,
4 | Head,
5 | Image,
6 | FullScreenCode,
7 | Horizontal,
8 | Invert,
9 | Split,
10 | SplitRight,
11 | } from '..'
12 |
13 | export default {
14 | Appear,
15 | Notes,
16 | Head,
17 | Image,
18 | FullScreenCode,
19 | Horizontal,
20 | Invert,
21 | Split,
22 | SplitRight,
23 | }
24 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/gatsby-plugin-theme-ui/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | colors: {
3 | text: '#000',
4 | background: '#fff',
5 | primary: '#07c',
6 | secondary: '#80c',
7 | muted: '#f6f6ff',
8 | },
9 | fonts: {
10 | body: 'system-ui, sans-serif',
11 | heading: 'inherit',
12 | monospace: '"Roboto Mono", Menlo, monospace',
13 | ui: 'system-ui, sans-serif',
14 | },
15 | lineHeights: {
16 | body: 1.5,
17 | heading: 1.125,
18 | },
19 | fontWeights: {
20 | body: 500,
21 | heading: 700,
22 | bold: 700,
23 | },
24 | text: {
25 | heading: {
26 | fontFamily: 'heading',
27 | lineHeight: 'heading',
28 | fontWeight: 'heading',
29 | },
30 | },
31 | styles: {
32 | Slide: {
33 | fontFamily: 'body',
34 | fontSize: [3, 4, 5, 6],
35 | },
36 | h1: {
37 | variant: 'text.heading',
38 | },
39 | h2: {
40 | variant: 'text.heading',
41 | },
42 | h3: {
43 | variant: 'text.heading',
44 | },
45 | h4: {
46 | variant: 'text.heading',
47 | },
48 | h5: {
49 | variant: 'text.heading',
50 | },
51 | h6: {
52 | variant: 'text.heading',
53 | },
54 | a: {
55 | color: 'primary',
56 | },
57 | ul: {
58 | m: 0,
59 | },
60 | ol: {
61 | m: 0,
62 | },
63 | inlineCode: {
64 | fontFamily: 'monospace',
65 | },
66 | code: {
67 | fontFamily: 'monospace',
68 | },
69 | pre: {
70 | fontFamily: 'monospace',
71 | p: 3,
72 | },
73 | img: {
74 | maxWidth: '100%',
75 | height: 'auto',
76 | objectFit: 'cover',
77 | },
78 | table: {
79 | width: '100%',
80 | borderCollapse: 'separate',
81 | borderSpacing: 0,
82 | },
83 | th: {
84 | textAlign: 'left',
85 | paddingRight: '.5em',
86 | paddingTop: '.25em',
87 | paddingBottom: '.25em',
88 | borderBottom: '1px solid',
89 | verticalAlign: 'top',
90 | },
91 | td: {
92 | textAlign: 'left',
93 | paddingRight: '.5em',
94 | paddingTop: '.25em',
95 | paddingBottom: '.25em',
96 | borderBottom: '1px solid',
97 | verticalAlign: 'top',
98 | },
99 | blockquote: {
100 | fontWeight: 'bold',
101 | },
102 | },
103 | }
104 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/hooks/use-deck.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import DeckContext from '../context'
3 |
4 | export const useDeck = () => useContext(DeckContext)
5 | export default useDeck
6 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/hooks/use-keyboard.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import { useEffect } from 'react'
3 | import { navigate } from '@reach/router'
4 | import useDeck from './use-deck'
5 | import { modes } from '../constants'
6 | import { previous, next } from '../navigate'
7 |
8 | const keys = {
9 | right: 39,
10 | left: 37,
11 | up: 38,
12 | down: 40,
13 | space: 32,
14 | p: 80,
15 | o: 79,
16 | g: 71,
17 | esc: 27,
18 | pageUp: 33,
19 | pageDown: 34,
20 | }
21 |
22 | const toggleMode = next => state =>
23 | state.mode === next
24 | ? {
25 | mode: modes.normal,
26 | }
27 | : {
28 | mode: next,
29 | }
30 |
31 | const inputElements = ['input', 'select', 'textarea', 'a', 'button']
32 |
33 | export const useKeyboard = () => {
34 | const context = useDeck()
35 |
36 | useEffect(() => {
37 | const handleKeyDown = e => {
38 | const { metaKey, ctrlKey, shiftKey, altKey } = e
39 | if (metaKey || ctrlKey) return
40 |
41 | // ignore custom keyboard shortcuts when elements are focused
42 | const el = document.activeElement.tagName.toLowerCase()
43 | if (inputElements.includes(el)) return
44 |
45 | if (shiftKey) {
46 | switch (e.keyCode) {
47 | case keys.space:
48 | previous(context)
49 | break
50 | case keys.p:
51 | context.setState(toggleMode(modes.print))
52 | navigate(`${context.slug}/print`)
53 | break
54 | }
55 | } else if (altKey) {
56 | switch (e.keyCode) {
57 | case keys.p:
58 | context.setState(toggleMode(modes.presenter))
59 | break
60 | case keys.o:
61 | context.setState(toggleMode(modes.overview))
62 | break
63 | case keys.g:
64 | context.setState(toggleMode(modes.grid))
65 | break
66 | }
67 | } else {
68 | switch (e.keyCode) {
69 | case keys.right:
70 | case keys.down:
71 | case keys.pageDown:
72 | case keys.space:
73 | next(context)
74 | break
75 | case keys.left:
76 | case keys.up:
77 | case keys.pageUp:
78 | previous(context)
79 | break
80 | case keys.esc:
81 | context.setState({ mode: modes.normal })
82 | break
83 | }
84 | }
85 | }
86 |
87 | window.addEventListener('keydown', handleKeyDown)
88 |
89 | return () => {
90 | window.removeEventListener('keydown', handleKeyDown)
91 | }
92 | }, [context])
93 | }
94 |
95 | export default useKeyboard
96 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/hooks/use-steps.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import useDeck from './use-deck'
3 |
4 | export const useSteps = length => {
5 | const context = useDeck()
6 | useEffect(() => {
7 | if (typeof context.register !== 'function') return
8 | context.register(context.index, 'steps', length)
9 | }, [])
10 | if (context.preview) return length
11 | return context.step
12 | }
13 |
14 | export default useSteps
15 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/hooks/use-storage.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { navigate } from '@reach/router'
3 | import useDeck from './use-deck'
4 |
5 | const keys = {
6 | slide: 'mdx-deck-slide',
7 | step: 'mdx-deck-step',
8 | }
9 |
10 | export const useStorage = () => {
11 | const context = useDeck()
12 | const [focused, setFocused] = useState(false)
13 |
14 | const handleFocus = () => setFocused(true)
15 | const handleBlur = () => setFocused(false)
16 |
17 | const handleStorageChange = e => {
18 | const n = parseInt(e.newValue, 10)
19 | // if (focused) return
20 | if (isNaN(n)) return
21 | switch (e.key) {
22 | case keys.slide:
23 | navigate([context.slug, n].join('/'))
24 | break
25 | case keys.step:
26 | context.setState({ step: n })
27 | break
28 | default:
29 | break
30 | }
31 | }
32 |
33 | useEffect(() => {
34 | setFocused(document.hasFocus())
35 | }, [])
36 |
37 | useEffect(() => {
38 | if (!focused) window.addEventListener('storage', handleStorageChange)
39 | window.addEventListener('focus', handleFocus)
40 | window.addEventListener('blur', handleBlur)
41 | return () => {
42 | if (!focused) window.removeEventListener('storage', handleStorageChange)
43 | window.removeEventListener('focus', handleFocus)
44 | window.removeEventListener('blur', handleBlur)
45 | }
46 | }, [focused])
47 |
48 | // store changes
49 | useEffect(() => {
50 | if (!focused) return
51 | localStorage.setItem(keys.slide, context.index)
52 | localStorage.setItem(keys.step, context.step)
53 | }, [focused, context.index, context.step])
54 | }
55 |
56 | export default useStorage
57 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/hooks/use-swipe.js:
--------------------------------------------------------------------------------
1 | import { useSwipeable } from 'react-swipeable'
2 | import useDeck from './use-deck'
3 | import { previous, next } from '../navigate'
4 | import { modes } from '../constants'
5 |
6 | const toggleMode = next => state =>
7 | state.mode === next ? { mode: modes.normal } : { mode: next }
8 |
9 | export const useSwipe = () => {
10 | const context = useDeck()
11 |
12 | const onSwipedLeft = e => {
13 | next(context)
14 | }
15 |
16 | const onSwipedRight = e => {
17 | previous(context)
18 | }
19 |
20 | const onSwipedUp = e => {
21 | context.setState({ mode: modes.presenter })
22 | }
23 |
24 | const onSwipedDown = e => {
25 | context.setState({ mode: modes.normal })
26 | }
27 |
28 | const props = useSwipeable({
29 | onSwipedLeft,
30 | onSwipedRight,
31 | onSwipedUp,
32 | onSwipedDown,
33 | })
34 |
35 | return props
36 | }
37 |
38 | export default useSwipe
39 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import App from './components/app'
3 |
4 | export const wrapPageElement = ({ element }) => {element}
5 |
6 | export { Appear } from './components/appear'
7 | export { Notes } from './components/notes'
8 | export { Head } from './components/head'
9 | export { Clock } from './components/clock'
10 | export { Timer } from './components/timer'
11 | export { Slide } from './components/slide'
12 | export { Zoom } from './components/zoom'
13 | export { Embed } from './components/embed'
14 | export { Image } from './components/image'
15 | export { FullScreenCode } from './components/full-screen-code'
16 | export { Horizontal } from './components/horizontal'
17 | export { Invert } from './components/invert'
18 | export { Split } from './components/split'
19 | export { SplitRight } from './components/split-right'
20 |
21 | export { useDeck } from './hooks/use-deck'
22 | export { useSteps } from './hooks/use-steps'
23 |
24 | export { convertLegacyTheme } from './convert-legacy-theme'
25 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/navigate.js:
--------------------------------------------------------------------------------
1 | // utilities for navigation
2 | import { navigate } from '@reach/router'
3 |
4 | const nextSlide = ({ slug, length, index, setState }) => {
5 | const n = index + 1
6 | if (n >= length) return
7 | navigate([slug, n].join('/'))
8 | setState({ step: 0 })
9 | }
10 |
11 | export const next = context => {
12 | const { steps, step, setState } = context
13 | if (!steps || step >= steps) return nextSlide(context)
14 | setState({ step: step + 1 })
15 | }
16 |
17 | const previousSlide = ({ slug, index, metadata, setState }) => {
18 | const n = index - 1
19 | if (n < 0) return
20 | navigate([slug, n].join('/'))
21 | const { steps = 0 } = metadata[n] || {}
22 | setState({ step: steps })
23 | }
24 |
25 | export const previous = context => {
26 | const { steps, step, setState } = context
27 | if (steps && step > 0) {
28 | return setState({ step: step - 1 })
29 | }
30 | previousSlide(context)
31 | }
32 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/split-slides.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default props => {
4 | const arr = React.Children.toArray(props.children)
5 | const splits = []
6 | const slides = []
7 | slides.head = {
8 | props: {},
9 | children: [],
10 | }
11 | arr.forEach((child, i) => {
12 | const {
13 | originalType,
14 | mdxType,
15 | parentName,
16 | children,
17 | ...childProps
18 | } = child.props
19 | if (originalType.mdxDeckHead) {
20 | slides.head.children.push(children)
21 | Object.assign(slides.head.props, childProps)
22 | arr.splice(i, 1)
23 | }
24 | if (mdxType === 'hr') splits.push(i)
25 | })
26 | let previousSplit = 0
27 | splits.forEach(i => {
28 | const children = [...arr.slice(previousSplit, i)]
29 | slides.push(children)
30 | previousSplit = i + 1
31 | })
32 |
33 | slides.push([...arr.slice(previousSplit)])
34 |
35 | slides.head.children = React.Children.toArray(slides.head.children).map(
36 | (child, i) => {
37 | const { originalType, mdxType, parentName, ...childProps } = child.props
38 | return React.createElement(originalType, {
39 | key: i,
40 | ...childProps,
41 | })
42 | }
43 | )
44 |
45 | return slides
46 | }
47 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/templates/deck.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { graphql } from 'gatsby'
3 | import { MDXRenderer } from 'gatsby-plugin-mdx'
4 | import Deck from '../components/deck'
5 | import splitSlides from '../split-slides'
6 |
7 | export const pageQuery = graphql`
8 | query($id: String!) {
9 | deck: deck(id: { eq: $id }) {
10 | id
11 | body
12 | title
13 | }
14 | }
15 | `
16 |
17 | const wrapper = props => {
18 | const slides = splitSlides(props)
19 | return
20 | }
21 |
22 | const components = {
23 | wrapper,
24 | }
25 |
26 | export default ({
27 | data: {
28 | deck: { id, body },
29 | },
30 | ...props
31 | }) => {
32 | const Component = props =>
33 |
34 | return
35 | }
36 |
--------------------------------------------------------------------------------
/packages/gatsby-theme/src/templates/decks.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Decks from '../components/decks'
3 |
4 | export default ({ pageContext, ...props }) => {
5 | const decks = pageContext.decks.map(d => d.node)
6 |
7 | return
8 | }
9 |
--------------------------------------------------------------------------------
/packages/mdx-deck/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | public
3 | static
4 |
--------------------------------------------------------------------------------
/packages/mdx-deck/.npmignore:
--------------------------------------------------------------------------------
1 | .cache
2 | public
3 | static
4 |
--------------------------------------------------------------------------------
/packages/mdx-deck/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # MDX Deck
4 |
5 | Award-winning React [MDX][]-based presentation decks
6 |
7 | [![Build Status][badge]][circleci]
8 | [![Version][]][npm]
9 | [![Downloads][]][npm]
10 |
11 | [badge]: https://flat.badgen.net/github/status/jxnblk/mdx-deck/master/ci/circleci
12 | [circleci]: https://circleci.com/gh/jxnblk/mdx-deck
13 | [version]: https://flat.badgen.net/npm/v/mdx-deck
14 | [downloads]: https://flat.badgen.net/npm/dm/mdx-deck
15 | [npm]: https://npmjs.com/package/mdx-deck
16 |
17 | - :memo: Write presentations in markdown
18 | - :atom_symbol: Import and use [React components](#imports)
19 | - :nail_care: Customizable [themes](#theming) and components
20 | - :zero: Zero-config CLI
21 | - :tipping_hand_woman: [Presenter mode](#presenter-mode)
22 | - :notebook: [Speaker notes](#speaker-notes)
23 |
24 | [View demo](https://mdx-deck.jxnblk.com)
25 |
26 | - [Getting Started](#getting-started)
27 | - [Using MDX](#using-mdx)
28 | - [Theming](#theming)
29 | - [Components](#components)
30 | - [Layouts](#layouts)
31 | - [Presenter Mode](#presenter-mode)
32 | - [Keyboard Shortcuts](#keyboard-shortcuts)
33 | - [CLI Options](#cli-options)
34 | - [Videos & Articles](#videos-articles)
35 | - [Examples](#examples)
36 |
37 | ## Getting Started
38 |
39 | ```sh
40 | npm i -D mdx-deck
41 | ```
42 |
43 | Create an [MDX][] file and separate each slide with `---`.
44 |
45 | ````mdx
46 |
47 | # Hello
48 |
49 | ---
50 |
51 | ## This is my deck
52 |
53 | ---
54 |
55 | ## The End
56 |
57 | ````
58 |
59 | Add a run script to your `package.json` with the MDX Deck CLI
60 | pointing to the `.mdx` file to start the development server:
61 |
62 | ```json
63 | "scripts": {
64 | "start": "mdx-deck deck.mdx"
65 | }
66 | ```
67 |
68 | Start the development server:
69 |
70 | ```sh
71 | npm start
72 | ```
73 |
74 | Use the left and right arrow keys to navigate through the presentation.
75 |
76 | ## Using MDX
77 |
78 | MDX uses Markdown syntax and can render React components inline with JSX.
79 |
80 | ### Imports
81 |
82 | To import components, use ES import syntax separated with empty lines between any markdown or JSX syntax.
83 |
84 | ```mdx
85 | import { Box } from 'theme-ui'
86 |
87 | Hello
88 | ```
89 |
90 | Read more about MDX syntax in the [MDX Docs][mdx].
91 |
92 | ## Theming
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | MDX Deck uses [Theme UI][] and [Emotion][] for styling, making practically any part of the presentation themeable.
101 | It also includes several built-in themes to change the look and feel of the presentation.
102 |
103 | - See the list of available [Themes](docs/themes.md)
104 | - Read more about theming in the [Theming docs](docs/theming.md).
105 |
106 | ## Components
107 |
108 | MDX Deck includes built-in components to help with creating presentations,
109 | a `Notes` component for adding speaker notes,
110 | a `Head` component for the document head,
111 | `Header` and `Footer` components for persistent header and footer content,
112 | and a `Steps` component for adding multiple intermediate steps in a single slide.
113 |
114 | Read more in the [Components](docs/components.md) docs.
115 |
116 | ### Third-Party Components
117 |
118 | These optional libraries are intended for use with MDX Deck.
119 |
120 | - [CodeSurfer][]: React component for scrolling, zooming and highlighting code.
121 | - [mdx-code][]: Runnable code playgrounds for MDX Deck.
122 | - [mdx-deck-live-code][]: Live React and JS coding in slides.
123 |
124 | _Note: please check with version compatibility when using these libraries._
125 |
126 | [codesurfer]: https://github.com/pomber/code-surfer
127 | [mdx-code]: https://github.com/pranaygp/mdx-code
128 | [mdx-deck-live-code]: https://github.com/JReinhold/mdx-deck-live-code
129 |
130 | ## Layouts
131 |
132 | Each slide can include a custom layout around its content,
133 | which can be used as a *template* for visually differentiating slides.
134 |
135 | ```js
136 | // example Layout.js
137 | import React from 'react'
138 |
139 | export default ({ children }) => (
140 |
146 | {children}
147 |
148 | )
149 | ```
150 |
151 | ```mdx
152 | import Layout from './Layout'
153 |
154 | # No Layout
155 |
156 | ---
157 |
158 |
159 |
160 | # Custom Layout
161 |
162 |
163 | ```
164 |
165 | The layout component will wrap the MDX elements within that slide,
166 | which means you can add custom layout styles
167 | or style child elements with CSS-in-JS.
168 |
169 | ## Presenter Mode
170 |
171 | Press `Option + P` to toggle *Presenter Mode*,
172 | which will show a preview of the next slide, a timer, and speaker notes.
173 |
174 | 
175 |
176 | The presentation can be opened in two separate windows at the same time,
177 | and it will stay in sync with the other window.
178 |
179 | ## Keyboard Shortcuts
180 |
181 | | Key | Description |
182 | | ----------- | -------------------------------------------- |
183 | | Left Arrow, Page Up, Shift + Space | Go to previous slide (or step in [Steps][]) |
184 | | Right Arrow, Page Down, Space | Go to next slide (or step in [Steps][]) |
185 | | Option + P | Toggle [Presenter Mode](#presenter-mode) |
186 | | Option + O | Toggle Overview Mode
187 | | Option + G | Toggle Grid Mode
188 |
189 | [steps]: docs/components.md#steps
190 |
191 | ## CLI Options
192 |
193 | ```
194 | -p --port Dev server port
195 | -h --host Host the dev server listens to
196 | --no-open Prevent from opening in default browser
197 | ```
198 |
199 | ## Videos & Articles
200 |
201 | - [Egghead Tutorial][egghead] by [Andrew Del Prete](https://github.com/andrewdelprete).
202 | - [mdx-deck: slide decks powered by markdown and react][kcd-blog] by [Kent C. Dodds][]
203 | - [Make Fast & Beautiful Presentations with MDX-Deck][hw-video] by [Harry Wolff][] ([Demo][hw-demo])
204 | - [What is MDX][kcd-video] by [Kent C. Dodds][]
205 | - [Build a Custom Provider Component for MDX-Deck][ks-egghead] by [Kyle Shevlin][]
206 |
207 | [egghead]: https://egghead.io/lessons/react-build-a-slide-deck-with-mdx-deck-using-markdown-react
208 | [kent c. dodds]: https://mobile.twitter.com/kentcdodds
209 | [kcd-video]: http://youtu.be/d2sQiI5NFAM?a
210 | [kcd-blog]: https://kentcdodds.com/blog/mdx-deck-slide-decks-powered-by-markdown-and-react
211 | [hw-video]: https://www.youtube.com/watch?v=LvP2EqCiQMg&feature=youtu.be
212 | [hw-demo]: https://github.com/hswolff/mdx-deck-demo
213 | [harry wolff]: https://mobile.twitter.com/hswolff
214 | [ks-egghead]: https://egghead.io/lessons/javascript-build-a-custom-provider-component-for-mdx-deck
215 | [kyle shevlin]: https://twitter.com/kyleshevlin
216 |
217 |
218 | ## Examples
219 |
220 | See how others have used MDX Deck for their presentations.
221 |
222 | - [Design Systems & React][design-systems-react] by [Diana Mounter](https://mobile.twitter.com/broccolini)
223 | - [Bringing Brazil to the Cloud, Now][brazil-now] by [Guillermo Rauch](https://mobile.twitter.com/rauchg/)
224 | - [Simplify React][simplify-react] by [Kent C. Dodds](https://mobile.twitter.com/kentcdodds)
225 | - [I Got 99 Problems but GraphQL Ain't One][99-problems] by [Sara Vieira](https://mobile.twitter.com/NikkitaFTW)
226 | - [Stop de #divFest][stop-div-fest] by [Sara Vieira](https://mobile.twitter.com/NikkitaFTW)
227 | - [MDX, authors and richer JAMstack content][mdx-talk] by [Josh Dzielak](https://mobile.twitter.com/dzello)
228 | - [Components as Data: A Cross Platform GraphQL Powered Component API][components-as-data] by [Luke Herrington](https://mobile.twitter.com/lukeherrington)
229 | - [A short history of webdevs future 🔮][webdev-intro] by [Hendrik Wallbaum](https://github.com/hoverbaum)
230 |
231 | ### Usage Examples
232 |
233 | The following examples will open in CodeSandbox.
234 |
235 | - [Basic Example](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/basic)
236 | - [Syntax Highlighting](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/syntax-highlighting)
237 | - [Steps](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/steps)
238 | - [Head](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/head)
239 | - [Header & Footer](https://codesandbox.io/s/github/jxnblk/mdx-deck/tree/master/examples/header-footer)
240 |
241 | ---
242 |
243 | ### Related
244 |
245 | - [MDX][]
246 | - [Gatsby][]
247 | - [Theme UI][]
248 | - [Emotion][]
249 | - [Spectacle][]
250 |
251 | [MIT License](LICENSE.md)
252 |
253 | [mdx]: https://mdxjs.com/
254 | [gatsby]: https://gatsbyjs.org
255 | [spectacle]: https://github.com/FormidableLabs/spectacle
256 | [emotion]: https://emotion.sh
257 | [theme ui]: https://theme-ui.com
258 |
259 |
260 | [design-systems-react]: https://github-ds.now.sh/#0
261 | [brazil-now]: https://braziljs.now.sh
262 | [simplify-react]: https://simply-react.netlify.com/#0
263 | [99-problems]: https://99-problems-graphql-aint-one.now.sh/#0
264 | [stop-div-fest]: https://stop-div-fest.now.sh/
265 | [mdx-talk]: https://mdx-talk.developermode.com/
266 | [components-as-data]: https://componentsasdata.lukeherrington.com
267 | [webdev-intro]: https://webdev-intro.talks.hoverbaum.net/
268 |
--------------------------------------------------------------------------------
/packages/mdx-deck/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const path = require('path')
3 | const meow = require('meow')
4 | const execa = require('execa')
5 | const chalk = require('chalk')
6 | const fs = require('fs-extra')
7 | const pkg = require('./package.json')
8 |
9 | const log = (...args) => {
10 | console.log(chalk.green('[mdx-deck]'), ...args)
11 | }
12 | log.error = (...args) => {
13 | console.log(chalk.red('[err]'), ...args)
14 | }
15 |
16 | const cli = meow(
17 | `
18 | ${chalk.gray('Usage')}
19 |
20 | $ ${chalk.green('mdx-deck deck.mdx')}
21 |
22 | $ ${chalk.green('mdx-deck build deck.mdx')}
23 |
24 | ${chalk.gray('Options')}
25 |
26 | -h --host Dev server host
27 | -p --port Dev server port
28 | --no-open Prevent from opening in default browser
29 |
30 | `,
31 | {
32 | description: chalk.green('@mdx-deck/lite ') + chalk.gray(pkg.description),
33 | flags: {
34 | port: {
35 | type: 'string',
36 | alias: 'p',
37 | default: '8000',
38 | },
39 | host: {
40 | type: 'string',
41 | alias: 'h',
42 | default: 'localhost',
43 | },
44 | open: {
45 | type: 'boolean',
46 | alias: 'o',
47 | default: true,
48 | },
49 | },
50 | }
51 | )
52 |
53 | const [cmd, file] = cli.input
54 | const filename = file || cmd
55 |
56 | if (!filename) cli.showHelp(0)
57 |
58 | process.env.__SRC__ = path.resolve(filename)
59 |
60 | const opts = Object.assign({}, cli.flags)
61 |
62 | let dev
63 |
64 | const gatsby = async (...args) => {
65 | await execa('gatsby', ['clean'], {
66 | cwd: __dirname,
67 | stdio: 'inherit',
68 | preferLocal: true,
69 | })
70 | return execa('gatsby', args.filter(Boolean), {
71 | cwd: __dirname,
72 | stdio: 'inherit',
73 | preferLocal: true,
74 | })
75 | }
76 |
77 | switch (cmd) {
78 | case 'build':
79 | gatsby('build').then(() => {
80 | const public = path.join(__dirname, 'public')
81 | const dist = path.join(process.cwd(), 'public')
82 | if (public === dist) return
83 | fs.copySync(public, dist)
84 | })
85 | break
86 | case 'dev':
87 | default:
88 | gatsby(
89 | 'develop',
90 | '--host',
91 | opts.host,
92 | '--port',
93 | opts.port,
94 | opts.open && '--open'
95 | )
96 | break
97 | }
98 |
--------------------------------------------------------------------------------
/packages/mdx-deck/gatsby-config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const src = process.env.__SRC__
4 | const dirname = path.dirname(src)
5 |
6 | module.exports = {
7 | plugins: [
8 | {
9 | resolve: '@mdx-deck/gatsby-plugin',
10 | options: {
11 | path: src,
12 | dirname,
13 | },
14 | },
15 | {
16 | resolve: 'gatsby-source-filesystem',
17 | options: {
18 | path: dirname,
19 | ignore: [
20 | 'node_modules',
21 | 'public',
22 | '.cache',
23 | ]
24 | },
25 | },
26 | {
27 | resolve: 'gatsby-plugin-compile-es6-packages',
28 | options: {
29 | modules: ['mdx-deck', '@mdx-deck/themes'],
30 | },
31 | },
32 | ],
33 | }
34 |
--------------------------------------------------------------------------------
/packages/mdx-deck/hello.mdx:
--------------------------------------------------------------------------------
1 |
2 |
3 | MDX Deck v4
4 |
5 |
6 |
7 |
8 | MDX Deck v4
9 |
10 |
11 |
12 | # Hello
13 |
14 |
15 |
16 | ## To do
17 |
18 | - [x] Print mode
19 | - [x] Grid mode
20 | - [x] page up/down
21 | - [x] Deprecate layouts/docs??
22 | - [x] Clean up docs/readme
23 | - [x] theme.components
24 | - [-] Changelog
25 | - [ ] Clean up demo
26 |
27 |
28 |
29 | ---
30 |
31 | ## This is MDX Deck v4
32 |
33 | MDX-based presentation decks
34 |
35 |
36 |
37 | These are top-secret speaker notes. Shhhh!
38 |
39 |
40 |
41 | ---
42 |
43 | ### What's New
44 |
45 |
46 |
47 | - Persistent headers and footers
48 | - Simplified API
49 | - Refactored internals & bug fixes
50 |
51 |
52 |
53 | ---
54 |
55 |
58 |
59 |
60 | Stand clear of the closing doors
61 |
62 |
63 | ---
64 |
65 | ```jsx
66 | import React from 'react'
67 |
68 | export default props =>
69 |
76 | ```
77 |
78 | ---
79 |
80 | ## Get Started :sunglasses:
81 |
82 | [GitHub](https://github.com/jxnblk/mdx-deck)
83 |
84 |
--------------------------------------------------------------------------------
/packages/mdx-deck/index.js:
--------------------------------------------------------------------------------
1 | import * as themes from '@mdx-deck/themes'
2 |
3 | export { themes }
4 | export * from '@mdx-deck/gatsby-plugin'
5 |
--------------------------------------------------------------------------------
/packages/mdx-deck/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mdx-deck",
3 | "version": "4.1.1",
4 | "description": "MDX-based presentation decks",
5 | "bin": {
6 | "mdx-deck": "./cli.js"
7 | },
8 | "main": "index.js",
9 | "scripts": {
10 | "start": "./cli.js hello.mdx",
11 | "build": "./cli.js build hello.mdx",
12 | "help": "./cli.js"
13 | },
14 | "keywords": [],
15 | "author": "Brent Jackson",
16 | "license": "MIT",
17 | "repository": "github:jxnblk/mdx-deck",
18 | "dependencies": {
19 | "@mdx-deck/gatsby-plugin": "^4.1.1",
20 | "@mdx-deck/themes": "^4.1.0",
21 | "chalk": "^3.0.0",
22 | "execa": "^4.0.0",
23 | "fs-extra": "^8.1.0",
24 | "gatsby": "^2.13.24",
25 | "gatsby-plugin-compile-es6-packages": "^2.0.0",
26 | "gatsby-source-filesystem": "^2.1.48",
27 | "initit": "^1.0.0-2",
28 | "meow": "^6.0.0",
29 | "react": "^16.8.6",
30 | "react-dom": "^16.8.6"
31 | },
32 | "gitHead": "13d00b47780424cc3b52d38ad6f19e99d007c06a"
33 | }
34 |
--------------------------------------------------------------------------------
/packages/starter/decks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jxnblk/mdx-deck/a4779fc0555e26bc241afd8176661d41fd6981ac/packages/starter/decks/.gitkeep
--------------------------------------------------------------------------------
/packages/starter/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['gatsby-theme-mdx-deck'],
3 | }
4 |
--------------------------------------------------------------------------------
/packages/starter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/gatsby-starter",
4 | "version": "4.1.0",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "scripts": {
8 | "start": "gatsby develop",
9 | "clean": "gatsby clean",
10 | "build": "gatsby build"
11 | },
12 | "dependencies": {
13 | "gatsby": "^2.13.25",
14 | "gatsby-theme-mdx-deck": "^4.1.0",
15 | "react": "^16.8.6",
16 | "react-dom": "^16.8.6"
17 | },
18 | "gitHead": "36497d5571f1f354261b9f72f1f67e23c07bc22e"
19 | }
20 |
--------------------------------------------------------------------------------
/packages/themes/README.md:
--------------------------------------------------------------------------------
1 | # @mdx-deck/themes
2 |
3 | Themes used in MDX Deck
4 |
5 | https://github.com/jxnblk/mdx-deck
6 |
--------------------------------------------------------------------------------
/packages/themes/base.js:
--------------------------------------------------------------------------------
1 | // kept for backwards compatibility
2 | export default {}
3 |
--------------------------------------------------------------------------------
/packages/themes/big.js:
--------------------------------------------------------------------------------
1 | const blue = '#0af'
2 |
3 | export default {
4 | googleFont: 'https://fonts.googleapis.com/css?family=Bowlby+One+SC',
5 | fonts: {
6 | body: '"Bowlby One SC", sans-serif',
7 | },
8 | colors: {
9 | text: '#dff',
10 | background: '#011',
11 | primary: blue,
12 | blue,
13 | black: '#000',
14 | },
15 | fontWeights: {
16 | heading: 600,
17 | bold: 600,
18 | },
19 | styles: {
20 | pre: {
21 | color: 'primary',
22 | bg: 'black',
23 | },
24 | code: {
25 | color: 'primary',
26 | },
27 | },
28 | }
29 |
--------------------------------------------------------------------------------
/packages/themes/book.js:
--------------------------------------------------------------------------------
1 | const white = '#fffceb'
2 | const black = '#11111f'
3 | const blue = '#2d5dd7'
4 |
5 | export default {
6 | fonts: {
7 | body: '"Crimson Text", serif',
8 | },
9 | googleFont: 'https://fonts.googleapis.com/css?family=Crimson+Text',
10 | colors: {
11 | text: black,
12 | background: white,
13 | link: blue,
14 | },
15 | styles: {
16 | root: {
17 | textAlign: 'left',
18 | },
19 | Slide: {
20 | display: 'block',
21 | padding: '2em',
22 | textAlign: 'left',
23 | },
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/packages/themes/code.js:
--------------------------------------------------------------------------------
1 | const blue = '#00cdf1'
2 | const black = '#003d48'
3 | const primary = '#0800e3'
4 |
5 | export default {
6 | googleFont: 'https://fonts.googleapis.com/css?family=Source+Code+Pro',
7 | fonts: {
8 | body: '"Source Code Pro", monospace',
9 | monospace: '"Source Code Pro", monospace',
10 | },
11 | colors: {
12 | text: black,
13 | background: blue,
14 | primary,
15 | },
16 | styles: {
17 | pre: {
18 | color: 'background',
19 | bg: 'text',
20 | },
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/packages/themes/comic.js:
--------------------------------------------------------------------------------
1 | const white = '#fffceb'
2 | const black = '#351e38'
3 | const blue = '#2d5dd7'
4 |
5 | export default {
6 | googleFont: 'https://fonts.googleapis.com/css?family=Gloria+Hallelujah',
7 | fonts: {
8 | body: '"Gloria Hallelujah", cursive',
9 | },
10 | colors: {
11 | text: black,
12 | background: white,
13 | primary: blue,
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/packages/themes/condensed.js:
--------------------------------------------------------------------------------
1 | const blue = '#0af'
2 |
3 | export default {
4 | googleFont: 'https://fonts.googleapis.com/css?family=Roboto+Condensed',
5 | fonts: {
6 | body: '"Roboto Condensed", system-ui, sans-serif',
7 | monospace: '"Roboto Mono", monospace',
8 | },
9 | colors: {
10 | text: '#fff',
11 | background: '#000',
12 | primary: blue,
13 | pre: blue,
14 | darkgray: '#111',
15 | },
16 | fontWeights: {
17 | heading: 600,
18 | bold: 600,
19 | },
20 | text: {
21 | heading: {
22 | textTransform: 'uppercase',
23 | },
24 | },
25 | styles: {
26 | pre: {
27 | color: 'primary',
28 | bg: 'darkgray',
29 | },
30 | code: {
31 | color: 'primary',
32 | },
33 | },
34 | }
35 |
--------------------------------------------------------------------------------
/packages/themes/dark.js:
--------------------------------------------------------------------------------
1 | export default {
2 | colors: {
3 | text: '#fff',
4 | background: '#000',
5 | primary: '#08f',
6 | secondary: '#f0f',
7 | darkgray: '#333',
8 | },
9 | styles: {
10 | pre: {
11 | color: 'secondary',
12 | bg: 'darkgray',
13 | },
14 | code: {
15 | color: 'secondary',
16 | },
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/packages/themes/future.js:
--------------------------------------------------------------------------------
1 | const blue = '#0af'
2 |
3 | export default {
4 | fonts: {
5 | body: '"Avenir Next", system-ui, sans-serif',
6 | },
7 | colors: {
8 | text: '#fff',
9 | background: '#111',
10 | primary: blue,
11 | black: '#000',
12 | },
13 | fontWeights: {
14 | heading: 600,
15 | bold: 600,
16 | },
17 | text: {
18 | heading: {
19 | textTransform: 'uppercase',
20 | letterSpacing: '0.1em',
21 | },
22 | },
23 | styles: {
24 | pre: {
25 | color: 'primary',
26 | bg: 'black',
27 | },
28 | code: {
29 | color: 'primary',
30 | },
31 | },
32 | }
33 |
--------------------------------------------------------------------------------
/packages/themes/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './base'
2 | export { default as dark } from './dark'
3 | export { default as future } from './future'
4 | export { default as condensed } from './condensed'
5 | export { default as yellow } from './yellow'
6 | export { default as swiss } from './swiss'
7 | export { default as poppins } from './poppins'
8 |
9 | // serif
10 | export { default as book } from './book'
11 |
12 | // script
13 | export { default as script } from './script'
14 | export { default as comic } from './comic'
15 | export { default as notes } from './notes'
16 | export { default as code } from './code'
17 | export { default as lobster } from './lobster'
18 |
19 | // syntax highlighting
20 | export {
21 | default as highlight,
22 | default as syntaxHighlighter,
23 | } from './syntax-highlighter'
24 | export {
25 | default as prism,
26 | default as syntaxHighlighterPrism,
27 | } from './syntax-highlighter-prism'
28 |
--------------------------------------------------------------------------------
/packages/themes/lobster.js:
--------------------------------------------------------------------------------
1 | const text = '#220011'
2 |
3 | export default {
4 | googleFont: 'https://fonts.googleapis.com/css?family=Lobster|Roboto+Mono',
5 | fonts: {
6 | body: 'Lobster, cursive',
7 | monospace: '"Roboto Mono", monospace',
8 | },
9 | colors: {
10 | text: text,
11 | background: 'tomato',
12 | primary: text,
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/packages/themes/notes.js:
--------------------------------------------------------------------------------
1 | const white = '#fff'
2 | const black = '#000'
3 |
4 | export default {
5 | googleFont:
6 | 'https://fonts.googleapis.com/css?family=Annie+Use+Your+Telescope',
7 | fonts: {
8 | body: '"Annie Use Your Telescope", cursive',
9 | },
10 | colors: {
11 | text: black,
12 | background: white,
13 | },
14 | styles: {
15 | root: {
16 | textAlign: 'center',
17 | },
18 | pre: {
19 | textAlign: 'left',
20 | },
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/packages/themes/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mdx-deck/themes",
3 | "version": "4.1.0",
4 | "main": "index.js",
5 | "author": "Brent Jackson ",
6 | "license": "MIT",
7 | "dependencies": {
8 | "lodash.merge": "^4.6.1",
9 | "react-syntax-highlighter": "^12.2.1"
10 | },
11 | "gitHead": "13d00b47780424cc3b52d38ad6f19e99d007c06a"
12 | }
13 |
--------------------------------------------------------------------------------
/packages/themes/poppins.js:
--------------------------------------------------------------------------------
1 | export default {
2 | googleFont: 'https://fonts.googleapis.com/css?family=Poppins:400,900',
3 | fonts: {
4 | body: '"Poppins", sans-serif',
5 | },
6 | fontWeights: {
7 | heading: 900,
8 | bold: 900,
9 | },
10 | text: {
11 | heading: {
12 | fontWeight: 900,
13 | letterSpacing: '-0.05em',
14 | },
15 | },
16 | styles: {
17 | blockquote: {
18 | fontSize: '1.75em',
19 | textAlign: 'left',
20 | letterSpacing: '-0.05em',
21 | },
22 | },
23 | }
24 |
--------------------------------------------------------------------------------
/packages/themes/script.js:
--------------------------------------------------------------------------------
1 | const cream = '#fe9'
2 | const black = '#320'
3 |
4 | export default {
5 | googleFont: 'https://fonts.googleapis.com/css?family=Yellowtail|Roboto+Mono',
6 | fonts: {
7 | body: '"Yellowtail", cursive',
8 | monospace: '"Roboto Mono", Menlo, monospace',
9 | },
10 | colors: {
11 | text: black,
12 | background: cream,
13 | primary: black,
14 | },
15 | styles: {
16 | root: {
17 | textAlign: 'center',
18 | },
19 | pre: {
20 | textAlign: 'left',
21 | },
22 | },
23 | }
24 |
--------------------------------------------------------------------------------
/packages/themes/swiss.js:
--------------------------------------------------------------------------------
1 | const white = '#fff'
2 | const black = '#000'
3 | const red = '#f00'
4 |
5 | export default {
6 | fonts: {
7 | body: '"Helvetica Neue", Helvetica, Arial, sans-serif',
8 | },
9 | colors: {
10 | text: black,
11 | background: white,
12 | primary: red,
13 | },
14 | styles: {
15 | root: {
16 | textAlign: 'left',
17 | },
18 | Slide: {
19 | display: 'block',
20 | padding: '2em',
21 | textAlign: 'left',
22 | },
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/packages/themes/syntax-highlighter-prism.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Prism } from 'react-syntax-highlighter'
3 |
4 | import { getLanguage } from './syntax-highlighter'
5 |
6 | export const pre = props => props.children
7 |
8 | export const code = props => {
9 | const language = getLanguage(props.className)
10 | return
11 | }
12 |
13 | export default {
14 | components: {
15 | pre,
16 | code,
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/packages/themes/syntax-highlighter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SyntaxHighlighter from 'react-syntax-highlighter'
3 |
4 | export const getLanguage = className => {
5 | const match = /language-(\w*)/.exec(className || 'language-javascript')
6 | let lang = 'javascript'
7 | if (match && match.length > 1) {
8 | lang = match[1]
9 | }
10 | return lang
11 | }
12 |
13 | export const pre = props => props.children
14 |
15 | export const code = props => {
16 | const language = getLanguage(props.className)
17 | return
18 | }
19 |
20 | export default {
21 | components: {
22 | pre,
23 | code,
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/packages/themes/yellow.js:
--------------------------------------------------------------------------------
1 | const yellow = '#fd0'
2 |
3 | export default {
4 | googleFont:
5 | 'https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700|Roboto+Mono',
6 | fonts: {
7 | body: '"Roboto Condensed", system-ui, sans-serif',
8 | monospace: '"Roboto Mono", monospace',
9 | },
10 | colors: {
11 | text: '#000',
12 | background: yellow,
13 | primary: '#333',
14 | },
15 | text: {
16 | heading: {
17 | textTransform: 'uppercase',
18 | },
19 | },
20 | styles: {
21 | pre: {
22 | textAlign: 'left',
23 | color: 'background',
24 | bg: 'text',
25 | },
26 | code: {
27 | color: 'background',
28 | bg: 'text',
29 | },
30 | },
31 | }
32 |
--------------------------------------------------------------------------------
/packages/website-pdf/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/packages/website-pdf/README.md:
--------------------------------------------------------------------------------
1 | # website-pdf
2 |
3 | Save a URL as a PDF
4 |
5 | ```sh
6 | npm i -D website-pdf
7 | ```
8 |
9 | ```sh
10 | website-pdf http://example.com -o example.pdf
11 | ```
12 |
13 | ## Options
14 |
15 | ```
16 | -o --out-file Output filename
17 | -w --width Width in pixels
18 | -h --height Height in pixels
19 | --no-sandbox Disable puppeteer sandbox
20 | ```
21 |
--------------------------------------------------------------------------------
/packages/website-pdf/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const path = require('path')
3 | const meow = require('meow')
4 |
5 | const cli = meow(
6 | `
7 | Usage:
8 |
9 | $ website-pdf http://example.com
10 |
11 | Options:
12 |
13 | -o --out-file Output filename
14 | -w --width Width in pixels
15 | -h --height Height in pixels
16 | --no-sandbox Disable puppeteer sandbox
17 |
18 | `,
19 | {
20 | flags: {
21 | outFile: {
22 | type: 'string',
23 | alias: 'o',
24 | default: 'website.pdf',
25 | },
26 | width: {
27 | type: 'string',
28 | alias: 'w',
29 | default: '1280',
30 | },
31 | height: {
32 | type: 'string',
33 | alias: 'h',
34 | default: '960',
35 | },
36 | sandbox: {
37 | type: 'boolean',
38 | default: true,
39 | },
40 | },
41 | }
42 | )
43 |
44 | const [url] = cli.input
45 |
46 | if (!url) {
47 | cli.showHelp(0)
48 | }
49 |
50 | const opts = Object.assign({}, cli.flags, {
51 | url,
52 | })
53 |
54 | require('./index')(opts)
55 | .then(filename => {
56 | console.log(`saved PDF to`, filename)
57 | process.exit(0)
58 | })
59 | .catch(err => {
60 | console.log(err)
61 | process.exit(1)
62 | })
63 |
--------------------------------------------------------------------------------
/packages/website-pdf/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const puppeteer = require('puppeteer')
3 | const mkdirp = require('mkdirp')
4 |
5 | module.exports = async ({ url, outFile, width, height, sandbox }) => {
6 | if (!url) {
7 | throw new Error('URL is required for website-pdf')
8 | }
9 |
10 | const args = []
11 | if (!sandbox) {
12 | args.push('--no-sandbox', '--disable-setuid-sandbox')
13 | }
14 |
15 | const browser = await puppeteer.launch({ args })
16 | const page = await browser.newPage()
17 | const filename = path.resolve(outFile)
18 | const outDir = path.dirname(filename)
19 | mkdirp.sync(outDir)
20 |
21 | await page.goto(url, {
22 | waitUntil: 'networkidle2',
23 | })
24 |
25 | await page.pdf({
26 | width,
27 | height,
28 | path: filename,
29 | scale: 1,
30 | printBackground: true,
31 | })
32 |
33 | await browser.close()
34 |
35 | return filename
36 | }
37 |
--------------------------------------------------------------------------------
/packages/website-pdf/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website-pdf",
3 | "version": "4.1.0",
4 | "author": "Brent Jackson ",
5 | "license": "MIT",
6 | "bin": {
7 | "website-pdf": "./cli.js"
8 | },
9 | "scripts": {
10 | "test": "./cli.js http://localhost:8000/print -o ../../docs/dist"
11 | },
12 | "dependencies": {
13 | "meow": "^6.0.0",
14 | "mkdirp": "^1.0.3",
15 | "puppeteer": "^2.0.0"
16 | },
17 | "gitHead": "13d00b47780424cc3b52d38ad6f19e99d007c06a"
18 | }
19 |
--------------------------------------------------------------------------------
/templates/basic/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/templates/basic/README.md:
--------------------------------------------------------------------------------
1 | # mdx-deck basic template
2 |
3 | This was generated with [mdx-deck][]'s `npm init deck` command.
4 |
5 | ## Development
6 |
7 | To run the presentation deck in development mode:
8 |
9 | ```sh
10 | npm start
11 | ```
12 |
13 | Edit the [`deck.mdx`](deck.mdx) file to get started.
14 |
15 | ## Exporting
16 |
17 | To build the presentation deck as static HTML:
18 |
19 | ```sh
20 | npm run build
21 | ```
22 |
23 | For more documentation see the [mdx-deck][] repo.
24 |
25 | [mdx-deck]: https://github.com/jxnblk/mdx-deck
26 |
--------------------------------------------------------------------------------
/templates/basic/deck.mdx:
--------------------------------------------------------------------------------
1 | import { Head, Notes } from 'mdx-deck'
2 | import { theme } from './theme'
3 |
4 | export const themes = [ theme ]
5 |
6 |
7 | Presentation Title
8 |
9 |
10 | # Hello
11 |
12 | ---
13 |
14 | ## Edit this file
15 |
16 | To create your presentation
17 |
18 |
19 | Create speaker notes with the Notes component
20 |
21 |
22 | ---
23 |
24 |
25 |
--------------------------------------------------------------------------------
/templates/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@mdx-deck/basic-template",
4 | "version": "4.1.1",
5 | "scripts": {
6 | "start": "mdx-deck deck.mdx",
7 | "build": "mdx-deck build deck.mdx",
8 | "help": "mdx-deck"
9 | },
10 | "devDependencies": {
11 | "mdx-deck": "^4.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/templates/basic/theme.js:
--------------------------------------------------------------------------------
1 | export const theme = {
2 | // Customize your presentation theme here.
3 | //
4 | // Read the docs for more info:
5 | // https://github.com/jxnblk/mdx-deck/blob/master/docs/theming.md
6 | // https://github.com/jxnblk/mdx-deck/blob/master/docs/themes.md
7 | }
8 |
--------------------------------------------------------------------------------