├── .dockerignore ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── Dockerfile ├── LICENSE ├── README.md ├── config.js ├── content ├── codeblock.md ├── codeblock │ └── 1-index.md ├── index.mdx └── introduction.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── netlify.toml ├── package-lock.json ├── package.json └── src ├── CommunityAuthor.js ├── GithubLink.js ├── YoutubeEmbed.js ├── components ├── DarkModeSwitch.js ├── Header.js ├── NextPrevious.js ├── images │ ├── closed.js │ ├── day.png │ ├── discord-brands-block.svg │ ├── github.svg │ ├── help.svg │ ├── logo.svg │ ├── night.png │ ├── opened.js │ ├── twitter-brands-block.svg │ └── twitter.svg ├── index.js ├── layout.js ├── link.js ├── mdxComponents │ ├── LiveProvider.js │ ├── anchor.js │ ├── codeBlock.js │ ├── index.js │ └── loading.js ├── rightSidebar.js ├── search │ ├── hitComps.js │ ├── index.js │ ├── input.js │ └── styles.js ├── sidebar │ ├── index.js │ ├── tree.js │ └── treeNode.js ├── styles │ ├── Docs.js │ ├── GlobalStyles.js │ ├── PageNavigationButtons.js │ └── Sidebar.js ├── theme.js ├── theme │ ├── index.js │ └── themeProvider.js └── themeProvider.js ├── custom-sw-code.js ├── custom ├── config │ └── codeBlockLanguages.js └── styles │ └── styles.js ├── html.js ├── pwa-512.png ├── templates └── docs.js └── utils └── algolia.js /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | .cache 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:import/errors", 5 | "plugin:react/recommended", 6 | "plugin:jsx-a11y/recommended", 7 | "prettier", 8 | "prettier/react" 9 | ], 10 | "plugins": ["react", "import", "jsx-a11y"], 11 | "settings": { 12 | "react": { 13 | "version": "detect" 14 | } 15 | }, 16 | "rules": { 17 | "react/prop-types": 0, 18 | "react/react-in-jsx-scope": "off", 19 | "lines-between-class-members": ["error", "always"], 20 | "padding-line-between-statements": [ 21 | "error", 22 | { "blankLine": "always", "prev": ["const", "let", "var"], "next": "*" }, 23 | { 24 | "blankLine": "always", 25 | "prev": ["const", "let", "var"], 26 | "next": ["const", "let", "var"] 27 | }, 28 | { "blankLine": "always", "prev": "directive", "next": "*" }, 29 | { "blankLine": "any", "prev": "directive", "next": "directive" } 30 | ] 31 | }, 32 | "parser": "babel-eslint", 33 | "parserOptions": { 34 | "ecmaVersion": 10, 35 | "sourceType": "module", 36 | "ecmaFeatures": { 37 | "jsx": true 38 | } 39 | }, 40 | "env": { 41 | "es6": true, 42 | "browser": true, 43 | "node": true 44 | }, 45 | "globals": { 46 | "graphql": false 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | public 2 | .cache 3 | node_modules 4 | *DS_Store 5 | *.env 6 | 7 | .idea/ 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "jsxBracketSameLine": false, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:buster 2 | 3 | # Create app directory 4 | WORKDIR /app 5 | 6 | ENV NODE_OPTIONS=--openssl-legacy-provider 7 | 8 | # Install app dependencies 9 | # RUN npm -g install serve 10 | RUN npm -g install gatsby-cli 11 | 12 | COPY package*.json ./ 13 | 14 | RUN npm ci 15 | 16 | # Bundle app source 17 | COPY . . 18 | 19 | # Build static files 20 | RUN npm run build 21 | 22 | # serve on port 8080 23 | # CMD ["serve", "-l", "tcp://0.0.0.0:8080", "public"] 24 | CMD ["gatsby", "serve", "--verbose", "--prefix-paths", "-p", "8080", "--host", "0.0.0.0"] 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hasura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gatsby-gitbook-starter 2 | 3 | Kick off your project with this starter to create a powerful/flexible docs/tutorial web apps. 4 | 5 | ![gatsby-gitbook-starter](https://graphql-engine-cdn.hasura.io/learn-hasura/gatsby-gitbook-starter/assets/documentation_app_blog.png) 6 | 7 | ## Motivation 8 | 9 | We wanted to create a [GraphQL tutorial](https://learn.hasura.io) series. The content would be written by developers for various languages/frameworks and what better than writing it in Markdown! And since this is a tutorial series we also needed rich embeds, syntax highlighting and more customisations. 10 | 11 | We also wanted to serve these tutorials in sub paths of [learn.hasura.io](https://learn.hasura.io). To serve all these requirements, we decided to use Gatsby + MDX (Markdown + JSX) to extend markdown and used a neat consistent theme like the one at [GitBook](https://www.gitbook.com) and deployed as docker containers. 12 | 13 | ## 🔥 Features 14 | - Write using Markdown / [MDX](https://github.com/mdx-js/mdx) 15 | - GitBook style theme 16 | - Syntax Highlighting using Prism [`Bonus`: Code diff highlighting] 17 | - Search Integration with Algolia 18 | - Progressive Web App, Works Offline 19 | - Google Analytics Integration 20 | - Automatically generated sidebar navigation, table of contents, previous/next 21 | - Dark Mode toggle 22 | - Edit on Github 23 | - Fully customisable 24 | - Rich embeds and live code editor using MDX 25 | - Easy deployment: Deploy on Netlify / Now.sh / Docker 26 | 27 | ## 🔗 Live Demo 28 | 29 | Here's a [live demo](https://learn.hasura.io/graphql/react) 30 | 31 | ## 🚀 Quickstart 32 | 33 | Get started by running the following commands: 34 | 35 | ``` 36 | $ git clone git@github.com:hasura/gatsby-gitbook-starter.git 37 | $ cd gatsby-gitbook-starter 38 | $ npm install 39 | $ npm start 40 | ``` 41 | 42 | Visit `http://localhost:8000/` to view the app. 43 | 44 | ## 🔧 Configure 45 | 46 | Write markdown files in `content` folder. 47 | 48 | Open `config.js` for templating variables. Broadly configuration is available for `gatsby`, `header`, `sidebar` and `siteMetadata`. 49 | 50 | - `gatsby` config for global configuration like 51 | - `pathPrefix` - Gatsby Path Prefix 52 | - `siteUrl` - Gatsby Site URL 53 | - `gaTrackingId` - Google Analytics Tracking ID 54 | 55 | - `header` config for site header configuration like 56 | - `title` - The title that appears on the top left 57 | - `githubUrl` - The Github URL for the docs website 58 | - `helpUrl` - Help URL for pointing to resources 59 | - `tweetText` - Tweet text 60 | - `links` - Links on the top right 61 | - `search` - Enable search and [configure Algolia](https://www.gatsbyjs.org/docs/adding-search-with-algolia/) 62 | 63 | - `sidebar` config for navigation links configuration 64 | - `forcedNavOrder` for left sidebar navigation order. It should be in the format "/\" 65 | - `frontLine` - whether to show a front line at the beginning of a nested menu.(Collapsing capability would be turned of if this option is set to true) 66 | - `links` - Links on the bottom left of the sidebar 67 | - `ignoreIndex` - Set this to true if the index.md file shouldn't appear on the left sidebar navigation. Typically this can be used for landing pages. 68 | 69 | - `siteMetadata` config for website related configuration 70 | - `title` - Title of the website 71 | - `description` - Description of the website 72 | - `ogImage` - Social Media share og:image tag 73 | - `docsLocation` - The Github URL for Edit on Github 74 | 75 | - For sub nesting in left sidebar, create a folder with the same name as the top level `.md` filename and the sub navigation is auto-generated. The sub navigation is alphabetically ordered. 76 | 77 | ### Algolia Configuration 78 | 79 | To setup Algolia, go to `config.js` and update the `search` object to look like the one below: 80 | 81 | ```..., 82 | "search": { 83 | "enabled": true, 84 | "indexName": "MY_INDEX_NAME", 85 | "algoliaAppId": process.env.GATSBY_ALGOLIA_APP_ID, 86 | "algoliaSearchKey": process.env.GATSBY_ALGOLIA_SEARCH_KEY, 87 | "algoliaAdminKey": process.env.ALGOLIA_ADMIN_KEY 88 | }, 89 | ``` 90 | 91 | Values for Algolia App ID, Search Key, and Admin Key can be obtained from Algolia Dashboard with the right set of permissions. Replace `MY_INDEX_NAME` with the Algolia Index name of your choice. To build the Algolia index, you need to run `npm run build` which will do a gatsby build along with content indexing in Algolia. 92 | 93 | ### Progressive Web App, Offline 94 | 95 | To enable PWA, go to `config.js` and update the `pwa` object to look like the one below: 96 | 97 | ``` 98 | "pwa": { 99 | "enabled": false, // disabling this will also remove the existing service worker. 100 | "manifest": { 101 | "name": "Gatsby Gitbook Starter", 102 | "short_name": "GitbookStarter", 103 | "start_url": "/", 104 | "background_color": "#6b37bf", 105 | "theme_color": "#6b37bf", 106 | "display": "standalone", 107 | "crossOrigin": "use-credentials", 108 | icons: [ 109 | { 110 | src: "src/pwa-512.png", 111 | sizes: `512x512`, 112 | type: `image/png`, 113 | }, 114 | ], 115 | }, 116 | } 117 | ``` 118 | 119 | ## Live Code Editor 120 | 121 | To render react components for live editing, add the `react-live=true` to the code section. For example: 122 | 123 | ```javascript react-live=true 124 | 125 | ``` 126 | 127 | In the above code, just add `javascript react-live=true` after the triple quote ``` to start rendering react components that can be edited by users. 128 | 129 | ## 🤖 SEO friendly 130 | 131 | This is a static site and comes with all the SEO benefits. Configure meta tags like title and description for each markdown file using MDX Frontmatter 132 | 133 | ```markdown 134 | --- 135 | title: "Title of the page" 136 | metaTitle: "Meta Title Tag for this page" 137 | metaDescription: "Meta Description Tag for this page" 138 | --- 139 | ``` 140 | 141 | Canonical URLs are generated automatically. 142 | 143 | ## ☁️ Deploy 144 | 145 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/hasura/gatsby-gitbook-starter) 146 | 147 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | gatsby: { 3 | pathPrefix: '/', 4 | siteUrl: 'https://hasura.io', 5 | gaTrackingId: null, 6 | trailingSlash: false, 7 | }, 8 | header: { 9 | logo: 'https://graphql-engine-cdn.hasura.io/learn-hasura/assets/homepage/brand.svg', 10 | logoLink: 'https://hasura.io/learn/', 11 | title: 12 | "Learn logo", 13 | githubUrl: 'https://github.com/hasura/gatsby-gitbook-boilerplate', 14 | helpUrl: '', 15 | tweetText: '', 16 | social: `
  • 17 | 18 | 21 | 22 |
  • 23 |
  • 24 | 25 |
    26 | {'Discord'}/ 27 |
    28 |
    29 |
  • `, 30 | links: [{ text: '', link: '' }], 31 | search: { 32 | enabled: false, 33 | indexName: '', 34 | algoliaAppId: process.env.GATSBY_ALGOLIA_APP_ID, 35 | algoliaSearchKey: process.env.GATSBY_ALGOLIA_SEARCH_KEY, 36 | algoliaAdminKey: process.env.ALGOLIA_ADMIN_KEY, 37 | }, 38 | }, 39 | sidebar: { 40 | forcedNavOrder: [ 41 | '/introduction', // add trailing slash if enabled above 42 | '/codeblock', 43 | ], 44 | collapsedNav: [ 45 | '/codeblock', // add trailing slash if enabled above 46 | ], 47 | links: [{ text: 'Hasura', link: 'https://hasura.io' }], 48 | frontLine: false, 49 | ignoreIndex: true, 50 | title: 51 | "graphql
    react", 52 | }, 53 | siteMetadata: { 54 | title: 'Gatsby Gitbook Boilerplate | Hasura', 55 | description: 'Documentation built with mdx. Powering hasura.io/learn ', 56 | ogImage: null, 57 | docsLocation: 'https://github.com/hasura/gatsby-gitbook-boilerplate/tree/master/content', 58 | favicon: 'https://graphql-engine-cdn.hasura.io/img/hasura_icon_black.svg', 59 | }, 60 | pwa: { 61 | enabled: false, // disabling this will also remove the existing service worker. 62 | manifest: { 63 | name: 'Gatsby Gitbook Starter', 64 | short_name: 'GitbookStarter', 65 | start_url: '/', 66 | background_color: '#6b37bf', 67 | theme_color: '#6b37bf', 68 | display: 'standalone', 69 | crossOrigin: 'use-credentials', 70 | icons: [ 71 | { 72 | src: 'src/pwa-512.png', 73 | sizes: `512x512`, 74 | type: `image/png`, 75 | }, 76 | ], 77 | }, 78 | }, 79 | }; 80 | 81 | module.exports = config; 82 | -------------------------------------------------------------------------------- /content/codeblock.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Syntax Highlighting" 3 | metaTitle: "Syntax Highlighting is the meta title tag for this page" 4 | metaDescription: "This is the meta description for this page" 5 | --- 6 | 7 | The following is a code block with JavaScript language syntax highlighting. 8 | 9 | ```javascript 10 | import React from 'react'; 11 | ``` 12 | 13 | Supports multiple languages. 14 | 15 | The following is a code block with diff. Lines with `+` highlighted in green shade indicating an addition. Lines with `-` highlighted in red shade indicating a deletion. 16 | 17 | ```javascript 18 | - const data = ['1','2']; 19 | + const data = [1,2]; 20 | ``` 21 | 22 | ## Live Editing example 23 | 24 | ```javascript react-live=true 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /content/codeblock/1-index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sub Page" 3 | metaTitle: "Syntax Highlighting is the meta title tag for this page" 4 | metaDescription: "This is the meta description for this page" 5 | --- 6 | 7 | The following is a code block with JavaScript language syntax highlighting. 8 | 9 | ```javascript 10 | import React from 'react'; 11 | ``` 12 | 13 | Supports multiple languages. 14 | 15 | The following is a code block with diff. Lines with `+` highlighted in green shade indicating an addition. Lines with `-` highlighted in red shade indicating a deletion. 16 | 17 | ```javascript 18 | - const data = ['1','2']; 19 | + const data = [1,2]; 20 | ``` 21 | 22 | ## Live Editing example 23 | 24 | ```javascript react-live=true 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /content/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Landing Page" 3 | --- 4 | 5 | Some introduction text. Lists out all the headings from h1 to h6. Markdown link handling for relative and absolute URLs. Easy to customise. 6 | 7 | # Heading H1 8 | Heading 1 text 9 | 10 | ## Heading H2 11 | Heading 2 text 12 | 13 | ### Heading H3 14 | Heading 3 text 15 | 16 | #### Heading H4 17 | Heading 4 text 18 | 19 | ##### Heading H5 20 | Heading 5 text 21 | 22 | ###### Heading H6 23 | Heading 6 text 24 | 25 | ## Lists 26 | - Item 1 27 | - Item 2 28 | - Item 3 29 | - Item 4 30 | - Item 5 31 | 32 | ## Links 33 | 34 | * Relative: [Codeblock](/codeblock) 35 | * Absolute: [Demo](https://learn.hasura.io/graphql/react) -------------------------------------------------------------------------------- /content/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction" 3 | metaTitle: "This is the title tag of this page" 4 | metaDescription: "This is the meta description" 5 | --- 6 | 7 | Some introduction text. Lists out all the headings from h1 to h6. Markdown link handling for relative and absolute URLs. Easy to customise. 8 | 9 | # Heading H1 10 | Heading 1 text 11 | 12 | ## Heading H2 13 | Heading 2 text 14 | 15 | ### Heading H3 16 | Heading 3 text 17 | 18 | #### Heading H4 19 | Heading 4 text 20 | 21 | ##### Heading H5 22 | Heading 5 text 23 | 24 | ###### Heading H6 25 | Heading 6 text 26 | 27 | ## Lists 28 | - Item 1 29 | - Item 2 30 | - Item 3 31 | - Item 4 32 | - Item 5 33 | 34 | ## Links 35 | 36 | * Relative: [Codeblock](/codeblock) 37 | * Absolute: [Demo](https://learn.hasura.io/graphql/react) -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | export const onServiceWorkerUpdateReady = () => { 2 | const answer = window.confirm( 3 | `This tutorial has been updated. ` + 4 | `Reload to display the latest version?` 5 | ) 6 | if (answer === true) { 7 | window.location.reload() 8 | } 9 | } -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const queries = require("./src/utils/algolia"); 3 | const config = require("./config"); 4 | const plugins = [ 5 | 'gatsby-plugin-sitemap', 6 | 'gatsby-plugin-sharp', 7 | { 8 | resolve: `gatsby-plugin-layout`, 9 | options: { 10 | component: require.resolve(`./src/templates/docs.js`) 11 | } 12 | }, 13 | 'gatsby-plugin-emotion', 14 | 'gatsby-plugin-react-helmet', 15 | { 16 | resolve: "gatsby-source-filesystem", 17 | options: { 18 | name: "docs", 19 | path: `${__dirname}/content/` 20 | } 21 | }, 22 | { 23 | resolve: 'gatsby-plugin-mdx', 24 | options: { 25 | gatsbyRemarkPlugins: [ 26 | { 27 | resolve: "gatsby-remark-images", 28 | options: { 29 | maxWidth: 1035, 30 | sizeByPixelDensity: true 31 | } 32 | }, 33 | { 34 | resolve: 'gatsby-remark-copy-linked-files' 35 | } 36 | ], 37 | extensions: [".mdx", ".md"] 38 | } 39 | }, 40 | { 41 | resolve: `gatsby-plugin-gtag`, 42 | options: { 43 | // your google analytics tracking id 44 | trackingId: config.gatsby.gaTrackingId, 45 | // Puts tracking script in the head instead of the body 46 | head: true, 47 | // enable ip anonymization 48 | anonymize: false, 49 | }, 50 | }, 51 | ]; 52 | // check and add algolia 53 | if (config.header.search && config.header.search.enabled && config.header.search.algoliaAppId && config.header.search.algoliaAdminKey) { 54 | plugins.push({ 55 | resolve: `gatsby-plugin-algolia`, 56 | options: { 57 | appId: config.header.search.algoliaAppId, // algolia application id 58 | apiKey: config.header.search.algoliaAdminKey, // algolia admin key to index 59 | queries, 60 | chunkSize: 10000, // default: 1000 61 | }} 62 | ) 63 | } 64 | // check and add pwa functionality 65 | if (config.pwa && config.pwa.enabled && config.pwa.manifest) { 66 | plugins.push({ 67 | resolve: `gatsby-plugin-manifest`, 68 | options: {...config.pwa.manifest}, 69 | }); 70 | plugins.push({ 71 | resolve: 'gatsby-plugin-offline', 72 | options: { 73 | appendScript: require.resolve(`./src/custom-sw-code.js`), 74 | }, 75 | }); 76 | } else { 77 | plugins.push('gatsby-plugin-remove-serviceworker'); 78 | } 79 | 80 | // check and remove trailing slash 81 | if (config.gatsby && !config.gatsby.trailingSlash) { 82 | plugins.push('gatsby-plugin-remove-trailing-slashes'); 83 | } 84 | 85 | module.exports = { 86 | pathPrefix: config.gatsby.pathPrefix, 87 | siteMetadata: { 88 | title: config.siteMetadata.title, 89 | description: config.siteMetadata.description, 90 | docsLocation: config.siteMetadata.docsLocation, 91 | ogImage: config.siteMetadata.ogImage, 92 | favicon: config.siteMetadata.favicon, 93 | logo: { link: config.header.logoLink ? config.header.logoLink : '/', image: config.header.logo }, // backwards compatible 94 | headerTitle: config.header.title, 95 | githubUrl: config.header.githubUrl, 96 | helpUrl: config.header.helpUrl, 97 | tweetText: config.header.tweetText, 98 | headerLinks: config.header.links, 99 | siteUrl: config.gatsby.siteUrl, 100 | }, 101 | plugins: plugins, 102 | flags: { 103 | DEV_SSR: false, 104 | FAST_DEV: false, // Enable all experiments aimed at improving develop server start time 105 | PRESERVE_WEBPACK_CACHE: false, // (Umbrella Issue (https://gatsby.dev/cache-clearing-feedback)) · Use webpack's persistent caching and don't delete webpack's cache when changing gatsby-node.js & gatsby-config.js files. 106 | PRESERVE_FILE_DOWNLOAD_CACHE: false, // (Umbrella Issue (https://gatsby.dev/cache-clearing-feedback)) · Don't delete the downloaded files cache when changing gatsby-node.js & gatsby-config.js files. 107 | PARALLEL_SOURCING: false, // EXPERIMENTAL · (Umbrella Issue (https://gatsby.dev/parallel-sourcing-feedback)) · Run all source plugins at the same time instead of serially. For sites with multiple source plugins, this can speedup sourcing and transforming considerably. 108 | FUNCTIONS: false // EXPERIMENTAL · (Umbrella Issue (https://gatsby.dev/functions-feedback)) · Compile Serverless functions in your Gatsby project and write them to disk, ready to deploy to Gatsby Cloud 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const componentWithMDXScope = require('gatsby-plugin-mdx/component-with-mdx-scope'); 2 | 3 | const path = require('path'); 4 | 5 | const startCase = require('lodash.startcase'); 6 | 7 | const config = require('./config'); 8 | 9 | exports.createPages = ({ graphql, actions }) => { 10 | const { createPage } = actions; 11 | 12 | return new Promise((resolve, reject) => { 13 | resolve( 14 | graphql( 15 | ` 16 | { 17 | allMdx { 18 | edges { 19 | node { 20 | fields { 21 | id 22 | } 23 | tableOfContents 24 | fields { 25 | slug 26 | } 27 | } 28 | } 29 | } 30 | } 31 | ` 32 | ).then(result => { 33 | if (result.errors) { 34 | console.log(result.errors); // eslint-disable-line no-console 35 | reject(result.errors); 36 | } 37 | 38 | // Create blog posts pages. 39 | result.data.allMdx.edges.forEach(({ node }) => { 40 | createPage({ 41 | path: node.fields.slug ? node.fields.slug : '/', 42 | component: path.resolve('./src/templates/docs.js'), 43 | context: { 44 | id: node.fields.id, 45 | }, 46 | }); 47 | }); 48 | }) 49 | ); 50 | }); 51 | }; 52 | 53 | exports.onCreateWebpackConfig = ({ actions }) => { 54 | actions.setWebpackConfig({ 55 | resolve: { 56 | modules: [path.resolve(__dirname, 'src'), 'node_modules'], 57 | alias: { 58 | $components: path.resolve(__dirname, 'src/components'), 59 | buble: '@philpl/buble', // to reduce bundle size 60 | }, 61 | }, 62 | }); 63 | }; 64 | 65 | exports.onCreateBabelConfig = ({ actions }) => { 66 | actions.setBabelPlugin({ 67 | name: '@babel/plugin-proposal-export-default-from', 68 | }); 69 | }; 70 | 71 | exports.onCreateNode = ({ node, getNode, actions }) => { 72 | const { createNodeField } = actions; 73 | 74 | if (node.internal.type === `Mdx`) { 75 | const parent = getNode(node.parent); 76 | 77 | let value = parent.relativePath.replace(parent.ext, ''); 78 | 79 | if (value === 'index') { 80 | value = ''; 81 | } 82 | 83 | if (config.gatsby && config.gatsby.trailingSlash) { 84 | createNodeField({ 85 | name: `slug`, 86 | node, 87 | value: value === '' ? `/` : `/${value}/`, 88 | }); 89 | } else { 90 | createNodeField({ 91 | name: `slug`, 92 | node, 93 | value: `/${value}`, 94 | }); 95 | } 96 | 97 | createNodeField({ 98 | name: 'id', 99 | node, 100 | value: node.id, 101 | }); 102 | 103 | createNodeField({ 104 | name: 'title', 105 | node, 106 | value: node.frontmatter.title || startCase(parent.name), 107 | }); 108 | } 109 | }; 110 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "public" 3 | command = "npm run build" 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-gitbook-boilerplate", 3 | "private": true, 4 | "description": "Documentation, built with mdx", 5 | "author": "Praveen (@praveenweb)", 6 | "version": "0.0.1", 7 | "dependencies": { 8 | "@babel/plugin-proposal-export-default-from": "^7.12.13", 9 | "@emotion/react": "^11.1.5", 10 | "@emotion/styled": "^11.3.0", 11 | "@emotion/styled-base": "^11.0.0", 12 | "@mdx-js/loader": "^1.6.22", 13 | "@mdx-js/mdx": "^1.6.22", 14 | "@mdx-js/react": "^1.6.22", 15 | "@philpl/buble": "^0.19.7", 16 | "@playlyfe/gql": "^2.6.2", 17 | "@styled-icons/fa-brands": "^10.26.0", 18 | "@styled-icons/fa-solid": "^10.32.0", 19 | "algoliasearch": "^4.9.3", 20 | "dotenv": "^8.5.1", 21 | "emotion": "^11.0.0", 22 | "emotion-server": "^11.0.0", 23 | "gatsby": "^4.0.2", 24 | "gatsby-link": "^4.0.0", 25 | "gatsby-plugin-algolia": "^0.19.0", 26 | "gatsby-plugin-emotion": "^7.0.0", 27 | "gatsby-plugin-gtag": "^1.0.13", 28 | "gatsby-plugin-layout": "^3.0.0", 29 | "gatsby-plugin-manifest": "^4.0.0", 30 | "gatsby-plugin-mdx": "^3.15.2", 31 | "gatsby-plugin-offline": "^5.0.0", 32 | "gatsby-plugin-react-helmet": "^5.0.0", 33 | "gatsby-plugin-remove-serviceworker": "^1.0.0", 34 | "gatsby-plugin-sharp": "^4.0.1", 35 | "gatsby-plugin-sitemap": "^5.0.0", 36 | "gatsby-remark-copy-linked-files": "^5.0.0", 37 | "gatsby-remark-images": "^6.0.0", 38 | "gatsby-source-filesystem": "^4.0.0", 39 | "gatsby-transformer-remark": "^5.0.0", 40 | "graphql": "^15.5.3", 41 | "is-absolute-url": "^3.0.3", 42 | "lodash.flatten": "^4.4.0", 43 | "lodash.startcase": "^4.4.0", 44 | "prismjs": "^1.27.0", 45 | "react": "^17.0.2", 46 | "react-dom": "^17.0.2", 47 | "react-feather": "^2.0.10", 48 | "react-github-btn": "^1.2.2", 49 | "react-helmet": "^6.1.0", 50 | "react-id-generator": "^3.0.2", 51 | "react-instantsearch-dom": "^6.11.2", 52 | "react-live": "^2.2.3", 53 | "react-loadable": "^5.5.0", 54 | "styled-components": "^5.3.6" 55 | }, 56 | "license": "MIT", 57 | "main": "n/a", 58 | "scripts": { 59 | "start": "NODE_OPTIONS=--openssl-legacy-provider gatsby develop", 60 | "build": "gatsby build --prefix-paths", 61 | "format": "prettier --write \"src/**/*.{js,jsx,css,json}\"", 62 | "lint": "eslint --fix \"src/**/*.{js,jsx}\"", 63 | "develop": "NODE_OPTIONS=--openssl-legacy-provider gatsby develop", 64 | "serve": "gatsby serve", 65 | "clean": "gatsby clean", 66 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1" 67 | }, 68 | "devDependencies": { 69 | "babel-eslint": "^10.1.0", 70 | "eslint": "^7.25.0", 71 | "eslint-config-prettier": "^8.3.0", 72 | "eslint-plugin-import": "^2.22.1", 73 | "eslint-plugin-jsx-a11y": "^6.4.1", 74 | "eslint-plugin-prettier": "^3.4.1", 75 | "eslint-plugin-react": "^7.23.2", 76 | "gatsby-plugin-remove-trailing-slashes": "^3.4.0", 77 | "prettier": "^2.2.1", 78 | "prism-react-renderer": "^1.2.1" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/CommunityAuthor.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const CommunityAuthor = ({ name, imageUrl, twitterUrl, githubUrl, description }) => { 4 | return ( 5 | <> 6 |

    About the community author

    7 |
    8 |
    9 | {name} 10 |
    11 |
    12 |
    13 | {name} 14 | {twitterUrl ? ( 15 | 16 | Twitter Icon 21 | 22 | ) : null} 23 | {githubUrl ? ( 24 | 25 | Github Icon 30 | 31 | ) : null} 32 |
    33 |
    {description}
    34 |
    35 |
    36 | 37 | ); 38 | }; 39 | 40 | export default CommunityAuthor; 41 | -------------------------------------------------------------------------------- /src/GithubLink.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const githubIcon = require('./components/images/github.svg').default; 3 | 4 | const GithubLink = ({ link, text }) => { 5 | return ( 6 | 7 | github 8 | {text} 9 | 10 | ); 11 | }; 12 | 13 | export default GithubLink; 14 | -------------------------------------------------------------------------------- /src/YoutubeEmbed.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const YoutubeEmbed = ({ link }) => { 4 | return ( 5 |
    6 | 15 |
    16 | ); 17 | }; 18 | 19 | export default YoutubeEmbed; 20 | -------------------------------------------------------------------------------- /src/components/DarkModeSwitch.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from '@emotion/styled'; 3 | 4 | import NightImage from './images/night.png'; 5 | import DayImage from './images/day.png'; 6 | 7 | const StyledSwitch = styled('div')` 8 | display: flex; 9 | justify-content: flex-end; 10 | width: 100%; 11 | padding: 0 20px 0 25px; 12 | 13 | /* The switch - the box around the slider */ 14 | .switch { 15 | position: relative; 16 | display: inline-block; 17 | width: 50px; 18 | height: 20px; 19 | } 20 | 21 | /* Hide default HTML checkbox */ 22 | .switch input { 23 | opacity: 0; 24 | width: 0; 25 | height: 0; 26 | } 27 | 28 | /* The slider */ 29 | .slider { 30 | position: absolute; 31 | cursor: pointer; 32 | top: 0; 33 | left: 0; 34 | right: 0; 35 | bottom: 0; 36 | background-color: #ccc; 37 | -webkit-transition: 0.4s; 38 | transition: 0.4s; 39 | } 40 | 41 | .slider:before { 42 | position: absolute; 43 | content: ''; 44 | height: 30px; 45 | width: 30px; 46 | left: 0px; 47 | bottom: 4px; 48 | top: 0; 49 | bottom: 0; 50 | margin: auto 0; 51 | -webkit-transition: 0.4s; 52 | transition: 0.4s; 53 | box-shadow: 0 0px 15px #2020203d; 54 | background: white url(${NightImage}); 55 | background-repeat: no-repeat; 56 | background-position: center; 57 | } 58 | 59 | input:checked + .slider { 60 | background: linear-gradient(to right, #fefb72, #f0bb31); 61 | } 62 | 63 | input:checked + .slider:before { 64 | -webkit-transform: translateX(24px); 65 | -ms-transform: translateX(24px); 66 | transform: translateX(24px); 67 | background: white url(${DayImage}); 68 | background-repeat: no-repeat; 69 | background-position: center; 70 | } 71 | 72 | /* Rounded sliders */ 73 | .slider.round { 74 | border-radius: 34px; 75 | } 76 | 77 | .slider.round:before { 78 | border-radius: 50%; 79 | } 80 | `; 81 | 82 | export const DarkModeSwitch = ({ isDarkThemeActive, toggleActiveTheme }) => ( 83 | 84 | 93 | 94 | ); 95 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from '@emotion/styled'; 3 | import { StaticQuery, graphql } from 'gatsby'; 4 | import GitHubButton from 'react-github-btn'; 5 | import Link from './link'; 6 | import Loadable from 'react-loadable'; 7 | 8 | import config from '../../config.js'; 9 | import LoadingProvider from './mdxComponents/loading'; 10 | import { DarkModeSwitch } from './DarkModeSwitch'; 11 | 12 | const help = require('./images/help.svg'); 13 | 14 | const isSearchEnabled = config.header.search && config.header.search.enabled ? true : false; 15 | 16 | let searchIndices = []; 17 | 18 | if (isSearchEnabled && config.header.search.indexName) { 19 | searchIndices.push({ 20 | name: `${config.header.search.indexName}`, 21 | title: `Results`, 22 | hitComp: `PageHit`, 23 | }); 24 | } 25 | 26 | import Sidebar from './sidebar'; 27 | 28 | const LoadableComponent = Loadable({ 29 | loader: () => import('./search/index'), 30 | loading: LoadingProvider, 31 | }); 32 | 33 | function myFunction() { 34 | var x = document.getElementById('navbar'); 35 | 36 | if (x.className === 'topnav') { 37 | x.className += ' responsive'; 38 | } else { 39 | x.className = 'topnav'; 40 | } 41 | } 42 | 43 | const StyledBgDiv = styled('div')` 44 | height: 60px; 45 | box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16); 46 | background-color: #f8f8f8; 47 | position: relative; 48 | display: none; 49 | background: ${(props) => (props.isDarkThemeActive ? '#001932' : undefined)}; 50 | 51 | @media (max-width: 767px) { 52 | display: block; 53 | } 54 | `; 55 | 56 | const Header = ({ location, isDarkThemeActive, toggleActiveTheme }) => ( 57 | { 79 | const logoImg = require('./images/logo.svg'); 80 | 81 | const twitter = require('./images/twitter.svg'); 82 | 83 | const discordBrandsBlock = require('./images/discord-brands-block.svg'); 84 | 85 | const twitterBrandsBlock = require('./images/twitter-brands-block.svg'); 86 | 87 | const { 88 | site: { 89 | siteMetadata: { headerTitle, githubUrl, helpUrl, tweetText, logo, headerLinks }, 90 | }, 91 | } = data; 92 | 93 | const finalLogoLink = logo.link !== '' ? logo.link : 'https://hasura.io/'; 94 | 95 | return ( 96 |
    97 | 193 | 194 |
    195 | 202 | 203 | 204 | 205 | 206 |
    207 | {isSearchEnabled ? ( 208 |
    209 | 210 |
    211 | ) : null} 212 |
    213 |
    214 | ); 215 | }} 216 | /> 217 | ); 218 | 219 | export default Header; 220 | -------------------------------------------------------------------------------- /src/components/NextPrevious.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Link from './link'; 3 | 4 | import { StyledNextPrevious } from './styles/PageNavigationButtons'; 5 | 6 | const NextPrevious = ({ mdx, nav }) => { 7 | let currentIndex; 8 | 9 | const currentPaginationInfo = nav.map((el, index) => { 10 | if (el && el.url === mdx.fields.slug) { 11 | currentIndex = index; 12 | } 13 | }); 14 | 15 | const nextInfo = {}; 16 | 17 | const previousInfo = {}; 18 | 19 | if (currentIndex === undefined) { 20 | // index 21 | if (nav[0]) { 22 | nextInfo.url = nav[0].url; 23 | nextInfo.title = nav[0].title; 24 | } 25 | previousInfo.url = null; 26 | previousInfo.title = null; 27 | currentIndex = -1; 28 | } else if (currentIndex === 0) { 29 | // first page 30 | nextInfo.url = nav[currentIndex + 1] ? nav[currentIndex + 1].url : null; 31 | nextInfo.title = nav[currentIndex + 1] ? nav[currentIndex + 1].title : null; 32 | previousInfo.url = null; 33 | previousInfo.title = null; 34 | } else if (currentIndex === nav.length - 1) { 35 | // last page 36 | nextInfo.url = null; 37 | nextInfo.title = null; 38 | previousInfo.url = nav[currentIndex - 1] ? nav[currentIndex - 1].url : null; 39 | previousInfo.title = nav[currentIndex - 1] ? nav[currentIndex - 1].title : null; 40 | } else if (currentIndex) { 41 | // any other page 42 | nextInfo.url = nav[currentIndex + 1].url; 43 | nextInfo.title = nav[currentIndex + 1].title; 44 | if (nav[currentIndex - 1]) { 45 | previousInfo.url = nav[currentIndex - 1].url; 46 | previousInfo.title = nav[currentIndex - 1].title; 47 | } 48 | } 49 | 50 | return ( 51 | 52 | {previousInfo.url && currentIndex >= 0 ? ( 53 | 54 |
    55 | 68 | 69 | 70 | 71 | 72 | 73 |
    74 |
    75 |
    76 | Previous 77 |
    78 |
    79 | {nav[currentIndex - 1].title} 80 |
    81 |
    82 | 83 | ) : null} 84 | {nextInfo.url && currentIndex >= 0 ? ( 85 | 86 |
    87 |
    88 | Next 89 |
    90 |
    91 | {nav[currentIndex + 1] && nav[currentIndex + 1].title} 92 |
    93 |
    94 |
    95 | 108 | 109 | 110 | 111 | 112 | 113 |
    114 | 115 | ) : null} 116 |
    117 | ); 118 | }; 119 | 120 | export default NextPrevious; 121 | -------------------------------------------------------------------------------- /src/components/images/closed.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const ClosedSvg = () => ( 4 | 5 | 6 | 7 | ); 8 | 9 | export default ClosedSvg; 10 | -------------------------------------------------------------------------------- /src/components/images/day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasura/gatsby-gitbook-starter/4eed15e896711e15d466272c087ad57176a7bebd/src/components/images/day.png -------------------------------------------------------------------------------- /src/components/images/discord-brands-block.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/images/help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 11 | 12 | 17 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/components/images/night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasura/gatsby-gitbook-starter/4eed15e896711e15d466272c087ad57176a7bebd/src/components/images/night.png -------------------------------------------------------------------------------- /src/components/images/opened.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const OpenedSvg = () => ( 4 | 5 | 6 | 7 | ); 8 | 9 | export default OpenedSvg; 10 | -------------------------------------------------------------------------------- /src/components/images/twitter-brands-block.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/images/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | import mdxComponents from './mdxComponents'; 3 | import ThemeProvider from './theme/themeProvider'; 4 | import Layout from './layout'; 5 | import Link from './link'; 6 | 7 | export {mdxComponents, ThemeProvider, Layout, Link} 8 | -------------------------------------------------------------------------------- /src/components/layout.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from '@emotion/styled'; 3 | import { MDXProvider } from '@mdx-js/react'; 4 | 5 | import ThemeProvider from './theme/themeProvider'; 6 | import mdxComponents from './mdxComponents'; 7 | import Sidebar from './sidebar'; 8 | import RightSidebar from './rightSidebar'; 9 | import config from '../../config.js'; 10 | 11 | const Wrapper = styled('div')` 12 | display: flex; 13 | justify-content: space-between; 14 | background: ${({ theme }) => theme.colors.background}; 15 | 16 | .sideBarUL li a { 17 | color: ${({ theme }) => theme.colors.text}; 18 | } 19 | 20 | .sideBarUL .item > a:hover { 21 | background-color: #1ed3c6; 22 | color: #fff !important; 23 | 24 | /* background: #F8F8F8 */ 25 | } 26 | 27 | @media only screen and (max-width: 767px) { 28 | display: block; 29 | } 30 | `; 31 | 32 | const Content = styled('main')` 33 | display: flex; 34 | flex-grow: 1; 35 | margin: 0px 88px; 36 | padding-top: 3rem; 37 | background: ${({ theme }) => theme.colors.background}; 38 | 39 | table tr { 40 | background: ${({ theme }) => theme.colors.background}; 41 | } 42 | 43 | @media only screen and (max-width: 1023px) { 44 | padding-left: 0; 45 | margin: 0 10px; 46 | padding-top: 3rem; 47 | } 48 | `; 49 | 50 | const MaxWidth = styled('div')` 51 | @media only screen and (max-width: 50rem) { 52 | width: 100%; 53 | position: relative; 54 | } 55 | `; 56 | 57 | const LeftSideBarWidth = styled('div')` 58 | width: 298px; 59 | `; 60 | 61 | const RightSideBarWidth = styled('div')` 62 | width: 224px; 63 | `; 64 | 65 | const Layout = ({ children, location }) => ( 66 | 67 | 68 | 69 | 70 | 71 | 72 | {config.sidebar.title ? ( 73 |
    77 | ) : null} 78 | 79 | {children} 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | ); 88 | 89 | export default Layout; 90 | -------------------------------------------------------------------------------- /src/components/link.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link as GatsbyLink } from 'gatsby'; 3 | import isAbsoluteUrl from 'is-absolute-url'; 4 | 5 | const Link = ({ to, ...props }) => 6 | isAbsoluteUrl(to) ? ( 7 | 8 | {props.children} 9 | 10 | ) : ( 11 | 12 | ); 13 | 14 | export default Link; 15 | -------------------------------------------------------------------------------- /src/components/mdxComponents/LiveProvider.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live'; 3 | 4 | const ReactLiveProvider = ({ code }) => { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | }; 13 | 14 | export default ReactLiveProvider; 15 | -------------------------------------------------------------------------------- /src/components/mdxComponents/anchor.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link as GatsbyLink } from 'gatsby'; 3 | import isAbsoluteUrl from 'is-absolute-url'; 4 | 5 | const AnchorTag = ({ children: link, ...props }) => { 6 | if (link) { 7 | if (isAbsoluteUrl(props.href)) { 8 | return ( 9 | 10 | {link} 11 | 12 | ); 13 | } else { 14 | return ( 15 | 16 | {link} 17 | 18 | ); 19 | } 20 | } else { 21 | return null; 22 | } 23 | }; 24 | 25 | export default AnchorTag; 26 | -------------------------------------------------------------------------------- /src/components/mdxComponents/codeBlock.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Highlight, { defaultProps, Prism } from 'prism-react-renderer'; 3 | import { applyLanguages, getTheme } from '../../custom/config/codeBlockLanguages'; 4 | import Loadable from 'react-loadable'; 5 | import LoadingProvider from './loading'; 6 | 7 | const theme = getTheme(); 8 | 9 | /** Removes the last token from a code example if it's empty. */ 10 | function cleanTokens(tokens) { 11 | const tokensLength = tokens.length; 12 | 13 | if (tokensLength === 0) { 14 | return tokens; 15 | } 16 | const lastToken = tokens[tokensLength - 1]; 17 | 18 | if (lastToken.length === 1 && lastToken[0].empty) { 19 | return tokens.slice(0, tokensLength - 1); 20 | } 21 | return tokens; 22 | } 23 | 24 | const LoadableComponent = Loadable({ 25 | loader: () => import('./LiveProvider'), 26 | loading: LoadingProvider, 27 | }); 28 | 29 | /* eslint-disable react/jsx-key */ 30 | const CodeBlock = ({ children: exampleCode, ...props }) => { 31 | const [_, updateView] = React.useState(0); 32 | 33 | React.useEffect(() => { 34 | var windowPrism = window.Prism; 35 | window.Prism = Prism; 36 | applyLanguages(Prism); 37 | window.Prism = windowPrism; 38 | updateView({ 39 | data: Date.now() 40 | }); 41 | }, []); 42 | 43 | if (props['react-live']) { 44 | return ; 45 | } else { 46 | return ( 47 | 48 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 49 |
     50 |             {cleanTokens(tokens).map((line, i) => {
     51 |               let lineClass = {};
     52 | 
     53 |               let isDiff = false;
     54 | 
     55 |               if (line[0] && line[0].content.length && line[0].content[0] === '+') {
     56 |                 lineClass = { backgroundColor: 'rgba(76, 175, 80, 0.2)' };
     57 |                 isDiff = true;
     58 |               } else if (line[0] && line[0].content.length && line[0].content[0] === '-') {
     59 |                 lineClass = { backgroundColor: 'rgba(244, 67, 54, 0.2)' };
     60 |                 isDiff = true;
     61 |               } else if (line[0] && line[0].content === '' && line[1] && line[1].content === '+') {
     62 |                 lineClass = { backgroundColor: 'rgba(76, 175, 80, 0.2)' };
     63 |                 isDiff = true;
     64 |               } else if (line[0] && line[0].content === '' && line[1] && line[1].content === '-') {
     65 |                 lineClass = { backgroundColor: 'rgba(244, 67, 54, 0.2)' };
     66 |                 isDiff = true;
     67 |               }
     68 |               const lineProps = getLineProps({ line, key: i });
     69 | 
     70 |               lineProps.style = lineClass;
     71 |               const diffStyle = {
     72 |                 userSelect: 'none',
     73 |                 MozUserSelect: '-moz-none',
     74 |                 WebkitUserSelect: 'none',
     75 |               };
     76 | 
     77 |               let splitToken;
     78 | 
     79 |               return (
     80 |                 
    81 | {line.map((token, key) => { 82 | if (isDiff) { 83 | if ( 84 | (key === 0 || key === 1) & 85 | (token.content.charAt(0) === '+' || token.content.charAt(0) === '-') 86 | ) { 87 | if (token.content.length > 1) { 88 | splitToken = { 89 | types: ['template-string', 'string'], 90 | content: token.content.slice(1), 91 | }; 92 | const firstChar = { 93 | types: ['operator'], 94 | content: token.content.charAt(0), 95 | }; 96 | 97 | return ( 98 | 99 | 103 | 104 | 105 | ); 106 | } else { 107 | return ; 108 | } 109 | } 110 | } 111 | return ; 112 | })} 113 |
    114 | ); 115 | })} 116 |
    117 | )} 118 |
    119 | ); 120 | } 121 | }; 122 | 123 | export default CodeBlock; 124 | -------------------------------------------------------------------------------- /src/components/mdxComponents/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from '@emotion/styled'; 3 | 4 | import CodeBlock from './codeBlock'; 5 | import AnchorTag from './anchor'; 6 | 7 | const StyledPre = styled('pre')` 8 | padding: 16px; 9 | background: ${(props) => props.theme.colors.preFormattedText}; 10 | `; 11 | 12 | const appendString = (children) => { 13 | if (Array.isArray(children)) { 14 | return children.reduce((acc, current) => { 15 | if (typeof current === 'string') { 16 | return acc.concat(current); 17 | } else if (typeof current === 'object') { 18 | return acc.concat(current.props.children); 19 | } else { 20 | return acc; 21 | } 22 | }, ''); 23 | } else { 24 | return children; 25 | } 26 | }; 27 | 28 | export default { 29 | h1: (props) => ( 30 |

    35 | ), 36 | h2: (props) => ( 37 |

    42 | ), 43 | h3: (props) => ( 44 |

    49 | ), 50 | h4: (props) => ( 51 |

    56 | ), 57 | h5: (props) => ( 58 |

    63 | ), 64 | h6: (props) => ( 65 |
    70 | ), 71 | p: (props) =>

    , 72 | pre: (props) => ( 73 | 74 |

    75 |     
    76 |   ),
    77 |   code: CodeBlock,
    78 |   a: AnchorTag,
    79 |   // TODO add `img`
    80 |   // TODO add `blockquote`
    81 |   // TODO add `ul`
    82 |   // TODO add `li`
    83 |   // TODO add `table`
    84 | };
    85 | 
    
    
    --------------------------------------------------------------------------------
    /src/components/mdxComponents/loading.js:
    --------------------------------------------------------------------------------
    1 | import * as React from 'react';
    2 | 
    3 | const LoadingProvider = ({ ...props }) => {
    4 |   return 
    ; 5 | }; 6 | 7 | export default LoadingProvider; 8 | -------------------------------------------------------------------------------- /src/components/rightSidebar.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StaticQuery, graphql } from 'gatsby'; 3 | 4 | // import Link from './link'; 5 | import config from '../../config'; 6 | import { Sidebar, ListItem } from './styles/Sidebar'; 7 | 8 | const SidebarLayout = ({ location }) => ( 9 | { 25 | let navItems = []; 26 | 27 | let finalNavItems; 28 | 29 | if (allMdx.edges !== undefined && allMdx.edges.length > 0) { 30 | const navItems = allMdx.edges.map((item, index) => { 31 | let innerItems; 32 | 33 | if (item !== undefined) { 34 | if ( 35 | item.node.fields.slug === location.pathname || 36 | config.gatsby.pathPrefix + item.node.fields.slug === location.pathname 37 | ) { 38 | if (item.node.tableOfContents.items) { 39 | innerItems = item.node.tableOfContents.items.map((innerItem, index) => { 40 | const itemId = innerItem.title 41 | ? innerItem.title.replace(/\s+/g, '').toLowerCase() 42 | : '#'; 43 | 44 | return ( 45 | 46 | {innerItem.title} 47 | 48 | ); 49 | }); 50 | } 51 | } 52 | } 53 | if (innerItems) { 54 | finalNavItems = innerItems; 55 | } 56 | }); 57 | } 58 | 59 | if (finalNavItems && finalNavItems.length) { 60 | return ( 61 | 62 |
      63 |
    • CONTENTS
    • 64 | {finalNavItems} 65 |
    66 |
    67 | ); 68 | } else { 69 | return ( 70 | 71 |
      72 |
      73 | ); 74 | } 75 | }} 76 | /> 77 | ); 78 | 79 | export default SidebarLayout; 80 | -------------------------------------------------------------------------------- /src/components/search/hitComps.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Highlight, Snippet } from 'react-instantsearch-dom'; 3 | import { Link } from 'gatsby'; 4 | 5 | export const PageHit = (clickHandler) => ({ hit }) => ( 6 |
      7 | 8 |
      9 | 10 |
      11 | 12 | 13 |
      14 | ); 15 | -------------------------------------------------------------------------------- /src/components/search/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, createRef } from 'react'; 2 | import { 3 | InstantSearch, 4 | Index, 5 | Hits, 6 | Configure, 7 | Pagination, 8 | connectStateResults, 9 | } from 'react-instantsearch-dom'; 10 | import algoliasearch from 'algoliasearch/lite'; 11 | import config from '../../../config.js'; 12 | 13 | import styled from '@emotion/styled'; 14 | import { css } from '@emotion/react'; 15 | import { PoweredBy } from './styles'; 16 | import { Search } from '@styled-icons/fa-solid/Search'; 17 | import Input from './input'; 18 | import * as hitComps from './hitComps'; 19 | 20 | const SearchIcon = styled(Search)` 21 | width: 1em; 22 | pointer-events: none; 23 | `; 24 | 25 | const HitsWrapper = styled.div` 26 | display: ${props => (props.show ? `grid` : `none`)}; 27 | max-height: 80vh; 28 | overflow: scroll; 29 | z-index: 2; 30 | -webkit-overflow-scrolling: touch; 31 | position: absolute; 32 | right: 0; 33 | top: calc(100% + 0.5em); 34 | width: 80vw; 35 | max-width: 30em; 36 | box-shadow: 0 0 5px 0; 37 | padding: 0.7em 1em 0.4em; 38 | background: white; 39 | @media only screen and (max-width: 991px) { 40 | width: 400px; 41 | max-width: 400px; 42 | } 43 | @media only screen and (max-width: 767px) { 44 | width: 100%; 45 | max-width: 500px; 46 | } 47 | border-radius: ${props => props.theme.smallBorderRadius}; 48 | > * + * { 49 | padding-top: 1em !important; 50 | border-top: 2px solid ${props => props.theme.darkGray}; 51 | } 52 | li + li { 53 | margin-top: 0.7em; 54 | padding-top: 0.7em; 55 | border-top: 1px solid ${props => props.theme.lightGray}; 56 | } 57 | * { 58 | margin-top: 0; 59 | padding: 0; 60 | color: black !important; 61 | } 62 | ul { 63 | list-style: none; 64 | } 65 | mark { 66 | color: ${props => props.theme.lightBlue}; 67 | background: ${props => props.theme.darkBlue}; 68 | } 69 | header { 70 | display: flex; 71 | justify-content: space-between; 72 | margin-bottom: 0.3em; 73 | h3 { 74 | color: black; 75 | background: ${props => props.theme.gray}; 76 | padding: 0.1em 0.4em; 77 | border-radius: ${props => props.theme.smallBorderRadius}; 78 | } 79 | } 80 | h3 { 81 | color: black; 82 | margin: 0 0 0.5em; 83 | } 84 | h4 { 85 | color: black; 86 | margin-bottom: 0.3em; 87 | } 88 | `; 89 | 90 | const Root = styled.div` 91 | position: relative; 92 | display: grid; 93 | grid-gap: 1em; 94 | @media only screen and (max-width: 767px) { 95 | width: 100%; 96 | } 97 | `; 98 | 99 | const Results = connectStateResults( 100 | ({ searching, searchState: state, searchResults: res }) => 101 | (searching && `Searching...`) || (res && res.nbHits === 0 && `No results for '${state.query}'`) 102 | ); 103 | 104 | const useClickOutside = (ref, handler, events) => { 105 | if (!events) events = [`mousedown`, `touchstart`]; 106 | const detectClickOutside = event => 107 | ref && ref.current && !ref.current.contains(event.target) && handler(); 108 | 109 | useEffect(() => { 110 | for (const event of events) document.addEventListener(event, detectClickOutside); 111 | return () => { 112 | for (const event of events) document.removeEventListener(event, detectClickOutside); 113 | }; 114 | }); 115 | }; 116 | 117 | const searchClient = algoliasearch( 118 | config.header.search.algoliaAppId, 119 | config.header.search.algoliaSearchKey 120 | ); 121 | 122 | export default function SearchComponent({ indices, collapse, hitsAsGrid }) { 123 | const ref = createRef(); 124 | 125 | const [query, setQuery] = useState(``); 126 | 127 | const [focus, setFocus] = useState(false); 128 | 129 | useClickOutside(ref, () => setFocus(false)); 130 | const displayResult = query.length > 0 && focus ? 'showResults' : 'hideResults'; 131 | return ( 132 | setQuery(query)} 136 | root={{ Root, props: { ref } }} 137 | > 138 | setFocus(true)} {...{ collapse, focus }} /> 139 | 0 && focus} 142 | asGrid={hitsAsGrid} 143 | > 144 | {indices.map(({ name, title, hitComp, type }) => { 145 | return ( 146 | 147 | 148 | setFocus(false))} /> 149 | 150 | ); 151 | })} 152 | 153 | 154 | 155 | 156 | ); 157 | } 158 | -------------------------------------------------------------------------------- /src/components/search/input.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { connectSearchBox } from 'react-instantsearch-dom'; 3 | 4 | import styled from '@emotion/styled'; 5 | import { css } from '@emotion/react'; 6 | import { Search } from '@styled-icons/fa-solid/Search'; 7 | 8 | const SearchIcon = styled(Search)` 9 | width: 1em; 10 | pointer-events: none; 11 | margin-right: 10px; 12 | position: absolute; 13 | left: 15px; 14 | color: #2fd2c5; 15 | `; 16 | 17 | const focus = (props) => css` 18 | background: white; 19 | color: ${(props) => props.theme.darkBlue}; 20 | cursor: text; 21 | width: 5em; 22 | + ${SearchIcon} { 23 | color: ${(props) => props.theme.darkBlue}; 24 | margin: 0.3em; 25 | } 26 | `; 27 | 28 | const collapse = (props) => css` 29 | width: 0; 30 | cursor: pointer; 31 | color: ${(props) => props.theme.lightBlue}; 32 | + ${SearchIcon} { 33 | color: white; 34 | } 35 | ${(props) => props.focus && focus()} 36 | margin-left: ${(props) => (props.focus ? `-1.6em` : `-1em`)}; 37 | padding-left: ${(props) => (props.focus ? `1.6em` : `1em`)}; 38 | ::placeholder { 39 | color: ${(props) => props.theme.gray}; 40 | } 41 | `; 42 | 43 | const expand = (props) => css` 44 | background: ${(props) => props.theme.veryLightGray}; 45 | width: 6em; 46 | margin-left: -1.6em; 47 | padding-left: 1.6em; 48 | + ${SearchIcon} { 49 | margin: 0.3em; 50 | } 51 | `; 52 | 53 | const collapseExpand = (props) => css` 54 | ${(props) => (props.collapse ? collapse() : expand())} 55 | `; 56 | 57 | const Input = styled.input` 58 | outline: none; 59 | border: none; 60 | font-size: 1em; 61 | background: white; 62 | transition: ${(props) => props.theme.shortTrans}; 63 | border-radius: ${(props) => props.theme.smallBorderRadius}; 64 | {collapseExpand} 65 | `; 66 | 67 | const Form = styled.form` 68 | display: flex; 69 | align-items: center; 70 | @media only screen and (max-width: 767px) { 71 | width: 100%; 72 | margin-left: 15px; 73 | } 74 | `; 75 | 76 | export default connectSearchBox(({ refine, ...rest }) => { 77 | const preventSubmit = (e) => { 78 | e.preventDefault(); 79 | }; 80 | 81 | return ( 82 |
      83 | 84 | refine(e.target.value)} 90 | {...rest} 91 | /> 92 | 93 | ); 94 | }); 95 | -------------------------------------------------------------------------------- /src/components/search/styles.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Algolia } from '@styled-icons/fa-brands/Algolia'; 3 | 4 | export const PoweredBy = () => ( 5 | 6 | Powered by{` `} 7 | 8 | Algolia 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /src/components/sidebar/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Tree from './tree'; 3 | import { StaticQuery, graphql } from 'gatsby'; 4 | import styled from '@emotion/styled'; 5 | import { ExternalLink } from 'react-feather'; 6 | import config from '../../../config'; 7 | 8 | // eslint-disable-next-line no-unused-vars 9 | const ListItem = styled(({ className, active, level, ...props }) => { 10 | return ( 11 |
    • 12 | 13 | {props.children} 14 | 15 |
    • 16 | ); 17 | })` 18 | list-style: none; 19 | 20 | a { 21 | color: #5c6975; 22 | text-decoration: none; 23 | font-weight: ${({ level }) => (level === 0 ? 700 : 400)}; 24 | padding: 0.45rem 0 0.45rem ${(props) => 2 + (props.level || 0) * 1}rem; 25 | display: block; 26 | position: relative; 27 | 28 | &:hover { 29 | color: #1ed3c6 !important; 30 | } 31 | 32 | ${(props) => 33 | props.active && 34 | ` 35 | // color: #663399; 36 | border-color: rgb(230,236,241) !important; 37 | border-style: solid none solid solid; 38 | border-width: 1px 0px 1px 1px; 39 | background-color: #fff; 40 | `} // external link icon 41 | svg { 42 | float: right; 43 | margin-right: 1rem; 44 | } 45 | } 46 | `; 47 | 48 | const Sidebar = styled('aside')` 49 | width: 100%; 50 | height: 100vh; 51 | overflow: auto; 52 | position: fixed; 53 | padding-left: 0px; 54 | position: -webkit-sticky; 55 | position: -moz-sticky; 56 | position: sticky; 57 | top: 0; 58 | padding-right: 0; 59 | -webkit-box-shadow: -1px 0px 4px 1px rgba(175, 158, 232, 0.4); 60 | 61 | @media only screen and (max-width: 1023px) { 62 | width: 100%; 63 | /* position: relative; */ 64 | height: 100vh; 65 | } 66 | 67 | @media (min-width: 767px) and (max-width: 1023px) { 68 | padding-left: 0; 69 | } 70 | 71 | @media only screen and (max-width: 767px) { 72 | padding-left: 0px; 73 | height: auto; 74 | } 75 | `; 76 | 77 | const Divider = styled((props) => ( 78 |
    • 79 |
      80 |
    • 81 | ))` 82 | list-style: none; 83 | padding: 0.5rem 0; 84 | 85 | hr { 86 | margin: 0; 87 | padding: 0; 88 | border: 0; 89 | border-bottom: 1px solid #ede7f3; 90 | } 91 | `; 92 | 93 | const SidebarLayout = ({ location }) => ( 94 | { 110 | return ( 111 | 112 | {config.sidebar.title ? ( 113 |
      117 | ) : null} 118 |
        119 | 120 | {config.sidebar.links && config.sidebar.links.length > 0 && } 121 | {config.sidebar.links.map((link, key) => { 122 | if (link.link !== '' && link.text !== '') { 123 | return ( 124 | 125 | {link.text} 126 | 127 | 128 | ); 129 | } 130 | })} 131 |
      132 | 133 | ); 134 | }} 135 | /> 136 | ); 137 | 138 | export default SidebarLayout; 139 | -------------------------------------------------------------------------------- /src/components/sidebar/tree.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import config from '../../../config'; 3 | import TreeNode from './treeNode'; 4 | 5 | const calculateTreeData = edges => { 6 | const originalData = config.sidebar.ignoreIndex 7 | ? edges.filter( 8 | ({ 9 | node: { 10 | fields: { slug }, 11 | }, 12 | }) => slug !== '/' 13 | ) 14 | : edges; 15 | 16 | const tree = originalData.reduce( 17 | ( 18 | accu, 19 | { 20 | node: { 21 | fields: { slug, title }, 22 | }, 23 | } 24 | ) => { 25 | const parts = slug.split('/'); 26 | 27 | let { items: prevItems } = accu; 28 | 29 | const slicedParts = 30 | config.gatsby && config.gatsby.trailingSlash ? parts.slice(1, -2) : parts.slice(1, -1); 31 | 32 | for (const part of slicedParts) { 33 | let tmp = prevItems && prevItems.find(({ label }) => label == part); 34 | 35 | if (tmp) { 36 | if (!tmp.items) { 37 | tmp.items = []; 38 | } 39 | } else { 40 | tmp = { label: part, items: [] }; 41 | prevItems.push(tmp); 42 | } 43 | prevItems = tmp.items; 44 | } 45 | const slicedLength = 46 | config.gatsby && config.gatsby.trailingSlash ? parts.length - 2 : parts.length - 1; 47 | 48 | const existingItem = prevItems.find(({ label }) => label === parts[slicedLength]); 49 | 50 | if (existingItem) { 51 | existingItem.url = slug; 52 | existingItem.title = title; 53 | } else { 54 | prevItems.push({ 55 | label: parts[slicedLength], 56 | url: slug, 57 | items: [], 58 | title, 59 | }); 60 | } 61 | return accu; 62 | }, 63 | { items: [] } 64 | ); 65 | 66 | const { 67 | sidebar: { forcedNavOrder = [] }, 68 | } = config; 69 | 70 | const tmp = [...forcedNavOrder]; 71 | 72 | if (config.gatsby && config.gatsby.trailingSlash) { 73 | } 74 | tmp.reverse(); 75 | return tmp.reduce((accu, slug) => { 76 | const parts = slug.split('/'); 77 | 78 | let { items: prevItems } = accu; 79 | 80 | const slicedParts = 81 | config.gatsby && config.gatsby.trailingSlash ? parts.slice(1, -2) : parts.slice(1, -1); 82 | 83 | for (const part of slicedParts) { 84 | let tmp = prevItems.find(item => item && item.label == part); 85 | 86 | if (tmp) { 87 | if (!tmp.items) { 88 | tmp.items = []; 89 | } 90 | } else { 91 | tmp = { label: part, items: [] }; 92 | prevItems.push(tmp); 93 | } 94 | if (tmp && tmp.items) { 95 | prevItems = tmp.items; 96 | } 97 | } 98 | // sort items alphabetically. 99 | prevItems.map(item => { 100 | item.items = item.items.sort(function(a, b) { 101 | if (a.label < b.label) return -1; 102 | if (a.label > b.label) return 1; 103 | return 0; 104 | }); 105 | }); 106 | const slicedLength = 107 | config.gatsby && config.gatsby.trailingSlash ? parts.length - 2 : parts.length - 1; 108 | 109 | const index = prevItems.findIndex(({ label }) => label === parts[slicedLength]); 110 | 111 | if (prevItems.length) { 112 | accu.items.unshift(prevItems.splice(index, 1)[0]); 113 | } 114 | return accu; 115 | }, tree); 116 | }; 117 | 118 | const Tree = ({ edges }) => { 119 | let [treeData] = useState(() => { 120 | return calculateTreeData(edges); 121 | }); 122 | 123 | const defaultCollapsed = {}; 124 | 125 | treeData.items.forEach(item => { 126 | if (config.sidebar.collapsedNav && config.sidebar.collapsedNav.includes(item.url)) { 127 | defaultCollapsed[item.url] = true; 128 | } else { 129 | defaultCollapsed[item.url] = false; 130 | } 131 | }); 132 | const [collapsed, setCollapsed] = useState(defaultCollapsed); 133 | 134 | const toggle = url => { 135 | setCollapsed({ 136 | ...collapsed, 137 | [url]: !collapsed[url], 138 | }); 139 | }; 140 | 141 | return ( 142 | 148 | ); 149 | }; 150 | 151 | export default Tree; 152 | -------------------------------------------------------------------------------- /src/components/sidebar/treeNode.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import OpenedSvg from '../images/opened'; 3 | import ClosedSvg from '../images/closed'; 4 | import config from '../../../config'; 5 | import Link from '../link'; 6 | 7 | const TreeNode = ({ className = '', setCollapsed, collapsed, url, title, items, ...rest }) => { 8 | const isCollapsed = collapsed[url]; 9 | 10 | const collapse = () => { 11 | setCollapsed(url); 12 | }; 13 | 14 | const hasChildren = items.length !== 0; 15 | 16 | let location; 17 | 18 | if (typeof document != 'undefined') { 19 | location = document.location; 20 | } 21 | const active = 22 | location && (location.pathname === url || location.pathname === config.gatsby.pathPrefix + url); 23 | 24 | const calculatedClassName = `${className} item ${active ? 'active' : ''}`; 25 | 26 | return ( 27 |
    • 28 | {title && ( 29 | 30 | {title} 31 | {!config.sidebar.frontLine && title && hasChildren ? ( 32 | 35 | ) : null} 36 | 37 | )} 38 | 39 | {!isCollapsed && hasChildren ? ( 40 |
        41 | {items.map((item, index) => ( 42 | 48 | ))} 49 |
      50 | ) : null} 51 |
    • 52 | ); 53 | }; 54 | 55 | export default TreeNode; 56 | -------------------------------------------------------------------------------- /src/components/styles/Docs.js: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | 3 | export const StyledHeading = styled('h1')` 4 | font-size: 32px; 5 | line-height: 1.5; 6 | font-weight: 500; 7 | border-left: 2px solid #1ed3c6; 8 | padding: 0 16px; 9 | flex: 1; 10 | margin-top: 0; 11 | padding-top: 0; 12 | color: ${props => props.theme.colors.heading}; 13 | `; 14 | 15 | export const Edit = styled('div')` 16 | padding: 1rem 1.5rem; 17 | text-align: right; 18 | 19 | a { 20 | font-size: 14px; 21 | font-weight: 500; 22 | line-height: 1em; 23 | text-decoration: none; 24 | color: #555; 25 | border: 1px solid rgb(211, 220, 228); 26 | cursor: pointer; 27 | border-radius: 3px; 28 | transition: all 0.2s ease-out 0s; 29 | text-decoration: none; 30 | color: rgb(36, 42, 49); 31 | background-color: rgb(255, 255, 255); 32 | box-shadow: rgba(116, 129, 141, 0.1) 0px 1px 1px 0px; 33 | height: 30px; 34 | padding: 5px 16px; 35 | &:hover { 36 | background-color: rgb(245, 247, 249); 37 | } 38 | } 39 | `; 40 | 41 | export const StyledMainWrapper = styled.div` 42 | max-width: 750px; 43 | color: ${props => props.theme.colors.text}; 44 | 45 | ul, 46 | ol { 47 | -webkit-padding-start: 40px; 48 | -moz-padding-start: 40px; 49 | -o-padding-start: 40px; 50 | margin: 24px 0px; 51 | padding: 0px 0px 0px 2em; 52 | 53 | li { 54 | font-size: 16px; 55 | line-height: 1.8; 56 | font-weight: 400; 57 | } 58 | } 59 | 60 | a { 61 | transition: color 0.15s; 62 | color: ${props => props.theme.colors.link}; 63 | } 64 | 65 | code { 66 | border: 1px solid #ede7f3; 67 | border-radius: 4px; 68 | padding: 2px 6px; 69 | font-size: 0.9375em; 70 | 71 | background: ${props => props.theme.colors.background}; 72 | } 73 | 74 | @media (max-width: 767px) { 75 | padding: 0 15px; 76 | } 77 | `; 78 | -------------------------------------------------------------------------------- /src/components/styles/GlobalStyles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export const baseStyles = css` 4 | @import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap'); 5 | @import url('https://fonts.googleapis.com/css?family=Poppins:300,400,500,600&display=swap'); 6 | * { 7 | margin: 0; 8 | padding: 0; 9 | box-sizing: border-box; 10 | font-display: swap; 11 | } 12 | ::-webkit-input-placeholder { 13 | /* Edge */ 14 | color: #c2c2c2; 15 | } 16 | 17 | :-ms-input-placeholder { 18 | /* Internet Explorer */ 19 | color: #c2c2c2; 20 | } 21 | 22 | ::placeholder { 23 | color: #c2c2c2; 24 | } 25 | html, 26 | body { 27 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Roboto Light', 'Oxygen', 28 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif, 29 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 30 | 31 | font-size: 16px; 32 | scroll-behavior: smooth; 33 | } 34 | 35 | a { 36 | transition: color 0.15s; 37 | /* color: #663399; */ 38 | } 39 | 40 | body { 41 | font-family: 'Roboto'; 42 | } 43 | .visibleMobile { 44 | display: none; 45 | } 46 | .visibleMobileView { 47 | display: none !important; 48 | } 49 | .video-responsive { 50 | position: relative; 51 | padding-bottom: 56.2%; 52 | } 53 | a { 54 | text-decoration: none; 55 | } 56 | a:hover { 57 | text-decoration: none; 58 | } 59 | .displayInline { 60 | display: inline-block; 61 | } 62 | .navBarToggle { 63 | border: 0px solid #fff; 64 | border-radius: 4px; 65 | width: 36px; 66 | height: 33px; 67 | position: absolute; 68 | right: 20px; 69 | padding: 8px 5px; 70 | display: none; 71 | } 72 | .navBarToggle .iconBar { 73 | display: block; 74 | width: 22px; 75 | height: 2px; 76 | border-radius: 1px; 77 | margin: 0 auto; 78 | margin-top: 4px; 79 | background-color: #001934; 80 | } 81 | .navBarToggle .iconBar:first-child { 82 | margin-top: 0px; 83 | } 84 | .video-responsive iframe { 85 | position: absolute; 86 | width: 100%; 87 | height: 100%; 88 | } 89 | 90 | .diffNewLine { 91 | color: #22863a; 92 | background-color: #f0fff4; 93 | } 94 | 95 | .diffRemoveLine { 96 | color: red; 97 | background-color: #ffcccc; 98 | } 99 | .navBarParent { 100 | width: 100%; 101 | float: left; 102 | display: flex; 103 | align-items: center; 104 | } 105 | .divider { 106 | height: 30px; 107 | margin: 0 15px; 108 | border-right: 1px solid rgba(255, 255, 255, 0.3); 109 | } 110 | .navBarULRight { 111 | /* position: absolute; 112 | right: 30px; */ 113 | } 114 | .githubIcon { 115 | width: 15px; 116 | margin-right: 5px; 117 | } 118 | 119 | .githubSection { 120 | display: flex; 121 | align-items: center; 122 | color: #000; 123 | opacity: 0.7; 124 | } 125 | 126 | .githubSection:hover { 127 | text-decoration: none; 128 | opacity: 1; 129 | } 130 | 131 | .navbar-default .navbar-toggle .icon-bar { 132 | background-color: #fff !important; 133 | } 134 | 135 | .navbar-default .navbar-toggle:focus, 136 | .navbar-default .navbar-toggle:hover { 137 | background-color: #001933; 138 | } 139 | 140 | .headerWrapper { 141 | border-bottom: 1px solid rgb(212, 218, 223); 142 | box-shadow: rgba(116, 129, 141, 0.1) 0px 1px 1px 0px; 143 | display: flex; 144 | align-items: center; 145 | } 146 | .formElement { 147 | background-color: transparent; 148 | padding: 4px; 149 | border-radius: 5px; 150 | position: relative; 151 | } 152 | .formElement:focus { 153 | outline: none; 154 | border: none; 155 | } 156 | .formElement svg path { 157 | fill: #2fd2c5; 158 | } 159 | .searchInput { 160 | width: 100%; 161 | background-color: rgba(28,211,198,.12) !important; 162 | border-width: 0 !important; 163 | color: #C2C2C2; 164 | padding: 10px; 165 | border-radius: 5px; 166 | color: #fff; 167 | opacity: .6; 168 | padding-left: 38px; 169 | max-width: 600px; 170 | } 171 | .searchInput:focus, 172 | .searchInput:visited, 173 | .searchInput:hover, 174 | .searchInput:focus-within { 175 | outline: none; 176 | border: 0; 177 | } 178 | .searchWrapper { 179 | padding-left: 0px; 180 | padding-right: 20px; 181 | flex: 1; 182 | position: relative; 183 | } 184 | .searchWrapper a { 185 | font-weight: 500; 186 | } 187 | .hitWrapper { 188 | background-color: #fff; 189 | padding: 0.7em 1em 0.4em; 190 | border-radius: 4px; 191 | position: absolute; 192 | width: 80vw; 193 | max-width: 30em; 194 | top: 40px; 195 | border: 1px solid #ccc; 196 | box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.16); 197 | height: auto; 198 | max-height: 80vh; 199 | overflow: scroll; 200 | left: 0; 201 | } 202 | .hitWrapper ul li { 203 | margin-top: 0.7em; 204 | padding-top: 0.7em; 205 | border-top: 1px solid; 206 | list-style-type: none; 207 | } 208 | .hitWrapper ul li:first-child { 209 | border-top: 0px; 210 | margin-top: 0px; 211 | color: black !important; 212 | padding: 0px; 213 | } 214 | .showResults { 215 | display: block; 216 | } 217 | .hideResults { 218 | display: none; 219 | } 220 | .hitWrapper span { 221 | color: black; 222 | font-size: 14px; 223 | } 224 | .headerTitle { 225 | height: auto; 226 | font-size: 16px; 227 | line-height: 1.5; 228 | font-weight: 300; 229 | color: #fff !important; 230 | margin-top: 16px; 231 | text-transform: uppercase; 232 | } 233 | .headerTitle a { 234 | color: #fff; 235 | } 236 | 237 | .headerTitle a:hover { 238 | text-decoration: none; 239 | opacity: 0.8; 240 | } 241 | .logoWrapper { 242 | padding: 21px 0; 243 | padding-left: 20px; 244 | } 245 | 246 | .logoContent { 247 | font-family: 'Roboto'; 248 | margin-left: 16px; 249 | font-size: 28px; 250 | line-height: 1.5; 251 | font-weight: 500; 252 | padding-right: 10px; 253 | } 254 | 255 | /* Header section starts here */ 256 | .removePadd { 257 | padding: 0 !important; 258 | } 259 | .navBarDefault { 260 | background-color: #001934; 261 | border-radius: 0; 262 | border-top: 0; 263 | margin-bottom: 0; 264 | border: 0; 265 | display: flex; 266 | align-items: center; 267 | box-shadow: -1px 0px 4px 1px rgba(175, 158, 232, 0.4); 268 | -webkit-box-shadow: -1px 0px 4px 1px rgba(175, 158, 232, 0.4); 269 | -moz-box-shadow: -1px 0px 4px 1px rgba(175, 158, 232, 0.8); 270 | -o-box-shadow: -1px 0px 4px 1px rgba(175, 158, 232, 0.4); 271 | z-index: 1; 272 | padding: 15px; 273 | position: relative; 274 | height: 80px; 275 | } 276 | .navBarHeader { 277 | min-width: 335px; 278 | padding-right: 20px; 279 | display: flex; 280 | align-items: center; 281 | } 282 | .navBarBrand { 283 | padding: 0px 0px; 284 | display: flex; 285 | align-items: center; 286 | } 287 | 288 | .navBarBrand img { 289 | width: 120px; 290 | margin-right: 6px; 291 | display: inline-block; 292 | } 293 | .navBarUL li { 294 | list-style-type: none; 295 | } 296 | .navBarUL { 297 | -webkit-overflow-scrolling: touch; 298 | } 299 | .navBarUL li a { 300 | font-family: 'Roboto'; 301 | color: #fff !important; 302 | font-size: 16px; 303 | font-weight: 500; 304 | line-height: 1em; 305 | opacity: 1; 306 | padding: 10px 15px; 307 | } 308 | .navBarNav { 309 | display: flex; 310 | align-items: center; 311 | } 312 | .navBarUL li a img, 313 | .navBarUL li a .shareIcon { 314 | width: 20px; 315 | } 316 | .navBarUL li a:hover { 317 | opacity: 0.7; 318 | } 319 | pre { 320 | border: 0 !important; 321 | background-color: rgb(245, 247, 249); /* !important; */ 322 | } 323 | 324 | blockquote { 325 | color: rgb(116, 129, 141); 326 | margin: 0px 0px 24px; 327 | padding: 0px 0px 0px 12px; 328 | border-left: 4px solid rgb(230, 236, 241); 329 | border-color: rgb(230, 236, 241); 330 | } 331 | .socialWrapper { 332 | display: flex; 333 | align-items: center; 334 | } 335 | .socialWrapper li { 336 | display: inline-block; 337 | } 338 | .socialWrapper li a { 339 | display: contents; 340 | } 341 | .discordBtn, .twitterBtn { 342 | border-radius: 4px; 343 | border: solid 1px #d1d2d3; 344 | background-color: #f1f5f8; 345 | width: 20px; 346 | height: 20px; 347 | padding-top: 2px; 348 | margin-left: 8px; 349 | display: flex; 350 | align-items: center; 351 | justify-content: center; 352 | opacity: .8; 353 | cursor: pointer; 354 | } 355 | .twitterBtn img { 356 | width: 12px !important; 357 | } 358 | .discordBtn img { 359 | width: 10px !important; 360 | } 361 | .discordBtn:hover, .twitterBtn:hover { 362 | opacity: 1; 363 | } 364 | .discordBtn { 365 | img { 366 | width: 10px; 367 | } 368 | } 369 | /* Header section ends here */ 370 | .sidebarTitle { 371 | /* box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16); */ 372 | background-color: #f8f8f8; 373 | padding: 18px 16px; 374 | font-family: 'Poppins'; 375 | font-size: 18px; 376 | font-weight: 600; 377 | color: #001934; 378 | display: flex; 379 | align-items: center; 380 | } 381 | 382 | .sideBarShow { 383 | display: none; 384 | } 385 | 386 | .sidebarTitle a { 387 | color: #001934; 388 | } 389 | 390 | .greenCircle { 391 | width: 8px; 392 | height: 8px; 393 | background-color: #1cd3c6; 394 | border-radius: 50%; 395 | margin: 0 12px; 396 | } 397 | 398 | .headerNav { 399 | font-family: 'Roboto'; 400 | padding: 0px 24px; 401 | color: #001933; 402 | font-size: 16px; 403 | font-weight: 500; 404 | line-height: 1em; 405 | } 406 | 407 | .headerNav a { 408 | color: #001933; 409 | text-decoration: none; 410 | } 411 | 412 | .headerNav a:hover { 413 | text-decoration: none; 414 | } 415 | 416 | .logoWrapper img { 417 | width: 40px; 418 | } 419 | 420 | .sideBarUL { 421 | margin-top: 32px; 422 | } 423 | 424 | .sideBarUL li { 425 | list-style-type: none; 426 | width: auto; 427 | } 428 | 429 | .sideBarUL li a { 430 | /* color: #fff; */ 431 | font-size: 14px; 432 | font-weight: 500; 433 | line-height: 1.5; 434 | padding: 7px 24px 7px 16px; 435 | padding-left: 10px; 436 | padding-right: 25px; 437 | border-style: solid none solid solid; 438 | border-width: 1px 0px 1px 1px; 439 | border-color: transparent currentcolor transparent transparent; 440 | } 441 | 442 | .hideFrontLine .collapser { 443 | background: transparent; 444 | border: none; 445 | outline: none; 446 | position: absolute; 447 | right: 20px; 448 | z-index: 1; 449 | cursor: pointer; 450 | } 451 | 452 | .hideFrontLine .active > a { 453 | background-color: #1ed3c6; 454 | color: #fff !important; 455 | } 456 | .firstLevel ul li .collapser svg path { 457 | fill: #fff !important; 458 | } 459 | .active .collapser > svg > path { 460 | fill: #001933 !important; 461 | } 462 | 463 | .firstLevel ul .item ul .item { 464 | border-left: 1px solid #e6ecf1; 465 | } 466 | 467 | .sideBarUL .item { 468 | list-style: none; 469 | padding: 0; 470 | } 471 | 472 | .sideBarUL .item > a { 473 | color: #1ED3C6; 474 | text-decoration: none; 475 | display: flex; 476 | align-items: center; 477 | position: relative; 478 | width: 100%; 479 | padding-right: 35px; 480 | padding-left: 15px; 481 | } 482 | 483 | .showFrontLine .item > a:hover { 484 | background-color: #001933; 485 | } 486 | 487 | .showFrontLine .active > a { 488 | /* color: #fff; */ 489 | background-color: #001933; 490 | } 491 | 492 | .sideBarUL .item .item { 493 | margin-left: 16px; 494 | } 495 | 496 | .firstLevel > ul > .item { 497 | margin-left: 0 !important; 498 | } 499 | 500 | .showFrontLine .item .item { 501 | border-left: 1px solid #e6ecf1; 502 | border-left-color: rgb(230, 236, 241); 503 | padding: 0; 504 | width: calc(100% - 16px) !important; 505 | } 506 | 507 | .showFrontLine .item .active > a { 508 | border-color: rgb(230, 236, 241) !important; 509 | border-style: solid none solid solid; 510 | border-width: 1px 0px 1px 1px; 511 | background-color: #1ed3c6 !important; 512 | color: #fff; 513 | } 514 | 515 | .titleWrapper { 516 | display: flex; 517 | align-items: center; 518 | padding-bottom: 40px; 519 | border-bottom: 1px solid rgb(230, 236, 241); 520 | margin-bottom: 32px; 521 | } 522 | 523 | .gitBtn { 524 | height: 30px; 525 | min-height: 30px; 526 | display: flex; 527 | align-items: center; 528 | } 529 | 530 | .gitBtn img { 531 | width: 15px; 532 | display: inline-block; 533 | margin-right: 5px; 534 | } 535 | 536 | .addPaddTopBottom { 537 | padding: 50px 0; 538 | } 539 | 540 | .preRightWrapper { 541 | display: block; 542 | margin: 0px; 543 | flex: 1 1 0%; 544 | padding: 16px; 545 | text-align: right; 546 | } 547 | 548 | .smallContent { 549 | display: block; 550 | margin: 0px; 551 | padding: 0px; 552 | color: #6e6e6e; 553 | } 554 | 555 | .smallContent span { 556 | font-size: 12px; 557 | line-height: 1.625; 558 | font-weight: 400; 559 | } 560 | 561 | /* **************************** */ 562 | 563 | .nextRightWrapper { 564 | display: block; 565 | margin: 0px; 566 | padding: 16px; 567 | flex: 1 1 0%; 568 | } 569 | 570 | /* tables.css */ 571 | table { 572 | padding: 0; 573 | } 574 | 575 | table tr { 576 | border-top: 1px solid #cccccc; 577 | margin: 0; 578 | padding: 0; 579 | } 580 | 581 | table tr:nth-child(2n) { 582 | background-color: #f8f8f8; 583 | } 584 | 585 | table tr th { 586 | font-weight: bold; 587 | border: 1px solid #cccccc; 588 | text-align: left; 589 | margin: 0; 590 | padding: 6px 13px; 591 | } 592 | 593 | table tr td { 594 | border: 1px solid #cccccc; 595 | text-align: left; 596 | margin: 0; 597 | padding: 6px 13px; 598 | } 599 | 600 | table tr th :first-child, 601 | table tr td :first-child { 602 | margin-top: 0; 603 | } 604 | 605 | table tr th :last-child, 606 | table tr td :last-child { 607 | margin-bottom: 0; 608 | } 609 | /* end - tables.css */ 610 | 611 | /* Image styling */ 612 | img { 613 | max-width: 100%; 614 | } 615 | /* end image */ 616 | .githubBtn { 617 | display: flex; 618 | align-items: center; 619 | font-size: 16px; 620 | padding: 10px 0px; 621 | padding-left: 15px; 622 | max-height: 40px; 623 | } 624 | .githubBtn span span { 625 | display: flex; 626 | align-items: center; 627 | } 628 | 629 | .communitySection { 630 | font-size: 24px; 631 | font-weight: 700; 632 | } 633 | .authorSection { 634 | padding: 20px 0; 635 | } 636 | .authorSection, 637 | .authorName { 638 | display: flex; 639 | align-items: center; 640 | } 641 | .authorImg img { 642 | width: 75px; 643 | height: 75px; 644 | border-radius: 50%; 645 | min-width: 75px; 646 | max-width: 75px; 647 | min-height: 75px; 648 | max-height: 75px; 649 | } 650 | .authorDetails { 651 | padding-left: 10px; 652 | } 653 | .authorDesc { 654 | padding-top: 5px; 655 | font-size: 14px; 656 | } 657 | .authorName img { 658 | margin-left: 10px; 659 | display: inline-block; 660 | width: 20px; 661 | } 662 | .authorName img:hover { 663 | opacity: 0.7; 664 | } 665 | 666 | .heading1 { 667 | font-size: 26px; 668 | font-weight: 800; 669 | line-height: 1.5; 670 | margin-bottom: 16px; 671 | margin-top: 32px; 672 | } 673 | 674 | .heading2 { 675 | font-size: 24px; 676 | font-weight: 700; 677 | line-height: 1.5; 678 | margin-bottom: 16px; 679 | margin-top: 32px; 680 | } 681 | 682 | .heading3 { 683 | font-size: 20px; 684 | font-weight: 600; 685 | line-height: 1.5; 686 | margin-bottom: 16px; 687 | margin-top: 32px; 688 | } 689 | 690 | .heading4 { 691 | font-size: 18px; 692 | font-weight: 500; 693 | line-height: 1.5; 694 | margin-bottom: 16px; 695 | margin-top: 32px; 696 | } 697 | 698 | .heading5 { 699 | font-size: 16px; 700 | font-weight: 400; 701 | line-height: 1.5; 702 | margin-bottom: 16px; 703 | margin-top: 32px; 704 | } 705 | 706 | .heading6 { 707 | font-size: 14px; 708 | font-weight: 300; 709 | line-height: 1.5; 710 | margin-bottom: 16px; 711 | margin-top: 32px; 712 | } 713 | 714 | .paragraph { 715 | margin: 16px 0px 32px; 716 | line-height: 1.625; 717 | } 718 | 719 | .pre { 720 | font-size: 14px; 721 | margin: 0px; 722 | padding: 16px; 723 | overflow: auto; 724 | } 725 | 726 | .poweredBy { 727 | font-size: 0.6em; 728 | text-align: end; 729 | padding: 0; 730 | } 731 | .topnav { 732 | -webkit-transition: top 0.5s, bottom 0.5s; 733 | } 734 | 735 | @media (max-width: 767px) { 736 | .formElement svg path { 737 | fill: #001934; 738 | } 739 | .visibleMobileView { 740 | display: block !important; 741 | } 742 | .searchInput { 743 | color: #001934; 744 | } 745 | .socialWrapper { 746 | position: absolute; 747 | right: 10px; 748 | top: 29px; 749 | } 750 | .responsive { 751 | margin-top: 15px; 752 | position: relative; 753 | padding-bottom: 20px; 754 | border-top: 1px solid #fff; 755 | } 756 | .headerTitle { 757 | padding-right: 50px; 758 | font-size: 16px; 759 | } 760 | .navBarBrand { 761 | min-height: 40px; 762 | } 763 | .navBarBrand img { 764 | margin-right: 8px; 765 | } 766 | .topnav.responsive .visibleMobile { 767 | display: block; 768 | } 769 | .topnav .navBarUL { 770 | display: none; 771 | } 772 | .topnav.responsive .navBarUL { 773 | display: block; 774 | text-align: left; 775 | } 776 | .hiddenMobile { 777 | display: none !important; 778 | } 779 | hr { 780 | margin-top: 0; 781 | margin-bottom: 0; 782 | } 783 | .navBarParent { 784 | display: block; 785 | } 786 | .separator { 787 | margin-top: 20px; 788 | margin-bottom: 20px; 789 | } 790 | .navBarULRight { 791 | position: static; 792 | } 793 | .navBarUL { 794 | display: flex; 795 | align-items: center; 796 | margin: 7.5px 0px; 797 | } 798 | .navBarUL li { 799 | height: 37px; 800 | } 801 | .navBarUL li a { 802 | font-size: 14px; 803 | padding: 10px 15px; 804 | } 805 | 806 | .navBarDefault { 807 | display: block; 808 | height: auto; 809 | } 810 | 811 | .navBarToggle { 812 | margin-right: 0; 813 | display: block; 814 | position: absolute; 815 | left: 11px; 816 | top: 15px; 817 | background: #fff; 818 | } 819 | 820 | .navBarHeader { 821 | display: flex; 822 | min-width: auto; 823 | padding-right: 0; 824 | align-items: center; 825 | } 826 | 827 | .navBarBrand { 828 | font-size: 20px; 829 | padding: 0 0; 830 | padding-left: 0; 831 | flex: initial; 832 | padding-right: 15px; 833 | } 834 | 835 | .titleWrapper { 836 | padding: 0 15px; 837 | display: block; 838 | } 839 | 840 | .gitBtn { 841 | display: inline-block; 842 | } 843 | 844 | .mobileView { 845 | text-align: left !important; 846 | padding-left: 0 !important; 847 | } 848 | 849 | .searchWrapper { 850 | padding: 0px 0; 851 | padding-top: 0px; 852 | position: absolute; 853 | bottom: 0px; 854 | width: calc(100% - 70px); 855 | position: absolute; 856 | left: 40px; 857 | top: 8px; 858 | } 859 | .hitWrapper { 860 | width: 100%; 861 | right: 0; 862 | top: 35px; 863 | max-height: fit-content; 864 | position: static; 865 | } 866 | } 867 | 868 | @media (min-width: 768px) and (max-width: 991px) { 869 | .navBarDefault { 870 | padding: 10px; 871 | } 872 | .navBarBrand { 873 | font-size: 22px; 874 | } 875 | .navBarHeader { 876 | min-width: 240px; 877 | flex: initial; 878 | } 879 | .githubBtn { 880 | padding: 10px 10px; 881 | } 882 | .divider { 883 | margin: 0 5px; 884 | height: 20px; 885 | } 886 | .hitWrapper { 887 | max-width: 500px; 888 | } 889 | .navBarUL li a { 890 | padding: 10px 5px; 891 | } 892 | .searchWrapper { 893 | padding-left: 0px; 894 | } 895 | } 896 | `; 897 | -------------------------------------------------------------------------------- /src/components/styles/PageNavigationButtons.js: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | 3 | export const StyledNextPrevious = styled('div')` 4 | margin: 0px; 5 | padding: 0px; 6 | width: auto; 7 | display: grid; 8 | grid-template-rows: auto; 9 | column-gap: 24px; 10 | grid-template-columns: calc(50% - 8px) calc(50% - 8px); 11 | 12 | .previousBtn { 13 | cursor: pointer; 14 | -moz-box-align: center; 15 | -moz-box-direction: normal; 16 | -moz-box-orient: horizontal; 17 | margin: 0px; 18 | padding: 0px; 19 | position: relative; 20 | display: flex; 21 | flex-direction: row; 22 | align-items: center; 23 | place-self: stretch; 24 | border-radius: 3px; 25 | border: 1px solid rgb(230, 236, 241); 26 | transition: border 200ms ease 0s; 27 | box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px; 28 | text-decoration: none; 29 | 30 | background-color: ${props => props.theme.colors.background}; 31 | color: ${props => props.theme.colors.text}; 32 | } 33 | 34 | .nextBtn { 35 | cursor: pointer; 36 | -moz-box-align: center; 37 | -moz-box-direction: normal; 38 | -moz-box-orient: horizontal; 39 | margin: 0px; 40 | padding: 0px; 41 | position: relative; 42 | display: flex; 43 | flex-direction: row; 44 | align-items: center; 45 | place-self: stretch; 46 | border-radius: 3px; 47 | border: 1px solid rgb(230, 236, 241); 48 | transition: border 200ms ease 0s; 49 | box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px; 50 | text-decoration: none; 51 | 52 | background-color: ${props => props.theme.colors.background}; 53 | color: ${props => props.theme.colors.text}; 54 | } 55 | 56 | .nextBtn:hover, 57 | .previousBtn:hover { 58 | text-decoration: none; 59 | border: 1px solid #1ed3c6; 60 | } 61 | 62 | .nextBtn:hover .rightArrow, 63 | .previousBtn:hover .leftArrow { 64 | color: #1ed3c6; 65 | } 66 | 67 | .leftArrow { 68 | display: flex; 69 | margin: 0px; 70 | color: rgb(157, 170, 182); 71 | flex: 0 0 auto; 72 | font-size: 24px; 73 | transition: color 200ms ease 0s; 74 | padding: 16px; 75 | padding-right: 16px; 76 | } 77 | 78 | .rightArrow { 79 | display: flex; 80 | flex: 0 0 auto; 81 | font-size: 24px; 82 | transition: color 200ms ease 0s; 83 | padding: 16px; 84 | padding-left: 16px; 85 | margin: 0px; 86 | color: rgb(157, 170, 182); 87 | } 88 | 89 | .nextPreviousTitle { 90 | display: block; 91 | margin: 0px; 92 | padding: 0px; 93 | transition: color 200ms ease 0s; 94 | } 95 | 96 | .nextPreviousTitle span { 97 | font-size: 16px; 98 | line-height: 1.5; 99 | font-weight: 500; 100 | } 101 | 102 | @media (max-width: 767px) { 103 | display: block; 104 | padding: 0 15px; 105 | 106 | .previousBtn { 107 | margin-bottom: 20px; 108 | } 109 | } 110 | `; 111 | -------------------------------------------------------------------------------- /src/components/styles/Sidebar.js: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | 3 | export const Sidebar = styled('aside')` 4 | width: 100%; 5 | border-right: 1px solid #ede7f3; 6 | height: 100vh; 7 | overflow: auto; 8 | position: fixed; 9 | padding-left: 24px; 10 | position: -webkit-sticky; 11 | position: -moz-sticky; 12 | position: sticky; 13 | top: 0; 14 | 15 | background: ${props => props.theme.colors.background}; 16 | 17 | .rightSideTitle { 18 | font-size: 10px; 19 | line-height: 1; 20 | font-weight: 700; 21 | text-transform: uppercase; 22 | letter-spacing: 1.2px; 23 | padding: 7px 24px 7px 16px; 24 | border-left: 1px solid #e6ecf1; 25 | border-left-color: rgb(230, 236, 241); 26 | 27 | color: ${props => props.theme.colors.text}; 28 | } 29 | 30 | .rightSideBarUL { 31 | margin-top: 32px; 32 | } 33 | 34 | .rightSideBarUL li { 35 | list-style-type: none; 36 | border-left: 1px solid #e6ecf1; 37 | border-left-color: rgb(230, 236, 241); 38 | } 39 | 40 | .rightSideBarUL li a { 41 | font-size: 12px; 42 | font-weight: 500; 43 | line-height: 1.5; 44 | padding: 7px 24px 7px 16px; 45 | 46 | color: ${props => props.theme.colors.text}; 47 | } 48 | 49 | @media only screen and (max-width: 50rem) { 50 | width: 100%; 51 | position: relative; 52 | } 53 | `; 54 | 55 | export const ListItem = styled(({ className, active, level, ...props }) => { 56 | return ( 57 |
    • 58 | 59 | {props.children} 60 | 61 |
    • 62 | ); 63 | })` 64 | list-style: none; 65 | 66 | a { 67 | color: #5c6975; 68 | text-decoration: none; 69 | font-weight: ${({ level }) => (level === 0 ? 700 : 400)}; 70 | padding: 0.45rem 0 0.45rem ${props => 2 + (props.level || 0) * 1}rem; 71 | display: block; 72 | position: relative; 73 | 74 | &:hover { 75 | color: #1ed3c6 !important; 76 | } 77 | 78 | ${props => 79 | props.active && 80 | ` 81 | color: #1ED3C6; 82 | border-color: rgb(230,236,241) !important; 83 | border-style: solid none solid solid; 84 | border-width: 1px 0px 1px 1px; 85 | background-color: #fff; 86 | `} // external link icon 87 | svg { 88 | float: right; 89 | margin-right: 1rem; 90 | } 91 | } 92 | `; 93 | -------------------------------------------------------------------------------- /src/components/theme.js: -------------------------------------------------------------------------------- 1 | export default { 2 | fonts: { 3 | mono: '"SF Mono", "Roboto Mono", Menlo, monospace', 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /src/components/theme/index.js: -------------------------------------------------------------------------------- 1 | const baseTheme = { 2 | fonts: { 3 | mono: '"SF Mono", "Roboto Mono", Menlo, monospace', 4 | }, 5 | }; 6 | 7 | const lightTheme = { 8 | ...baseTheme, 9 | colors: { 10 | background: '#fff', 11 | heading: '#000', 12 | text: '#3B454E', 13 | preFormattedText: 'rgb(245, 247, 249)', 14 | link: '#1000EE', 15 | }, 16 | }; 17 | 18 | const darkTheme = { 19 | ...baseTheme, 20 | colors: { 21 | background: '#001933', 22 | heading: '#fff', 23 | text: '#fff', 24 | preFormattedText: '#000', 25 | link: '#1ED3C6', 26 | }, 27 | }; 28 | 29 | export { lightTheme, darkTheme }; 30 | -------------------------------------------------------------------------------- /src/components/theme/themeProvider.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ThemeProvider as EmotionThemeProvider, Global, css } from '@emotion/react'; 3 | 4 | import { lightTheme, darkTheme } from './index'; 5 | import Header from '../Header'; 6 | import { baseStyles } from '../styles/GlobalStyles'; 7 | import { styles } from '../../custom/styles/styles'; 8 | 9 | class ThemeProvider extends React.Component { 10 | state = { 11 | isDarkThemeActive: false, 12 | }; 13 | 14 | componentDidMount() { 15 | this.retrieveActiveTheme(); 16 | } 17 | 18 | retrieveActiveTheme = () => { 19 | const isDarkThemeActive = JSON.parse(window.localStorage.getItem('isDarkThemeActive')); 20 | 21 | this.setState({ isDarkThemeActive }); 22 | }; 23 | 24 | toggleActiveTheme = () => { 25 | this.setState(prevState => ({ isDarkThemeActive: !prevState.isDarkThemeActive })); 26 | 27 | window.localStorage.setItem('isDarkThemeActive', JSON.stringify(!this.state.isDarkThemeActive)); 28 | }; 29 | 30 | render() { 31 | const { children, location } = this.props; 32 | 33 | const { isDarkThemeActive } = this.state; 34 | 35 | const currentActiveTheme = isDarkThemeActive ? darkTheme : lightTheme; 36 | 37 | return ( 38 |
      39 | 40 |
      45 | {children} 46 |
      47 | ); 48 | } 49 | } 50 | 51 | export default ThemeProvider; 52 | -------------------------------------------------------------------------------- /src/components/themeProvider.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ThemeProvider as EmotionThemeProvider } from '@emotion/react'; 3 | import { default as defaultTheme } from './theme'; 4 | import Header from './Header'; 5 | 6 | export default function ThemeProvider({ children, theme = {}, location }) { 7 | return ( 8 |
      9 |
      10 | {children} 11 |
      12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/custom-sw-code.js: -------------------------------------------------------------------------------- 1 | workbox.routing.registerRoute( 2 | new RegExp('https:.*min.(css|js)'), 3 | workbox.strategies.staleWhileRevalidate({ 4 | cacheName: 'cdn-cache', 5 | }) 6 | ); 7 | -------------------------------------------------------------------------------- /src/custom/config/codeBlockLanguages.js: -------------------------------------------------------------------------------- 1 | export function applyLanguages(_Prism) { 2 | /** 3 | * Here you have the possibility to add other supported languages to Prism and thus to the code highlighting. 4 | * 5 | * We support the following languages automatically: 6 | * markup, bash, clike, c, cpp, css, javascript, jsx, coffeescript, actionscript, css-extr, 7 | * diff, git, go, graphql, handlebars, json, less, makefile, markdown, objectivec, ocaml, python, 8 | * reason, sass, scss, sql, stylus, tsx, typescript, wasm, yaml 9 | * 10 | * If you need more languages you can add them yourself as follows: 11 | * (https://github.com/PrismJS/prism/tree/master/components) 12 | * 13 | * ```js 14 | * require("prismjs/components/prism-docker"); 15 | * require("prismjs/components/prism-ada"); 16 | * ``` 17 | */ 18 | 19 | } 20 | 21 | export function getTheme(_Prism) { 22 | /** 23 | * Here you have the possibility to change the prism highlighting. 24 | */ 25 | return require('prism-react-renderer/themes/vsDark').default; 26 | } 27 | -------------------------------------------------------------------------------- /src/custom/styles/styles.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | const customStyles = css` 4 | 5 | `; 6 | 7 | export const styles = [customStyles]; 8 | -------------------------------------------------------------------------------- /src/html.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import config from '../config'; 4 | 5 | export default class HTML extends React.Component { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | {config.siteMetadata.ogImage ? ( 14 | 15 | ) : null} 16 | 17 | {config.siteMetadata.ogImage ? ( 18 | 19 | ) : null} 20 | {config.siteMetadata.favicon ? ( 21 | 22 | ) : null} 23 | 24 | {this.props.headComponents} 25 | 26 | 27 | {this.props.preBodyComponents} 28 |
      29 | {this.props.postBodyComponents} 30 |