[#45] [command] gatsby new docs https://github.com/hasura/gatsby-gitbook-starter
This commit is contained in:
Родитель
0703e3a41a
Коммит
7eec15b051
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
public
|
||||
.cache
|
|
@ -0,0 +1,8 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"globals": {
|
||||
"graphql": false
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 10,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
public
|
||||
.cache
|
||||
node_modules
|
||||
*DS_Store
|
||||
*.env
|
||||
|
||||
.idea/
|
|
@ -0,0 +1,22 @@
|
|||
FROM node:buster
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install app dependencies
|
||||
# RUN npm -g install serve
|
||||
RUN npm -g install gatsby-cli
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm ci
|
||||
|
||||
# Bundle app source
|
||||
COPY . .
|
||||
|
||||
# Build static files
|
||||
RUN npm run build
|
||||
|
||||
# serve on port 8080
|
||||
# CMD ["serve", "-l", "tcp://0.0.0.0:8080", "public"]
|
||||
CMD ["gatsby", "serve", "--verbose", "--prefix-paths", "-p", "8080", "--host", "0.0.0.0"]
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Hasura
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,144 @@
|
|||
# gatsby-gitbook-starter
|
||||
|
||||
Kick off your project with this starter to create a powerful/flexible docs/tutorial web apps.
|
||||
|
||||
![gatsby-gitbook-starter](https://graphql-engine-cdn.hasura.io/learn-hasura/gatsby-gitbook-starter/assets/documentation_app_blog.png)
|
||||
|
||||
## Motivation
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## 🔥 Features
|
||||
- Write using Markdown / [MDX](https://github.com/mdx-js/mdx)
|
||||
- GitBook style theme
|
||||
- Syntax Highlighting using Prism [`Bonus`: Code diff highlighting]
|
||||
- Search Integration with Algolia
|
||||
- Progressive Web App, Works Offline
|
||||
- Google Analytics Integration
|
||||
- Automatically generated sidebar navigation, table of contents, previous/next
|
||||
- Edit on Github
|
||||
- Fully customisable
|
||||
- Rich embeds and live code editor using MDX
|
||||
- Easy deployment: Deploy on Netlify / Now.sh / Docker
|
||||
|
||||
## 🔗 Live Demo
|
||||
|
||||
Here's a [live demo](https://learn.hasura.io/graphql/react)
|
||||
|
||||
## 🚀 Quickstart
|
||||
|
||||
Get started by running the following commands:
|
||||
|
||||
```
|
||||
$ git clone git@github.com:hasura/gatsby-gitbook-starter.git
|
||||
$ npm install
|
||||
$ npm start
|
||||
```
|
||||
|
||||
Visit `http://localhost:8000/` to view the app.
|
||||
|
||||
## 🔧 Configure
|
||||
|
||||
Write markdown files in `content` folder.
|
||||
|
||||
Open `config.js` for templating variables. Broadly configuration is available for `gatsby`, `header`, `sidebar` and `siteMetadata`.
|
||||
|
||||
- `gatsby` config for global configuration like
|
||||
- `pathPrefix` - Gatsby Path Prefix
|
||||
- `siteUrl` - Gatsby Site URL
|
||||
- `gaTrackingId` - Google Analytics Tracking ID
|
||||
|
||||
- `header` config for site header configuration like
|
||||
- `title` - The title that appears on the top left
|
||||
- `githubUrl` - The Github URL for the docs website
|
||||
- `helpUrl` - Help URL for pointing to resources
|
||||
- `tweetText` - Tweet text
|
||||
- `links` - Links on the top right
|
||||
- `search` - Enable search and [configure Algolia](https://www.gatsbyjs.org/docs/adding-search-with-algolia/)
|
||||
|
||||
- `sidebar` config for navigation links configuration
|
||||
- `forcedNavOrder` for left sidebar navigation order. It should be in the format "/<filename>"
|
||||
- `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)
|
||||
- `links` - Links on the bottom left of the sidebar
|
||||
- `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.
|
||||
|
||||
- `siteMetadata` config for website related configuration
|
||||
- `title` - Title of the website
|
||||
- `description` - Description of the website
|
||||
- `ogImage` - Social Media share og:image tag
|
||||
- `docsLocation` - The Github URL for Edit on Github
|
||||
|
||||
- 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.
|
||||
|
||||
### Algolia Configuration
|
||||
|
||||
To setup Algolia, go to `config.js` and update the `search` object to look like the one below:
|
||||
|
||||
```...,
|
||||
"search": {
|
||||
"enabled": true,
|
||||
"indexName": "MY_INDEX_NAME",
|
||||
"algoliaAppId": process.env.GATSBY_ALGOLIA_APP_ID,
|
||||
"algoliaSearchKey": process.env.GATSBY_ALGOLIA_SEARCH_KEY,
|
||||
"algoliaAdminKey": process.env.ALGOLIA_ADMIN_KEY
|
||||
},
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Progressive Web App, Offline
|
||||
|
||||
To enable PWA, go to `config.js` and update the `pwa` object to look like the one below:
|
||||
|
||||
```
|
||||
"pwa": {
|
||||
"enabled": false, // disabling this will also remove the existing service worker.
|
||||
"manifest": {
|
||||
"name": "Gatsby Gitbook Starter",
|
||||
"short_name": "GitbookStarter",
|
||||
"start_url": "/",
|
||||
"background_color": "#6b37bf",
|
||||
"theme_color": "#6b37bf",
|
||||
"display": "standalone",
|
||||
"crossOrigin": "use-credentials",
|
||||
icons: [
|
||||
{
|
||||
src: "src/pwa-512.png",
|
||||
sizes: `512x512`,
|
||||
type: `image/png`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Live Code Editor
|
||||
|
||||
To render react components for live editing, add the `react-live=true` to the code section. For example:
|
||||
|
||||
```javascript react-live=true
|
||||
<button>Edit my text</button>
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## 🤖 SEO friendly
|
||||
|
||||
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
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Title of the page"
|
||||
metaTitle: "Meta Title Tag for this page"
|
||||
metaDescription: "Meta Description Tag for this page"
|
||||
---
|
||||
```
|
||||
|
||||
Canonical URLs are generated automatically.
|
||||
|
||||
## ☁️ Deploy
|
||||
|
||||
[![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)
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
const config = {
|
||||
"gatsby": {
|
||||
"pathPrefix": "/",
|
||||
"siteUrl": "https://learn.hasura.io",
|
||||
"gaTrackingId": null,
|
||||
"trailingSlash": false
|
||||
},
|
||||
"header": {
|
||||
"logo": "https://graphql-engine-cdn.hasura.io/learn-hasura/assets/homepage/favicon.png",
|
||||
"logoLink": "https://learn.hasura.io",
|
||||
"title": "Gatsby Gitbook Boilerplate",
|
||||
"githubUrl": "https://github.com/hasura/gatsby-gitbook-boilerplate",
|
||||
"helpUrl": "",
|
||||
"tweetText": "",
|
||||
"links": [
|
||||
{ "text": "", "link": ""}
|
||||
],
|
||||
"search": {
|
||||
"enabled": false,
|
||||
"indexName": "",
|
||||
"algoliaAppId": process.env.GATSBY_ALGOLIA_APP_ID,
|
||||
"algoliaSearchKey": process.env.GATSBY_ALGOLIA_SEARCH_KEY,
|
||||
"algoliaAdminKey": process.env.ALGOLIA_ADMIN_KEY
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"forcedNavOrder": [
|
||||
"/introduction", // add trailing slash if enabled above
|
||||
"/codeblock"
|
||||
],
|
||||
"collapsedNav": [
|
||||
"/codeblock" // add trailing slash if enabled above
|
||||
],
|
||||
"links": [
|
||||
{ "text": "Hasura", "link": "https://hasura.io"},
|
||||
],
|
||||
"frontline": false,
|
||||
"ignoreIndex": true,
|
||||
},
|
||||
"siteMetadata": {
|
||||
"title": "Gatsby Gitbook Boilerplate | Hasura",
|
||||
"description": "Documentation built with mdx. Powering learn.hasura.io ",
|
||||
"ogImage": null,
|
||||
"docsLocation": "https://github.com/hasura/gatsby-gitbook-boilerplate/tree/master/content",
|
||||
"favicon": "https://graphql-engine-cdn.hasura.io/img/hasura_icon_black.svg"
|
||||
},
|
||||
"pwa": {
|
||||
"enabled": false, // disabling this will also remove the existing service worker.
|
||||
"manifest": {
|
||||
"name": "Gatsby Gitbook Starter",
|
||||
"short_name": "GitbookStarter",
|
||||
"start_url": "/",
|
||||
"background_color": "#6b37bf",
|
||||
"theme_color": "#6b37bf",
|
||||
"display": "standalone",
|
||||
"crossOrigin": "use-credentials",
|
||||
icons: [
|
||||
{
|
||||
src: "src/pwa-512.png",
|
||||
sizes: `512x512`,
|
||||
type: `image/png`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: "Syntax Highlighting"
|
||||
metaTitle: "Syntax Highlighting is the meta title tag for this page"
|
||||
metaDescription: "This is the meta description for this page"
|
||||
---
|
||||
|
||||
The following is a code block with JavaScript language syntax highlighting.
|
||||
|
||||
```javascript
|
||||
import React from 'react';
|
||||
```
|
||||
|
||||
Supports multiple languages.
|
||||
|
||||
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.
|
||||
|
||||
```javascript
|
||||
- const data = ['1','2'];
|
||||
+ const data = [1,2];
|
||||
```
|
||||
|
||||
## Live Editing example
|
||||
|
||||
```javascript react-live=true
|
||||
<button className={'btn btn-default'}>Change my text</button>
|
||||
```
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: "Sub Page"
|
||||
metaTitle: "Syntax Highlighting is the meta title tag for this page"
|
||||
metaDescription: "This is the meta description for this page"
|
||||
---
|
||||
|
||||
The following is a code block with JavaScript language syntax highlighting.
|
||||
|
||||
```javascript
|
||||
import React from 'react';
|
||||
```
|
||||
|
||||
Supports multiple languages.
|
||||
|
||||
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.
|
||||
|
||||
```javascript
|
||||
- const data = ['1','2'];
|
||||
+ const data = [1,2];
|
||||
```
|
||||
|
||||
## Live Editing example
|
||||
|
||||
```javascript react-live=true
|
||||
<button className={'btn btn-default'}>Change my text</button>
|
||||
```
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
title: "Landing Page"
|
||||
---
|
||||
|
||||
Some introduction text. Lists out all the headings from h1 to h6. Easy to customise. Some more text. Additional text.
|
||||
|
||||
# Heading H1
|
||||
Heading 1 text
|
||||
|
||||
## Heading H2
|
||||
Heading 2 text
|
||||
|
||||
### Heading H3
|
||||
Heading 3 text
|
||||
|
||||
#### Heading H4
|
||||
Heading 4 text
|
||||
|
||||
##### Heading H5
|
||||
Heading 5 text
|
||||
|
||||
###### Heading H6
|
||||
Heading 6 text
|
||||
|
||||
## Lists
|
||||
- Item 1
|
||||
- Item 2
|
||||
- Item 3
|
||||
- Item 4
|
||||
- Item 5
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
title: "Introduction"
|
||||
metaTitle: "This is the title tag of this page"
|
||||
metaDescription: "This is the meta description"
|
||||
---
|
||||
|
||||
Some introduction text. Lists out all the headings from h1 to h6. Easy to customise.
|
||||
|
||||
# Heading H1
|
||||
Heading 1 text
|
||||
|
||||
## Heading H2
|
||||
Heading 2 text
|
||||
|
||||
### Heading H3
|
||||
Heading 3 text
|
||||
|
||||
#### Heading H4
|
||||
Heading 4 text
|
||||
|
||||
##### Heading H5
|
||||
Heading 5 text
|
||||
|
||||
###### Heading H6
|
||||
Heading 6 text
|
||||
|
||||
## Lists
|
||||
- Item 1
|
||||
- Item 2
|
||||
- Item 3
|
||||
- Item 4
|
||||
- Item 5
|
|
@ -1,22 +0,0 @@
|
|||
# Contributors
|
||||
|
||||
### Checkin
|
||||
|
||||
- Do checkin source (src)
|
||||
- Do checkin build output (lib)
|
||||
- Do checkin runtime node_modules
|
||||
- Do not checkin devDependency node_modules (husky can help see below)
|
||||
|
||||
### devDependencies
|
||||
|
||||
In order to handle correctly checking in node_modules without devDependencies, we run [Husky](https://github.com/typicode/husky) before each commit.
|
||||
This step ensures that formatting and checkin rules are followed and that devDependencies are excluded. To make sure Husky runs correctly, please use the following workflow:
|
||||
|
||||
```
|
||||
npm install # installs all devDependencies including Husky
|
||||
git add abc.ext # Add the files you've changed. This should include files in src, lib, and node_modules (see above)
|
||||
git commit -m "Informative commit message" # Commit. This will run Husky
|
||||
```
|
||||
|
||||
During the commit step, Husky will take care of formatting all files with [Prettier](https://github.com/prettier/prettier) as well as pruning out devDependencies using `npm prune --production`.
|
||||
It will also make sure these changes are appropriately included in your commit (no further work is needed)
|
|
@ -0,0 +1,9 @@
|
|||
export const onServiceWorkerUpdateReady = () => {
|
||||
const answer = window.confirm(
|
||||
`This tutorial has been updated. ` +
|
||||
`Reload to display the latest version?`
|
||||
)
|
||||
if (answer === true) {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
require("dotenv").config();
|
||||
const queries = require("./src/utils/algolia");
|
||||
const config = require("./config");
|
||||
const plugins = [
|
||||
'gatsby-plugin-sitemap',
|
||||
'gatsby-plugin-sharp',
|
||||
{
|
||||
resolve: `gatsby-plugin-layout`,
|
||||
options: {
|
||||
component: require.resolve(`./src/templates/docs.js`)
|
||||
}
|
||||
},
|
||||
'gatsby-plugin-emotion',
|
||||
'gatsby-plugin-react-helmet',
|
||||
{
|
||||
resolve: "gatsby-source-filesystem",
|
||||
options: {
|
||||
name: "docs",
|
||||
path: `${__dirname}/content/`
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-plugin-mdx',
|
||||
options: {
|
||||
gatsbyRemarkPlugins: [
|
||||
{
|
||||
resolve: "gatsby-remark-images",
|
||||
options: {
|
||||
maxWidth: 1035,
|
||||
sizeByPixelDensity: true
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-remark-copy-linked-files'
|
||||
}
|
||||
],
|
||||
extensions: [".mdx", ".md"]
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-gtag`,
|
||||
options: {
|
||||
// your google analytics tracking id
|
||||
trackingId: config.gatsby.gaTrackingId,
|
||||
// Puts tracking script in the head instead of the body
|
||||
head: true,
|
||||
// enable ip anonymization
|
||||
anonymize: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
// check and add algolia
|
||||
if (config.header.search && config.header.search.enabled && config.header.search.algoliaAppId && config.header.search.algoliaAdminKey) {
|
||||
plugins.push({
|
||||
resolve: `gatsby-plugin-algolia`,
|
||||
options: {
|
||||
appId: config.header.search.algoliaAppId, // algolia application id
|
||||
apiKey: config.header.search.algoliaAdminKey, // algolia admin key to index
|
||||
queries,
|
||||
chunkSize: 10000, // default: 1000
|
||||
}}
|
||||
)
|
||||
}
|
||||
// check and add pwa functionality
|
||||
if (config.pwa && config.pwa.enabled && config.pwa.manifest) {
|
||||
plugins.push({
|
||||
resolve: `gatsby-plugin-manifest`,
|
||||
options: {...config.pwa.manifest},
|
||||
});
|
||||
plugins.push({
|
||||
resolve: 'gatsby-plugin-offline',
|
||||
options: {
|
||||
appendScript: require.resolve(`./src/custom-sw-code.js`),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
plugins.push('gatsby-plugin-remove-serviceworker');
|
||||
}
|
||||
|
||||
// check and remove trailing slash
|
||||
if (config.gatsby && !config.gatsby.trailingSlash) {
|
||||
plugins.push('gatsby-plugin-remove-trailing-slashes');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
pathPrefix: config.gatsby.pathPrefix,
|
||||
siteMetadata: {
|
||||
title: config.siteMetadata.title,
|
||||
description: config.siteMetadata.description,
|
||||
docsLocation: config.siteMetadata.docsLocation,
|
||||
ogImage: config.siteMetadata.ogImage,
|
||||
favicon: config.siteMetadata.favicon,
|
||||
logo: { link: config.header.logoLink ? config.header.logoLink : '/', image: config.header.logo }, // backwards compatible
|
||||
headerTitle: config.header.title,
|
||||
githubUrl: config.header.githubUrl,
|
||||
helpUrl: config.header.helpUrl,
|
||||
tweetText: config.header.tweetText,
|
||||
headerLinks: config.header.links,
|
||||
siteUrl: config.gatsby.siteUrl,
|
||||
},
|
||||
plugins: plugins
|
||||
};
|
|
@ -0,0 +1,104 @@
|
|||
const componentWithMDXScope = require("gatsby-plugin-mdx/component-with-mdx-scope");
|
||||
const path = require("path");
|
||||
const startCase = require("lodash.startcase");
|
||||
const config = require("./config");
|
||||
|
||||
exports.createPages = ({ graphql, actions }) => {
|
||||
const { createPage } = actions;
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(
|
||||
graphql(
|
||||
`
|
||||
{
|
||||
allMdx {
|
||||
edges {
|
||||
node {
|
||||
fields {
|
||||
id
|
||||
}
|
||||
tableOfContents
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
).then(result => {
|
||||
if (result.errors) {
|
||||
console.log(result.errors); // eslint-disable-line no-console
|
||||
reject(result.errors);
|
||||
}
|
||||
|
||||
// Create blog posts pages.
|
||||
result.data.allMdx.edges.forEach(({ node }) => {
|
||||
createPage({
|
||||
path: node.fields.slug ? node.fields.slug : "/",
|
||||
component: path.resolve("./src/templates/docs.js"),
|
||||
context: {
|
||||
id: node.fields.id
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
exports.onCreateWebpackConfig = ({ actions }) => {
|
||||
actions.setWebpackConfig({
|
||||
resolve: {
|
||||
modules: [path.resolve(__dirname, "src"), "node_modules"],
|
||||
alias: {
|
||||
$components: path.resolve(__dirname, "src/components"),
|
||||
buble: '@philpl/buble' // to reduce bundle size
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.onCreateBabelConfig = ({ actions }) => {
|
||||
actions.setBabelPlugin({
|
||||
name: "@babel/plugin-proposal-export-default-from"
|
||||
});
|
||||
};
|
||||
|
||||
exports.onCreateNode = ({ node, getNode, actions }) => {
|
||||
const { createNodeField } = actions;
|
||||
|
||||
if (node.internal.type === `Mdx`) {
|
||||
const parent = getNode(node.parent);
|
||||
let value = parent.relativePath.replace(parent.ext, "");
|
||||
|
||||
if (value === "index") {
|
||||
value = "";
|
||||
}
|
||||
|
||||
if(config.gatsby && config.gatsby.trailingSlash) {
|
||||
createNodeField({
|
||||
name: `slug`,
|
||||
node,
|
||||
value: value === "" ? `/` : `/${value}/`
|
||||
});
|
||||
} else {
|
||||
createNodeField({
|
||||
name: `slug`,
|
||||
node,
|
||||
value: `/${value}`
|
||||
});
|
||||
}
|
||||
|
||||
createNodeField({
|
||||
name: "id",
|
||||
node,
|
||||
value: node.id
|
||||
});
|
||||
|
||||
createNodeField({
|
||||
name: "title",
|
||||
node,
|
||||
value: node.frontmatter.title || startCase(parent.name)
|
||||
});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
[build]
|
||||
publish = "public"
|
||||
command = "npm run build"
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"name": "gatsby-gitbook-boilerplate",
|
||||
"private": true,
|
||||
"description": "Documentation, built with mdx",
|
||||
"author": "Praveen <praveen@hasura.io> (@praveenweb)",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-export-default-from": "^7.7.4",
|
||||
"@emotion/core": "^10.0.22",
|
||||
"@emotion/styled": "^10.0.23",
|
||||
"@emotion/styled-base": "^10.0.24",
|
||||
"@mdx-js/loader": "^1.5.1",
|
||||
"@mdx-js/mdx": "^1.5.1",
|
||||
"@mdx-js/react": "^1.5.1",
|
||||
"@philpl/buble": "^0.19.7",
|
||||
"@playlyfe/gql": "^2.6.2",
|
||||
"algoliasearch": "^3.35.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"emotion": "^10.0.23",
|
||||
"emotion-server": "^10.0.17",
|
||||
"emotion-theming": "^10.0.19",
|
||||
"gatsby": "^2.18.10",
|
||||
"gatsby-link": "^2.2.27",
|
||||
"gatsby-plugin-algolia": "^0.5.0",
|
||||
"gatsby-plugin-emotion": "^4.1.18",
|
||||
"gatsby-plugin-gtag": "^1.0.12",
|
||||
"gatsby-plugin-layout": "^1.1.18",
|
||||
"gatsby-plugin-manifest": "^2.2.33",
|
||||
"gatsby-plugin-mdx": "^1.0.61",
|
||||
"gatsby-plugin-offline": "^3.0.29",
|
||||
"gatsby-plugin-react-helmet": "^3.1.18",
|
||||
"gatsby-plugin-remove-serviceworker": "^1.0.0",
|
||||
"gatsby-plugin-sharp": "^2.3.7",
|
||||
"gatsby-plugin-sitemap": "^2.2.24",
|
||||
"gatsby-remark-copy-linked-files": "^2.1.33",
|
||||
"gatsby-remark-images": "^3.1.37",
|
||||
"gatsby-source-filesystem": "^2.1.42",
|
||||
"gatsby-transformer-remark": "^2.6.42",
|
||||
"graphql": "^14.5.8",
|
||||
"is-absolute-url": "^3.0.3",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"lodash.startcase": "^4.4.0",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-feather": "^2.0.3",
|
||||
"react-github-btn": "^1.1.1",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-id-generator": "^3.0.0",
|
||||
"react-instantsearch-dom": "^6.0.0",
|
||||
"react-live": "^2.2.2",
|
||||
"react-loadable": "^5.5.0",
|
||||
"styled-components": "^4.4.1",
|
||||
"styled-icons": "^9.0.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "n/a",
|
||||
"scripts": {
|
||||
"start": "gatsby develop",
|
||||
"build": "gatsby build --prefix-paths"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gatsby-plugin-remove-trailing-slashes": "^2.1.17",
|
||||
"prism-react-renderer": "^1.0.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import './components/styles.css';
|
||||
|
||||
const CommunityAuthor = ({name, imageUrl, twitterUrl, githubUrl, description}) => {
|
||||
return(
|
||||
<>
|
||||
<h2 className="communitySection">About the community author</h2>
|
||||
<div className="authorSection">
|
||||
<div className="authorImg">
|
||||
<img src={imageUrl} alt={name} />
|
||||
</div>
|
||||
<div className="authorDetails">
|
||||
<div className="authorName">
|
||||
<strong>{name}</strong>
|
||||
{twitterUrl ? (<a href={twitterUrl} target="_blank" rel="noopener"><img src="https://storage.googleapis.com/graphql-engine-cdn.hasura.io/learn-hasura/assets/social-media/twitter-icon.svg" alt="Twitter Icon" aria-label="Twitter" /></a>) : null}
|
||||
{githubUrl ? (<a href={githubUrl} target="_blank" rel="noopener"><img src="https://storage.googleapis.com/graphql-engine-cdn.hasura.io/learn-hasura/assets/social-media/github-icon.svg" alt="Github Icon" aria-label="Github" /></a>) : null}
|
||||
</div>
|
||||
<div className="authorDesc">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
export default CommunityAuthor;
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react';
|
||||
const githubIcon = require('./components/images/github.svg');
|
||||
import './components/styles.css';
|
||||
|
||||
const GithubLink = ({link, text}) => {
|
||||
return (
|
||||
<a href={link} className="githubSection">
|
||||
<img className="githubIcon" src={githubIcon} alt="github"/>
|
||||
{text}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
export default GithubLink;
|
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import './components/styles.css';
|
||||
|
||||
const YoutubeEmbed = ({link}) => {
|
||||
return (
|
||||
<div className="video-responsive">
|
||||
<iframe width="750" height="422" src={link} frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen>
|
||||
</iframe>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default YoutubeEmbed;
|
|
@ -0,0 +1,141 @@
|
|||
import React from 'react';
|
||||
import { StaticQuery, graphql } from 'gatsby';
|
||||
import GitHubButton from 'react-github-btn'
|
||||
import Link from './link';
|
||||
import './styles.css';
|
||||
import config from '../../config.js';
|
||||
|
||||
import Loadable from 'react-loadable';
|
||||
import LoadingProvider from './mdxComponents/loading';
|
||||
|
||||
const help = require('./images/help.svg');
|
||||
const isSearchEnabled = config.header.search && config.header.search.enabled ? true : false;
|
||||
|
||||
let searchIndices = [];
|
||||
if(isSearchEnabled && config.header.search.indexName) {
|
||||
searchIndices.push(
|
||||
{ name: `${config.header.search.indexName}`, title: `Results`, hitComp: `PageHit` },
|
||||
);
|
||||
}
|
||||
|
||||
import Sidebar from "./sidebar";
|
||||
|
||||
const LoadableComponent = Loadable({
|
||||
loader: () => import('./search/index'),
|
||||
loading: LoadingProvider,
|
||||
});
|
||||
|
||||
function myFunction() {
|
||||
var x = document.getElementById("navbar");
|
||||
if (x.className === "topnav") {
|
||||
x.className += " responsive";
|
||||
} else {
|
||||
x.className = "topnav";
|
||||
}
|
||||
}
|
||||
|
||||
const Header = ({location}) => (
|
||||
<StaticQuery
|
||||
query={
|
||||
graphql`
|
||||
query headerTitleQuery {
|
||||
site {
|
||||
siteMetadata {
|
||||
headerTitle
|
||||
githubUrl
|
||||
helpUrl
|
||||
tweetText
|
||||
logo {
|
||||
link
|
||||
image
|
||||
}
|
||||
headerLinks {
|
||||
link
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={(data) => {
|
||||
const logoImg = require('./images/logo.svg');
|
||||
const twitter = require('./images/twitter.svg');
|
||||
const {
|
||||
site: {
|
||||
siteMetadata: {
|
||||
headerTitle,
|
||||
githubUrl,
|
||||
helpUrl,
|
||||
tweetText,
|
||||
logo,
|
||||
headerLinks,
|
||||
}
|
||||
}
|
||||
} = data;
|
||||
const finalLogoLink = logo.link !== '' ? logo.link : '/';
|
||||
return (
|
||||
<div className={'navBarWrapper'}>
|
||||
<nav className={'navBarDefault'}>
|
||||
<div className={'navBarHeader'}>
|
||||
<Link to={finalLogoLink} className={'navBarBrand'}>
|
||||
<img className={'img-responsive displayInline'} src={(logo.image !== '') ? logo.image : logoImg} alt={'logo'} />
|
||||
</Link>
|
||||
<div className={"headerTitle displayInline"} dangerouslySetInnerHTML={{__html: headerTitle}} />
|
||||
<span onClick={myFunction} className={'navBarToggle'}>
|
||||
<span className={'iconBar'}></span>
|
||||
<span className={'iconBar'}></span>
|
||||
<span className={'iconBar'}></span>
|
||||
</span>
|
||||
</div>
|
||||
{isSearchEnabled ? (
|
||||
<div className={'searchWrapper hiddenMobile navBarUL'}>
|
||||
<LoadableComponent collapse={true} indices={searchIndices} />
|
||||
</div>
|
||||
): null}
|
||||
<div id="navbar" className={'topnav'}>
|
||||
<div className={'visibleMobile'}>
|
||||
<Sidebar location={location} />
|
||||
<hr/>
|
||||
{isSearchEnabled ? (
|
||||
<div className={'searchWrapper'}>
|
||||
<LoadableComponent collapse={true} indices={searchIndices} />
|
||||
</div>
|
||||
): null}
|
||||
</div>
|
||||
<ul className={'navBarUL navBarNav navBarULRight'}>
|
||||
{headerLinks.map((link, key) => {
|
||||
if(link.link !== '' && link.text !== '') {
|
||||
return(
|
||||
<li key={key}>
|
||||
<a className="sidebarLink" href={link.link} target="_blank" rel="noopener" dangerouslySetInnerHTML={{__html: link.text}} />
|
||||
</li>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{helpUrl !== '' ?
|
||||
(<li><a href={helpUrl}><img src={help} alt={'Help icon'}/></a></li>) : null
|
||||
}
|
||||
{(tweetText !== '' || githubUrl !== '') ?
|
||||
(<li className="divider hiddenMobile"></li>): null
|
||||
}
|
||||
{tweetText !== '' ?
|
||||
(<li>
|
||||
<a href={'https://twitter.com/intent/tweet?&text=' + tweetText} target="_blank" rel="noopener">
|
||||
<img className={'shareIcon'} src={twitter} alt={'Twitter'} />
|
||||
</a>
|
||||
</li>) : null
|
||||
}
|
||||
{githubUrl !== '' ?
|
||||
(<li className={'githubBtn'}>
|
||||
<GitHubButton href={githubUrl} data-show-count="true" aria-label="Star on GitHub">Star</GitHubButton>
|
||||
</li>) : null}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Header;
|
|
@ -0,0 +1,78 @@
|
|||
import React from 'react';
|
||||
import Link from "./link";
|
||||
import './styles.css';
|
||||
class NextPrevious extends React.Component {
|
||||
render() {
|
||||
const { mdx, nav } = this.props;
|
||||
let currentIndex;
|
||||
const currentPaginationInfo = nav.map((el, index) => {
|
||||
if (el && (el.url === mdx.fields.slug)) {
|
||||
currentIndex = index;
|
||||
}
|
||||
});
|
||||
const nextInfo = {};
|
||||
const previousInfo = {};
|
||||
if (currentIndex === undefined) { // index
|
||||
if(nav[0]) {
|
||||
nextInfo.url = nav[0].url;
|
||||
nextInfo.title = nav[0].title;
|
||||
}
|
||||
previousInfo.url = null;
|
||||
previousInfo.title = null;
|
||||
currentIndex = -1;
|
||||
} else if (currentIndex === 0) { // first page
|
||||
nextInfo.url = nav[currentIndex+1] ? nav[currentIndex+1].url : null;
|
||||
nextInfo.title = nav[currentIndex+1] ? nav[currentIndex+1].title : null;
|
||||
previousInfo.url = null;
|
||||
previousInfo.title = null;
|
||||
} else if (currentIndex === (nav.length-1)) { // last page
|
||||
nextInfo.url = null;
|
||||
nextInfo.title = null;
|
||||
previousInfo.url = nav[currentIndex-1] ? nav[currentIndex-1].url : null;
|
||||
previousInfo.title = nav[currentIndex-1] ? nav[currentIndex-1].title : null;
|
||||
} else if (currentIndex) { // any other page
|
||||
nextInfo.url = nav[currentIndex+1].url;
|
||||
nextInfo.title = nav[currentIndex+1].title;
|
||||
if(nav[currentIndex-1]) {
|
||||
previousInfo.url = nav[currentIndex-1].url;
|
||||
previousInfo.title = nav[currentIndex-1].title;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className={'nextPreviousWrapper'}>
|
||||
{previousInfo.url && currentIndex >= 0 ?
|
||||
(<Link to={nav[currentIndex-1].url} className={'previousBtn'}>
|
||||
<div className={'leftArrow'}>
|
||||
<svg preserveAspectRatio="xMidYMid meet" height="1em" width="1em" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" stroke="currentColor" className="_13gjrqj"><g><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></g></svg>
|
||||
</div>
|
||||
<div className={'preRightWrapper'}>
|
||||
<div className={'smallContent'}>
|
||||
<span>Previous</span>
|
||||
</div>
|
||||
<div className={'nextPreviousTitle'}>
|
||||
<span>{nav[currentIndex-1].title}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>) : null
|
||||
}
|
||||
{nextInfo.url && currentIndex >= 0 ?
|
||||
(<Link to={nav[currentIndex+1].url} className={'nextBtn'}>
|
||||
<div className={'nextRightWrapper'}>
|
||||
<div className={'smallContent'}>
|
||||
<span>Next</span>
|
||||
</div>
|
||||
<div className={'nextPreviousTitle'}>
|
||||
<span>{nav[currentIndex+1] && nav[currentIndex+1].title}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'rightArrow'}>
|
||||
<svg preserveAspectRatio="xMidYMid meet" height="1em" width="1em" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" stroke="currentColor" className="_13gjrqj"><g><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></g></svg>
|
||||
</div>
|
||||
</Link>) : null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NextPrevious;
|
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
const ClosedSvg = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M7.33 24l-2.83-2.829 9.339-9.175-9.339-9.167 2.83-2.829 12.17 11.996z"/></svg>
|
||||
)
|
||||
|
||||
export default ClosedSvg;
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 2350 2314.8" xml:space="preserve">
|
||||
<path d="M1175,0C525.8,0,0,525.8,0,1175c0,552.2,378.9,1010.5,890.1,1139.7c-5.9-14.7-8.8-35.3-8.8-55.8v-199.8H734.4
|
||||
c-79.3,0-152.8-35.2-185.1-99.9c-38.2-70.5-44.1-179.2-141-246.8c-29.4-23.5-5.9-47,26.4-44.1c61.7,17.6,111.6,58.8,158.6,120.4
|
||||
c47,61.7,67.6,76.4,155.7,76.4c41.1,0,105.7-2.9,164.5-11.8c32.3-82.3,88.1-155.7,155.7-190.9c-393.6-47-581.6-240.9-581.6-505.3
|
||||
c0-114.6,49.9-223.3,132.2-317.3c-26.4-91.1-61.7-279.1,11.8-352.5c176.3,0,282,114.6,308.4,143.9c88.1-29.4,185.1-47,284.9-47
|
||||
c102.8,0,196.8,17.6,284.9,47c26.4-29.4,132.2-143.9,308.4-143.9c70.5,70.5,38.2,261.4,8.8,352.5c82.3,91.1,129.3,202.7,129.3,317.3
|
||||
c0,264.4-185.1,458.3-575.7,499.4c108.7,55.8,185.1,214.4,185.1,331.9V2256c0,8.8-2.9,17.6-2.9,26.4
|
||||
C2021,2123.8,2350,1689.1,2350,1175C2350,525.8,1824.2,0,1175,0L1175,0z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" id="baseline-help-24px" width="24" height="24" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{fill:none}.cls-2{fill:#fff}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="Path_196" d="M0 0h24v24H0z" class="cls-1" data-name="Path 196"/>
|
||||
<path id="Path_197" d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm1 17h-2v-2h2zm2.07-7.75l-.9.92A3.4 3.4 0 0 0 13 15h-2v-.5a4.025 4.025 0 0 1 1.17-2.83l1.24-1.26A1.955 1.955 0 0 0 14 9a2 2 0 0 0-4 0H8a4 4 0 0 1 8 0 3.182 3.182 0 0 1-.93 2.25z" class="cls-2" data-name="Path 197"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 581 B |
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 85 95" style="enable-background:new 0 0 85 95;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<g>
|
||||
<path class="st0" d="M79.9,31.8c2.6-6.3,2.6-19.1-0.8-28.9l0,0c-0.9-1.8-3.6-1.2-3.7,0.7v0.7c-0.6,9.4-4,14.5-9.1,17
|
||||
c-0.8,0.4-2.1,0.4-3-0.2c-6-3.8-13.2-6-20.9-6s-14.8,2.3-20.9,6c-0.8,0.5-1.8,0.6-2.6,0.2c-5.2-1.9-8.8-7.6-9.4-17.1V3.5
|
||||
C9.5,1.6,6.7,1,5.9,2.8c-3.4,10-3.3,22.8-0.8,29c1.3,3.1,1.3,6.6,0.2,9.8c-1.4,4-2.1,8.5-2,13c0.5,20.6,18,38.2,38.7,38.5
|
||||
C63.8,93.5,81.5,76,81.5,54.1c0-4.4-0.7-8.5-2-12.4C78.5,38.5,78.6,34.9,79.9,31.8z M41.9,83.9C26.1,83.5,13,70.6,12.8,54.7
|
||||
C12.4,37.8,26.2,24,43,24.4c15.9,0.2,28.9,13.3,29.2,29.2C72.5,70.4,58.8,84.1,41.9,83.9z"/>
|
||||
<path class="st0" d="M47,50.9l-7-10.8c-1.1-1.8-3.4-2.3-5.2-1.2c-1.1,0.7-1.8,1.9-1.8,3.2c0,0.7,0.2,1.4,0.6,2l4.7,7.4
|
||||
c0.4,0.6,0.2,1.3-0.1,1.8l-7.4,8.1c-1.4,1.5-1.3,3.9,0.2,5.3c0.7,0.6,1.7,0.9,2.6,0.9c1.1,0,2-0.5,2.7-1.2l5.5-6.4
|
||||
c0.5-0.5,1.2-0.5,1.5,0.1l3.9,5.6c0.2,0.5,0.6,0.8,1.1,1.1c1.3,0.9,3,0.9,4.3,0.1l0,0c1.1-0.7,1.8-1.9,1.8-3.2
|
||||
c0-0.7-0.2-1.4-0.6-2L47,50.9z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
const OpenedSvg = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M0 7.33l2.829-2.83 9.175 9.339 9.167-9.339 2.829 2.83-11.996 12.17z"/></svg>
|
||||
);
|
||||
|
||||
export default OpenedSvg;
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 35 27" style="enable-background:new 0 0 35 27;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M17,7.2l0.1,1.2l-1.3-0.1c-4.6-0.5-8.6-2.4-12-5.5L2.1,1.1L1.7,2.3C0.8,4.8,1.3,7.5,3.2,9.3c1,1,0.8,1.1-1,0.5
|
||||
C1.7,9.7,1.1,9.5,1.1,9.6C0.9,9.8,1.5,11.9,2,12.8c0.7,1.2,2,2.4,3.5,3.1l1.2,0.5l-1.5,0c-1.4,0-1.5,0-1.3,0.5
|
||||
c0.5,1.5,2.5,3.2,4.7,3.9l1.6,0.5l-1.4,0.8c-2,1.1-4.4,1.7-6.8,1.8C0.9,23.9,0,24,0,24.1c0,0.2,3.1,1.6,4.9,2.1
|
||||
c5.4,1.5,11.8,0.9,16.6-1.8c3.4-1.9,6.8-5.6,8.4-9.2c0.9-1.9,1.7-5.5,1.7-7.1c0-1.1,0.1-1.2,1.5-2.6c0.8-0.8,1.6-1.6,1.8-1.8
|
||||
c0.3-0.5,0.2-0.5-1.1,0c-2.1,0.7-2.5,0.6-1.4-0.5c0.8-0.8,1.7-2.1,1.7-2.5c0-0.1-0.4,0-0.8,0.3c-0.5,0.2-1.5,0.6-2.2,0.8l-1.4,0.4
|
||||
l-1.2-0.8c-0.7-0.4-1.6-0.9-2.1-1c-1.3-0.3-3.3-0.3-4.4,0.1C18.8,1.4,16.8,4.2,17,7.2z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -0,0 +1,5 @@
|
|||
export theme from "./theme";
|
||||
export mdxComponents from "./mdxComponents";
|
||||
export ThemeProvider from "./themeProvider";
|
||||
export Layout from "./layout";
|
||||
export Link from "./link";
|
|
@ -0,0 +1,62 @@
|
|||
import React from "react";
|
||||
import styled from "@emotion/styled";
|
||||
import { MDXProvider } from "@mdx-js/react";
|
||||
import ThemeProvider from "./themeProvider";
|
||||
import mdxComponents from "./mdxComponents";
|
||||
import Sidebar from "./sidebar";
|
||||
import RightSidebar from "./rightSidebar";
|
||||
|
||||
const Wrapper = styled('div')`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
const Content = styled('main')`
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
margin: 0px 88px;
|
||||
margin-top: 3rem;
|
||||
|
||||
@media only screen and (max-width: 1023px) {
|
||||
padding-left: 0;
|
||||
margin: 0 10px;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const MaxWidth = styled('div')`
|
||||
|
||||
@media only screen and (max-width: 50rem) {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
const LeftSideBarWidth = styled('div')`
|
||||
width: 298px;
|
||||
`;
|
||||
const RightSideBarWidth = styled('div')`
|
||||
width: 224px;
|
||||
`;
|
||||
const Layout = ({ children, location }) => (
|
||||
<ThemeProvider location={location}>
|
||||
<MDXProvider components={mdxComponents}>
|
||||
<Wrapper>
|
||||
<LeftSideBarWidth className={'hiddenMobile'}>
|
||||
<Sidebar location={location} />
|
||||
</LeftSideBarWidth>
|
||||
<Content>
|
||||
<MaxWidth>{children}</MaxWidth>
|
||||
</Content>
|
||||
<RightSideBarWidth className={'hiddenMobile'}>
|
||||
<RightSidebar location={location} />
|
||||
</RightSideBarWidth>
|
||||
</Wrapper>
|
||||
</MDXProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
export default Layout;
|
|
@ -0,0 +1,12 @@
|
|||
import React from "react";
|
||||
import { Link as GatsbyLink } from "gatsby";
|
||||
import isAbsoluteUrl from "is-absolute-url";
|
||||
|
||||
const Link = ({ to, ...props }) =>
|
||||
isAbsoluteUrl(to) ? (
|
||||
<a href={to} {...props} />
|
||||
) : (
|
||||
<GatsbyLink to={to} {...props} />
|
||||
);
|
||||
|
||||
export default Link;
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
|
||||
|
||||
const ReactLiveProvider = ({ code }) => {
|
||||
return (
|
||||
<LiveProvider code={code}>
|
||||
<LiveEditor />
|
||||
<LiveError />
|
||||
<LivePreview />
|
||||
</LiveProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReactLiveProvider;
|
|
@ -0,0 +1,13 @@
|
|||
import * as React from "react";
|
||||
|
||||
const AnchorTag = ({ children: link, ...props }) => {
|
||||
if(link) {
|
||||
return (
|
||||
<a href={props.href} target="_blank" rel="noopener">{link}</a>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default AnchorTag;
|
|
@ -0,0 +1,97 @@
|
|||
import * as React from "react";
|
||||
import Highlight, { defaultProps } from "prism-react-renderer";
|
||||
import prismTheme from "prism-react-renderer/themes/vsDark";
|
||||
import Loadable from 'react-loadable';
|
||||
import LoadingProvider from './loading';
|
||||
import '../styles.css';
|
||||
|
||||
/** Removes the last token from a code example if it's empty. */
|
||||
function cleanTokens(tokens) {
|
||||
const tokensLength = tokens.length;
|
||||
if (tokensLength === 0) {
|
||||
return tokens;
|
||||
}
|
||||
const lastToken = tokens[tokensLength - 1];
|
||||
if (lastToken.length === 1 && lastToken[0].empty) {
|
||||
return tokens.slice(0, tokensLength - 1);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
const LoadableComponent = Loadable({
|
||||
loader: () => import('./LiveProvider'),
|
||||
loading: LoadingProvider,
|
||||
});
|
||||
|
||||
/* eslint-disable react/jsx-key */
|
||||
const CodeBlock = ({ children: exampleCode, ...props }) => {
|
||||
if (props["react-live"]) {
|
||||
return (
|
||||
<LoadableComponent code={exampleCode} />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Highlight
|
||||
{...defaultProps}
|
||||
code={exampleCode}
|
||||
language="javascript"
|
||||
theme={prismTheme}
|
||||
>
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<pre className={className + ' pre'} style={style} p={3}>
|
||||
{cleanTokens(tokens).map((line, i) => {
|
||||
let lineClass = {};
|
||||
let isDiff = false;
|
||||
if (line[0] && line[0].content.length && line[0].content[0] === '+') {
|
||||
lineClass = {'backgroundColor': 'rgba(76, 175, 80, 0.2)'};
|
||||
isDiff = true;
|
||||
}
|
||||
else if (line[0] && line[0].content.length && line[0].content[0] === '-') {
|
||||
lineClass = {'backgroundColor': 'rgba(244, 67, 54, 0.2)'};
|
||||
isDiff = true;
|
||||
}
|
||||
else if(line[0] && line[0].content === '' && line[1] && line[1].content === '+') {
|
||||
lineClass = {'backgroundColor': 'rgba(76, 175, 80, 0.2)'};
|
||||
isDiff = true;
|
||||
} else if(line[0] && line[0].content === ''&& line[1] && line[1].content === '-') {
|
||||
lineClass = {'backgroundColor': 'rgba(244, 67, 54, 0.2)'};
|
||||
isDiff = true;
|
||||
}
|
||||
const lineProps = getLineProps({line, key: i});
|
||||
lineProps.style = lineClass;
|
||||
const diffStyle = {'userSelect': 'none', 'MozUserSelect': '-moz-none', 'WebkitUserSelect': 'none'};
|
||||
let splitToken;
|
||||
return (
|
||||
<div {...lineProps} key={line+i}>
|
||||
{line.map((token, key) => {
|
||||
if(isDiff) {
|
||||
if ((key === 0 || key === 1) & (token.content.charAt(0) === '+' || token.content.charAt(0) === '-')) {
|
||||
if(token.content.length > 1) {
|
||||
splitToken = { 'types': ['template-string','string'], 'content': token.content.slice(1)};
|
||||
const firstChar = { 'types': ['operator'], 'content': token.content.charAt(0)};
|
||||
return (
|
||||
<React.Fragment key={token+key}>
|
||||
<span {...getTokenProps({ token: firstChar, key })} style={diffStyle} />
|
||||
<span {...getTokenProps({ token: splitToken, key })} />
|
||||
</React.Fragment>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<span {...getTokenProps({ token, key })} style={diffStyle} />
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return (<span {...getTokenProps({ token, key })} />)
|
||||
} )}
|
||||
</div>
|
||||
)})}
|
||||
</pre>
|
||||
)}
|
||||
</Highlight>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default CodeBlock;
|
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
import CodeBlock from "./codeBlock";
|
||||
import AnchorTag from "./anchor";
|
||||
import '../styles.css';
|
||||
|
||||
export default {
|
||||
h1: props => <h1 className='heading1' id={props.children.replace(/\s+/g, '').toLowerCase()} {...props} />,
|
||||
h2: props => <h2 className='heading2' id={props.children.replace(/\s+/g, '').toLowerCase()} {...props} />,
|
||||
h3: props => <h3 className='heading3' id={props.children.replace(/\s+/g, '').toLowerCase()} {...props} />,
|
||||
h4: props => <h4 className='heading4' id={props.children.replace(/\s+/g, '').toLowerCase()} {...props} />,
|
||||
h5: props => <h5 className='heading5' id={props.children.replace(/\s+/g, '').toLowerCase()} {...props} />,
|
||||
h6: props => <h6 className='heading6' id={props.children.replace(/\s+/g, '').toLowerCase()} {...props} />,
|
||||
p: props => <p className='paragraph' {...props} />,
|
||||
pre: props => <pre className='pre' {...props} />,
|
||||
code: CodeBlock,
|
||||
a: AnchorTag
|
||||
// TODO add `img`
|
||||
// TODO add `blockquote`
|
||||
// TODO add `ul`
|
||||
// TODO add `li`
|
||||
// TODO add `table`
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import * as React from "react";
|
||||
|
||||
const LoadingProvider = ({ ...props }) => {
|
||||
return (
|
||||
<div></div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingProvider;
|
|
@ -0,0 +1,130 @@
|
|||
import React from "react";
|
||||
import { StaticQuery, graphql } from "gatsby";
|
||||
import styled from "@emotion/styled";
|
||||
import Link from "./link";
|
||||
import './styles.css';
|
||||
import config from '../../config';
|
||||
|
||||
const Sidebar = styled('aside')`
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #ede7f3;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
padding-left: 24px;
|
||||
position: -webkit-sticky;
|
||||
position: -moz-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@media only screen and (max-width: 50rem) {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const ListItem = styled(({ className, active, level, ...props }) => {
|
||||
return (
|
||||
<li className={className}>
|
||||
<a href={props.to} {...props} />
|
||||
</li>
|
||||
);
|
||||
})`
|
||||
list-style: none;
|
||||
|
||||
a {
|
||||
color: #5C6975;
|
||||
text-decoration: none;
|
||||
font-weight: ${({ level }) => (level === 0 ? 700 : 400)};
|
||||
padding: 0.45rem 0 0.45rem ${props => 2 + (props.level || 0) * 1}rem;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
color: rgb(116, 76, 188) !important;
|
||||
}
|
||||
|
||||
${props =>
|
||||
props.active &&
|
||||
`
|
||||
color: #663399;
|
||||
border-color: rgb(230,236,241) !important;
|
||||
border-style: solid none solid solid;
|
||||
border-width: 1px 0px 1px 1px;
|
||||
background-color: #fff;
|
||||
`} // external link icon
|
||||
svg {
|
||||
float: right;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const SidebarLayout = ({ location }) => (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
allMdx {
|
||||
edges {
|
||||
node {
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
tableOfContents
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={({ allMdx }) => {
|
||||
let navItems = [];
|
||||
let finalNavItems;
|
||||
if (allMdx.edges !== undefined && allMdx.edges.length > 0) {
|
||||
const navItems = allMdx.edges.map((item, index) => {
|
||||
let innerItems;
|
||||
if(item !== undefined) {
|
||||
if ((item.node.fields.slug === location.pathname) || (config.gatsby.pathPrefix + item.node.fields.slug) === location.pathname) {
|
||||
if (item.node.tableOfContents.items) {
|
||||
innerItems = item.node.tableOfContents.items.map((innerItem, index) => {
|
||||
const itemId = innerItem.title ? innerItem.title.replace(/\s+/g, '').toLowerCase() : '#';
|
||||
return (
|
||||
<ListItem
|
||||
key={index}
|
||||
to={`#${itemId}`}
|
||||
level={1}
|
||||
>
|
||||
{innerItem.title}
|
||||
</ListItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (innerItems) {
|
||||
finalNavItems = innerItems;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (finalNavItems && finalNavItems.length) {
|
||||
return (
|
||||
<Sidebar>
|
||||
<ul className={'rightSideBarUL'}>
|
||||
<li className={'rightSideTitle'}>CONTENTS</li>
|
||||
{finalNavItems}
|
||||
</ul>
|
||||
</Sidebar>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Sidebar>
|
||||
<ul></ul>
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default SidebarLayout;
|
|
@ -0,0 +1,14 @@
|
|||
import React, { Fragment } from "react"
|
||||
import { Highlight, Snippet } from "react-instantsearch-dom"
|
||||
import { Link } from "gatsby"
|
||||
|
||||
export const PageHit = clickHandler => ({ hit }) => (
|
||||
<div>
|
||||
<Link to={hit.slug} onClick={clickHandler}>
|
||||
<div>
|
||||
<Highlight attribute="title" hit={hit} tagName="mark" />
|
||||
</div>
|
||||
</Link>
|
||||
<Snippet attribute="excerpt" hit={hit} tagName="mark" />
|
||||
</div>
|
||||
)
|
|
@ -0,0 +1,150 @@
|
|||
import React, { useState, useEffect, createRef } from "react";
|
||||
import {
|
||||
InstantSearch,
|
||||
Index,
|
||||
Hits,
|
||||
Configure,
|
||||
Pagination,
|
||||
connectStateResults,
|
||||
} from "react-instantsearch-dom";
|
||||
import algoliasearch from "algoliasearch/lite";
|
||||
import config from "../../../config.js";
|
||||
|
||||
import styled from "@emotion/styled";
|
||||
import { css } from "@emotion/core";
|
||||
import { PoweredBy } from "./styles"
|
||||
import { Search } from "styled-icons/fa-solid/Search"
|
||||
import Input from "./input"
|
||||
import * as hitComps from "./hitComps"
|
||||
import '../styles.css';
|
||||
|
||||
const SearchIcon = styled(Search)`
|
||||
width: 1em;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
const HitsWrapper = styled.div`
|
||||
display: ${props => (props.show ? `grid` : `none`)};
|
||||
max-height: 80vh;
|
||||
overflow: scroll;
|
||||
z-index: 2;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 0.5em);
|
||||
width: 80vw;
|
||||
max-width: 30em;
|
||||
box-shadow: 0 0 5px 0;
|
||||
padding: 0.7em 1em 0.4em;
|
||||
background: white;
|
||||
@media only screen and (max-width: 991px) {
|
||||
width: 400px;
|
||||
max-width: 400px;
|
||||
}
|
||||
@media only screen and (max-width: 767px) {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
border-radius: ${props => props.theme.smallBorderRadius};
|
||||
> * + * {
|
||||
padding-top: 1em !important;
|
||||
border-top: 2px solid ${props => props.theme.darkGray};
|
||||
}
|
||||
li + li {
|
||||
margin-top: 0.7em;
|
||||
padding-top: 0.7em;
|
||||
border-top: 1px solid ${props => props.theme.lightGray};
|
||||
}
|
||||
* {
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
color: black !important;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
mark {
|
||||
color: ${props => props.theme.lightBlue};
|
||||
background: ${props => props.theme.darkBlue};
|
||||
}
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.3em;
|
||||
h3 {
|
||||
color: black;
|
||||
background: ${props => props.theme.gray};
|
||||
padding: 0.1em 0.4em;
|
||||
border-radius: ${props => props.theme.smallBorderRadius};
|
||||
}
|
||||
}
|
||||
h3 {
|
||||
color: black;
|
||||
margin: 0 0 0.5em;
|
||||
}
|
||||
h4 {
|
||||
color: black;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
`
|
||||
const Root = styled.div`
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-gap: 1em;
|
||||
@media only screen and (max-width: 767px) {
|
||||
width: 100%;
|
||||
}
|
||||
`
|
||||
|
||||
const Results = connectStateResults(
|
||||
({ searching, searchState: state, searchResults: res }) =>
|
||||
(searching && `Searching...`) ||
|
||||
(res && res.nbHits === 0 && `No results for '${state.query}'`)
|
||||
)
|
||||
|
||||
const useClickOutside = (ref, handler, events) => {
|
||||
if (!events) events = [`mousedown`, `touchstart`]
|
||||
const detectClickOutside = event =>
|
||||
ref && ref.current && !ref.current.contains(event.target) && handler()
|
||||
useEffect(() => {
|
||||
for (const event of events)
|
||||
document.addEventListener(event, detectClickOutside)
|
||||
return () => {
|
||||
for (const event of events)
|
||||
document.removeEventListener(event, detectClickOutside)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default function SearchComponent({ indices, collapse, hitsAsGrid }) {
|
||||
const ref = createRef()
|
||||
const [query, setQuery] = useState(``)
|
||||
const [focus, setFocus] = useState(false)
|
||||
const searchClient = algoliasearch(
|
||||
config.header.search.algoliaAppId,
|
||||
config.header.search.algoliaSearchKey
|
||||
)
|
||||
useClickOutside(ref, () => setFocus(false))
|
||||
const displayResult = (query.length > 0 && focus) ? 'showResults' : 'hideResults';
|
||||
return (
|
||||
<InstantSearch
|
||||
searchClient={searchClient}
|
||||
indexName={indices[0].name}
|
||||
onSearchStateChange={({ query }) => setQuery(query)}
|
||||
root={{ Root, props: { ref } }}
|
||||
>
|
||||
<Input onFocus={() => setFocus(true)} {...{ collapse, focus }} />
|
||||
<HitsWrapper className={'hitWrapper ' + displayResult} show={query.length > 0 && focus} asGrid={hitsAsGrid}>
|
||||
{indices.map(({ name, title, hitComp, type }) => {
|
||||
return (
|
||||
<Index key={name} indexName={name}>
|
||||
<Results />
|
||||
<Hits hitComponent={hitComps[hitComp](() => setFocus(false))} />
|
||||
</Index>
|
||||
)})}
|
||||
<PoweredBy />
|
||||
</HitsWrapper>
|
||||
<Configure hitsPerPage={5} />
|
||||
</InstantSearch>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import React from "react"
|
||||
import { connectSearchBox } from "react-instantsearch-dom"
|
||||
|
||||
import styled from "@emotion/styled";
|
||||
import { css } from "@emotion/core";
|
||||
import { Search } from "styled-icons/fa-solid/Search";
|
||||
|
||||
const SearchIcon = styled(Search)`
|
||||
width: 1em;
|
||||
pointer-events: none;
|
||||
margin-right: 10px
|
||||
`
|
||||
const focus = (props) => css`
|
||||
background: white;
|
||||
color: ${props => props.theme.darkBlue};
|
||||
cursor: text;
|
||||
width: 5em;
|
||||
+ ${SearchIcon} {
|
||||
color: ${props => props.theme.darkBlue};
|
||||
margin: 0.3em;
|
||||
}
|
||||
`
|
||||
const collapse = (props) => css`
|
||||
width: 0;
|
||||
cursor: pointer;
|
||||
color: ${props => props.theme.lightBlue};
|
||||
+ ${SearchIcon} {
|
||||
color: white;
|
||||
}
|
||||
${props => props.focus && focus()}
|
||||
margin-left: ${props => (props.focus ? `-1.6em` : `-1em`)};
|
||||
padding-left: ${props => (props.focus ? `1.6em` : `1em`)};
|
||||
::placeholder {
|
||||
color: ${props => props.theme.gray};
|
||||
}
|
||||
`
|
||||
const expand = (props) => css`
|
||||
background: ${props => props.theme.veryLightGray};
|
||||
width: 6em;
|
||||
margin-left: -1.6em;
|
||||
padding-left: 1.6em;
|
||||
+ ${SearchIcon} {
|
||||
margin: 0.3em;
|
||||
}
|
||||
`
|
||||
|
||||
const collapseExpand = (props) => css`
|
||||
${props => (props.collapse ? collapse() : expand())}
|
||||
`
|
||||
|
||||
const Input = styled.input`
|
||||
outline: none;
|
||||
border: none;
|
||||
font-size: 1em;
|
||||
background: white;
|
||||
transition: ${props => props.theme.shortTrans};
|
||||
border-radius: ${props => props.theme.smallBorderRadius};
|
||||
{collapseExpand}
|
||||
`
|
||||
const Form = styled.form`
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
@media only screen and (max-width: 767px) {
|
||||
width: 100%;
|
||||
margin-left: 15px;
|
||||
}
|
||||
`
|
||||
|
||||
export default connectSearchBox(({ refine, ...rest }) => {
|
||||
const preventSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
}
|
||||
return (
|
||||
<Form className={'formElement'} onSubmit={preventSubmit}>
|
||||
<SearchIcon />
|
||||
<Input
|
||||
className={'searchInput '}
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
aria-label="Search"
|
||||
onChange={e => refine(e.target.value)}
|
||||
{...rest}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
);
|
|
@ -0,0 +1,12 @@
|
|||
import React from "react"
|
||||
import { Algolia } from "styled-icons/fa-brands/Algolia"
|
||||
import "../styles.css";
|
||||
|
||||
export const PoweredBy = () => (
|
||||
<span className='poweredBy'>
|
||||
Powered by{` `}
|
||||
<a href="https://algolia.com">
|
||||
<Algolia size="1em" /> Algolia
|
||||
</a>
|
||||
</span>
|
||||
)
|
|
@ -0,0 +1,153 @@
|
|||
import React from "react";
|
||||
import Tree from './tree';
|
||||
import {StaticQuery, graphql} from "gatsby";
|
||||
import styled from "@emotion/styled";
|
||||
import {ExternalLink} from "react-feather";
|
||||
import '../styles.css';
|
||||
import config from '../../../config';
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const ListItem = styled(({ className, active, level, ...props }) => {
|
||||
return (
|
||||
<li className={className}>
|
||||
<a href={props.to} {...props} />
|
||||
</li>
|
||||
);
|
||||
})`
|
||||
list-style: none;
|
||||
|
||||
a {
|
||||
color: #5C6975;
|
||||
text-decoration: none;
|
||||
font-weight: ${({ level }) => (level === 0 ? 700 : 400)};
|
||||
padding: 0.45rem 0 0.45rem ${props => 2 + (props.level || 0) * 1}rem;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
color: rgb(116, 76, 188) !important;
|
||||
}
|
||||
|
||||
${props =>
|
||||
props.active &&
|
||||
`
|
||||
color: #663399;
|
||||
border-color: rgb(230,236,241) !important;
|
||||
border-style: solid none solid solid;
|
||||
border-width: 1px 0px 1px 1px;
|
||||
background-color: #fff;
|
||||
`} // external link icon
|
||||
svg {
|
||||
float: right;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Sidebar = styled('aside')`
|
||||
width: 100%;
|
||||
/* background-color: rgb(245, 247, 249); */
|
||||
/* border-right: 1px solid #ede7f3; */
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
padding-left: 0px;
|
||||
position: -webkit-sticky;
|
||||
position: -moz-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding-right: 0;
|
||||
background-color: #372476;
|
||||
/* Safari 4-5, Chrome 1-9 */
|
||||
background: linear-gradient(#372476, #3b173b);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#372476), to(#3b173b));
|
||||
/* Safari 5.1, Chrome 10+ */
|
||||
background: -webkit-linear-gradient(top, #372476, #3b173b);
|
||||
/* Firefox 3.6+ */
|
||||
background: -moz-linear-gradient(top, #372476, #3b173b);
|
||||
/* IE 10 */
|
||||
background: -ms-linear-gradient(top, #372476, #3b173b);
|
||||
/* Opera 11.10+ */
|
||||
background: -o-linear-gradient(top, #372476, #3b173b);
|
||||
|
||||
@media only screen and (max-width: 1023px) {
|
||||
width: 100%;
|
||||
/* position: relative; */
|
||||
height: 100vh;
|
||||
}
|
||||
@media (min-width: 767px) and (max-width:1023px)
|
||||
{
|
||||
padding-left: 0;
|
||||
}
|
||||
@media only screen and (max-width: 767px) {
|
||||
padding-left: 0px;
|
||||
background-color: #372476;
|
||||
background: #372476;
|
||||
height: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
const Divider = styled(props => (
|
||||
<li {...props}>
|
||||
<hr />
|
||||
</li>
|
||||
))`
|
||||
list-style: none;
|
||||
padding: 0.5rem 0;
|
||||
|
||||
hr {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ede7f3;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
const SidebarLayout = ({location}) => (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
allMdx {
|
||||
edges {
|
||||
node {
|
||||
fields {
|
||||
slug
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={({allMdx}) => {
|
||||
return (
|
||||
<Sidebar>
|
||||
<ul className={'sideBarUL'}>
|
||||
<Tree
|
||||
edges={allMdx.edges}
|
||||
/>
|
||||
{
|
||||
config.sidebar.links
|
||||
&& config.sidebar.links.length > 0
|
||||
&& (<Divider />)
|
||||
}
|
||||
{config.sidebar.links.map((link, key) => {
|
||||
if (link.link !== '' && link.text !== '') {
|
||||
return (
|
||||
<ListItem key={key} to={link.link}>
|
||||
{link.text}
|
||||
<ExternalLink size={14} />
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
</Sidebar>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default SidebarLayout;
|
|
@ -0,0 +1,110 @@
|
|||
import React, {useState} from 'react';
|
||||
import config from '../../../config';
|
||||
import TreeNode from './treeNode';
|
||||
|
||||
const calculateTreeData = edges => {
|
||||
const originalData = config.sidebar.ignoreIndex ? edges.filter(({node: {fields: {slug}}}) => slug !== '/') : edges;
|
||||
const tree = originalData.reduce((accu, {node: {fields: {slug, title}}}) => {
|
||||
const parts = slug.split('/');
|
||||
let {items: prevItems} = accu;
|
||||
const slicedParts = config.gatsby && config.gatsby.trailingSlash ? parts.slice(1, -2) : parts.slice(1, -1);
|
||||
for (const part of slicedParts) {
|
||||
let tmp = prevItems && prevItems.find(({label}) => label == part);
|
||||
if (tmp) {
|
||||
if (!tmp.items) {
|
||||
tmp.items = [];
|
||||
}
|
||||
} else {
|
||||
tmp = {label: part, items: []};
|
||||
prevItems.push(tmp)
|
||||
}
|
||||
prevItems = tmp.items;
|
||||
}
|
||||
const slicedLength = config.gatsby && config.gatsby.trailingSlash ? parts.length - 2 : parts.length - 1;
|
||||
const existingItem = prevItems.find(({label}) => label === parts[slicedLength]);
|
||||
if (existingItem) {
|
||||
existingItem.url = slug;
|
||||
existingItem.title = title;
|
||||
} else {
|
||||
prevItems.push({
|
||||
label: parts[slicedLength],
|
||||
url: slug,
|
||||
items: [],
|
||||
title
|
||||
});
|
||||
}
|
||||
return accu;
|
||||
}, {items: []});
|
||||
const {sidebar: {forcedNavOrder = []}} = config;
|
||||
const tmp = [...forcedNavOrder];
|
||||
if(config.gatsby && config.gatsby.trailingSlash) {
|
||||
}
|
||||
tmp.reverse();
|
||||
return tmp.reduce((accu, slug) => {
|
||||
const parts = slug.split('/');
|
||||
let {items: prevItems} = accu;
|
||||
const slicedParts = config.gatsby && config.gatsby.trailingSlash ? parts.slice(1, -2) : parts.slice(1, -1);
|
||||
for (const part of slicedParts) {
|
||||
let tmp = prevItems.find((item) => item && item.label == part);
|
||||
if (tmp) {
|
||||
if (!tmp.items) {
|
||||
tmp.items = [];
|
||||
}
|
||||
} else {
|
||||
tmp = {label: part, items: []};
|
||||
prevItems.push(tmp)
|
||||
}
|
||||
if(tmp && tmp.items) {
|
||||
prevItems = tmp.items;
|
||||
}
|
||||
}
|
||||
// sort items alphabetically.
|
||||
prevItems.map((item) => {
|
||||
item.items = item.items
|
||||
.sort(function (a, b) {
|
||||
if (a.label < b.label)
|
||||
return -1;
|
||||
if (a.label > b.label)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
})
|
||||
const slicedLength = config.gatsby && config.gatsby.trailingSlash ? (parts.length - 2) : (parts.length - 1);
|
||||
const index = prevItems.findIndex(({label}) => label === parts[slicedLength]);
|
||||
if(prevItems.length) {
|
||||
accu.items.unshift(prevItems.splice(index, 1)[0]);
|
||||
}
|
||||
return accu;
|
||||
}, tree);
|
||||
}
|
||||
|
||||
const Tree = ({edges}) => {
|
||||
let [treeData] = useState(() => {
|
||||
return calculateTreeData(edges);
|
||||
});
|
||||
const defaultCollapsed = {};
|
||||
treeData.items.forEach(item => {
|
||||
if (config.sidebar.collapsedNav && config.sidebar.collapsedNav.includes(item.url)) {
|
||||
defaultCollapsed[item.url] = true;
|
||||
} else {
|
||||
defaultCollapsed[item.url] = false;
|
||||
}
|
||||
});
|
||||
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
||||
const toggle = (url) => {
|
||||
setCollapsed({
|
||||
...collapsed,
|
||||
[url]: !collapsed[url],
|
||||
});
|
||||
}
|
||||
return (
|
||||
<TreeNode
|
||||
className={`${config.sidebar.frontLine ? 'showFrontLine' : 'hideFrontLine'} firstLevel`}
|
||||
setCollapsed={toggle}
|
||||
collapsed={collapsed}
|
||||
{...treeData}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Tree
|
|
@ -0,0 +1,55 @@
|
|||
import React from "react";
|
||||
import OpenedSvg from '../images/opened';
|
||||
import ClosedSvg from '../images/closed';
|
||||
import config from '../../../config';
|
||||
import Link from "../link";
|
||||
|
||||
const TreeNode = ({className = '', setCollapsed, collapsed, url, title, items, ...rest}) => {
|
||||
const isCollapsed = collapsed[url];
|
||||
const collapse = () => {
|
||||
setCollapsed(url);
|
||||
}
|
||||
const hasChildren = items.length !== 0;
|
||||
let location;
|
||||
if(typeof(document) != 'undefined') {
|
||||
location = document.location;
|
||||
}
|
||||
const active =
|
||||
location && (location.pathname === url || location.pathname === (config.gatsby.pathPrefix + url));
|
||||
const calculatedClassName = `${className} item ${active ? 'active' : ''}`;
|
||||
return (
|
||||
<li
|
||||
className={calculatedClassName}
|
||||
>
|
||||
{title && (
|
||||
<Link
|
||||
to={url}
|
||||
>
|
||||
{title}
|
||||
{!config.sidebar.frontLine && title && hasChildren ? (
|
||||
<button
|
||||
onClick={collapse}
|
||||
aria-label='collapse'
|
||||
className='collapser'>
|
||||
{!isCollapsed ? <OpenedSvg /> : <ClosedSvg />}
|
||||
</button>
|
||||
) : null}
|
||||
</Link>)
|
||||
}
|
||||
|
||||
{!isCollapsed && hasChildren ? (
|
||||
<ul>
|
||||
{items.map((item, index) => (
|
||||
<TreeNode
|
||||
key={item.url + index.toString()}
|
||||
setCollapsed={setCollapsed}
|
||||
collapsed={collapsed}
|
||||
{...item}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
export default TreeNode
|
|
@ -0,0 +1,971 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-display: swap;
|
||||
}
|
||||
::-webkit-input-placeholder { /* Edge */
|
||||
color: #C2C2C2;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder { /* Internet Explorer */
|
||||
color: #C2C2C2;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #C2C2C2;
|
||||
}
|
||||
html, body {
|
||||
font-family: -apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
"Roboto",
|
||||
"Roboto Light",
|
||||
"Oxygen",
|
||||
"Ubuntu",
|
||||
"Cantarell",
|
||||
"Fira Sans",
|
||||
"Droid Sans",
|
||||
"Helvetica Neue",
|
||||
sans-serif,
|
||||
"Apple Color Emoji",
|
||||
"Segoe UI Emoji",
|
||||
"Segoe UI Symbol";
|
||||
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
transition: color 0.15s;
|
||||
color: #663399;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
.visibleMobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-responsive {
|
||||
position: relative;
|
||||
padding-bottom: 56.2%;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.displayInline {
|
||||
display: inline-block;
|
||||
}
|
||||
.navBarToggle {
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
width: 36px;
|
||||
height: 33px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
padding: 8px 5px;
|
||||
display: none;
|
||||
}
|
||||
.navBarToggle .iconBar {
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 2px;
|
||||
border-radius: 1px;
|
||||
margin: 0 auto;
|
||||
margin-top: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.navBarToggle .iconBar:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.video-responsive iframe{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.diffNewLine {
|
||||
color: #22863a;
|
||||
background-color: #f0fff4;
|
||||
}
|
||||
|
||||
.diffRemoveLine {
|
||||
color: red;
|
||||
background-color: #ffcccc;
|
||||
}
|
||||
.navBarParent
|
||||
{
|
||||
width: 100%;
|
||||
float: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.divider
|
||||
{
|
||||
height: 30px;
|
||||
margin: 0 15px;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.navBarULRight {
|
||||
/* position: absolute;
|
||||
right: 30px; */
|
||||
}
|
||||
.githubIcon {
|
||||
width: 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.githubSection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #000;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.githubSection:hover {
|
||||
text-decoration: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-toggle .icon-bar {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover {
|
||||
background-color: #542683;
|
||||
}
|
||||
|
||||
.headerWrapper {
|
||||
border-bottom: 1px solid rgb(212, 218, 223);
|
||||
box-shadow: rgba(116, 129, 141, 0.1) 0px 1px 1px 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.formElement
|
||||
{
|
||||
background-color: transparent;
|
||||
padding: 4px;
|
||||
/* border-radius: 4px; */
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.formElement:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
.formElement svg path {
|
||||
fill: #fff;
|
||||
}
|
||||
.searchInput {
|
||||
width: calc(100% - 26px);
|
||||
background-color: transparent !important;
|
||||
border-width: 0 !important;
|
||||
color: #C2C2C2;
|
||||
|
||||
}
|
||||
.searchInput:focus, .searchInput:visited, .searchInput:hover, .searchInput:focus-within {
|
||||
outline: none;
|
||||
border: 0;
|
||||
}
|
||||
.searchWrapper
|
||||
{
|
||||
padding-left: 0px;
|
||||
padding-right: 20px;
|
||||
flex: 1;
|
||||
max-width: 600px;
|
||||
position: relative;
|
||||
}
|
||||
.searchWrapper a {
|
||||
font-weight: 500;
|
||||
}
|
||||
.hitWrapper
|
||||
{
|
||||
background-color: #fff;
|
||||
padding: 0.7em 1em 0.4em;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
width: 80vw;
|
||||
max-width: 30em;
|
||||
top: 40px;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.16);
|
||||
height: auto;
|
||||
max-height: 80vh;
|
||||
overflow: scroll;
|
||||
left: 0;
|
||||
}
|
||||
.hitWrapper ul li {
|
||||
margin-top: 0.7em;
|
||||
padding-top: 0.7em;
|
||||
border-top: 1px solid;
|
||||
list-style-type: none;
|
||||
}
|
||||
.hitWrapper ul li:first-child {
|
||||
border-top: 0px;
|
||||
margin-top: 0px;
|
||||
color: black !important;
|
||||
padding: 0px;
|
||||
}
|
||||
.showResults {
|
||||
display: block;
|
||||
}
|
||||
.hideResults {
|
||||
display: none;
|
||||
}
|
||||
.hitWrapper span {
|
||||
color: black;
|
||||
font-size: 14px;
|
||||
}
|
||||
.headerTitle {
|
||||
height: auto;
|
||||
font-size: 28px;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
color: #fff !important;
|
||||
}
|
||||
.headerTitle a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.headerTitle a:hover {
|
||||
text-decoration: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.logoWrapper {
|
||||
padding: 21px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.logoContent {
|
||||
font-family: 'Roboto';
|
||||
margin-left: 16px;
|
||||
font-size: 28px;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
/* Header section starts here */
|
||||
.navBarDefault {
|
||||
background-color: #372476;
|
||||
border-radius: 0;
|
||||
border-top: 0;
|
||||
margin-bottom: 0;
|
||||
border: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: -1px 0px 4px 1px rgba(175,158,232,.4);
|
||||
-webkit-box-shadow: -1px 0px 4px 1px rgba(175,158,232,.4);
|
||||
-moz-box-shadow: -1px 0px 4px 1px rgba(175,158,232,.8);
|
||||
-o-box-shadow: -1px 0px 4px 1px rgba(175,158,232,.4);
|
||||
z-index: 1;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
}
|
||||
.navBarHeader {
|
||||
min-width: 335px;
|
||||
padding-right: 20px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.navBarBrand {
|
||||
padding: 0px 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navBarBrand img {
|
||||
width: 40px;
|
||||
margin-right: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
.navBarUL li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.navBarUL {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.navBarUL li a {
|
||||
font-family: 'Roboto';
|
||||
color: #fff !important;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 1em;
|
||||
opacity: 1;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.navBarNav
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.navBarUL li a img, .navBarUL li a .shareIcon
|
||||
{
|
||||
width: 20px;
|
||||
}
|
||||
.mainWrapper {
|
||||
color: rgb(59, 69, 78);
|
||||
max-width: 750px;
|
||||
}
|
||||
.navBarUL li a:hover {
|
||||
opacity: .7;
|
||||
}
|
||||
pre {
|
||||
border: 0 !important;
|
||||
background-color: rgb(245, 247, 249); /* !important; */
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: rgb(116, 129, 141);
|
||||
margin: 0px 0px 24px;
|
||||
padding: 0px 0px 0px 12px;
|
||||
border-left: 4px solid rgb(230, 236, 241);
|
||||
border-color: rgb(230, 236, 241);
|
||||
}
|
||||
|
||||
.mainWrapper ul, .mainWrapper ol {
|
||||
-webkit-padding-start: 40px;
|
||||
-moz-padding-start: 40px;
|
||||
-o-padding-start: 40px;
|
||||
margin: 24px 0px;
|
||||
padding: 0px 0px 0px 2em;
|
||||
}
|
||||
|
||||
.mainWrapper ul li, .mainWrapper ol li {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* Header section ends here */
|
||||
.headerNav {
|
||||
font-family: 'Roboto';
|
||||
padding: 0px 24px;
|
||||
color: rgb(116, 76, 188);
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.headerNav a {
|
||||
color: rgb(116, 76, 188);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.headerNav a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logoWrapper img {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.sideBarUL {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.sideBarUL li {
|
||||
list-style-type: none;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.sideBarUL li a {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
padding: 7px 24px 7px 16px;
|
||||
padding-left: 10px;
|
||||
padding-right: 25px;
|
||||
border-style: solid none solid solid;
|
||||
border-width: 1px 0px 1px 1px;
|
||||
border-color: transparent currentcolor transparent transparent;
|
||||
}
|
||||
|
||||
.rightSideTitle {
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.2px;
|
||||
padding: 7px 24px 7px 16px;
|
||||
border-left: 1px solid #e6ecf1;
|
||||
border-left-color: rgb(230, 236, 241);
|
||||
color: #4C5767;
|
||||
}
|
||||
|
||||
.rightSideBarUL {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.rightSideBarUL li {
|
||||
list-style-type: none;
|
||||
border-left: 1px solid #e6ecf1;
|
||||
border-left-color: rgb(230, 236, 241);
|
||||
}
|
||||
|
||||
.rightSideBarUL li a {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
padding: 7px 24px 7px 16px;
|
||||
}
|
||||
|
||||
.hideFrontLine .collapser {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hideFrontLine .active >a{
|
||||
background-color: #542683;
|
||||
color: #fff !important;
|
||||
}
|
||||
.firstLevel ul li .collapser svg path {
|
||||
fill: #fff !important;
|
||||
}
|
||||
.active .collapser >svg >path {
|
||||
fill: #663399 !important;
|
||||
}
|
||||
|
||||
.firstLevel ul .item ul .item {
|
||||
border-left: 1px solid #e6ecf1;
|
||||
}
|
||||
|
||||
.sideBarUL .item{
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sideBarUL .item >a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-right: 35px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.sideBarUL .item>a:hover {
|
||||
background-color: #542683;
|
||||
color: #fff !important;
|
||||
}
|
||||
.showFrontLine .item >a:hover {
|
||||
background-color: #542683;
|
||||
}
|
||||
.showFrontLine .active >a {
|
||||
color: #fff;
|
||||
background-color: #473485;
|
||||
}
|
||||
|
||||
.sideBarUL .item .item{
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.firstLevel>ul>.item{
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.showFrontLine .item .item {
|
||||
border-left: 1px solid #e6ecf1;
|
||||
border-left-color: rgb(230, 236, 241);
|
||||
padding: 0;
|
||||
width: calc(100% - 16px) !important;
|
||||
}
|
||||
|
||||
.showFrontLine .item .active>a {
|
||||
border-color: rgb(230,236,241) !important;
|
||||
border-style: solid none solid solid;
|
||||
border-width: 1px 0px 1px 1px;
|
||||
background-color: #542683;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.titleWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 40px;
|
||||
border-bottom: 1px solid rgb(230, 236, 241);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32px;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
border-left: 2px solid rgb(116, 76, 188);
|
||||
padding: 0 16px;
|
||||
flex: 1;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.gitBtn {
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gitBtn img {
|
||||
width: 15px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.addPaddTopBottom {
|
||||
padding: 50px 0;
|
||||
}
|
||||
|
||||
.nextPreviousWrapper {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
width: auto;
|
||||
display: grid;
|
||||
grid-template-rows: auto;
|
||||
column-gap: 24px;
|
||||
grid-template-columns: calc(50% - 8px) calc(50% - 8px);
|
||||
}
|
||||
|
||||
.previousBtn {
|
||||
cursor: pointer;
|
||||
-moz-box-align: center;
|
||||
-moz-box-direction: normal;
|
||||
-moz-box-orient: horizontal;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
place-self: stretch;
|
||||
color: rgb(36, 42, 49);
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgb(230, 236, 241);
|
||||
transition: border 200ms ease 0s;
|
||||
box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.leftArrow {
|
||||
display: block;
|
||||
margin: 0px;
|
||||
color: rgb(157, 170, 182);
|
||||
flex: 0 0 auto;
|
||||
font-size: 24px;
|
||||
transition: color 200ms ease 0s;
|
||||
padding: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.preRightWrapper {
|
||||
display: block;
|
||||
margin: 0px;
|
||||
flex: 1 1 0%;
|
||||
padding: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.smallContent {
|
||||
display: block;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
color: #6E6E6E;
|
||||
}
|
||||
|
||||
.smallContent span {
|
||||
font-size: 12px;
|
||||
line-height: 1.625;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.nextPreviousTitle {
|
||||
display: block;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
transition: color 200ms ease 0s;
|
||||
}
|
||||
|
||||
.nextPreviousTitle span {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nextBtn {
|
||||
cursor: pointer;
|
||||
-moz-box-align: center;
|
||||
-moz-box-direction: normal;
|
||||
-moz-box-orient: horizontal;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
place-self: stretch;
|
||||
color: rgb(36, 42, 49);
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgb(230, 236, 241);
|
||||
transition: border 200ms ease 0s;
|
||||
box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.rightArrow {
|
||||
flex: 0 0 auto;
|
||||
font-size: 24px;
|
||||
transition: color 200ms ease 0s;
|
||||
padding: 16px;
|
||||
padding-left: 16px;
|
||||
display: block;
|
||||
margin: 0px;
|
||||
color: rgb(157, 170, 182);
|
||||
}
|
||||
|
||||
.nextRightWrapper {
|
||||
display: block;
|
||||
margin: 0px;
|
||||
padding: 16px;
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.nextBtn:hover, .previousBtn:hover {
|
||||
color: rgb(116, 76, 188);
|
||||
text-decoration: none;
|
||||
border: 1px solid rgb(116, 76, 188);
|
||||
}
|
||||
|
||||
.nextBtn:hover .rightArrow, .previousBtn:hover .leftArrow {
|
||||
color: rgb(116, 76, 188);
|
||||
}
|
||||
|
||||
/* tables.css */
|
||||
table {
|
||||
padding: 0; }
|
||||
|
||||
table tr {
|
||||
border-top: 1px solid #cccccc;
|
||||
background-color: white;
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
|
||||
table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8; }
|
||||
|
||||
table tr th {
|
||||
font-weight: bold;
|
||||
border: 1px solid #cccccc;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px; }
|
||||
|
||||
table tr td {
|
||||
border: 1px solid #cccccc;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px; }
|
||||
|
||||
table tr th :first-child, table tr td :first-child {
|
||||
margin-top: 0; }
|
||||
|
||||
table tr th :last-child, table tr td :last-child {
|
||||
margin-bottom: 0; }
|
||||
/* end - tables.css */
|
||||
|
||||
/* Image styling */
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
/* end image */
|
||||
.githubBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
padding: 10px 0px;
|
||||
padding-left: 15px;
|
||||
max-height: 40px;
|
||||
}
|
||||
.githubBtn span span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.communitySection {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.authorSection {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.authorSection, .authorName {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.authorImg img
|
||||
{
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
border-radius: 50%;
|
||||
min-width: 75px;
|
||||
max-width: 75px;
|
||||
min-height: 75px;
|
||||
max-height: 75px;
|
||||
}
|
||||
.authorDetails
|
||||
{
|
||||
padding-left: 10px;
|
||||
}
|
||||
.authorDesc
|
||||
{
|
||||
padding-top: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.authorName img {
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
}
|
||||
.authorName img:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.heading1 {
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.heading2 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.heading3 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.heading4 {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.heading5 {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.heading6 {
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
margin: 16px 0px 32px;
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
.pre {
|
||||
font-size: 14px;
|
||||
margin: 0px;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.mainWrapper code {
|
||||
background: #f9f7fb;
|
||||
border: 1px solid #ede7f3;
|
||||
border-radius: 4px;
|
||||
padding: 2px 6px;
|
||||
font-size: 0.9375em;
|
||||
}
|
||||
|
||||
.poweredBy {
|
||||
font-size: 0.6em;
|
||||
text-align: end;
|
||||
padding: 0;
|
||||
}
|
||||
.topnav {
|
||||
-webkit-transition: top 0.5s, bottom 0.5s;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.responsive {
|
||||
margin-top: 15px;
|
||||
position: relative;
|
||||
padding-bottom: 20px;
|
||||
border-top: 1px solid #fff;
|
||||
}
|
||||
.headerTitle {
|
||||
padding-right: 50px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.navBarBrand {
|
||||
min-height: 40px;
|
||||
}
|
||||
.navBarBrand img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.topnav.responsive .visibleMobile {
|
||||
display: block;
|
||||
}
|
||||
.topnav .navBarUL {
|
||||
display: none;
|
||||
}
|
||||
.topnav.responsive .navBarUL{
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
.hiddenMobile {
|
||||
display: none !important;
|
||||
}
|
||||
hr {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.navBarParent {
|
||||
display: block;
|
||||
}
|
||||
.separator {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.navBarULRight {
|
||||
position: static;
|
||||
}
|
||||
.navBarUL {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 7.5px 0px;
|
||||
}
|
||||
.navBarUL li {
|
||||
height: 37px;
|
||||
}
|
||||
.navBarUL li a {
|
||||
font-size: 14px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.navBarDefault {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navBarToggle {
|
||||
margin-right: 0;
|
||||
display: block;
|
||||
}
|
||||
.navBarHeader {
|
||||
display: flex;
|
||||
min-width: auto;
|
||||
padding-right: 0;
|
||||
align-items: center;
|
||||
}
|
||||
.navBarBrand {
|
||||
font-size: 20px;
|
||||
padding: 0 0;
|
||||
padding-left: 0;
|
||||
flex: initial;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.titleWrapper {
|
||||
padding: 0 15px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.gitBtn {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mainWrapper {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.nextPreviousWrapper {
|
||||
display: block;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.previousBtn {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.mobileView {
|
||||
text-align: left !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.searchWrapper {
|
||||
padding: 0px 0;
|
||||
padding-top: 20px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
.hitWrapper {
|
||||
width: 100%;
|
||||
right: 0;
|
||||
top: 35px;
|
||||
max-height: fit-content;
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
.navBarDefault {
|
||||
padding: 10px;
|
||||
}
|
||||
.navBarBrand {
|
||||
font-size: 22px;
|
||||
}
|
||||
.navBarHeader {
|
||||
min-width: 240px;
|
||||
flex: initial;
|
||||
}
|
||||
.githubBtn {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
.divider {
|
||||
margin: 0 5px;
|
||||
height: 20px;
|
||||
}
|
||||
.hitWrapper {
|
||||
max-width: 500px;
|
||||
}
|
||||
.navBarUL li a {
|
||||
padding: 10px 5px;
|
||||
}
|
||||
.searchWrapper {
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export default {
|
||||
fonts: {
|
||||
mono: '"SF Mono", "Roboto Mono", Menlo, monospace'
|
||||
}
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { ThemeProvider as EmotionThemeProvider } from "emotion-theming";
|
||||
import { default as defaultTheme } from "./theme";
|
||||
import Header from './Header';
|
||||
import './styles.css';
|
||||
|
||||
export default function ThemeProvider({ children, theme={}, location }) {
|
||||
return (
|
||||
<div>
|
||||
<Header location={location} />
|
||||
<EmotionThemeProvider theme={{ ...defaultTheme, ...theme }}>
|
||||
{children}
|
||||
</EmotionThemeProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
workbox.routing.registerRoute(
|
||||
new RegExp('https:.*min\.(css|js)'),
|
||||
workbox.strategies.staleWhileRevalidate({
|
||||
cacheName: 'cdn-cache'
|
||||
})
|
||||
);
|
|
@ -0,0 +1,65 @@
|
|||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import config from "../config";
|
||||
|
||||
export default class HTML extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<html {...this.props.htmlAttributes} lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
{config.siteMetadata.ogImage ?
|
||||
(<meta property="og:image" content={config.siteMetadata.ogImage} />) : null
|
||||
}
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
{config.siteMetadata.ogImage ?
|
||||
(<meta property="twitter:image" content={config.siteMetadata.ogImage} />) : null
|
||||
}
|
||||
{config.siteMetadata.favicon ?
|
||||
(<link rel="shortcut icon" type="image/svg" href={config.siteMetadata.favicon} />) : null
|
||||
}
|
||||
<noscript key="noscript"></noscript>
|
||||
{this.props.headComponents}
|
||||
</head>
|
||||
<body {...this.props.bodyAttributes}>
|
||||
{this.props.preBodyComponents}
|
||||
<div
|
||||
key={`body`}
|
||||
id="___gatsby"
|
||||
dangerouslySetInnerHTML={{ __html: this.props.body }}
|
||||
/>
|
||||
{this.props.postBodyComponents}
|
||||
<script
|
||||
defer
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
function navBarClose() {
|
||||
document.getElementById("navbar").classList.toggle("responsive");
|
||||
}
|
||||
document.addEventListener('click',function(e){
|
||||
if(e.target && e.target.tagName.toLowerCase() === 'a'){
|
||||
navBarClose();
|
||||
}
|
||||
});
|
||||
`
|
||||
}}
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HTML.propTypes = {
|
||||
htmlAttributes: PropTypes.object,
|
||||
headComponents: PropTypes.array,
|
||||
bodyAttributes: PropTypes.object,
|
||||
preBodyComponents: PropTypes.array,
|
||||
body: PropTypes.string,
|
||||
postBodyComponents: PropTypes.array,
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 18 KiB |
|
@ -0,0 +1,172 @@
|
|||
import React, { Component } from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import { graphql } from "gatsby";
|
||||
import MDXRenderer from "gatsby-plugin-mdx/mdx-renderer";
|
||||
import styled from "@emotion/styled";
|
||||
import { Layout, Link } from "$components";
|
||||
import NextPrevious from '../components/NextPrevious';
|
||||
import '../components/styles.css';
|
||||
import config from '../../config';
|
||||
|
||||
const forcedNavOrder = config.sidebar.forcedNavOrder;
|
||||
|
||||
const Edit = styled('div')`
|
||||
padding: 1rem 1.5rem;
|
||||
text-align: right;
|
||||
|
||||
a {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1em;
|
||||
text-decoration: none;
|
||||
color: #555;
|
||||
border: 1px solid rgb(211, 220, 228);
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
transition: all 0.2s ease-out 0s;
|
||||
text-decoration: none;
|
||||
color: rgb(36, 42, 49);
|
||||
background-color: rgb(255, 255, 255);
|
||||
box-shadow: rgba(116, 129, 141, 0.1) 0px 1px 1px 0px;
|
||||
height: 30px;
|
||||
padding: 5px 16px;
|
||||
&:hover {
|
||||
background-color: rgb(245, 247, 249);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default class MDXRuntimeTest extends Component {
|
||||
render() {
|
||||
const { data } = this.props;
|
||||
if(!data) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
allMdx,
|
||||
mdx,
|
||||
site: {
|
||||
siteMetadata: { docsLocation, title }
|
||||
}
|
||||
} = data;
|
||||
const gitHub = require('../components/images/github.svg');
|
||||
|
||||
const navItems = allMdx.edges
|
||||
.map(({ node }) => node.fields.slug)
|
||||
.filter(slug => slug !== "/")
|
||||
.sort()
|
||||
.reduce(
|
||||
(acc, cur) => {
|
||||
if (forcedNavOrder.find(url => url === cur)) {
|
||||
return { ...acc, [cur]: [cur] };
|
||||
}
|
||||
|
||||
let prefix = cur.split("/")[1];
|
||||
if(config.gatsby && config.gatsby.trailingSlash) {
|
||||
prefix = prefix + '/';
|
||||
}
|
||||
|
||||
if (prefix && forcedNavOrder.find(url => url === `/${prefix}`)) {
|
||||
return { ...acc, [`/${prefix}`]: [...acc[`/${prefix}`], cur] };
|
||||
} else {
|
||||
return { ...acc, items: [...acc.items, cur] };
|
||||
}
|
||||
},
|
||||
{ items: [] }
|
||||
);
|
||||
|
||||
const nav = forcedNavOrder
|
||||
.reduce((acc, cur) => {
|
||||
return acc.concat(navItems[cur]);
|
||||
}, [])
|
||||
.concat(navItems.items)
|
||||
.map(slug => {
|
||||
if(slug) {
|
||||
const { node } = allMdx.edges.find(
|
||||
({ node }) => node.fields.slug === slug
|
||||
);
|
||||
|
||||
return { title: node.fields.title, url: node.fields.slug };
|
||||
}
|
||||
});
|
||||
|
||||
// meta tags
|
||||
const metaTitle = mdx.frontmatter.metaTitle;
|
||||
const metaDescription = mdx.frontmatter.metaDescription;
|
||||
let canonicalUrl = config.gatsby.siteUrl;
|
||||
canonicalUrl = config.gatsby.pathPrefix !== '/' ? canonicalUrl + config.gatsby.pathPrefix : canonicalUrl;
|
||||
canonicalUrl = canonicalUrl + mdx.fields.slug;
|
||||
|
||||
return (
|
||||
<Layout {...this.props}>
|
||||
<Helmet>
|
||||
{metaTitle ? <title>{metaTitle}</title> : null }
|
||||
{metaTitle ? <meta name="title" content={metaTitle} /> : null}
|
||||
{metaDescription ? <meta name="description" content={metaDescription} /> : null}
|
||||
{metaTitle ? <meta property="og:title" content={metaTitle} /> : null}
|
||||
{metaDescription ? <meta property="og:description" content={metaDescription} /> : null}
|
||||
{metaTitle ? <meta property="twitter:title" content={metaTitle} /> : null}
|
||||
{metaDescription ? <meta property="twitter:description" content={metaDescription} /> : null}
|
||||
<link rel="canonical" href={canonicalUrl} />
|
||||
</Helmet>
|
||||
<div className={'titleWrapper'}>
|
||||
<h1 className={'title'}>
|
||||
{mdx.fields.title}
|
||||
</h1>
|
||||
<Edit className={'mobileView'}>
|
||||
{docsLocation &&
|
||||
<Link className={'gitBtn'} to={`${docsLocation}/${mdx.parent.relativePath}`}>
|
||||
<img src={gitHub} alt={'Github logo'} /> Edit on GitHub
|
||||
</Link>
|
||||
}
|
||||
</Edit>
|
||||
</div>
|
||||
<div className={'mainWrapper'}>
|
||||
<MDXRenderer>{mdx.body}</MDXRenderer>
|
||||
</div>
|
||||
<div className={'addPaddTopBottom'}>
|
||||
<NextPrevious mdx={mdx} nav={nav} />
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const pageQuery = graphql`
|
||||
query($id: String!) {
|
||||
site {
|
||||
siteMetadata {
|
||||
title
|
||||
docsLocation
|
||||
}
|
||||
}
|
||||
mdx(fields: { id: { eq: $id } }) {
|
||||
fields {
|
||||
id
|
||||
title
|
||||
slug
|
||||
}
|
||||
body
|
||||
tableOfContents
|
||||
parent {
|
||||
... on File {
|
||||
relativePath
|
||||
}
|
||||
}
|
||||
frontmatter {
|
||||
metaTitle
|
||||
metaDescription
|
||||
}
|
||||
}
|
||||
allMdx {
|
||||
edges {
|
||||
node {
|
||||
fields {
|
||||
slug
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,43 @@
|
|||
const config = require("../../config.js");
|
||||
|
||||
const pageQuery = `{
|
||||
pages: allMdx {
|
||||
edges {
|
||||
node {
|
||||
objectID: id
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
headings {
|
||||
value
|
||||
}
|
||||
frontmatter {
|
||||
title
|
||||
metaDescription
|
||||
}
|
||||
excerpt(pruneLength: 50000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
const flatten = arr =>
|
||||
arr.map(({ node: { frontmatter, fields, ...rest } }) => ({
|
||||
...frontmatter,
|
||||
...fields,
|
||||
...rest,
|
||||
}))
|
||||
const settings = { attributesToSnippet: [`excerpt:20`] }
|
||||
|
||||
const indexName = config.header.search ? config.header.search.indexName : '';
|
||||
|
||||
const queries = [
|
||||
{
|
||||
query: pageQuery,
|
||||
transformer: ({ data }) => flatten(data.pages.edges),
|
||||
indexName: `${indexName}`,
|
||||
settings,
|
||||
},
|
||||
]
|
||||
|
||||
module.exports = queries
|
Загрузка…
Ссылка в новой задаче