This commit is contained in:
Sergey Yarkov 2020-07-21 23:14:50 +03:00
parent 96f29a1a74
commit 42df591c08
69 changed files with 1683 additions and 1236 deletions

View File

@ -1,3 +1,4 @@
# devthread.ru
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Netlify Status](https://api.netlify.com/api/v1/badges/bde70986-e5af-4e69-b62c-ff0ed4b76401/deploy-status)](https://app.netlify.com/sites/devthread/deploys)

View File

@ -1,38 +1,40 @@
module.exports = {
title: 'devthread.ru',
keywords: 'веб разработка, javascript, seo, программирование, reactjs, graphql',
siteName: 'Cборник статей и руководств - devthread.ru',
titleTemplate: '%s · статьи и руководства по веб-разработке',
description: 'Cборник статей и руководств по тематике веб-разработки и IT для того чтобы помочь вам',
url: 'https://devthread.netlify.app',
title: "devthread.ru",
keywords:
"веб разработка, javascript, seo, программирование, reactjs, graphql",
siteName: "Cборник статей и руководств - devthread.ru",
titleTemplate: "%s · статьи и руководства по веб-разработке",
description:
"Cборник статей и руководств по тематике веб-разработки и IT для того чтобы помочь вам",
url: "https://devthread.netlify.app",
menuLinks: [
{
name: 'Статьи',
link: '/articles'
name: "Статьи",
link: "/articles",
},
{
name: 'О сайте',
link: '/about'
name: "О сайте",
link: "/about",
},
{
name: 'Контакты',
link: '/contacts'
name: "Контакты",
link: "/contacts",
},
{
name: 'Сниппеты',
link: '/snippets'
}
name: "Сниппеты",
link: "/snippets",
},
],
social: {
email: 'support@devthread.ru',
telegram: 'https://t.me/devthread',
twitter: 'https://twitter.com/devthread',
yadzen: 'https://zen.yandex.ru/'
email: "support@devthread.ru",
telegram: "https://t.me/devthread",
twitter: "https://twitter.com/devthread",
yadzen: "https://zen.yandex.ru/",
},
options: {
articles: {
onPage: 6,
onLoadMore: 2
}
}
onLoadMore: 2,
},
},
}

View File

@ -20,11 +20,13 @@ tags:
- graphql
- reactjs
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -41,9 +43,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -20,17 +20,18 @@ tags:
- graphql
- reactjs
---
## Lorem ipsum dolor
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
@ -42,9 +43,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -19,11 +19,13 @@ description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
tags:
- javascript
---
## Lorem ipsum dolor
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -40,9 +42,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -22,17 +22,18 @@ tags:
- javascript
- graphql
---
## Lorem ipsum dolor
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
@ -44,9 +45,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -19,11 +19,13 @@ tags:
- graphql
- reactjs
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -37,7 +39,13 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
![wallpaper](https://wallpaperplay.com/walls/full/2/9/7/164604.jpg)
```js
const { siteMetadata: { title, menuLinks, social: { twitter, telegram } } } = useSiteMetadataQuery() //L1
const {
siteMetadata: {
title,
menuLinks,
social: { twitter, telegram },
},
} = useSiteMetadataQuery() //L1
const [isScrolled, setIsScrolled] = React.useState(false)
const [isMobileOpen, setIsMobileOpen] = React.useState(false)
const [isDark, setIsDark] = React.useState(false)
@ -45,8 +53,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
React.useEffect(() => {
setIsDark(window.__isDarkTheme)
isMobileOpen
? document.body.style.overflowY = 'hidden'
: document.body.style.overflowY = ''
? (document.body.style.overflowY = "hidden")
: (document.body.style.overflowY = "")
document.body.onscroll = () =>
window.pageYOffset >= 100 ? setIsScrolled(true) : setIsScrolled(false)
}, [setIsDark, isMobileOpen])

View File

@ -18,11 +18,13 @@ description: |-
tags:
- reactjs
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -39,9 +41,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -19,11 +19,13 @@ tags:
- reactjs
- javascript
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -40,9 +42,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -19,11 +19,13 @@ tags:
- reactjs
- javascript
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -37,7 +39,13 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
![wallpaper](https://wallpaperplay.com/walls/full/2/9/7/164604.jpg)
```js
const { siteMetadata: { title, menuLinks, social: { twitter, telegram } } } = useSiteMetadataQuery() //L1
const {
siteMetadata: {
title,
menuLinks,
social: { twitter, telegram },
},
} = useSiteMetadataQuery() //L1
const [isScrolled, setIsScrolled] = React.useState(false)
const [isMobileOpen, setIsMobileOpen] = React.useState(false)
const [isDark, setIsDark] = React.useState(false)
@ -45,8 +53,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
React.useEffect(() => {
setIsDark(window.__isDarkTheme)
isMobileOpen
? document.body.style.overflowY = 'hidden'
: document.body.style.overflowY = ''
? (document.body.style.overflowY = "hidden")
: (document.body.style.overflowY = "")
document.body.onscroll = () =>
window.pageYOffset >= 100 ? setIsScrolled(true) : setIsScrolled(false)
}, [setIsDark, isMobileOpen])

View File

@ -20,11 +20,13 @@ tags:
- javascript
- graphql
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -41,9 +43,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -20,11 +20,13 @@ tags:
- javascript
- graphql
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -41,9 +43,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -21,11 +21,13 @@ tags:
- javascript
- graphql
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -42,9 +44,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -20,11 +20,13 @@ tags:
- javascript
- graphql
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -41,9 +43,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -20,11 +20,13 @@ tags:
- javascript
- graphql
---
## this is second article
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
@ -41,9 +43,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -13,17 +13,18 @@ tags:
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non
fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo.
---
## Lorem ipsum dolor
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
@ -35,9 +36,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -13,17 +13,18 @@ tags:
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non
fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo.
---
## Lorem ipsum dolor
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
@ -35,9 +36,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -13,17 +13,18 @@ tags:
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non
fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo.
---
## Lorem ipsum dolor
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
* Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
- Nunc non fermentum ligula?
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligula. Aenean et ex nec magna faucibus congue eu ac justo. Cras malesuada diam lectus, vitae feugiat nulla condimentum id. Duis interdum sodales eros ornare mattis. Nunc id viverra ante. Maecenas suscipit est sed rhoncus condimentum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur vehicula risus nisi, quis hendrerit magna tincidunt eu. Phasellus fringilla accumsan viverra. Mauris a cursus orci.
@ -35,9 +36,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non fermentum ligu
module.exports = {
plugins: [
{
resolve: `plugin`
}
]
resolve: `plugin`,
},
],
}
```

View File

@ -1,27 +1,25 @@
import 'firebase/database';
import "firebase/database"
import 'gatsby-remark-vscode/styles.css'
import "gatsby-remark-vscode/styles.css"
import './src/styles/global.scss'
import './src/components/Header/Header.scss'
import './src/components/About/About.scss'
import './src/components/Offers/Offers.scss'
import './src/components/Articles/Articles.scss'
import './src/components/Articles/ArticleCard/ArticleCard.scss'
import './src/components/Newsletter/Newsletter.scss'
import './src/components/Article/Article.scss'
import './src/components/Snippet/Snippet.scss'
import './src/components/Snippets/Snippets.scss'
import './src/components/Articles/MoreArticles/MoreArticles.scss'
import './src/components/Books/Books.scss'
import './src/components/Contacts/Contacts.scss'
import './src/components/Footer/Footer.scss'
import "./src/styles/global.scss"
import "./src/components/Header/Header.scss"
import "./src/components/About/About.scss"
import "./src/components/Offers/Offers.scss"
import "./src/components/Articles/Articles.scss"
import "./src/components/Articles/ArticleCard/ArticleCard.scss"
import "./src/components/Newsletter/Newsletter.scss"
import "./src/components/Article/Article.scss"
import "./src/components/Snippet/Snippet.scss"
import "./src/components/Snippets/Snippets.scss"
import "./src/components/Articles/MoreArticles/MoreArticles.scss"
import "./src/components/Books/Books.scss"
import "./src/components/Contacts/Contacts.scss"
import "./src/components/Footer/Footer.scss"
export const shouldUpdateScroll = ({
routerProps: { location }
}) => {
export const shouldUpdateScroll = ({ routerProps: { location } }) => {
const { pathname } = location
const stopScrollLinks = ['/articles/']
const stopScrollLinks = ["/articles/"]
if (stopScrollLinks.includes(pathname)) return false

View File

@ -1,19 +1,19 @@
require('dotenv').config()
require("dotenv").config()
const siteConfig = require('./config')
const siteConfig = require("./config")
module.exports = {
siteMetadata: {
...siteConfig
...siteConfig,
},
plugins: [
'gatsby-plugin-react-helmet',
'gatsby-plugin-sass',
"gatsby-plugin-react-helmet",
"gatsby-plugin-sass",
{
resolve: 'gatsby-source-filesystem',
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/static/assets`,
name: 'assets',
name: "assets",
},
},
{
@ -40,7 +40,7 @@ module.exports = {
},
},
{
resolve: 'gatsby-plugin-firebase',
resolve: "gatsby-plugin-firebase",
options: {
credentials: {
apiKey: process.env.API_KEY,
@ -69,8 +69,8 @@ module.exports = {
{
resolve: `gatsby-remark-relative-images`,
options: {
name: "assets"
}
name: "assets",
},
},
{
resolve: `gatsby-remark-images`,
@ -89,41 +89,41 @@ module.exports = {
removeAccents: true,
isIconAfterHeader: true,
elements: [`h1`, `h2`, `h3`, `h4`],
}
},
},
{
resolve: `gatsby-remark-vscode`,
options: {
extensions: ['material-theme'],
extensions: ["material-theme"],
theme: {
default: 'Light+ (default light)',
default: "Light+ (default light)",
parentSelector: {
'body.dark': 'One Dark Pro'
}
"body.dark": "One Dark Pro",
},
},
},
},
],
},
},
}
}
]
}
}
],
},
},
{
resolve: 'gatsby-plugin-react-svg',
resolve: "gatsby-plugin-react-svg",
options: {
rule: {
include: /images/
}
}
include: /images/,
},
},
},
{
resolve: 'gatsby-source-filesystem',
resolve: "gatsby-source-filesystem",
options: {
name: 'content',
path: `${__dirname}/content`
}
name: "content",
path: `${__dirname}/content`,
},
'gatsby-plugin-netlify-cms',
},
"gatsby-plugin-netlify-cms",
],
}

