839 2020-02-26 21:10:34 +09:00
Родитель 0703e3a41a
Коммит 7eec15b051
54 изменённых файлов: 25145 добавлений и 22 удалений

3
docs/.dockerignore Normal file
Просмотреть файл

@ -0,0 +1,3 @@
node_modules
public
.cache

8
docs/.editorconfig Normal file
Просмотреть файл

@ -0,0 +1,8 @@
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

15
docs/.eslintrc.json Executable file
Просмотреть файл

@ -0,0 +1,15 @@
{
"env": {
"browser": true
},
"globals": {
"graphql": false
},
"parserOptions": {
"ecmaVersion": 10,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
}
}

7
docs/.gitignore поставляемый Executable file
Просмотреть файл

@ -0,0 +1,7 @@
public
.cache
node_modules
*DS_Store
*.env
.idea/

22
docs/Dockerfile Normal file
Просмотреть файл

@ -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"]

21
docs/LICENSE Normal file
Просмотреть файл

@ -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.

144
docs/README.md Executable file
Просмотреть файл

@ -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)

68
docs/config.js Normal file
Просмотреть файл

@ -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;

26
docs/content/codeblock.md Normal file
Просмотреть файл

@ -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>
```

30
docs/content/index.mdx Normal file
Просмотреть файл

@ -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)

9
docs/gatsby-browser.js Normal file
Просмотреть файл

@ -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()
}
}

102
docs/gatsby-config.js Executable file
Просмотреть файл

@ -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
};

104
docs/gatsby-node.js Normal file
Просмотреть файл

@ -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)
});
}
};

3
docs/netlify.toml Normal file
Просмотреть файл

@ -0,0 +1,3 @@
[build]
publish = "public"
command = "npm run build"

21892
docs/package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

65
docs/package.json Executable file
Просмотреть файл

@ -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;

14
docs/src/GithubLink.js Normal file
Просмотреть файл

@ -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;

13
docs/src/YoutubeEmbed.js Normal file
Просмотреть файл

@ -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'
})
);

65
docs/src/html.js Normal file
Просмотреть файл

@ -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,
}

Двоичные данные
docs/src/pwa-512.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 18 KiB

172
docs/src/templates/docs.js Normal file
Просмотреть файл

@ -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
}
}
}
}
}
`;

43
docs/src/utils/algolia.js Normal file
Просмотреть файл

@ -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