├── .prettierignore ├── public ├── uses.png ├── favicon.ico ├── httriri.png ├── animonica.png ├── github-issue.png ├── devto-issue-example.png ├── contributing-to-react.png ├── types-of-oss-projects.png └── zeit.svg ├── .prettierrc ├── constants └── modes.js ├── screenshots ├── next-mdx-deck-main.png ├── next-mdx-deck-example.gif └── next-mdx-deck-presentation.png ├── components ├── SpeakerNotes.jsx ├── Cover.jsx ├── Slide.jsx ├── ThemeProvider.jsx ├── Header.jsx ├── MDXProvider.jsx └── PresentationMode.jsx ├── .babelrc ├── layouts ├── AboutPage.jsx ├── TransitionPage.jsx └── SlidePage.jsx ├── context ├── TotalPagesContext.js ├── ModeContext.jsx └── CurrentSlideContext.jsx ├── next.config.js ├── site.config.js ├── pages ├── index.jsx ├── _document.js ├── _app.jsx └── slides │ └── [slide].js ├── .gitignore ├── .eslintrc.js ├── README.md ├── LICENSE.md ├── hooks ├── useEventListener.js └── useStorage.js ├── package.json ├── CONTRIBUTING.md ├── INSTRUCTIONS.md └── slides ├── talk.mdx └── 1.mdx /.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules -------------------------------------------------------------------------------- /public/uses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/uses.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/httriri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/httriri.png -------------------------------------------------------------------------------- /public/animonica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/animonica.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /public/github-issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/github-issue.png -------------------------------------------------------------------------------- /constants/modes.js: -------------------------------------------------------------------------------- 1 | export const MODES = { 2 | SLIDESHOW: 'slideshow', 3 | SPEAKER: 'speaker', 4 | } 5 | 6 | export default MODES 7 | -------------------------------------------------------------------------------- /public/devto-issue-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/devto-issue-example.png -------------------------------------------------------------------------------- /public/contributing-to-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/contributing-to-react.png -------------------------------------------------------------------------------- /public/types-of-oss-projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/public/types-of-oss-projects.png -------------------------------------------------------------------------------- /screenshots/next-mdx-deck-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/screenshots/next-mdx-deck-main.png -------------------------------------------------------------------------------- /components/SpeakerNotes.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function SpeakerNotes({ children }) { 4 | return children 5 | } 6 | -------------------------------------------------------------------------------- /screenshots/next-mdx-deck-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/screenshots/next-mdx-deck-example.gif -------------------------------------------------------------------------------- /screenshots/next-mdx-deck-presentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M0nica/getting-started-with-open-source/HEAD/screenshots/next-mdx-deck-presentation.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [ 4 | ["styled-components", { 5 | "ssr": true 6 | }] 7 | ] 8 | } -------------------------------------------------------------------------------- /layouts/AboutPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function AboutPage({ children }) { 4 | return
{children}
5 | } 6 | -------------------------------------------------------------------------------- /context/TotalPagesContext.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const TotalPagesContext = React.createContext(0) 4 | 5 | export const useTotalPages = () => React.useContext(TotalPagesContext) 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const withMDX = require('@next/mdx')({ 2 | extension: /\.(md|mdx)$/, 3 | }) 4 | module.exports = withMDX({ 5 | // Pick up MDX files in the /pages/ directory 6 | pageExtensions: ['js', 'jsx', 'md', 'mdx'], 7 | }) 8 | -------------------------------------------------------------------------------- /components/Cover.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const StyledCover = styled.div`` 5 | 6 | export default function Cover({ children }) { 7 | return {children} 8 | } 9 | -------------------------------------------------------------------------------- /site.config.js: -------------------------------------------------------------------------------- 1 | export const siteConfig = { 2 | name: `@waterproofheart`, 3 | title: `Getting Started with Open Source`, 4 | date: ``, 5 | author: { 6 | url: `https://twitter.com/waterproofheart`, 7 | }, 8 | } 9 | 10 | export default siteConfig 11 | -------------------------------------------------------------------------------- /pages/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { useRouter } from 'next/router' 3 | 4 | export default function index() { 5 | const router = useRouter() 6 | 7 | useEffect(() => { 8 | router.replace('/slides/1') 9 | }) 10 | return
11 | } 12 | -------------------------------------------------------------------------------- /components/Slide.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const StyledSlide = styled.div` 5 | width: 100%; 6 | ` 7 | 8 | export default function Slide({ children, id, className }) { 9 | return ( 10 | 11 | {children} 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /layouts/TransitionPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { motion } from 'framer-motion' 3 | 4 | export default function TransitionPage({ children }) { 5 | return ( 6 | 11 | {children} 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /components/ThemeProvider.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ThemeProvider as StyledThemeProvider } from 'styled-components' 3 | 4 | const theme = { 5 | colors: { 6 | primary: '#0070f3', 7 | }, 8 | } 9 | 10 | export const ThemeProvider = ({ children }) => { 11 | return {children} 12 | } 13 | 14 | export default ThemeProvider 15 | -------------------------------------------------------------------------------- /components/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'next/link' 3 | 4 | export default function Header({ name, title, date, url }) { 5 | return ( 6 |
7 |
8 | 9 | {name} 10 | {' '} 11 | —{' '} 12 | 13 | {title} 14 | 15 |
16 | 17 |
18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: ['airbnb', 'prettier'], 4 | plugins: ['react', 'jsx-a11y', 'import'], 5 | ignorePatterns: ['slides/'], 6 | rules: { 7 | // Next imports React into pages automatically 8 | 'react/react-in-jsx-scope': 'off', 9 | // Next includes packages like MDX using their bundle 10 | 'import/no-extraneous-dependencies': 'off', 11 | // Turn off const conversion 12 | 'prefer-const': 'off', 13 | }, 14 | env: { 15 | browser: true, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /public/zeit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /context/ModeContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useEffect, useState } from 'react' 2 | import { useRouter } from 'next/router' 3 | import { MODES } from '../constants/modes' 4 | 5 | 6 | export const ModeContext = createContext({}) 7 | 8 | export function ModeProvider({ children }) { 9 | const [mode, setMode] = useState(MODES.SLIDESHOW) 10 | const router = useRouter() 11 | const newMode = router.query.mode 12 | 13 | useEffect(() => { 14 | if (newMode) setMode(newMode) 15 | }, [newMode]) 16 | 17 | 18 | return ( 19 | 20 | {children} 21 | 22 | ) 23 | } 24 | 25 | export const useMode = () => useContext(ModeContext) 26 | -------------------------------------------------------------------------------- /context/CurrentSlideContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useState } from 'react' 2 | 3 | export const CurrentSlideContext = createContext({ 4 | currentSlide: 0, 5 | setSlide: () => {}, 6 | }) 7 | 8 | export function CurrentSlideProvider({ children }) { 9 | // Grab initial slide from hash (#) in URL 10 | const initialSlide = 11 | process.browser && window.location.hash 12 | ? parseInt(window.location.hash.replace('#', '')) 13 | : 0 14 | const [currentSlide, setSlide] = useState(initialSlide) 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ) 21 | } 22 | 23 | export const useCurrentSlide = () => useContext(CurrentSlideContext) 24 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document from 'next/document' 2 | import { ServerStyleSheet } from 'styled-components' 3 | 4 | export default class MyDocument extends Document { 5 | static async getInitialProps(ctx) { 6 | const sheet = new ServerStyleSheet() 7 | const originalRenderPage = ctx.renderPage 8 | 9 | try { 10 | ctx.renderPage = () => 11 | originalRenderPage({ 12 | enhanceApp: (App) => (props) => 13 | sheet.collectStyles(), 14 | }) 15 | 16 | const initialProps = await Document.getInitialProps(ctx) 17 | return { 18 | ...initialProps, 19 | styles: ( 20 | <> 21 | {initialProps.styles} 22 | {sheet.getStyleElement()} 23 | 24 | ), 25 | } 26 | } finally { 27 | sheet.seal() 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pages/_app.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { AnimatePresence } from 'framer-motion' 3 | import { CurrentSlideProvider } from '../context/CurrentSlideContext' 4 | import { ModeProvider } from '../context/ModeContext' 5 | import MDXProvider from '../components/MDXProvider' 6 | import { ThemeProvider } from '../components/ThemeProvider' 7 | import TransitionPage from '../layouts/TransitionPage' 8 | 9 | export default ({ Component, pageProps }) => ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Getting Started With Open Source 3 | 4 | Interested in contributing to open source? Not sure where to start? Start this month at our Hacktober event with Monica Powell. 5 | 6 | Topics the workshop will cover: 7 | - What is Open Source? What are some of the different roles within Open Source? 8 | - Best practices for contributing to open source 9 | - How to identify opportunities to contribute to open source 10 | - Opening an open source pull request 11 | 12 | What you'll need before the workshop: 13 | a GitHub account https://github.com/ 14 | 15 | About our speaker: 16 | Monica Powell is a software engineer who is passionate about making contributing to open-source more approachable and building community. She currently works at Newsela as a software engineer building educational software and was recently selected to be an inaugural GitHub Star based on her community involvement. 17 | 18 | -------------------------------------------------------------------------------- /components/MDXProvider.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { MDXProvider } from '@mdx-js/react' 3 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' 4 | import okaidia from 'react-syntax-highlighter/dist/cjs/styles/prism/okaidia' 5 | import SlidePage from '../layouts/SlidePage' 6 | import Cover from './Cover' 7 | import SpeakerNotes from './SpeakerNotes' 8 | 9 | const mdComponents = { 10 | h1: (props) =>

, 11 | pre: (props) => props.children, 12 | code: (props) => { 13 | const { className } = props 14 | const language = className.replace('language-', '') 15 | return ( 16 | 22 | ) 23 | }, 24 | Cover, 25 | SlidePage, 26 | SpeakerNotes, 27 | } 28 | 29 | export default ({ children }) => ( 30 | {children} 31 | ) 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2020 Ryosuke Hana 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /hooks/useEventListener.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react' 2 | 3 | export default function useEventListener(eventName, handler, element = window) { 4 | // Create a ref that stores handler 5 | const savedHandler = useRef() 6 | 7 | // Update ref.current value if handler changes. 8 | // This allows our effect below to always get latest handler ... 9 | // ... without us needing to pass it in effect deps array ... 10 | // ... and potentially cause effect to re-run every render. 11 | useEffect(() => { 12 | savedHandler.current = handler 13 | }, [handler]) 14 | 15 | useEffect( 16 | () => { 17 | // Make sure element supports addEventListener 18 | // On 19 | const isSupported = element && element.addEventListener 20 | if (!isSupported) return 21 | 22 | // Create event listener that calls handler function stored in ref 23 | const eventListener = (event) => savedHandler.current(event) 24 | 25 | // Add event listener 26 | element.addEventListener(eventName, eventListener) 27 | 28 | // Remove event listener on cleanup 29 | return () => { 30 | element.removeEventListener(eventName, eventListener) 31 | } 32 | }, 33 | [eventName, element] // Re-run if eventName or element changes 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-with-open-source", 3 | "version": "0.1.0", 4 | "description": "A presentation on getting started with Open Source", 5 | "private": true, 6 | "author": { 7 | "name": "Monica Powell", 8 | "email": "github@aboutmonica.com", 9 | "url": "https://aboutmonica.com/" 10 | }, 11 | "scripts": { 12 | "dev": "next dev", 13 | "build": "next build", 14 | "format": "prettier --write '**/*.{js,jsx}'", 15 | "start": "next start", 16 | "export": "next export" 17 | }, 18 | "dependencies": { 19 | "@mdx-js/loader": "^1.5.8", 20 | "@next/mdx": "^9.3.4", 21 | "framer-motion": "^1.10.3", 22 | "next": "9.3.4", 23 | "react": "16.13.1", 24 | "react-dom": "16.13.1", 25 | "react-swipeable": "^5.5.1", 26 | "react-syntax-highlighter": "^12.2.1", 27 | "styled-components": "^5.1.0" 28 | }, 29 | "devDependencies": { 30 | "babel-eslint": "^10.1.0", 31 | "babel-plugin-styled-components": "^1.10.7", 32 | "eslint": "^6.8.0", 33 | "eslint-config-airbnb": "^18.1.0", 34 | "eslint-config-prettier": "^6.10.1", 35 | "eslint-plugin-import": "^2.20.2", 36 | "eslint-plugin-jsx-a11y": "^6.2.3", 37 | "eslint-plugin-react": "^7.19.0", 38 | "eslint-plugin-react-hooks": "^2.5.1", 39 | "prettier": "^2.0.4" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /components/PresentationMode.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { MODES } from '../constants/modes' 4 | 5 | const PresentationFrame = styled.div` 6 | display: flex; 7 | padding: 3rem; 8 | max-height: 100vh; 9 | ` 10 | 11 | const SlideWindow = styled.div` 12 | width: 65%; 13 | overflow-y: scroll; 14 | 15 | & > div { 16 | padding: 1rem; 17 | max-height: 80vh; 18 | } 19 | 20 | #slide { 21 | border: 1px solid #fff; 22 | align-items:flex-start; 23 | height:auto; 24 | } 25 | ` 26 | 27 | const Sidebar = styled.div` 28 | width: 35%; 29 | 30 | & > div { 31 | padding: 1rem; 32 | } 33 | ` 34 | 35 | const SpeakerNotesWindow = styled.div` 36 | width: 100%; 37 | height: 50vh; 38 | overflow-y: scroll; 39 | border: 1px solid #fff; 40 | 41 | font-size: 16px; 42 | 43 | & > div { 44 | padding: 1rem; 45 | } 46 | ` 47 | 48 | export default function PresentationMode({ 49 | mode, 50 | notes, 51 | currentSlide, 52 | children, 53 | }) { 54 | if (mode === MODES.SPEAKER) { 55 | return ( 56 | 57 | 58 |
{children}
59 |
60 | 61 |
62 | {/* */} 63 | 64 |
{notes[currentSlide]}
65 |
66 |
67 |
68 |
69 | ) 70 | } 71 | return children 72 | } 73 | -------------------------------------------------------------------------------- /pages/slides/[slide].js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | import Head from 'next/head' 4 | import dynamic from 'next/dynamic' 5 | import Header from '../../components/Header' 6 | import { TotalPagesContext } from '../../context/TotalPagesContext' 7 | import { siteConfig } from '../../site.config.js' 8 | 9 | const SlideshowPage = ({ totalSlidePages, currentSlide, filename }) => { 10 | const MDXContent = dynamic(() => import(`../../${filename}`)) 11 | return ( 12 | 13 | 14 | 15 | {siteConfig.name} - {siteConfig.title} 16 | 17 | 18 | 22 | 23 |
29 | 30 | 31 | ) 32 | } 33 | 34 | export async function getStaticProps({ params }) { 35 | const filename = path.join('slides', `${params.slide}.mdx`) 36 | const slidesDirectory = path.join(process.cwd(), 'slides') 37 | const mdxFiles = fs.readdirSync(slidesDirectory) 38 | const totalSlidePages = mdxFiles.length 39 | 40 | return { 41 | props: { 42 | totalSlidePages, 43 | currentSlide: params.slide, 44 | filename, 45 | }, 46 | } 47 | } 48 | 49 | export async function getStaticPaths() { 50 | const postsDirectory = path.join(process.cwd(), 'slides') 51 | const mdxFiles = fs.readdirSync(postsDirectory) 52 | // Loop through all post files and create array of slugs (to create links) 53 | const paths = mdxFiles.map((filename) => ({ 54 | params: { 55 | slide: filename.replace('.mdx', ''), 56 | }, 57 | })) 58 | 59 | return { 60 | paths, 61 | fallback: false, 62 | } 63 | } 64 | 65 | export default SlideshowPage 66 | -------------------------------------------------------------------------------- /hooks/useStorage.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { useRouter } from 'next/router' 3 | import { useCurrentSlide } from '../context/CurrentSlideContext' 4 | 5 | const keys = { 6 | slide: 'next-mdx-deck-slide', 7 | page: 'next-mdx-deck-page', 8 | } 9 | 10 | export const useStorage = () => { 11 | const { currentSlide, setSlide } = useCurrentSlide() 12 | const router = useRouter() 13 | const currentPage = 14 | router.query && 'slide' in router.query && parseInt(router.query.slide, 10) 15 | const [focused, setFocused] = useState(false) 16 | 17 | /** 18 | * Checks when user enters (focus) or 19 | * leaves (blur) browser window/tab 20 | */ 21 | const handleFocus = () => setFocused(true) 22 | const handleBlur = () => setFocused(false) 23 | 24 | /** 25 | * Updates route or context with local storage data 26 | * from event listener 27 | * @param {*} e 28 | */ 29 | const handleStorageChange = (e) => { 30 | const n = parseInt(e.newValue, 10) 31 | const syncedSlide = localStorage.getItem(keys.slide) 32 | // if (focused) return 33 | if (Number.isNaN(n)) return 34 | switch (e.key) { 35 | case keys.page: 36 | router.push(`/slides/${parseInt(n, 10)}#${syncedSlide}`) 37 | break 38 | case keys.slide: 39 | window.location.hash = `#${n}` 40 | setSlide(n) 41 | break 42 | default: 43 | break 44 | } 45 | } 46 | 47 | useEffect(() => { 48 | setFocused(document.hasFocus()) 49 | }, []) 50 | 51 | useEffect(() => { 52 | if (!focused) window.addEventListener('storage', handleStorageChange) 53 | window.addEventListener('focus', handleFocus) 54 | window.addEventListener('blur', handleBlur) 55 | return () => { 56 | if (!focused) window.removeEventListener('storage', handleStorageChange) 57 | window.removeEventListener('focus', handleFocus) 58 | window.removeEventListener('blur', handleBlur) 59 | } 60 | }, [focused]) 61 | 62 | /** 63 | * Sync localstorage with changes to slides or pages 64 | */ 65 | useEffect(() => { 66 | if (!focused) return 67 | localStorage.setItem(keys.slide, currentSlide) 68 | localStorage.setItem(keys.page, currentPage) 69 | }, [focused, currentSlide, currentPage]) 70 | } 71 | 72 | export const Storage = () => { 73 | useStorage() 74 | return false 75 | } 76 | 77 | export default useStorage 78 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | Twitter, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct (see below), please follow it in all your interactions with the project. Be excellent to eachother, and party on 🤘 7 | 8 | ## Setup 9 | 10 | 1. Clone project `git clone git@github.com:whoisryosuke/next-mdx-deck.git` 11 | 1. Navigate to project `cd next-mdx-deck` 12 | 1. Install dependencies `yarn` 13 | 1. Run the development server `yarn dev` 14 | 15 | ## Development 16 | 17 | See README.md for information on working with project. 18 | 19 | ## Pull Request Process 20 | 21 | 1. Make a PR with working or conceptual code. 22 | 1. Submit it. 23 | 24 | > That's it. I'd say more if we had tests, a CI/CD setup, versioning, etc. But it's pretty casual right now. Make sure if you have working code to test the build process and ensure it doesn't fail. 25 | 26 | ## Code of Conduct 27 | 28 | ### Our Pledge 29 | 30 | In the interest of fostering an open and welcoming environment, we as 31 | contributors and maintainers pledge to making participation in our project and 32 | our community a harassment-free experience for everyone, regardless of age, body 33 | size, disability, ethnicity, gender identity and expression, level of experience, 34 | nationality, personal appearance, race, religion, or sexual identity and 35 | orientation. 36 | 37 | ### Our Standards 38 | 39 | Examples of behavior that contributes to creating a positive environment 40 | include: 41 | 42 | - Using welcoming and inclusive language 43 | - Being respectful of differing viewpoints and experiences 44 | - Gracefully accepting constructive criticism 45 | - Focusing on what is best for the community 46 | - Showing empathy towards other community members 47 | 48 | Examples of unacceptable behavior by participants include: 49 | 50 | - The use of sexualized language or imagery and unwelcome sexual attention or 51 | advances 52 | - Trolling, insulting/derogatory comments, and personal or political attacks 53 | - Public or private harassment 54 | - Publishing others' private information, such as a physical or electronic 55 | address, without explicit permission 56 | - Other conduct which could reasonably be considered inappropriate in a 57 | professional setting 58 | 59 | ### Our Responsibilities 60 | 61 | Project maintainers are responsible for clarifying the standards of acceptable 62 | behavior and are expected to take appropriate and fair corrective action in 63 | response to any instances of unacceptable behavior. 64 | 65 | Project maintainers have the right and responsibility to remove, edit, or 66 | reject comments, commits, code, wiki edits, issues, and other contributions 67 | that are not aligned to this Code of Conduct, or to ban temporarily or 68 | permanently any contributor for other behaviors that they deem inappropriate, 69 | threatening, offensive, or harmful. 70 | 71 | ### Scope 72 | 73 | This Code of Conduct applies both within project spaces and in public spaces 74 | when an individual is representing the project or its community. Examples of 75 | representing a project or community include using an official project e-mail 76 | address, posting via an official social media account, or acting as an appointed 77 | representative at an online or offline event. Representation of a project may be 78 | further defined and clarified by project maintainers. 79 | 80 | ### Enforcement 81 | 82 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 83 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 84 | complaints will be reviewed and investigated and will result in a response that 85 | is deemed necessary and appropriate to the circumstances. The project team is 86 | obligated to maintain confidentiality with regard to the reporter of an incident. 87 | Further details of specific enforcement policies may be posted separately. 88 | 89 | Project maintainers who do not follow or enforce the Code of Conduct in good 90 | faith may face temporary or permanent repercussions as determined by other 91 | members of the project's leadership. 92 | 93 | ### Attribution 94 | 95 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 96 | available at [http://contributor-covenant.org/version/1/4][version] 97 | 98 | [homepage]: http://contributor-covenant.org 99 | [version]: http://contributor-covenant.org/version/1/4/ 100 | -------------------------------------------------------------------------------- /INSTRUCTIONS.md: -------------------------------------------------------------------------------- 1 | ![Presentation Mode](./screenshots/next-mdx-deck-example.gif) 2 | 3 | # Next MDX Deck 4 | 5 | Create presentation decks using MDX, React, and [Next.js](https://nextjs.org/). 6 | 7 | ## Features 8 | 9 | - 📽 React-based Slideshow 10 | - ✍️ Write using Markdown, React components, even HTML! 11 | - 🎨 Themeable with CSS vars or Styled Components 12 | - 👉 Swipe to change slides 13 | - ♻️ Sync slides between browser tabs 14 | - 👨‍💻 Presentation Mode 15 | - 📝 Speaker Notes 16 | 17 | ## Getting Started 18 | 19 | 1. Clone the project: `git clone https://github.com/whoisryosuke/next-mdx-deck` 20 | 2. Install dependencies: `npm i` or `yarn` 21 | 3. Run the dev server: `npm run dev` or `yarn dev` 22 | 4. Edit the first slide in `/slides/1.mdx` and save to [**see changes**](http://localhost:3000/)! 23 | 24 | When you're done, run `npm run build && npm run export` or `yarn build && yarn export` will create a static app you can deploy anywhere (or use locally). See below for more details. 25 | 26 | ### Deploying 27 | 28 | This project is easy to build locally or using a host with build services (like Netlify or Now). 29 | 30 | 1. ⚙️ Run the build process: `npm run build && npm run export` 31 | 1. 🚀 Upload the static contents of `out` folder to host/CDN (or run the `out/index.html` locally) 32 | 33 | ## How to Use 34 | 35 | ### 💬 Changing the Title/Date/etc 36 | 37 | The default theme includes a title, date, author (and link to the author's website) in of the `
` component. You can edit this data inside the `site.config.js` file. 38 | 39 | ### ✍️ Writing JSX 40 | 41 | You can use JSX in [a few ways](https://mdxjs.com/getting-started) in your MDX files: 42 | 43 | - You can use the syntax with HTML (`