View File

@ -1,14 +1,14 @@
import slugify from './src/helpers/slugify'
import { fmImagesToRelative } from 'gatsby-remark-relative-images'
import path from 'path'
import slugify from "./src/helpers/slugify"
import { fmImagesToRelative } from "gatsby-remark-relative-images"
import path from "path"
exports.createPages = async ({ actions, graphql }) => {
const { createPage } = actions
const articleTemplate = path.resolve('src/templates/articleTemplate.js')
const snippetTemplate = path.resolve('src/templates/snippetTemplate.js')
const categoryTemplate = path.resolve('src/templates/categoryTemplate.js')
const tagTemplate = path.resolve('src/templates/tagTemplate.js')
const articleTemplate = path.resolve("src/templates/articleTemplate.js")
const snippetTemplate = path.resolve("src/templates/snippetTemplate.js")
const categoryTemplate = path.resolve("src/templates/categoryTemplate.js")
const tagTemplate = path.resolve("src/templates/tagTemplate.js")
const result = await graphql(
`
@ -33,19 +33,25 @@ exports.createPages = async ({ actions, graphql }) => {
}
const all = result.data.allMarkdownRemark.edges
const articles = all.filter(article => article.node.frontmatter.templateKey === 'article')
const snippets = all.filter(snippet => snippet.node.frontmatter.templateKey === 'snippet')
const categories = all.filter(category => category.node.frontmatter.templateKey === 'category')
const tags = all.filter(tag => tag.node.frontmatter.templateKey === 'tag')
const articles = all.filter(
article => article.node.frontmatter.templateKey === "article"
)
const snippets = all.filter(
snippet => snippet.node.frontmatter.templateKey === "snippet"
)
const categories = all.filter(
category => category.node.frontmatter.templateKey === "category"
)
const tags = all.filter(tag => tag.node.frontmatter.templateKey === "tag")
articles.forEach(({ node }) => {
createPage({
path: `article/${node.frontmatter.slug}`,
component: articleTemplate,
context: {
slug: node.frontmatter.slug
slug: node.frontmatter.slug,
},
});
})
})
snippets.forEach(({ node }) => {
@ -53,7 +59,7 @@ exports.createPages = async ({ actions, graphql }) => {
path: `snippet/${node.frontmatter.slug}`,
component: snippetTemplate,
context: {
slug: node.frontmatter.slug
slug: node.frontmatter.slug,
},
})
})
@ -63,9 +69,9 @@ exports.createPages = async ({ actions, graphql }) => {
path: `category/${slugify(node.frontmatter.title)}`,
component: categoryTemplate,
context: {
title: node.frontmatter.title
title: node.frontmatter.title,
},
});
})
})
tags.forEach(({ node }) => {
@ -73,12 +79,12 @@ exports.createPages = async ({ actions, graphql }) => {
path: `tag/${slugify(node.frontmatter.title)}`,
component: tagTemplate,
context: {
title: node.frontmatter.title
}
title: node.frontmatter.title,
},
})
})
}
exports.onCreateNode = ({ node }) => {
fmImagesToRelative(node);
};
fmImagesToRelative(node)
}

View File

@ -1,2 +1,2 @@
require = require('esm')(module)
module.exports = require('./gatsby-node.esm.js')
require = require("esm")(module)
module.exports = require("./gatsby-node.esm.js")

View File

@ -1,10 +1,11 @@
import React from 'react';
import 'firebase/database';
import React from "react"
import "firebase/database"
const OnDark = () => {
return (
<script
dangerouslySetInnerHTML={{ __html: `
dangerouslySetInnerHTML={{
__html: `
(() => {
window.__isDarkTheme = false
@ -31,11 +32,12 @@ const OnDark = () => {
}
}
})()
` }}
`,
}}
/>
)
}
export const onRenderBody = ({ setPreBodyComponents }) => {
setPreBodyComponents(<OnDark key='isDarkTheme' />)
setPreBodyComponents(<OnDark key="isDarkTheme" />)
}

View File

@ -1,8 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import React from "react"
import PropTypes from "prop-types"
import { Link } from "gatsby"
import ContactsIcon from '../../images/contacts-icon.svg'
import ContactsIcon from "../../images/contacts-icon.svg"
const About = ({ title, pageTitle, social }) => {
return (
@ -15,11 +15,12 @@ const About = ({ title, pageTitle, social }) => {
<h2>{title}</h2>
<h3>публикуем статьи о мире веб-разрабоки и IT</h3>
<p>
этот сайт придуман для того, чтобы помочь вам разобраться с вашей проблемой и решить её. Сайт содержит
в себе статьи и руководства.
этот сайт придуман для того, чтобы помочь вам разобраться с вашей
проблемой и решить её. Сайт содержит в себе статьи и руководства.
</p>
<p>
~ к тому же здесь публикуются различные новости которые произошли за последнее время
~ к тому же здесь публикуются различные новости которые произошли за
последнее время
</p>
</div>
<div className="about-content__contacts">
@ -28,9 +29,15 @@ const About = ({ title, pageTitle, social }) => {
<span>контакты</span>
</div>
<ul>
<li><a href={`mailto:${social.email}`}>{social.email}</a></li>
<li><a href={social.telegram} target='_blank' rel='noreferrer'>telegram</a></li>
<Link to='/contacts'>форма обратной связи</Link>
<li>
<a href={`mailto:${social.email}`}>{social.email}</a>
</li>
<li>
<a href={social.telegram} target="_blank" rel="noreferrer">
telegram
</a>
</li>
<Link to="/contacts">форма обратной связи</Link>
</ul>
</div>
</div>
@ -43,5 +50,5 @@ export default About
About.propTypes = {
title: PropTypes.string,
pageTitle: PropTypes.string,
email: PropTypes.string
email: PropTypes.string,
}

View File

@ -1,25 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types'
import SEO from '../SEO/SEO'
import React from "react"
import PropTypes from "prop-types"
import SEO from "../SEO/SEO"
import Img from "gatsby-image"
import ViewCounter from '../ViewCounter/ViewCounter'
import useSiteMetadataQuery from '../../hooks/useSiteMetadataQuery'
import slugify from '../../helpers/slugify'
import { Link } from 'gatsby'
import { useLocation } from '@reach/router'
import ViewCounter from "../ViewCounter/ViewCounter"
import useSiteMetadataQuery from "../../hooks/useSiteMetadataQuery"
import slugify from "../../helpers/slugify"
import { Link } from "gatsby"
import { useLocation } from "@reach/router"
import ViewsIcon from '../../images/views-icon.svg'
import VkIcon from '../../images/vk-icon.svg'
import TwitterIcon from '../../images/twitter-icon__large.svg'
import FacebookIcon from '../../images/facebook-icon__large.svg'
import ViewsIcon from "../../images/views-icon.svg"
import VkIcon from "../../images/vk-icon.svg"
import TwitterIcon from "../../images/twitter-icon__large.svg"
import FacebookIcon from "../../images/facebook-icon__large.svg"
const Article = ({ data: { slug, keywords, title, image, description, category, tags, time, date }, html }) => {
const { siteMetadata: { siteUrl } } = useSiteMetadataQuery()
const Article = ({
data: {
slug,
keywords,
title,
image,
description,
category,
tags,
time,
date,
},
html,
}) => {
const {
siteMetadata: { siteUrl },
} = useSiteMetadataQuery()
const { pathname } = useLocation()
return (
<>
<SEO title={title} titleTemplate={false} keywords={keywords.join(', ')} description={description} image={`${siteUrl}${image.childImageSharp.fluid.src}`} type='article' />
<SEO
title={title}
titleTemplate={false}
keywords={keywords.join(", ")}
description={description}
image={`${siteUrl}${image.childImageSharp.fluid.src}`}
type="article"
/>
<div className="article">
<article>
<div className="article-info">
@ -40,13 +62,25 @@ const Article = ({ data: { slug, keywords, title, image, description, category,
</div>
<div className="article-share">
<div className="article-share__social">
<a href={`https://vk.com/share.php?url=${siteUrl}/article/${slug}&title=${title}&image=${siteUrl}${image.childImageSharp.fluid.src}`} target='_blank' rel='noreferrer'>
<a
href={`https://vk.com/share.php?url=${siteUrl}/article/${slug}&title=${title}&image=${siteUrl}${image.childImageSharp.fluid.src}`}
target="_blank"
rel="noreferrer"
>
<VkIcon />
</a>
<a href={`https://twitter.com/intent/tweet?text=${title}&url=${siteUrl}/article/${slug}&image=${siteUrl}${image.childImageSharp.fluid.src}`} target='_blank' rel='noreferrer'>
<a
href={`https://twitter.com/intent/tweet?text=${title}&url=${siteUrl}/article/${slug}&image=${siteUrl}${image.childImageSharp.fluid.src}`}
target="_blank"
rel="noreferrer"
>
<TwitterIcon />
</a>
<a href={`https://www.facebook.com/sharer/sharer.php?u=${siteUrl}/article/${slug}&t=${title}`} target='_blank' rel='noreferrer'>
<a
href={`https://www.facebook.com/sharer/sharer.php?u=${siteUrl}/article/${slug}&t=${title}`}
target="_blank"
rel="noreferrer"
>
<FacebookIcon />
</a>
</div>
@ -58,30 +92,37 @@ const Article = ({ data: { slug, keywords, title, image, description, category,
<Link to={`/category/${slugify(category)}`}>{category}</Link>
</div>
<div className="article-description">
<p>
{description}
</p>
<p>{description}</p>
</div>
<div className="article-tags">
{tags.map((tag, i) => <div key={i} className="tag-item">
{tags.map((tag, i) => (
<div key={i} className="tag-item">
<Link to={`/tag/${slugify(tag)}`}>{tag}</Link>
</div>)}
</div>
))}
</div>
<div className="article-image">
<Img loading="eager" fadeIn={false} fluid={image.childImageSharp.fluid} alt={title} />
<Img
loading="eager"
fadeIn={false}
fluid={image.childImageSharp.fluid}
alt={title}
/>
</div>
<div className="article-inner inner">
<div dangerouslySetInnerHTML={{ __html: html }}></div>
</div>
<div className="article-more">
<span>Смотреть еще в категории <Link to={`/category/${slugify(category)}`}>{category}</Link></span>
<span>
Смотреть еще в категории{" "}
<Link to={`/category/${slugify(category)}`}>{category}</Link>
</span>
</div>
<div className="article-comments">
<div className="article-comments__heading">
<h2>Комментарии</h2>
</div>
<div className="article-comments__content">
</div>
<div className="article-comments__content"></div>
</div>
</article>
</div>
@ -101,7 +142,7 @@ Article.propTypes = {
category: PropTypes.string,
tags: PropTypes.array,
time: PropTypes.string,
date: PropTypes.string
date: PropTypes.string,
}),
html: PropTypes.string
html: PropTypes.string,
}

View File

@ -1,32 +1,43 @@
import React from 'react';
import PropTypes from 'prop-types'
import slugify from '../../../helpers/slugify'
import React from "react"
import PropTypes from "prop-types"
import slugify from "../../../helpers/slugify"
import Img from "gatsby-image"
import { Link } from 'gatsby'
import { Link } from "gatsby"
const ArticleCard = ({ data: { slug, title, image, category, description, tags } }) => {
const ArticleCard = ({
data: { slug, title, image, category, description, tags },
}) => {
return (
<article className="article-card">
<div className="article-card__image">
<Link to={`/article/${slug}`}>
<Img loading="eager" fadeIn={false} fluid={image.childImageSharp.fluid} alt={title} />
<Img
loading="eager"
fadeIn={false}
fluid={image.childImageSharp.fluid}
alt={title}
/>
</Link>
</div>
<div className="article-card__heading">
<h2><Link to={`/article/${slug}`}>{title}</Link></h2>
<h2>
<Link to={`/article/${slug}`}>{title}</Link>
</h2>
</div>
<div className="article-card__category">
<Link to={`/category/${slugify(category)}`}>{category}</Link>
</div>
<div className="article-card__tags">
<ul>
{tags.map((tag, i) => <li key={i}><Link to={`/tag/${tag}`}>{tag}</Link></li>)}
{tags.map((tag, i) => (
<li key={i}>
<Link to={`/tag/${tag}`}>{tag}</Link>
</li>
))}
</ul>
</div>
<div className="article-card__description">
<p>
{description}
</p>
<p>{description}</p>
</div>
</article>
)
@ -41,6 +52,6 @@ ArticleCard.propTypes = {
image: PropTypes.object,
category: PropTypes.string,
description: PropTypes.string,
tags: PropTypes.array
})
tags: PropTypes.array,
}),
}

View File

@ -1,10 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types'
import useSiteMetadataQuery from '../../hooks/useSiteMetadataQuery'
import ArticleCard from './ArticleCard/ArticleCard'
import React from "react"
import PropTypes from "prop-types"
import useSiteMetadataQuery from "../../hooks/useSiteMetadataQuery"
import ArticleCard from "./ArticleCard/ArticleCard"
const Articles = ({ data: { edges }, limit = 6, setLimit = () => null }) => {
const { siteMetadata: { options: { articles: { onLoadMore } } } } = useSiteMetadataQuery()
const {
siteMetadata: {
options: {
articles: { onLoadMore },
},
},
} = useSiteMetadataQuery()
return (
<>
<div className="articles">
@ -16,14 +22,20 @@ const Articles = ({ data: { edges }, limit = 6, setLimit = () => null }) => {
return <ArticleCard key={i} data={node.frontmatter} />
}
return null
})
}
})}
</div>
<div className="articles-loadMore">
{edges.length === limit || edges.length <= limit
? null
: <button className='btn-loadMore' onClick={() => setLimit(limit + onLoadMore)}>Загрузить еще&nbsp;&nbsp;<span role='img' aria-label='load more'>👇</span></button>
}
{edges.length === limit || edges.length <= limit ? null : (
<button
className="btn-loadMore"
onClick={() => setLimit(limit + onLoadMore)}
>
Загрузить еще&nbsp;&nbsp;
<span role="img" aria-label="load more">
👇
</span>
</button>
)}
</div>
</div>
</>
@ -36,6 +48,6 @@ Articles.propTypes = {
data: PropTypes.shape({
edges: PropTypes.array,
limit: PropTypes.number,
setLimit: PropTypes.func
})
setLimit: PropTypes.func,
}),
}

