commit ece9bb737e93ae011cfbff5b17a6e29fee0a2b85 Author: Fabian Schultz Date: Fri Nov 24 00:22:47 2017 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..248335d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Project dependencies +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules +.cache/ +# Build directory +public/ +.DS_Store +yarn-error.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..89fdcd3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 Fabian Schultz + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a26f6e4 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# gatsby-deck +[![deploys by netlify](https://img.shields.io/badge/deploys%20by-netlify-00c7b7.svg)](https://www.netlify.com) +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) + +Create presentations using Gatsby & React. Inspired by Gulliermo Rauch’s [deck on Next.js](https://deck.now.sh/). + +- [Demo](//gatsby-deck.netlify.com) + +## Usage + + $ git clone git@github.com:fabe/gatsby-deck.git + $ cd gatsby-deck + $ npm i + $ npm run develop + +Then edit and extend your slides inside the `src/pages` directory. Navigate with the arrow keys. + +## Author +- Fabian Schultz ([@fschultz_](https://twitter.com/fschultz_)) diff --git a/gatsby-browser.js b/gatsby-browser.js new file mode 100644 index 0000000..3325bcd --- /dev/null +++ b/gatsby-browser.js @@ -0,0 +1,84 @@ +/* eslint-disable react/prop-types */ +/* globals window CustomEvent */ +import React, { createElement } from 'react'; +import { Transition } from 'react-transition-group'; +import createHistory from 'history/createBrowserHistory'; + +import getTransitionStyle from './src/utils/getTransitionStyle'; + +const timeout = 500; +const historyExitingEventType = `history::exiting`; + +const getUserConfirmation = (message, callback) => { + const event = new CustomEvent(historyExitingEventType, { + detail: { message }, + }); + window.dispatchEvent(event); + setTimeout(() => { + callback(true); + }, timeout); +}; +const history = createHistory({ getUserConfirmation }); +// block must return a string to conform +history.block((location, action) => location.key); +exports.replaceHistory = () => history; + +class ReplaceComponentRenderer extends React.Component { + constructor(props) { + super(props); + this.state = { exiting: false }; + this.listenerHandler = this.listenerHandler.bind(this); + } + + listenerHandler(event) { + this.setState({ exiting: true }); + } + + componentDidMount() { + window.addEventListener(historyExitingEventType, this.listenerHandler); + } + + componentWillUnmount() { + window.removeEventListener(historyExitingEventType, this.listenerHandler); + } + + componentWillReceiveProps(nextProps) { + if (this.props.location.key !== nextProps.location.key) { + this.setState({ exiting: false }); + } + } + + render() { + const transitionProps = { + timeout: { + enter: 0, + exit: timeout, + }, + appear: true, + in: !this.state.exiting, + key: this.props.location.key, + }; + return ( + + {status => + createElement(this.props.pageResources.component, { + ...this.props, + ...this.props.pageResources.json, + transition: { + status, + timeout, + style: getTransitionStyle({ status, timeout }), + }, + })} + + ); + } +} + +// eslint-disable-next-line react/display-name +exports.replaceComponentRenderer = ({ props }) => { + if (props.layout) { + return undefined; + } + return createElement(ReplaceComponentRenderer, props); +}; diff --git a/gatsby-config.js b/gatsby-config.js new file mode 100644 index 0000000..9517fce --- /dev/null +++ b/gatsby-config.js @@ -0,0 +1,8 @@ +module.exports = { + siteMetadata: { + name: `Fabian Schultz`, + title: `Gatsby Deck`, + date: `November 23, 2017`, + }, + plugins: [`gatsby-plugin-react-helmet`], +}; diff --git a/gatsby-node.js b/gatsby-node.js new file mode 100644 index 0000000..d1a1c26 --- /dev/null +++ b/gatsby-node.js @@ -0,0 +1,21 @@ +// Implement the Gatsby API “onCreatePage”. This is +// called after every page is created. +exports.onCreatePage = ({ page, boundActionCreators }) => { + const { createPage, deletePage } = boundActionCreators; + + return new Promise((resolve, reject) => { + // Remove trailing slash + const newPage = Object.assign({}, page, { + path: page.path === `/` ? page.path : page.path.replace(/\/$/, ``), + }); + + if (newPage.path !== page.path) { + // Remove the old page + deletePage(page); + // Add the new page + createPage(newPage); + } + + resolve(); + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..775d369 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "gatsby-deck", + "description": "Create presentations using Gatsby & React.", + "version": "1.0.0", + "author": "Fabian Schultz ", + "dependencies": { + "gatsby": "^1.9.119", + "gatsby-cli": "^1.1.23", + "gatsby-link": "^1.6.28", + "gatsby-plugin-react-helmet": "^1.0.8" + }, + "keywords": ["gatsby"], + "license": "MIT", + "main": "n/a", + "scripts": { + "build": "gatsby build", + "develop": "gatsby develop", + "format": + "prettier --trailing-comma es5 --no-semi --single-quote --write \"src/**/*.js\"", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies": { + "prettier": "^1.8.2" + } +} diff --git a/src/components/Quote.js b/src/components/Quote.js new file mode 100644 index 0000000..32ab40c --- /dev/null +++ b/src/components/Quote.js @@ -0,0 +1,8 @@ +import React from 'react'; + +export default ({ children, cite }) => ( +
+
{children}
+ {cite} +
+); diff --git a/src/layouts/index.css b/src/layouts/index.css new file mode 100644 index 0000000..71c7815 --- /dev/null +++ b/src/layouts/index.css @@ -0,0 +1,123 @@ +:root { + --bg: #fdfdfd; + --meta: #888; + --accent: #00baff; + --text: black; + --base: 1.5rem; +} + +* { + box-sizing: border-box; +} + +body, +html { + font-family: BlinkMacSystemFont; + font-size: var(--base); + -webkit-font-smoothing: antialiased; + font-feature-settings: 'calt', 'liga', 'hist', 'onum', 'pnum'; + + overflow: auto; + + width: 100%; + height: 100%; + margin: 0; + padding: 0; + + color: var(--text); + background-color: var(--bg); +} + +a { + color: var(--text); + + text-decoration-skip: ink; +} + +blockquote { + font-size: 2rem; + font-weight: bold; + + width: 50vw; + + text-align: left; +} + +blockquote div::before { + content: '\201C'; +} + +blockquote div::after { + content: '\201D'; +} + +cite { + font-size: var(--base); + font-weight: normal; + font-style: normal; + + display: block; + + margin-top: 2rem; +} + +cite::before { + content: '\2014\00a0'; +} + +a:hover { + color: var(--accent); +} + +h1 { + font-size: 3rem; + margin-bottom: 0.5rem; +} + +p { + margin-top: 1rem; + margin-bottom: 1rem; +} + +header { + position: fixed; + font-size: 18px; + top: 0; + left: 0; + + display: flex; + justify-content: space-between; + align-items: center; + + width: 100%; + padding: 20px; + + user-select: none; +} + +header a, +time { + text-decoration: none; + + color: var(--meta); +} + +header a:hover { + color: var(--meta); +} + +header span { + color: var(--text); +} + +#slide { + display: flex; + overflow: auto; + justify-content: center; + align-items: center; + + width: 100vw; + height: 100vh; + + text-align: center; +} diff --git a/src/layouts/index.js b/src/layouts/index.js new file mode 100644 index 0000000..19ae5cd --- /dev/null +++ b/src/layouts/index.js @@ -0,0 +1,92 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Link, { navigateTo } from 'gatsby-link'; +import Helmet from 'react-helmet'; + +import './index.css'; + +const Header = ({ name, title, date }) => ( +
+ + {name} — {title} + + +
+); + +class TemplateWrapper extends Component { + componentDidMount() { + document.addEventListener('keydown', ({ keyCode }) => { + const now = parseInt(location.pathname.substr(1)); + const NEXT = 39; + const PREV = 37; + + const slides = this.props.data.allSitePage.edges.filter(page => { + const id = parseInt(page.node.path.substr(1)); + if (id && id !== 404) { + return true; + } + }); + + if (now) { + if (keyCode === PREV && now === 1) { + return false; + } else if (keyCode === NEXT && now === slides.length) { + return false; + } else if (keyCode === NEXT) { + navigateTo(`/${now + 1}`); + } else if (keyCode === PREV) { + navigateTo(`/${now - 1}`); + } + } + }); + } + render() { + const { children, data } = this.props; + return ( +
+ +
+
{children()}
+
+ ); + } +} + +TemplateWrapper.propTypes = { + children: PropTypes.func, + data: PropTypes.object, +}; + +export default TemplateWrapper; + +export const pageQuery = graphql` + query PageQuery { + site { + siteMetadata { + name + title + date + } + } + allSitePage { + edges { + node { + path + component + pluginCreator { + name + pluginFilepath + } + } + } + } + } +`; diff --git a/src/pages/1.js b/src/pages/1.js new file mode 100644 index 0000000..5b21134 --- /dev/null +++ b/src/pages/1.js @@ -0,0 +1,9 @@ +import React from 'react'; +import Link from 'gatsby-link'; + +export default ({ transition }) => ( +
+

Gatsby Deck

+

Create presentations using Gatsby & React.

+
+); diff --git a/src/pages/2.js b/src/pages/2.js new file mode 100644 index 0000000..d85d3b4 --- /dev/null +++ b/src/pages/2.js @@ -0,0 +1,14 @@ +import React from 'react'; +import Link from 'gatsby-link'; +import Quote from '../components/Quote'; + +export default ({ transition }) => ( +
+ + Inscrutable icons litter the face of the devices even though the research + community has long demonstrated that people cannot remember the meaning of + more than a small number of icons […] Who can remember what each icon + means? Not me. + +
+); diff --git a/src/pages/3.js b/src/pages/3.js new file mode 100644 index 0000000..c7d4faa --- /dev/null +++ b/src/pages/3.js @@ -0,0 +1,8 @@ +import React from 'react'; +import Link from 'gatsby-link'; + +export default ({ transition }) => ( +
+

🤫

+
+); diff --git a/src/pages/4.js b/src/pages/4.js new file mode 100644 index 0000000..e2536ae --- /dev/null +++ b/src/pages/4.js @@ -0,0 +1,11 @@ +import React from 'react'; +import Link from 'gatsby-link'; + +export default ({ transition }) => ( +
+ Monkey +

+ Star it on GitHub 🌟 +

+
+); diff --git a/src/pages/404.js b/src/pages/404.js new file mode 100644 index 0000000..a091a00 --- /dev/null +++ b/src/pages/404.js @@ -0,0 +1,10 @@ +import React from 'react' + +const NotFoundPage = () => ( +
+

NOT FOUND

+

You just hit a route that doesn't exist... the sadness.

+
+) + +export default NotFoundPage diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 0000000..c8b2828 --- /dev/null +++ b/src/pages/index.js @@ -0,0 +1,12 @@ +import React, { Component } from 'react'; +import { navigateTo } from 'gatsby-link'; + +class Index extends Component { + componentDidMount() { + navigateTo(`/1`); + } + render() { + return
; + } +} +export default Index; diff --git a/src/utils/getTransitionStyle.js b/src/utils/getTransitionStyle.js new file mode 100644 index 0000000..1b3b46e --- /dev/null +++ b/src/utils/getTransitionStyle.js @@ -0,0 +1,20 @@ +const getTransitionStyles = timeout => { + return { + entering: { + opacity: 0, + }, + entered: { + transition: `opacity ${timeout}ms cubic-bezier(0.645, 0.045, 0.355, 1)`, + opacity: 1, + }, + exiting: { + transition: `opacity ${timeout}ms cubic-bezier(0.645, 0.045, 0.355, 1)`, + opacity: 0, + }, + }; +}; + +const getTransitionStyle = ({ timeout, status }) => + getTransitionStyles(timeout)[status]; + +export default getTransitionStyle;