mirror of
https://github.com/sergeyyarkov/devthread.ru.git
synced 2026-10-11 02:09:04 +03:00
format
This commit is contained in:
parent
96f29a1a74
commit
42df591c08
@ -1,3 +1,4 @@
|
||||
# devthread.ru
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://app.netlify.com/sites/devthread/deploys)
|
||||
46
config.js
46
config.js
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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
|
||||

|
||||
|
||||
```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])
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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
|
||||

|
||||
|
||||
```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])
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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`,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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",
|
||||
],
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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" />)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
@ -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,
|
||||
}),
|
||||
}
|
||||
@ -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)}>Загрузить еще <span role='img' aria-label='load more'>👇</span></button>
|
||||
}
|
||||
{edges.length === limit || edges.length <= limit ? null : (
|
||||
<button
|
||||
className="btn-loadMore"
|
||||
onClick={() => setLimit(limit + onLoadMore)}
|
||||
>
|
||||
Загрузить еще
|
||||
<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,
|
||||
}),
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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} />
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
@ -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,
|
||||
}),
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React from "react"
|
||||
|
||||
const ContactsFrom = () => {
|
||||
return (
|
||||
|
||||
@ -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,
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
@ -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 (
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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,
|
||||
}
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
@ -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,
|
||||
}
|
||||
@ -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
|
||||
@ -3,10 +3,10 @@ const toFrontmatter = array => {
|
||||
edges: array.map(el => {
|
||||
return {
|
||||
node: {
|
||||
frontmatter: el
|
||||
frontmatter: el,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 />
|
||||
|
||||
@ -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 /> Ничего не найдено</p> : null : null}
|
||||
{query && query !== "" ? (
|
||||
filtredArticles.length === 0 ? (
|
||||
<p>
|
||||
<SearchIcon />
|
||||
Ничего не найдено
|
||||
</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 {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 }
|
||||
Loading…
x
Reference in New Issue
Block a user