View File

@ -1,18 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types'
import { useLocation } from '@reach/router'
import { Link } from 'gatsby'
import React from "react"
import PropTypes from "prop-types"
import { useLocation } from "@reach/router"
import { Link } from "gatsby"
const Categories = ({ children, setQuery = () => null }) => {
const { search, pathname } = useLocation()
return (
<div className="articles-categories">
<div className={
pathname === `/articles` || (pathname === `/articles/` && search === '')
? 'tag-item active'
: 'tag-item'
}>
<Link onClick={() => setQuery('')} to='/articles'>Все</Link>
<div
className={
pathname === `/articles` ||
(pathname === `/articles/` && search === "")
? "tag-item active"
: "tag-item"
}
>
<Link onClick={() => setQuery("")} to="/articles">
Все
</Link>
</div>
{children}
</div>
@ -22,5 +27,5 @@ const Categories = ({ children, setQuery = () => null }) => {
export default Categories
Categories.propTypes = {
setQuery: PropTypes.func
setQuery: PropTypes.func,
}

View File

@ -1,13 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types'
import { useLocation } from '@reach/router'
import { Link } from 'gatsby'
import slugify from '../../../helpers/slugify'
import React from "react"
import PropTypes from "prop-types"
import { useLocation } from "@reach/router"
import { Link } from "gatsby"
import slugify from "../../../helpers/slugify"
const CategoryItem = ({ title }) => {
const { pathname } = useLocation()
return (
<div className={pathname === `/category/${slugify(title)}` || pathname === `/category/${slugify(title)}/` ? 'tag-item active' : 'tag-item'}>
<div
className={
pathname === `/category/${slugify(title)}` ||
pathname === `/category/${slugify(title)}/`
? "tag-item active"
: "tag-item"
}
>
<Link to={`/category/${slugify(title)}`}>{title}</Link>
</div>
)
@ -16,6 +23,5 @@ const CategoryItem = ({ title }) => {
export default CategoryItem
CategoryItem.propTypes = {
title: PropTypes.string
title: PropTypes.string,
}

View File

@ -1,12 +1,12 @@
import React from 'react';
import { useLocation } from '@reach/router'
import useMoreArticlesQuery from '../../../hooks/useMoreArticlesQuery'
import ArticleCard from '../ArticleCard/ArticleCard'
import React from "react"
import { useLocation } from "@reach/router"
import useMoreArticlesQuery from "../../../hooks/useMoreArticlesQuery"
import ArticleCard from "../ArticleCard/ArticleCard"
const MoreArticles = () => {
const { edges } = useMoreArticlesQuery()
const { pathname } = useLocation()
const query = pathname.split('/')[pathname.split('/').length - 1]
const query = pathname.split("/")[pathname.split("/").length - 1]
return (
<div className="more-articles">
@ -14,7 +14,10 @@ const MoreArticles = () => {
<h2>Вам также может понравится...</h2>
</div>
<div className="more-articles__content">
{edges.filter(({ node }) => node.frontmatter.slug !== query).sort(() => Math.random() - 0.5).map(({ node }, i) => {
{edges
.filter(({ node }) => node.frontmatter.slug !== query)
.sort(() => Math.random() - 0.5)
.map(({ node }, i) => {
if (i < 4) {
return <ArticleCard key={i} data={node.frontmatter} />
}

View File

@ -1,13 +1,23 @@
import React from 'react'
import { navigate } from 'gatsby'
import PropTypes from 'prop-types'
import React from "react"
import { navigate } from "gatsby"
import PropTypes from "prop-types"
const Search = ({ allArticles, filterArticles, articlesLength, resultsLength, query, setQuery, setFiltredArticles, setLimit, onPage }) => {
const Search = ({
allArticles,
filterArticles,
articlesLength,
resultsLength,
query,
setQuery,
setFiltredArticles,
setLimit,
onPage,
}) => {
const handleInput = e => {
const query = e.target.value
if (query === '') {
navigate('/articles/')
setQuery('')
if (query === "") {
navigate("/articles/")
setQuery("")
setLimit(onPage)
return null
}
@ -19,8 +29,13 @@ const Search = ({ allArticles, filterArticles, articlesLength, resultsLength, qu
return (
<div className="articles-search">
<input onChange={e => handleInput(e)} value={query} placeholder="Напишите сюда чтобы отфильтровать статьи" type="text" />
<span>{query === '' ? articlesLength : resultsLength}</span>
<input
onChange={e => handleInput(e)}
value={query}
placeholder="Напишите сюда чтобы отфильтровать статьи"
type="text"
/>
<span>{query === "" ? articlesLength : resultsLength}</span>
</div>
)
}
@ -36,5 +51,5 @@ Search.propTypes = {
setQuery: PropTypes.func,
setFiltredArticles: PropTypes.func,
setLimit: PropTypes.func,
onPage: PropTypes.number
onPage: PropTypes.number,
}

View File

@ -1,17 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types'
import slugify from '../../helpers/slugify'
import React from "react"
import PropTypes from "prop-types"
import slugify from "../../helpers/slugify"
import Img from "gatsby-image"
const Books = ({ data: { edges } }) => {
return (
<div className="books">
<div className="books-inner inner">
{edges.map(({ node }, i) => <div key={i}>
<h2>{node.frontmatter.title} <a id={slugify(node.frontmatter.title)} href={`/books#${slugify(node.frontmatter.title)}`} className='anchor'>{node.frontmatter.title}</a></h2>
<Img loading="eager" fadeIn={false} fluid={node.frontmatter.image.childImageSharp.fluid} alt={node.frontmatter.title} />
<div className='md' dangerouslySetInnerHTML={{__html: node.html}}></div>
</div>)}
{edges.map(({ node }, i) => (
<div key={i}>
<h2>
{node.frontmatter.title}{" "}
<a
id={slugify(node.frontmatter.title)}
href={`/books#${slugify(node.frontmatter.title)}`}
className="anchor"
>
{node.frontmatter.title}
</a>
</h2>
<Img
loading="eager"
fadeIn={false}
fluid={node.frontmatter.image.childImageSharp.fluid}
alt={node.frontmatter.title}
/>
<div
className="md"
dangerouslySetInnerHTML={{ __html: node.html }}
></div>
</div>
))}
</div>
</div>
)
@ -21,6 +40,6 @@ export default Books
Books.propTypes = {
data: PropTypes.shape({
edges: PropTypes.array
})
edges: PropTypes.array,
}),
}

View File

@ -1,8 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types'
import ContactsFrom from './ContactsForm/ContactsForm';
import React from "react"
import PropTypes from "prop-types"
import ContactsFrom from "./ContactsForm/ContactsForm"
import MemoIcon from '../../images/memo-icon.svg'
import MemoIcon from "../../images/memo-icon.svg"
const Contacts = ({ email, telegram }) => {
return (
@ -13,15 +13,21 @@ const Contacts = ({ email, telegram }) => {
</div>
<div className="contacts-description">
<p>
Для того чтобы задать вопрос, сообщить об ошибках или просто поздороваться, вы можете воспользоваться формой обратной связи или использовать контактные данные.
Для того чтобы задать вопрос, сообщить об ошибках или просто
поздороваться, вы можете воспользоваться формой обратной связи или
использовать контактные данные.
</p>
</div>
<ContactsFrom />
<div className="contacts-info">
<h2>контактные данные</h2>
<ul>
<li><a href={`mailto:${email}`}>{email}</a></li>
<li><a href={telegram}>telegram</a></li>
<li>
<a href={`mailto:${email}`}>{email}</a>
</li>
<li>
<a href={telegram}>telegram</a>
</li>
</ul>
</div>
</div>
@ -32,5 +38,5 @@ export default Contacts
Contacts.propTypes = {
email: PropTypes.string,
telegram: PropTypes.string
telegram: PropTypes.string,
}

View File

@ -1,4 +1,4 @@
import React from 'react';
import React from "react"
const ContactsFrom = () => {
return (

View File

@ -1,54 +1,92 @@
import React from 'react';
import { Link } from 'gatsby'
import PropTypes from 'prop-types'
import useSiteMetadataQuery from '../../hooks/useSiteMetadataQuery'
import React from "react"
import { Link } from "gatsby"
import PropTypes from "prop-types"
import useSiteMetadataQuery from "../../hooks/useSiteMetadataQuery"
import LogoIcon from '../../images/logo-icon.svg'
import GatsbyIcon from '../../images/gatsby-icon.svg'
import NetlifyIcon from '../../images/netlify-icon.svg'
import LogoIcon from "../../images/logo-icon.svg"
import GatsbyIcon from "../../images/gatsby-icon.svg"
import NetlifyIcon from "../../images/netlify-icon.svg"
const Footer = () => {
const { siteMetadata: { title, menuLinks, social: { email, telegram, twitter, yadzen } } } = useSiteMetadataQuery()
const {
siteMetadata: {
title,
menuLinks,
social: { email, telegram, twitter, yadzen },
},
} = useSiteMetadataQuery()
return (
<footer>
<div className="footer-content">
<div className="footer-content__logo">
<LogoIcon />
<Link to='/'>{title}</Link>
<Link to="/">{title}</Link>
</div>
<div className="footer-content__nav">
<div className="footer-nav__links">
<div className="links-nav">
<p>Навигация</p>
<ul>
{menuLinks.map((page, i) => page.link !== '/contacts' ? <li key={i}><Link to={page.link}>{page.name}</Link></li> : null)}
{menuLinks.map((page, i) =>
page.link !== "/contacts" ? (
<li key={i}>
<Link to={page.link}>{page.name}</Link>
</li>
) : null
)}
</ul>
</div>
<div className="links-contacts">
<p>Контакты</p>
<ul>
<li><Link to='/contacts'>Форма обратной связи</Link></li>
<li><a href={telegram}>Телеграмм</a></li>
<li><a href={`mailto:${email}`}>Почта</a></li>
<li>
<Link to="/contacts">Форма обратной связи</Link>
</li>
<li>
<a href={telegram}>Телеграмм</a>
</li>
<li>
<a href={`mailto:${email}`}>Почта</a>
</li>
</ul>
</div>
<div className="links-social">
<p>Социальные сети</p>
<ul>
<li><a href={telegram} rel='noreferrer' target='_blank'>Наш телеграмм канал</a></li>
<li><a href={yadzen} rel='noreferrer' target='_blank'>Яндекс.Дзен</a></li>
<li><a href={twitter} rel='noreferrer' target='_blank'>Твиттер</a></li>
<li>
<a href={telegram} rel="noreferrer" target="_blank">
Наш телеграмм канал
</a>
</li>
<li>
<a href={yadzen} rel="noreferrer" target="_blank">
Яндекс.Дзен
</a>
</li>
<li>
<a href={twitter} rel="noreferrer" target="_blank">
Твиттер
</a>
</li>
</ul>
</div>
</div>
<div className="footer-nav__helpers">
<span>Разработка при помощи:</span>
<div className="helpers-icons">
<a href="https://www.gatsbyjs.org/" rel='noreferrer' target="_blank">
<a
href="https://www.gatsbyjs.org/"
rel="noreferrer"
target="_blank"
>
<GatsbyIcon />
GatsbyJS
</a>
<a href="https://www.netlify.com/" rel='noreferrer' target="_blank">
<a
href="https://www.netlify.com/"
rel="noreferrer"
target="_blank"
>
<NetlifyIcon />
Netlify
</a>
@ -66,9 +104,9 @@ const Footer = () => {
export default Footer
Footer.propTypes = {
title: PropTypes.string
title: PropTypes.string,
}
Footer.defaultProps = {
title: null
title: null,
}

View File

@ -1,17 +1,23 @@
import React from 'react'
import { useLocation } from '@reach/router'
import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import useSiteMetadataQuery from '../../hooks/useSiteMetadataQuery'
import React from "react"
import { useLocation } from "@reach/router"
import PropTypes from "prop-types"
import { Link } from "gatsby"
import useSiteMetadataQuery from "../../hooks/useSiteMetadataQuery"
import LogoIcon from '../../images/logo-icon.svg'
import TelegramIcon from '../../images/telegram-icon.svg'
import TwitterIcon from '../../images/twitter-icon.svg'
import MoonIcon from '../../images/moon-icon.svg'
import SunIcon from '../../images/sun-icon.svg'
import LogoIcon from "../../images/logo-icon.svg"
import TelegramIcon from "../../images/telegram-icon.svg"
import TwitterIcon from "../../images/twitter-icon.svg"
import MoonIcon from "../../images/moon-icon.svg"
import SunIcon from "../../images/sun-icon.svg"
const Header = () => {
const { siteMetadata: { title, menuLinks, social: { twitter, telegram } } } = useSiteMetadataQuery()
const {
siteMetadata: {
title,
menuLinks,
social: { twitter, telegram },
},
} = useSiteMetadataQuery()
const [isScrolled, setIsScrolled] = React.useState(false)
const [isMobileOpen, setIsMobileOpen] = React.useState(false)
const [isDark, setIsDark] = React.useState(false)
@ -19,8 +25,11 @@ const Header = () => {
React.useEffect(() => {
setIsDark(window.__isDarkTheme)
isMobileOpen ? document.body.style.overflowY = 'hidden' : document.body.style.overflowY = ''
document.body.onscroll = () => window.pageYOffset >= 100 ? setIsScrolled(true) : setIsScrolled(false)
isMobileOpen
? (document.body.style.overflowY = "hidden")
: (document.body.style.overflowY = "")
document.body.onscroll = () =>
window.pageYOffset >= 100 ? setIsScrolled(true) : setIsScrolled(false)
}, [setIsDark, isMobileOpen])
const mobileHandler = () => {
@ -30,49 +39,70 @@ const Header = () => {
const renderLinks = () => {
return (
<ul>
{menuLinks.map((page, i) => <li key={i} className={page.link === pathname || pathname === `${page.link}/` ? 'active' : null}><Link to={page.link}>{page.name}</Link></li>)}
{menuLinks.map((page, i) => (
<li
key={i}
className={
page.link === pathname || pathname === `${page.link}/`
? "active"
: null
}
>
<Link to={page.link}>{page.name}</Link>
</li>
))}
</ul>
)
}
return (
<header className={isScrolled ? 'headerScrolled' : null}>
<header className={isScrolled ? "headerScrolled" : null}>
<div className="header-content">
<div className="header-content__logo">
<LogoIcon />
<Link to='/'>{title}</Link>
<Link to="/">{title}</Link>
</div>
<div className="header-content__nav desktop">
<div className="header-nav__social">
<a href={telegram} target="_blank" rel='noreferrer'>
<a href={telegram} target="_blank" rel="noreferrer">
<TelegramIcon />
</a>
<a href={twitter} target="_blank" rel='noreferrer'>
<a href={twitter} target="_blank" rel="noreferrer">
<TwitterIcon />
</a>
</div>
<div className="header-nav__links">
<nav>
{renderLinks()}
</nav>
<nav>{renderLinks()}</nav>
</div>
<div className="header-nav__theme">
{isDark ? <SunIcon onClick={() => {
window.__setTheme('light')
{isDark ? (
<SunIcon
onClick={() => {
window.__setTheme("light")
setIsDark(!isDark)
}} /> : <MoonIcon onClick={() => {
window.__setTheme('dark')
}}
/>
) : (
<MoonIcon
onClick={() => {
window.__setTheme("dark")
setIsDark(!isDark)
}} />}
}}
/>
)}
</div>
</div>
<div className={isMobileOpen ? 'header-content__nav mobile mobileOpen' : 'header-content__nav mobile'}>
{isMobileOpen
? <>
<div
className={
isMobileOpen
? "header-content__nav mobile mobileOpen"
: "header-content__nav mobile"
}
>
{isMobileOpen ? (
<>
<span>{title}</span>
<nav>
{renderLinks()}
</nav>
<nav>{renderLinks()}</nav>
<div className="mobile-social">
<a href="/" target="_blank">
<TelegramIcon />
@ -82,10 +112,17 @@ const Header = () => {
</a>
</div>
</>
: null
}
) : null}
</div>
<button aria-label='menu' onClick={mobileHandler} className={isMobileOpen ? "header-content__button mobile active" : 'header-content__button mobile'}>
<button
aria-label="menu"
onClick={mobileHandler}
className={
isMobileOpen
? "header-content__button mobile active"
: "header-content__button mobile"
}
>
<span />
<span />
<span />
@ -102,12 +139,12 @@ Header.propTypes = {
menuLinks: PropTypes.arrayOf(
PropTypes.shape({
link: PropTypes.string,
name: PropTypes.string
name: PropTypes.string,
})
)
),
}
Header.defaultProps = {
title: null,
menuLinks: null
menuLinks: null,
}

View File

@ -1,6 +1,6 @@
import React from 'react'
import Header from '../Header/Header'
import Footer from '../Footer/Footer'
import React from "react"
import Header from "../Header/Header"
import Footer from "../Footer/Footer"
const Layout = ({ children }) => {
return (

View File

@ -1,17 +1,20 @@
import React from 'react';
import React from "react"
const Newsletter = () => {
return (
<div className="newsletter">
<div className="newsletter-heading">
Новостная рассылка
</div>
<div className="newsletter-heading">Новостная рассылка</div>
<div className="newsletter-content">
<form action="#">
<p>
<span role="img" aria-label="hot" >🔥</span> At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum
deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non
provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga
<span role="img" aria-label="hot">
🔥
</span>{" "}
At vero eos et accusamus et iusto odio dignissimos ducimus qui
blanditiis praesentium voluptatum deleniti atque corrupti quos
dolores et quas molestias excepturi sint occaecati cupiditate non
provident, similique sunt in culpa qui officia deserunt mollitia
animi, id est laborum et dolorum fuga
</p>
<div className="email-field">
<input placeholder="ваш e-mail" type="text" name="email" />

View File

@ -1,13 +1,13 @@
import React from 'react';
import { useLocation } from '@reach/router'
import usePopularArticlesQuery from '../../hooks/usePopularArticlesQuery'
import useTagsQuery from '../../hooks/useTagsQuery'
import Popular from './Popular/Popular'
import Tags from './Tags/Tags'
import Subscription from './Subscription/Subscription'
import React from "react"
import { useLocation } from "@reach/router"
import usePopularArticlesQuery from "../../hooks/usePopularArticlesQuery"
import useTagsQuery from "../../hooks/useTagsQuery"
import Popular from "./Popular/Popular"
import Tags from "./Tags/Tags"
import Subscription from "./Subscription/Subscription"
import PopularItem from './Popular/PopularItem'
import TagItem from './Tags/TagItem'
import PopularItem from "./Popular/PopularItem"
import TagItem from "./Tags/TagItem"
const Offers = () => {
const { pathname } = useLocation()
@ -15,12 +15,23 @@ const Offers = () => {
const { tags } = useTagsQuery()
return (
<div style={{ height: pathname === '/' ? '78%' : '78.5%' }} className="offers">
<div
style={{ height: pathname === "/" ? "78%" : "78.5%" }}
className="offers"
>
<Popular>
{articles.map(({ node }, i) => <PopularItem key={i} slug={node.frontmatter.slug} title={node.frontmatter.title} />)}
{articles.map(({ node }, i) => (
<PopularItem
key={i}
slug={node.frontmatter.slug}
title={node.frontmatter.title}
/>
))}
</Popular>
<Tags>
{tags.map(({ node }, i) => <TagItem key={i} title={node.frontmatter.title} />)}
{tags.map(({ node }, i) => (
<TagItem key={i} title={node.frontmatter.title} />
))}
</Tags>
<Subscription />
</div>

View File

@ -1,16 +1,15 @@
import React from 'react';
import React from "react"
import PopularIcon from '../../../images/popular-icon.svg'
import PopularIcon from "../../../images/popular-icon.svg"
const Popular = ({ children }) => {
return (
<div className="offers-popular offer-box">
<div className="offers-popular__heading offer-heading">
<PopularIcon />
Популярные статьи</div>
<div className="offers-popular__content">
{children}
Популярные статьи
</div>
<div className="offers-popular__content">{children}</div>
</div>
)
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import React from "react"
import PropTypes from "prop-types"
import { Link } from "gatsby"
const PopularItem = ({ slug, title }) => {
return (
@ -8,8 +8,7 @@ const PopularItem = ({ slug, title }) => {
<div className="article-item__title">
<Link to={`/article/${slug}`}>{title}</Link>
</div>
<div className="article-item__views">
</div>
<div className="article-item__views"></div>
</div>
)
}
@ -18,5 +17,5 @@ export default PopularItem
PopularItem.propTypes = {
slug: PropTypes.string,
title: PropTypes.string
title: PropTypes.string,
}

View File

@ -1,19 +1,40 @@
import React from 'react';
import { useLocation } from '@reach/router'
import React from "react"
import { useLocation } from "@reach/router"
const Subscription = () => {
const { pathname } = useLocation()
return (
<div style={pathname !== '/about' && pathname !== '/about/' ? { height: '100%' } : null}>
<div style={pathname !== '/about' && pathname !== '/about/' ? { position: 'sticky', top: 75 } : null} className="offers-subscription offer-box">
<div
style={
pathname !== "/about" && pathname !== "/about/"
? { height: "100%" }
: null
}
>
<div
style={
pathname !== "/about" && pathname !== "/about/"
? { position: "sticky", top: 75 }
: null
}
className="offers-subscription offer-box"
>
<div className="offers-subscription__heading offer-heading">
Канал в Telegram
</div>
<div className="offers-subscription__content">
<p>
В нашем канале telegram вы не пропустите статьи и новости, присоединяйтесь к другим разработчикам
В нашем канале telegram вы не пропустите статьи и новости,
присоединяйтесь к другим разработчикам
</p>
<a href="https://t.me/devthread" rel='noreferrer' target="_blank" className="btn-primary">Подписаться</a>
<a
href="https://t.me/devthread"
rel="noreferrer"
target="_blank"
className="btn-primary"
>
Подписаться
</a>
</div>
</div>
</div>

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import sligify from '../../../helpers/slugify'
import React from "react"
import PropTypes from "prop-types"
import { Link } from "gatsby"
import sligify from "../../../helpers/slugify"
const TagItem = ({ title }) => {
return (
@ -14,5 +14,5 @@ const TagItem = ({ title }) => {
export default TagItem
TagItem.propTypes = {
title: PropTypes.string
title: PropTypes.string,
}

View File

@ -1,18 +1,15 @@
import React from 'react'
import React from "react"
import TagIcon from '../../../images/tag-icon.svg'
import TagIcon from "../../../images/tag-icon.svg"
const Tags = ({ children }) => {
return (
<div className="offers-tags offer-box">
<div className="offers-tags__heading offer-heading">
<TagIcon />
Тэги
</div>
<div className="offers-tags__content">
{children}
</div>
<div className="offers-tags__content">{children}</div>
</div>
)
}

View File

@ -1,10 +1,18 @@
import React from 'react';
import useSiteMetadataQuery from '../../hooks/useSiteMetadataQuery'
import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet'
import { useLocation } from '@reach/router'
import React from "react"
import useSiteMetadataQuery from "../../hooks/useSiteMetadataQuery"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
const SEO = ({ title, titleTemplate, keywords, description, image, type, canonical }) => {
const SEO = ({
title,
titleTemplate,
keywords,
description,
image,
type,
canonical,
}) => {
const { pathname } = useLocation()
const { siteMetadata } = useSiteMetadataQuery()
@ -12,55 +20,62 @@ const SEO = ({ title, titleTemplate, keywords, description, image, type, canonic
title: title || siteMetadata.title,
titleTemplate: true,
siteName: siteMetadata.siteName,
type: type || 'website',
image: image || 'custom image url...',
type: type || "website",
image: image || "custom image url...",
description: description || siteMetadata.description,
keywords: keywords || siteMetadata.keywords,
url: `${siteMetadata.siteUrl}${pathname}`,
canonical: canonical || `${siteMetadata.siteUrl}${pathname}`
canonical: canonical || `${siteMetadata.siteUrl}${pathname}`,
}
return (
<Helmet title={seo.title} titleTemplate={titleTemplate === false ? null : siteMetadata.titleTemplate}>
<Helmet
title={seo.title}
titleTemplate={
titleTemplate === false ? null : siteMetadata.titleTemplate
}
>
<html lang="ru" />
<meta name="description" content={seo.description} />
{seo.keywords && (
<meta name='keywords' content={seo.keywords} />
)}
<meta property='og:locale' content='ru_RU' />
{seo.keywords && <meta name="keywords" content={seo.keywords} />}
<meta property="og:locale" content="ru_RU" />
<meta property="og:url" content={seo.url} />
<meta property='og:title' content={seo.title}
/>
<meta property='og:site_name' content={seo.siteName}
/>
{seo.type && (
<meta property='og:type' content={seo.type} />
)}
{seo.image && (
<meta property="og:image" content={seo.image} />
)}
<meta property="og:title" content={seo.title} />
<meta property="og:site_name" content={seo.siteName} />
{seo.type && <meta property="og:type" content={seo.type} />}
{seo.image && <meta property="og:image" content={seo.image} />}
{seo.description && (
<meta property="og:description" content={seo.description} />
)}
<link rel="canonical" href={seo.canonical} />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#191919" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="theme-color" content="#ffffff" />
<script type="application/ld+json">
{
JSON.stringify({
'@context': 'http://schema.org',
'@type': 'Organization',
name: 'devthread',
{JSON.stringify({
"@context": "http://schema.org",
"@type": "Organization",
name: "devthread",
url: siteMetadata.siteUrl,
sameAs : [
siteMetadata.social.twitter,
]
})
}
sameAs: [siteMetadata.social.twitter],
})}
</script>
</Helmet>
)
@ -75,6 +90,5 @@ SEO.propTypes = {
description: PropTypes.string,
image: PropTypes.string,
type: PropTypes.string,
canonical: PropTypes.string
canonical: PropTypes.string,
}

View File

@ -1,23 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types'
import SEO from '../SEO/SEO'
import React from "react"
import PropTypes from "prop-types"
import SEO from "../SEO/SEO"
const Snippet = ({ data: { keywords, description, title, date }, html }) => {
return (
<>
<SEO title={title} titleTemplate={false} description={description} keywords={keywords.join(', ')} type='article' />
<SEO
title={title}
titleTemplate={false}
description={description}
keywords={keywords.join(", ")}
type="article"
/>
<div className="snippet">
<article>
<div className="snippet-date">
{date}
</div>
<div className="snippet-date">{date}</div>
<div className="snippet-heading">
<h1>{title}</h1>
</div>
<div className="snippet-description">
<p>
{description}
</p>
<p>{description}</p>
</div>
<div className="snippet-inner inner">
<div dangerouslySetInnerHTML={{ __html: html }}></div>
@ -26,8 +28,7 @@ const Snippet = ({ data: { keywords, description, title, date }, html }) => {
<div className="snippet-comments__heading">
<h2>Комментарии</h2>
</div>
<div className="snippet-comments__content">
</div>
<div className="snippet-comments__content"></div>
</div>
</article>
</div>
@ -42,7 +43,7 @@ Snippet.propTypes = {
keywords: PropTypes.array,
title: PropTypes.string,
description: PropTypes.string,
date: PropTypes.string
date: PropTypes.string,
}),
html: PropTypes.string
html: PropTypes.string,
}

View File

@ -1,13 +1,11 @@
import React from 'react';
import { Link } from 'gatsby'
import React from "react"
import { Link } from "gatsby"
const SnippetItem = ({ slug, title, date, tags }) => {
return (
<div className='snippet-item'>
<div className="snippet-item">
<div className="left-info">
<div className="snippet-item__date">
{date}
</div>
<div className="snippet-item__date">{date}</div>
<div className="snippet-item__heading">
<Link to={`/snippet/${slug}`}>{title}</Link>
</div>
@ -15,7 +13,9 @@ const SnippetItem = ({ slug, title, date, tags }) => {
<div className="right-info">
<div className="snippet-item__tags">
{tags.map((tag, i) => (
<div key={i} className="tag-item">{tag}</div>
<div key={i} className="tag-item">
{tag}
</div>
))}
</div>
</div>

View File

@ -1,11 +1,7 @@
import React from 'react';
import React from "react"
const Snippets = ({ children }) => {
return (
<div className='snippets'>
{children}
</div>
)
return <div className="snippets">{children}</div>
}
export default Snippets

View File

@ -1,36 +1,32 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types'
import firebase from 'gatsby-plugin-firebase';
import incrementViews from '../../lib/increment-views';
import React, { useEffect, useState } from "react"
import PropTypes from "prop-types"
import firebase from "gatsby-plugin-firebase"
import incrementViews from "../../lib/increment-views"
const ViewCounter = ({ id }) => {
const [viewCount, setViewCount] = useState('');
const [viewCount, setViewCount] = useState("")
useEffect(() => {
const onViews = (newViews) => {
setViewCount(newViews.val() === 1 ? 0 : newViews.val());
};
const onViews = newViews => {
setViewCount(newViews.val() === 1 ? 0 : newViews.val())
}
incrementViews(id);
incrementViews(id)
firebase.database().ref(`/views`).child(id).on(`value`, onViews);
firebase.database().ref(`/views`).child(id).on(`value`, onViews)
return () => {
if (firebase.database()) {
firebase.database().ref(`/views`).child(id).off(`value`, onViews);
firebase.database().ref(`/views`).child(id).off(`value`, onViews)
}
};
}, [id]);
}
}, [id])
return (
<span>
{viewCount ? viewCount : `---`}
</span>
);
};
return <span>{viewCount ? viewCount : `---`}</span>
}
export default ViewCounter;
export default ViewCounter
ViewCounter.propTypes = {
id: PropTypes.string
id: PropTypes.string,
}

View File

@ -1,6 +1,6 @@
const slugify = str => {
const patterns = {
rus: /^[0-9а-яё-\s]*$/i
rus: /^[0-9а-яё-\s]*$/i,
}
const translit = str => {
@ -35,33 +35,33 @@ const slugify = str => {
ы: "y",
э: "e",
ю: "u",
я: "ya"
я: "ya",
},
newStr = [];
newStr = []
for (var i = 0; i < str.length; ++i) {
const candidate = str[i].toLowerCase()
if (ru[candidate]) {
newStr.push(ru[candidate]);
newStr.push(ru[candidate])
}
if (candidate === '-' || !isNaN(candidate)) {
if (candidate === "-" || !isNaN(candidate)) {
newStr.push(candidate)
}
}
return newStr.join("");
return newStr.join("")
}
return 'not rus'
return "not rus"
}
if (str.match(patterns.rus)) {
return translit(str).split(' ').join('-').toLowerCase()
return translit(str).split(" ").join("-").toLowerCase()
}
return str.split(' ').join('-').toLowerCase()
};
return str.split(" ").join("-").toLowerCase()
}
export default slugify

View File

@ -3,10 +3,10 @@ const toFrontmatter = array => {
edges: array.map(el => {
return {
node: {
frontmatter: el
frontmatter: el,
},
}
}
})
}),
}
}

View File

@ -1,9 +1,13 @@
import { useStaticQuery, graphql } from 'gatsby'
import { useStaticQuery, graphql } from "gatsby"
const useCategoriesQuery = () => {
const { allMarkdownRemark: { edges } } = useStaticQuery(graphql`
const {
allMarkdownRemark: { edges },
} = useStaticQuery(graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "category"}}}) {
allMarkdownRemark(
filter: { frontmatter: { templateKey: { eq: "category" } } }
) {
edges {
node {
frontmatter {

View File

@ -1,9 +1,15 @@
import { useStaticQuery, graphql } from 'gatsby'
import { useStaticQuery, graphql } from "gatsby"
const useMoreArticlesQuery = () => {
const { allMarkdownRemark: { edges } } = useStaticQuery(graphql`
const {
allMarkdownRemark: { edges },
} = useStaticQuery(graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "article"}}}, sort: {order: DESC, fields: frontmatter___date}, limit: 6) {
allMarkdownRemark(
filter: { frontmatter: { templateKey: { eq: "article" } } }
sort: { order: DESC, fields: frontmatter___date }
limit: 6
) {
edges {
node {
frontmatter {

View File

@ -1,9 +1,17 @@
import { useStaticQuery, graphql } from 'gatsby'
import { useStaticQuery, graphql } from "gatsby"
const usePopularArticlesQuery = () => {
const { allMarkdownRemark: { edges } } = useStaticQuery(graphql`
const {
allMarkdownRemark: { edges },
} = useStaticQuery(graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "article"}, popular: {eq: true}}}, sort: {fields: frontmatter___date, order: DESC}, limit: 5) {
allMarkdownRemark(
filter: {
frontmatter: { templateKey: { eq: "article" }, popular: { eq: true } }
}
sort: { fields: frontmatter___date, order: DESC }
limit: 5
) {
edges {
node {
frontmatter {

View File

@ -1,7 +1,9 @@
import { useStaticQuery, graphql } from 'gatsby'
import { useStaticQuery, graphql } from "gatsby"
const useSiteMetadata = () => {
const { site: { siteMetadata } } = useStaticQuery(graphql`
const {
site: { siteMetadata },
} = useStaticQuery(graphql`
{
site {
siteMetadata {

View File

@ -1,9 +1,14 @@
import { useStaticQuery, graphql } from 'gatsby'
import { useStaticQuery, graphql } from "gatsby"
const useSnippetsQuery = () => {
const { allMarkdownRemark: { edges } } = useStaticQuery(graphql`
const {
allMarkdownRemark: { edges },
} = useStaticQuery(graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "snippet"}}}, sort: {order: DESC, fields: frontmatter___date}) {
allMarkdownRemark(
filter: { frontmatter: { templateKey: { eq: "snippet" } } }
sort: { order: DESC, fields: frontmatter___date }
) {
edges {
node {
frontmatter {

View File

@ -1,9 +1,13 @@
import { useStaticQuery, graphql } from 'gatsby'
import { useStaticQuery, graphql } from "gatsby"
const useTagsQuery = () => {
const { allMarkdownRemark: { edges } } = useStaticQuery(graphql`
const {
allMarkdownRemark: { edges },
} = useStaticQuery(graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "tag"}}}) {
allMarkdownRemark(
filter: { frontmatter: { templateKey: { eq: "tag" } } }
) {
edges {
node {
frontmatter {

View File

@ -1,9 +1,9 @@
import firebase from 'gatsby-plugin-firebase';
import firebase from "gatsby-plugin-firebase"
const incrementViews = async id => {
const ref = firebase.database().ref(`/views`).child(id);
const ref = firebase.database().ref(`/views`).child(id)
ref.transaction(currentViews => currentViews + 1)
}
export default incrementViews;
export default incrementViews

View File

@ -1,9 +1,9 @@
import React from 'react';
import { Main } from '../ui/ui'
import { Grid } from 'react-flexbox-grid'
import { Link } from 'gatsby'
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import React from "react"
import { Main } from "../ui/ui"
import { Grid } from "react-flexbox-grid"
import { Link } from "gatsby"
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
const NotFound = () => {
return (
@ -14,7 +14,7 @@ const NotFound = () => {
<div className="error">
<span>#404</span>
<h1>страница не найдена</h1>
<Link to='/'>вернуться на главную</Link>
<Link to="/">вернуться на главную</Link>
</div>
</Grid>
</Main>

View File

@ -1,22 +1,33 @@
import React from 'react';
import { Grid, Row, Col } from 'react-flexbox-grid'
import { Main } from '../ui/ui'
import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery'
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import Offers from '../components/Offers/Offers'
import About from '../components/About/About'
import React from "react"
import { Grid, Row, Col } from "react-flexbox-grid"
import { Main } from "../ui/ui"
import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery"
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
import Offers from "../components/Offers/Offers"
import About from "../components/About/About"
const AboutPage = () => {
const { siteMetadata: { title, menuLinks, social } } = useSiteMetadataQuery()
const {
siteMetadata: { title, menuLinks, social },
} = useSiteMetadataQuery()
return (
<Layout>
<SEO title='О сайте' description='Этот сайт придуман для того, чтобы помочь вам разобраться с вашей проблемой и решить её. Сайт содержит в себе статьи и руководства.' />
<SEO
title="О сайте"
description="Этот сайт придуман для того, чтобы помочь вам разобраться с вашей проблемой и решить её. Сайт содержит в себе статьи и руководства."
/>
<Main>
<Grid fluid>
<Row>
<Col lg={9} xs={12}>
<About title={title} pageTitle={menuLinks.filter(page => page.link === '/about')[0].name} social={social} />
<About
title={title}
pageTitle={
menuLinks.filter(page => page.link === "/about")[0].name
}
social={social}
/>
</Col>
<Col lg={3} xs={12}>
<Offers />

View File

@ -1,51 +1,71 @@
import React from "react"
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import { graphql } from 'gatsby'
import { useLocation } from '@reach/router'
import * as queryString from 'query-string'
import useCategoriesQuery from '../hooks/useCategoriesQuery'
import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery'
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import { graphql } from "gatsby"
import { useLocation } from "@reach/router"
import * as queryString from "query-string"
import useCategoriesQuery from "../hooks/useCategoriesQuery"
import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery"
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import Articles from '../components/Articles/Articles'
import Categories from '../components/Articles/Categories/Categories'
import CategoryItem from '../components/Articles/Categories/CategoryItem'
import Search from '../components/Articles/Search/Search'
import Offers from '../components/Offers/Offers'
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
import Articles from "../components/Articles/Articles"
import Categories from "../components/Articles/Categories/Categories"
import CategoryItem from "../components/Articles/Categories/CategoryItem"
import Search from "../components/Articles/Search/Search"
import Offers from "../components/Offers/Offers"
import SearchIcon from '../images/search-icon.svg'
import SearchIcon from "../images/search-icon.svg"
const ArticlesPage = ({ data: { allMarkdownRemark: edges }, }) => {
const ArticlesPage = ({ data: { allMarkdownRemark: edges } }) => {
const { categories } = useCategoriesQuery()
const { siteMetadata: { options: { articles: { onPage } } } } = useSiteMetadataQuery()
const {
siteMetadata: {
options: {
articles: { onPage },
},
},
} = useSiteMetadataQuery()
const { search } = useLocation()
let [limit, setLimit] = React.useState(onPage)
const [filtredArticles, setFiltredArticles] = React.useState([])
const [query, setQuery] = React.useState(queryString.parse(search).search || '')
const [query, setQuery] = React.useState(
queryString.parse(search).search || ""
)
const allArticles = edges
const filterArticles = React.useCallback(arr => {
return arr.filter(({ node: { frontmatter } }) =>
frontmatter.title.toLowerCase().includes(query.toLowerCase().trim())
|| frontmatter.description.toLowerCase().includes(query.toLowerCase().trim())
|| frontmatter.category.toLowerCase().includes(query.toLowerCase().trim())
|| frontmatter.tags.join('').toLowerCase().includes(query.toLowerCase().trim())
const filterArticles = React.useCallback(
arr => {
return arr.filter(
({ node: { frontmatter } }) =>
frontmatter.title
.toLowerCase()
.includes(query.toLowerCase().trim()) ||
frontmatter.description
.toLowerCase()
.includes(query.toLowerCase().trim()) ||
frontmatter.category
.toLowerCase()
.includes(query.toLowerCase().trim()) ||
frontmatter.tags
.join("")
.toLowerCase()
.includes(query.toLowerCase().trim())
)
},
[query]
)
}, [query])
React.useEffect(() => {
setLimit(onPage)
if (query !== '') setFiltredArticles(filterArticles(allArticles.edges))
if (query !== "") setFiltredArticles(filterArticles(allArticles.edges))
}, [setFiltredArticles, filterArticles, allArticles.edges, query, onPage])
return (
<Layout>
<SEO title='Статьи' />
<SEO title="Статьи" />
<Main>
<Grid fluid>
<Row>
@ -55,21 +75,47 @@ const ArticlesPage = ({ data: { allMarkdownRemark: edges }, }) => {
<h1>Статьи</h1>
</div>
<Categories setQuery={setQuery}>
{categories.map(({ node }, i) => <CategoryItem key={i} title={node.frontmatter.title} />)}
{categories.map(({ node }, i) => (
<CategoryItem key={i} title={node.frontmatter.title} />
))}
</Categories>
<Search allArticles={allArticles.edges} filterArticles={filterArticles} articlesLength={edges.edges.length} resultsLength={filtredArticles.length} query={query} setQuery={setQuery} setFiltredArticles={setFiltredArticles} setLimit={setLimit} onPage={onPage} />
<Search
allArticles={allArticles.edges}
filterArticles={filterArticles}
articlesLength={edges.edges.length}
resultsLength={filtredArticles.length}
query={query}
setQuery={setQuery}
setFiltredArticles={setFiltredArticles}
setLimit={setLimit}
onPage={onPage}
/>
<div className="articles-info">
{query && query !== '' ? filtredArticles.length === 0 ? <p><SearchIcon />&nbsp;&nbsp;Ничего не найдено</p> : null : null}
{query && query !== "" ? (
filtredArticles.length === 0 ? (
<p>
<SearchIcon />
&nbsp;&nbsp;Ничего не найдено
</p>
) : null
) : null}
</div>
</div>
</Col>
</Row>
<Row>
<Col lg={9} xs={12}>
{query && query !== ''
? filtredArticles.length > 0 ? <Articles data={{edges: filtredArticles}} limit={limit} setLimit={setLimit} /> : null
: <Articles data={edges} limit={limit} setLimit={setLimit} />
}
{query && query !== "" ? (
filtredArticles.length > 0 ? (
<Articles
data={{ edges: filtredArticles }}
limit={limit}
setLimit={setLimit}
/>
) : null
) : (
<Articles data={edges} limit={limit} setLimit={setLimit} />
)}
</Col>
<Col lg={3} xs={12}>
<Offers />
@ -83,7 +129,9 @@ const ArticlesPage = ({ data: { allMarkdownRemark: edges }, }) => {
export const query = graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "article"}}}) {
allMarkdownRemark(
filter: { frontmatter: { templateKey: { eq: "article" } } }
) {
edges {
node {
frontmatter {

View File

@ -1,16 +1,23 @@
import React from 'react';
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery'
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import Contacts from '../components/Contacts/Contacts'
import React from "react"
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery"
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
import Contacts from "../components/Contacts/Contacts"
const ContactsPage = () => {
const { siteMetadata: { social: { email, telegram } } } = useSiteMetadataQuery()
const {
siteMetadata: {
social: { email, telegram },
},
} = useSiteMetadataQuery()
return (
<Layout>
<SEO title='📝 Контакты' description='Для того чтобы задать вопрос, сообщить об ошибках или просто поздороваться, вы можете воспользоваться формой обратной связи или использовать контактные данные.' />
<SEO
title="📝 Контакты"
description="Для того чтобы задать вопрос, сообщить об ошибках или просто поздороваться, вы можете воспользоваться формой обратной связи или использовать контактные данные."
/>
<Main>
<Grid fluid>
<Row>

View File

@ -1,18 +1,24 @@
import React from "react"
import { Link, graphql } from 'gatsby'
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery'
import useSnippetsQuery from '../hooks/useSnippetsQuery'
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import Articles from '../components/Articles/Articles'
import Snippets from '../components/Snippets/Snippets'
import SnippetItem from '../components/Snippets/SnippetItem/SnippetItem'
import Offers from '../components/Offers/Offers'
import { Link, graphql } from "gatsby"
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery"
import useSnippetsQuery from "../hooks/useSnippetsQuery"
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
import Articles from "../components/Articles/Articles"
import Snippets from "../components/Snippets/Snippets"
import SnippetItem from "../components/Snippets/SnippetItem/SnippetItem"
import Offers from "../components/Offers/Offers"
const IndexPage = ({ data: { allMarkdownRemark: edges } }) => {
const { siteMetadata: { options: { articles: { onPage } } } } = useSiteMetadataQuery()
const {
siteMetadata: {
options: {
articles: { onPage },
},
},
} = useSiteMetadataQuery()
const { snippets } = useSnippetsQuery()
let [limit, setLimit] = React.useState(onPage)
@ -26,7 +32,7 @@ const IndexPage = ({ data: { allMarkdownRemark: edges } }) => {
<div className="articles">
<div className="articles-heading">
<h1>Последние статьи</h1>
<Link to='/articles'>смотреть все</Link>
<Link to="/articles">смотреть все</Link>
</div>
</div>
</Col>
@ -36,7 +42,7 @@ const IndexPage = ({ data: { allMarkdownRemark: edges } }) => {
<Articles data={edges} limit={limit} setLimit={setLimit} />
<Snippets>
<h2>Сниппеты</h2>
{snippets.map(({ node: { frontmatter } }, i) =>
{snippets.map(({ node: { frontmatter } }, i) => (
<SnippetItem
key={i}
slug={frontmatter.slug}
@ -44,7 +50,7 @@ const IndexPage = ({ data: { allMarkdownRemark: edges } }) => {
date={frontmatter.date}
tags={frontmatter.tags}
/>
)}
))}
</Snippets>
</Col>
<Col lg={3} xs={12}>
@ -59,7 +65,10 @@ const IndexPage = ({ data: { allMarkdownRemark: edges } }) => {
export const query = graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "article"}}}, sort: {order: DESC, fields: frontmatter___date}) {
allMarkdownRemark(
filter: { frontmatter: { templateKey: { eq: "article" } } }
sort: { order: DESC, fields: frontmatter___date }
) {
edges {
node {
frontmatter {

View File

@ -1,13 +1,17 @@
import React from "react"
import { graphql } from "gatsby"
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import Snippets from '../components/Snippets/Snippets'
import SnippetItem from '../components/Snippets/SnippetItem/SnippetItem'
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
import Snippets from "../components/Snippets/Snippets"
import SnippetItem from "../components/Snippets/SnippetItem/SnippetItem"
const SnippetsPage = ({ data: { allMarkdownRemark: { snippets } } }) => {
const SnippetsPage = ({
data: {
allMarkdownRemark: { snippets },
},
}) => {
return (
<Layout>
<SEO />
@ -17,8 +21,11 @@ const SnippetsPage = ({ data: { allMarkdownRemark: { snippets } } }) => {
<Col lg={12}>
<Snippets>
<h1>Сниппеты</h1>
<p>Сниппеты это готовые решения некоторых проблем, которые можно скопировать и вставить</p>
{snippets.map(({ node: { frontmatter } }, i) =>
<p>
Сниппеты это готовые решения некоторых проблем, которые можно
скопировать и вставить
</p>
{snippets.map(({ node: { frontmatter } }, i) => (
<SnippetItem
key={i}
slug={frontmatter.slug}
@ -26,7 +33,7 @@ const SnippetsPage = ({ data: { allMarkdownRemark: { snippets } } }) => {
date={frontmatter.date}
tags={frontmatter.tags}
/>
)}
))}
</Snippets>
</Col>
</Row>
@ -38,7 +45,10 @@ const SnippetsPage = ({ data: { allMarkdownRemark: { snippets } } }) => {
export const query = graphql`
{
allMarkdownRemark(filter: {frontmatter: {templateKey: {eq: "snippet"}}}, sort: {order: DESC, fields: frontmatter___date}) {
allMarkdownRemark(
filter: { frontmatter: { templateKey: { eq: "snippet" } } }
sort: { order: DESC, fields: frontmatter___date }
) {
snippets: edges {
node {
frontmatter {

View File

@ -1,14 +1,18 @@
import React from 'react'
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import Layout from '../components/Layout/Layout'
import Article from '../components/Article/Article'
import Offers from '../components/Offers/Offers'
import MoreArticles from '../components/Articles/MoreArticles/MoreArticles'
import Newsletter from '../components/Newsletter/Newsletter'
import React from "react"
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import Layout from "../components/Layout/Layout"
import Article from "../components/Article/Article"
import Offers from "../components/Offers/Offers"
import MoreArticles from "../components/Articles/MoreArticles/MoreArticles"
import Newsletter from "../components/Newsletter/Newsletter"
import { graphql } from "gatsby"
const articleTemplate = ({ data: { markdownRemark: { frontmatter, html } } }) => {
const articleTemplate = ({
data: {
markdownRemark: { frontmatter, html },
},
}) => {
return (
<Layout>
<Main>

View File

@ -1,19 +1,29 @@
import React from "react"
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import { graphql } from 'gatsby'
import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery'
import useCategoriesQuery from '../hooks/useCategoriesQuery'
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import Articles from '../components/Articles/Articles'
import Categories from '../components/Articles/Categories/Categories'
import CategoryItem from '../components/Articles/Categories/CategoryItem'
import Offers from '../components/Offers/Offers'
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import { graphql } from "gatsby"
import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery"
import useCategoriesQuery from "../hooks/useCategoriesQuery"
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
import Articles from "../components/Articles/Articles"
import Categories from "../components/Articles/Categories/Categories"
import CategoryItem from "../components/Articles/Categories/CategoryItem"
import Offers from "../components/Offers/Offers"
const CategoryPage = ({ data: { allMarkdownRemark: edges }, pageContext: { title } }) => {
const CategoryPage = ({
data: { allMarkdownRemark: edges },
pageContext: { title },
}) => {
const { categories } = useCategoriesQuery()
const { siteMetadata: { siteUrl, options: { articles: { onPage } } } } = useSiteMetadataQuery()
const {
siteMetadata: {
siteUrl,
options: {
articles: { onPage },
},
},
} = useSiteMetadataQuery()
let [limit, setLimit] = React.useState(onPage)
@ -26,13 +36,19 @@ const CategoryPage = ({ data: { allMarkdownRemark: edges }, pageContext: { title
<Col lg={9} xs={12}>
<div className="articles">
<div className="articles-heading">
<h1>Категория: <u>{title}</u></h1>
<h1>
Категория: <u>{title}</u>
</h1>
</div>
<Categories>
{categories.map(({ node }, i) => <CategoryItem key={i} title={node.frontmatter.title} />)}
{categories.map(({ node }, i) => (
<CategoryItem key={i} title={node.frontmatter.title} />
))}
</Categories>
<div className="articles-info">
<p>Найдено {edges.edges.length} из категории "{title}"</p>
<p>
Найдено {edges.edges.length} из категории "{title}"
</p>
</div>
</div>
</Col>
@ -53,7 +69,14 @@ const CategoryPage = ({ data: { allMarkdownRemark: edges }, pageContext: { title
export const pageQuery = graphql`
query articlesByCategory($title: String!) {
allMarkdownRemark(filter: {frontmatter: {category: {eq: $title}, templateKey: {eq: "article"}}}) {
allMarkdownRemark(
filter: {
frontmatter: {
category: { eq: $title }
templateKey: { eq: "article" }
}
}
) {
edges {
node {
frontmatter {

View File

@ -1,11 +1,15 @@
import React from 'react'
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import Snippet from '../components/Snippet/Snippet'
import Layout from '../components/Layout/Layout'
import React from "react"
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import Snippet from "../components/Snippet/Snippet"
import Layout from "../components/Layout/Layout"
import { graphql } from "gatsby"
const snippetTemplate = ({ data: { markdownRemark: { frontmatter, html } } }) => {
const snippetTemplate = ({
data: {
markdownRemark: { frontmatter, html },
},
}) => {
return (
<Layout>
<Main>
@ -23,7 +27,9 @@ const snippetTemplate = ({ data: { markdownRemark: { frontmatter, html } } }) =>
export const pageQuery = graphql`
query Snippet($slug: String!) {
markdownRemark(frontmatter: {slug: {eq: $slug}, templateKey: {eq: "snippet"}}) {
markdownRemark(
frontmatter: { slug: { eq: $slug }, templateKey: { eq: "snippet" } }
) {
html
frontmatter {
slug

View File

@ -1,19 +1,29 @@
import React from "react"
import { Main } from '../ui/ui'
import { Grid, Row, Col } from 'react-flexbox-grid'
import { graphql } from 'gatsby'
import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery'
import useCategoriesQuery from '../hooks/useCategoriesQuery'
import SEO from '../components/SEO/SEO'
import Layout from '../components/Layout/Layout'
import Articles from '../components/Articles/Articles'
import Categories from '../components/Articles/Categories/Categories'
import CategoryItem from '../components/Articles/Categories/CategoryItem'
import Offers from '../components/Offers/Offers'
import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid"
import { graphql } from "gatsby"
import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery"
import useCategoriesQuery from "../hooks/useCategoriesQuery"
import SEO from "../components/SEO/SEO"
import Layout from "../components/Layout/Layout"
import Articles from "../components/Articles/Articles"
import Categories from "../components/Articles/Categories/Categories"
import CategoryItem from "../components/Articles/Categories/CategoryItem"
import Offers from "../components/Offers/Offers"
const TagPage = ({ data: { allMarkdownRemark: edges }, pageContext: { title } }) => {
const TagPage = ({
data: { allMarkdownRemark: edges },
pageContext: { title },
}) => {
const { categories } = useCategoriesQuery()
const { siteMetadata: { siteUrl, options: { articles: { onPage } } } } = useSiteMetadataQuery()
const {
siteMetadata: {
siteUrl,
options: {
articles: { onPage },
},
},
} = useSiteMetadataQuery()
let [limit, setLimit] = React.useState(onPage)
@ -26,13 +36,19 @@ const TagPage = ({ data: { allMarkdownRemark: edges }, pageContext: { title } })
<Col lg={9} xs={12}>
<div className="articles">
<div className="articles-heading">
<h1>Тэг: <u>{title}</u></h1>
<h1>
Тэг: <u>{title}</u>
</h1>
</div>
<Categories>
{categories.map(({ node }, i) => <CategoryItem key={i} title={node.frontmatter.title} />)}
{categories.map(({ node }, i) => (
<CategoryItem key={i} title={node.frontmatter.title} />
))}
</Categories>
<div className="articles-info">
<p>Найдено {edges.edges.length} по тегу "{title}"</p>
<p>
Найдено {edges.edges.length} по тегу "{title}"
</p>
</div>
</div>
</Col>
@ -53,7 +69,11 @@ const TagPage = ({ data: { allMarkdownRemark: edges }, pageContext: { title } })
export const pageQuery = graphql`
query articlesByTag($title: [String!]) {
allMarkdownRemark(filter: {frontmatter: {tags: {in: $title}, templateKey: {eq: "article"}}}) {
allMarkdownRemark(
filter: {
frontmatter: { tags: { in: $title }, templateKey: { eq: "article" } }
}
) {
edges {
node {
frontmatter {

View File

@ -1,12 +1,7 @@
import React from 'react';
import React from "react"
const Main = ({ children, ...props }) => {
return (
<main {...props}>
{children}
</main>
)
return <main {...props}>{children}</main>
}
export { Main }