mirror of
https://github.com/kremalicious/blog.git
synced 2025-02-14 21:10:25 +01:00
Merge pull request #498 from kremalicious/feature/css-modules-plugin-image
Gatsby v3
This commit is contained in:
commit
f976a5581a
@ -5,7 +5,7 @@
|
|||||||
"stylelint-prettier/recommended"
|
"stylelint-prettier/recommended"
|
||||||
],
|
],
|
||||||
"plugins": ["stylelint-prettier"],
|
"plugins": ["stylelint-prettier"],
|
||||||
"syntax": "scss",
|
"syntax": "css",
|
||||||
"rules": {
|
"rules": {
|
||||||
"prettier/prettier": true,
|
"prettier/prettier": true,
|
||||||
"property-no-unknown": [
|
"property-no-unknown": [
|
||||||
|
@ -42,9 +42,5 @@ module.exports = {
|
|||||||
title: '/Uses',
|
title: '/Uses',
|
||||||
link: '/uses'
|
link: '/uses'
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
darkModeConfig: {
|
|
||||||
classNameDark: 'dark',
|
|
||||||
classNameLight: 'light'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ These icons are free for your personal use and include icons for all file types
|
|||||||
Get them and have fun.
|
Get them and have fun.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/aperturefiletypes_by_kremalicious.zip">Download</a>
|
<a class="icon-download btn btn-primary" href="../media/aperturefiletypes_by_kremalicious.zip">Download</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
And don't forget to read my article about [how to change the generic image icons in Mac OS X Leopard](/changing-the-image-icons-in-mac-os-x-leopard/).
|
And don't forget to read my article about [how to change the generic image icons in Mac OS X Leopard](/changing-the-image-icons-in-mac-os-x-leopard/).
|
||||||
|
@ -19,5 +19,5 @@ I have added my first wallpaper to the Goodies section on this website. It's a s
|
|||||||
You can get the wallpaper by browsing [my Goodies section](http://www.kremalicious.com/goodies/) and clicking on the download link for the wallpaper. The download package also includes a custom folder icon (512px as .icns, .png, .ico) with the Chives wallpaper on it.
|
You can get the wallpaper by browsing [my Goodies section](http://www.kremalicious.com/goodies/) and clicking on the download link for the wallpaper. The download package also includes a custom folder icon (512px as .icns, .png, .ico) with the Chives wallpaper on it.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/chives_by_kremalicious.zip">Download Chives Wallpaper</a>
|
<a class="icon-download btn btn-primary" href="../media/chives_by_kremalicious.zip">Download Chives Wallpaper</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -92,7 +92,7 @@ Brief description of Daguerres role in the invention of photography process by T
|
|||||||
This icon package was exclusively announced first on [MacThemes](http://macthemes2.net) and you can download it from the [Goodies section on this website](/goodies/) or directly via this link:
|
This icon package was exclusively announced first on [MacThemes](http://macthemes2.net) and you can download it from the [Goodies section on this website](/goodies/) or directly via this link:
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="btn icon-download" href="../media/niepces_camera_obscura_by_kremalicious.zip">Download</a>
|
<a class="btn btn-primary icon-download" href="../media/niepces_camera_obscura_by_kremalicious.zip">Download</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
In addition to these icons you can download the [associated wallpapers for your desktop or your iPhone](http://www.kremalicious.com/2008/06/new-goodie-niepces-camera-obscura-wallpaper-pack/).
|
In addition to these icons you can download the [associated wallpapers for your desktop or your iPhone](http://www.kremalicious.com/2008/06/new-goodie-niepces-camera-obscura-wallpaper-pack/).
|
||||||
|
@ -19,7 +19,7 @@ In addition to my Niépce's Camera Obscura icons for Aperture and iPhoto I have
|
|||||||
All Wallpapers are using a custom designed background which imitates the look of a metal plate like it was used in Niépce's experiments although it wasn't golden. To make it more Mac style I have added a stenciled dots pattern (which is a commonly used reference to the front design of the MacPro).
|
All Wallpapers are using a custom designed background which imitates the look of a metal plate like it was used in Niépce's experiments although it wasn't golden. To make it more Mac style I have added a stenciled dots pattern (which is a commonly used reference to the front design of the MacPro).
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/niepces_camera_obscura_wallpaper_pack_by_kremalicious.zip">Download Niépce's Camera Obscura Wallpaper</a>
|
<a class="icon-download btn btn-primary" href="../media/niepces_camera_obscura_wallpaper_pack_by_kremalicious.zip">Download Niépce's Camera Obscura Wallpaper</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Here are the details:
|
Here are the details:
|
||||||
|
@ -376,7 +376,7 @@ Here you can see the icons included in the Server Displays icon pack:
|
|||||||
Because I've just modified Apple's standard icons these icons are just available via this blog post and they will not show up in my Goodies section. Just download the whole package directly via this link:
|
Because I've just modified Apple's standard icons these icons are just available via this blog post and they will not show up in my Goodies section. Just download the whole package directly via this link:
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/server_displays_by_kremalicious.zip">Download Server Display Icons <span>zip</span></a>
|
<a class="icon-download btn btn-primary" href="../media/server_displays_by_kremalicious.zip">Download Server Display Icons <span>zip</span></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### How to use the icons
|
### How to use the icons
|
||||||
|
@ -20,7 +20,7 @@ As always these desktop icons are free for you personal and non-commercial use.
|
|||||||
The whole package includes 7 icons either packed in a nice tagged iContainer for use with Candybar or in Mac + Win + Linux compatible formats. If you have such an Icy Box case grab the icons. Have fun!
|
The whole package includes 7 icons either packed in a nice tagged iContainer for use with Candybar or in Mac + Win + Linux compatible formats. If you have such an Icy Box case grab the icons. Have fun!
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/icybox_by_kremalicious.zip">Download Icy Box Icons <span>zip</span></a>
|
<a class="icon-download btn btn-primary" href="../media/icybox_by_kremalicious.zip">Download Icy Box Icons <span>zip</span></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
- Replacement icons for the silver and black Icy Box external aluminium case with USB interface
|
- Replacement icons for the silver and black Icy Box external aluminium case with USB interface
|
||||||
|
@ -18,6 +18,6 @@ Show your geeky love for extraterrestrial universities! This is a simple Futuram
|
|||||||
|
|
||||||
As usual you can [grab the whole zip package from my Goodies page](http://www.kremalicious.com/goodies/#wall) and have fun.
|
As usual you can [grab the whole zip package from my Goodies page](http://www.kremalicious.com/goodies/#wall) and have fun.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download ">
|
||||||
<a class="icon-download" href="../media/mars-u-wall-by-kremalicious.zip">Download Futurama Mars U Wallpaper <span>zip</span></a>
|
<a class="icon-download btn btn-primary" href="../media/mars-u-wall-by-kremalicious.zip">Download Futurama Mars U Wallpaper <span>zip</span></a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -22,5 +22,5 @@ I’ve just released my own coffee cup icon, enjoy:
|
|||||||
## Download
|
## Download
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/coffee_cup_by_kremalicious.zip">Download Coffee Cup Icons</a>
|
<a class="icon-download btn btn-primary" href="../media/coffee_cup_by_kremalicious.zip">Download Coffee Cup Icons</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -29,5 +29,5 @@ Simple, high-resolution Futurama tribute wallpaper pack inspired by the latest F
|
|||||||
Seriously, the pink versions are burning my eyes but the pink is a good reference to the events in the recent movie.
|
Seriously, the pink versions are burning my eyes but the pink is a good reference to the events in the recent movie.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/out-of-whale-oil-wall-by-kremalicious.zip">Download</a>
|
<a class="icon-download btn btn-primary" href="../media/out-of-whale-oil-wall-by-kremalicious.zip">Download</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -16,7 +16,7 @@ Here's a quick twitter icon for use on your websites which is kind of a by-produ
|
|||||||
This icon comes in various formats (PNG, ICNS, iContainer) and in 4 different sizes (128px, 48px, 32px, 16px) with each icon size redrawn (of course). Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab these icons while they're hot.
|
This icon comes in various formats (PNG, ICNS, iContainer) and in 4 different sizes (128px, 48px, 32px, 16px) with each icon size redrawn (of course). Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab these icons while they're hot.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/twitter-crisp-by-kremalicious.zip">Download</a>
|
<a class="icon-download btn btn-primary" href="../media/twitter-crisp-by-kremalicious.zip">Download</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Use them on any web project you like and/or [convert them into a send to twitter link](http://kremalicious.com/ultimate-coda-wordpress-share-link-bonanza/). Have fun!
|
Use them on any web project you like and/or [convert them into a send to twitter link](http://kremalicious.com/ultimate-coda-wordpress-share-link-bonanza/). Have fun!
|
||||||
|
@ -26,7 +26,7 @@ The icon comes in various formats (iContainer, icns, png) in sizes from 512px-16
|
|||||||
Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab this icon while it's hot.
|
Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab this icon while it's hot.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/adiumeetie-by-kremalicious.zip">Download</a>
|
<a class="icon-download btn btn-primary" href="../media/adiumeetie-by-kremalicious.zip">Download</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Adium Icon Usage
|
## Adium Icon Usage
|
||||||
|
@ -20,7 +20,7 @@ tags:
|
|||||||
Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab these replacement icons.
|
Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab these replacement icons.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/delibar-by-kremalicious.zip">Download</a>
|
<a class="icon-download btn btn-primary" href="../media/delibar-by-kremalicious.zip">Download</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Icon Usage
|
## Icon Usage
|
||||||
|
@ -27,7 +27,7 @@ The homescreen icons pixels in the 16px version needed all more vivid colors to
|
|||||||
The icons come in various formats: iContainer, ICNS, ICO and PNG files for each size. Just click the following download button to grab the whole pack:
|
The icons come in various formats: iContainer, ICNS, ICO and PNG files for each size. Just click the following download button to grab the whole pack:
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/ipixelpad_by_kremalicious.zip">Download</a>
|
<a class="icon-download btn btn-primary" href="../media/ipixelpad_by_kremalicious.zip">Download</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
@ -23,6 +23,6 @@ The wallpaper comes in four versions with two color variations and two text vari
|
|||||||
You can grab the full zip-package with versions for Desktop, iPad, iPhone & Android included:
|
You can grab the full zip-package with versions for Desktop, iPad, iPhone & Android included:
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="btn-primary icon-download" href="../media/momcorp_wall_by_kremalicious.zip">Download</a>
|
<a class="btn btn-primary icon-download" href="../media/momcorp_wall_by_kremalicious.zip">Download</a>
|
||||||
<a href="http://krlc.us/givecoffee" class="icon-heart">Donate</a>
|
<a href="http://krlc.us/givecoffee" class="btn icon-heart">Donate</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -24,7 +24,7 @@ The plugin is localized in english, german & spanish (thanks to Andrew Kurtis fr
|
|||||||
You can just install the plugin via the automatic backend installer under _Plugins > Add New_, activate and enjoy the red badges.
|
You can just install the plugin via the automatic backend installer under _Plugins > Add New_, activate and enjoy the red badges.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a href="http://wordpress.org/extend/plugins/badged" class="btn-primary icon-wordpress">Plugin Page</a> <a class="btn-primary icon-github" href="https://github.com/kremalicious/Badged">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart btn">Donate</a>
|
<a href="http://wordpress.org/extend/plugins/badged" class="btn btn-primary icon-wordpress">Plugin Page</a> <a class="btn btn-primary icon-github" href="https://github.com/kremalicious/Badged">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart btn">Donate</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
The plugin is hosted on GitHub and will always be mirrored in the WordPress plugins directory. But in case you want to install the plugin manually:
|
The plugin is hosted on GitHub and will always be mirrored in the WordPress plugins directory. But in case you want to install the plugin manually:
|
||||||
|
@ -33,7 +33,7 @@ So if you value quality and want pixel perfect icons in your admin area you need
|
|||||||
I’ve put the template along with the implementation examples from the next section on [github](https://github.com/kremalicious/wp-icons-template). You can just download the whole package right away:
|
I’ve put the template along with the implementation examples from the next section on [github](https://github.com/kremalicious/wp-icons-template). You can just download the whole package right away:
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a href="https://github.com/kremalicious/wp-icons-template/zipball/master" class="btn-primary icon-download">Download</a> <a href="https://github.com/kremalicious/wp-icons-template" class="icon-github">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart">Donate</a>
|
<a href="https://github.com/kremalicious/wp-icons-template/zipball/master" class="btn btn-primary icon-download">Download</a> <a href="https://github.com/kremalicious/wp-icons-template" class="icon-github btn btn-primary">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart btn">Donate</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
@ -21,8 +21,8 @@ The above picture might be blurry depending on the device you're using so here's
|
|||||||
They are completely styled with CSS3 so they're sharp on all screens no matter how high the dpi. Have a look at the [full demo](http://lab.kremalicious.com/kbdfun/) or grab the project folder with the CSS & LESS files from GitHub. The code is under the MIT license so you're free to use it in any personal or commercial project.
|
They are completely styled with CSS3 so they're sharp on all screens no matter how high the dpi. Have a look at the [full demo](http://lab.kremalicious.com/kbdfun/) or grab the project folder with the CSS & LESS files from GitHub. The code is under the MIT license so you're free to use it in any personal or commercial project.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="btn-primary icon-compass" href="http://lab.kremalicious.com/kbdfun/">Demo</a>
|
<a class="btn btn-primary icon-compass" href="http://lab.kremalicious.com/kbdfun/">Demo</a>
|
||||||
<a class="icon-github" href="https://github.com/kremalicious/kbdfun/">Github</a>
|
<a class="icon-github btn" href="https://github.com/kremalicious/kbdfun/">Github</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -30,7 +30,7 @@ The full size I designed the wallpaper in is 3200x2048px. I don't know why Apple
|
|||||||
Download the whole package with all the sizes included or grab the individual ones from the list, all linked to the image files:
|
Download the whole package with all the sizes included or grab the individual ones from the list, all linked to the image files:
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-download" href="../media/project-purple-kremalicious.zip">Download <span> zip</span></a>
|
<a class="icon-download btn btn-primary" href="../media/project-purple-kremalicious.zip">Download <span> zip</span></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
- [Desktop/rMBP/iPad 3 (3200x2048)](../media/project-purple-kremalicious.png)
|
- [Desktop/rMBP/iPad 3 (3200x2048)](../media/project-purple-kremalicious.png)
|
||||||
|
@ -15,9 +15,9 @@ tags:
|
|||||||
The badges provided by all app store providers just don't play well together with their varying typography and different sizing. So let's make them all visually unified, infinitely scalable, with pure text for easier localization and some web interaction styles. And while we’re at it: different sizes with the same markup by using some modifier classes.
|
The badges provided by all app store providers just don't play well together with their varying typography and different sizing. So let's make them all visually unified, infinitely scalable, with pure text for easier localization and some web interaction styles. And while we’re at it: different sizes with the same markup by using some modifier classes.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="btn-primary icon-compass" href="https://lab.kremalicious.com/appstorebadges/">Demo</a>
|
<a class="btn btn-primary icon-compass" href="https://lab.kremalicious.com/appstorebadges/">Demo</a>
|
||||||
<a class="icon-github" href="https://github.com/kremalicious/appstorebadges/">GitHub</a>
|
<a class="icon-github btn" href="https://github.com/kremalicious/appstorebadges/">GitHub</a>
|
||||||
<a class="icon-code" href="http://codepen.io/kremalicious/details/EVVraP/">Codepen</a>
|
<a class="icon-code btn" href="http://codepen.io/kremalicious/details/EVVraP/">Codepen</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Styling
|
## Styling
|
||||||
|
@ -41,5 +41,5 @@ hpm install hyper-mac-pro
|
|||||||
Head over to GitHub to take a peek into the code or report some issues.
|
Head over to GitHub to take a peek into the code or report some issues.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-github btn-primary" href="https://github.com/kremalicious/hyper-mac-pro">GitHub</a>
|
<a class="icon-github btn btn-primary" href="https://github.com/kremalicious/hyper-mac-pro">GitHub</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -59,5 +59,5 @@ plugins: [
|
|||||||
Head over to GitHub for more documentation, take a peek into the code, or to report some bugs.
|
Head over to GitHub for more documentation, take a peek into the code, or to report some bugs.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-github btn-primary" href="https://github.com/kremalicious/gatsby-plugin-matomo">GitHub</a>
|
<a class="icon-github btn btn-primary" href="https://github.com/kremalicious/gatsby-plugin-matomo">GitHub</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -110,5 +110,5 @@ plugins: [
|
|||||||
Head over to GitHub for more documentation, take a peek into the code, or to report some bugs.
|
Head over to GitHub for more documentation, take a peek into the code, or to report some bugs.
|
||||||
|
|
||||||
<p class="content-download">
|
<p class="content-download">
|
||||||
<a class="icon-github btn-primary" href="https://github.com/kremalicious/gatsby-redirect-from">GitHub</a>
|
<a class="icon-github btn btn-primary" href="https://github.com/kremalicious/gatsby-redirect-from">GitHub</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import './src/styles/global.scss'
|
import './src/global/global.css'
|
||||||
|
|
||||||
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
||||||
export const wrapPageElement = wrapPageElementWithLayout
|
export const wrapPageElement = wrapPageElementWithLayout
|
||||||
|
@ -23,6 +23,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
...sources,
|
...sources,
|
||||||
|
'gatsby-plugin-image',
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-plugin-sharp',
|
resolve: 'gatsby-plugin-sharp',
|
||||||
options: {
|
options: {
|
||||||
@ -77,7 +78,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
injectStyles: false,
|
injectStyles: false,
|
||||||
extensions: [
|
extensions: [
|
||||||
`${__dirname}/vendor/nord-visual-studio-code-0.15.0.vsix`,
|
'nord-visual-studio-code',
|
||||||
`${__dirname}/vendor/polar-0.0.6.vsix`
|
`${__dirname}/vendor/polar-0.0.6.vsix`
|
||||||
],
|
],
|
||||||
languageAliases: {}
|
languageAliases: {}
|
||||||
@ -86,14 +87,6 @@ module.exports = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
resolve: 'gatsby-plugin-sass',
|
|
||||||
options: {
|
|
||||||
sassOptions: {
|
|
||||||
includePaths: [`${__dirname}/node_modules`, `${__dirname}/src/styles`]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-plugin-svgr',
|
resolve: 'gatsby-plugin-svgr',
|
||||||
options: {
|
options: {
|
||||||
@ -227,13 +220,6 @@ module.exports = {
|
|||||||
exclude: ['/archive', '/archive/**/*', '/thanks', '/tags']
|
exclude: ['/archive', '/archive/**/*', '/thanks', '/tags']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
resolve: 'gatsby-plugin-use-dark-mode',
|
|
||||||
options: {
|
|
||||||
...siteConfig.darkModeConfig,
|
|
||||||
minify: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'gatsby-plugin-react-helmet',
|
'gatsby-plugin-react-helmet',
|
||||||
'gatsby-plugin-catch-links',
|
'gatsby-plugin-catch-links',
|
||||||
'gatsby-redirect-from',
|
'gatsby-redirect-from',
|
||||||
|
@ -64,7 +64,7 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
archive: allMarkdownRemark(
|
archive: allMarkdownRemark(
|
||||||
filter: { fields: { type: { ne: "photo" } } }
|
filter: { fields: { type: { nin: "photo" } } }
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
@ -95,15 +95,15 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
|
|||||||
// Generate post pages
|
// Generate post pages
|
||||||
generatePostPages(createPage, all)
|
generatePostPages(createPage, all)
|
||||||
|
|
||||||
// Generate archive pages
|
|
||||||
generateArchivePages(createPage, archiveLength)
|
|
||||||
|
|
||||||
// Generate photos archive pages
|
// Generate photos archive pages
|
||||||
generatePhotosPages(createPage, photosLength)
|
generatePhotosPages(createPage, photosLength)
|
||||||
|
|
||||||
// Generate tag pages
|
// Generate tag pages
|
||||||
generateTagPages(createPage, tags)
|
generateTagPages(createPage, tags)
|
||||||
|
|
||||||
|
// Generate archive pages
|
||||||
|
generateArchivePages(createPage, archiveLength)
|
||||||
|
|
||||||
// Create manual redirects
|
// Create manual redirects
|
||||||
generateRedirectPages(createRedirect)
|
generateRedirectPages(createRedirect)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ exports.generatePostPages = (createPage, posts) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateIndexPages(createPage, length, slug, template) {
|
function generateIndexPages(createPage, length, slug, template, tag) {
|
||||||
const numPages = Math.ceil(length / itemsPerPage)
|
const numPages = Math.ceil(length / itemsPerPage)
|
||||||
|
|
||||||
Array.from({ length: numPages }).forEach((_, i) => {
|
Array.from({ length: numPages }).forEach((_, i) => {
|
||||||
@ -67,7 +67,8 @@ function generateIndexPages(createPage, length, slug, template) {
|
|||||||
numPages: numPages,
|
numPages: numPages,
|
||||||
currentPageNumber: i + 1,
|
currentPageNumber: i + 1,
|
||||||
prevPagePath,
|
prevPagePath,
|
||||||
nextPagePath
|
nextPagePath,
|
||||||
|
...(tag && { tag })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -90,7 +91,8 @@ exports.generateTagPages = (createPage, tags) => {
|
|||||||
createPage,
|
createPage,
|
||||||
totalCount,
|
totalCount,
|
||||||
`/archive/${tag}/`,
|
`/archive/${tag}/`,
|
||||||
archiveTemplate
|
archiveTemplate,
|
||||||
|
tag
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,10 @@ module.exports = [
|
|||||||
url: 'https://api.github.com/graphql',
|
url: 'https://api.github.com/graphql',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `bearer ${process.env.GATSBY_GITHUB_TOKEN}`
|
Authorization: `bearer ${process.env.GATSBY_GITHUB_TOKEN}`
|
||||||
},
|
}
|
||||||
// Additional options to pass to node-fetch
|
// Additional options to pass to node-fetch
|
||||||
fetchOptions: {},
|
// fetchOptions: {},
|
||||||
refetchInterval: 300 // 5 min.
|
// refetchInterval: 300 // 5 min.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
20125
package-lock.json
generated
20125
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
101
package.json
101
package.json
@ -8,16 +8,15 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "gatsby develop --host 0.0.0.0",
|
"start": "gatsby develop --host 0.0.0.0",
|
||||||
"build": "gatsby build && npm run copy",
|
"build": "gatsby build",
|
||||||
"ssr": "npm run build && serve -s public/",
|
"ssr": "npm run build && serve -s public/",
|
||||||
"test": "npm run lint && jest -c jest/jest.config.js --coverage --silent",
|
"test": "npm run lint && jest -c jest/jest.config.js --coverage --silent",
|
||||||
"test:watch": "npm run lint && jest -c jest/jest.config.js --coverage --watch",
|
"test:watch": "npm run lint && jest -c jest/jest.config.js --coverage --watch",
|
||||||
"copy": "cp -R content/media/ public",
|
|
||||||
"lint": "run-p --continue-on-error lint:js lint:css lint:md",
|
"lint": "run-p --continue-on-error lint:js lint:css lint:md",
|
||||||
"lint:js": "eslint --ignore-path .gitignore --ext .js,.jsx,.ts,.tsx .",
|
"lint:js": "eslint --ignore-path .gitignore --ext .js,.jsx,.ts,.tsx .",
|
||||||
"lint:css": "stylelint 'src/**/*.{css,scss}'",
|
"lint:css": "stylelint 'src/**/*.css'",
|
||||||
"lint:md": "markdownlint './**/*.{md,markdown}' --ignore './{node_modules,public,.cache,.git,coverage}/**/*'",
|
"lint:md": "markdownlint './**/*.{md,markdown}' --ignore './{node_modules,public,.cache,.git,coverage}/**/*'",
|
||||||
"format": "prettier --ignore-path .gitignore --write '**/*.{js,jsx,ts,tsx,md,json,css,scss}'",
|
"format": "prettier --ignore-path .gitignore --write '**/*.{js,jsx,ts,tsx,md,json,css}'",
|
||||||
"tsc": "tsc --noEmit",
|
"tsc": "tsc --noEmit",
|
||||||
"deploy": "./scripts/deploy-s3.sh",
|
"deploy": "./scripts/deploy-s3.sh",
|
||||||
"new": "ts-node ./scripts/new.ts"
|
"new": "ts-node ./scripts/new.ts"
|
||||||
@ -29,106 +28,104 @@
|
|||||||
"not op_mini all"
|
"not op_mini all"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersproject/providers": "^5.0.23",
|
"@ethersproject/providers": "^5.0.24",
|
||||||
"@ethersproject/units": "^5.0.10",
|
"@ethersproject/units": "^5.0.11",
|
||||||
"@loadable/component": "^5.14.1",
|
"@loadable/component": "^5.14.1",
|
||||||
"@web3-react/core": "^6.1.9",
|
"@web3-react/core": "^6.1.9",
|
||||||
"@web3-react/injected-connector": "^6.0.7",
|
"@web3-react/injected-connector": "^6.0.7",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"date-fns": "^2.17.0",
|
"date-fns": "^2.19.0",
|
||||||
"dms2dec": "^1.1.0",
|
"dms2dec": "^1.1.0",
|
||||||
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
||||||
"fast-exif": "^1.0.1",
|
"fast-exif": "^1.0.1",
|
||||||
"feather-icons": "^4.28.0",
|
"feather-icons": "^4.28.0",
|
||||||
"fraction.js": "^4.0.13",
|
"fraction.js": "^4.0.13",
|
||||||
"gatsby": "^2.32.8",
|
"gatsby": "^3.1.1",
|
||||||
"gatsby-image": "^2.11.0",
|
"gatsby-plugin-catch-links": "^3.1.0",
|
||||||
"gatsby-plugin-catch-links": "^2.10.0",
|
"gatsby-plugin-feed": "^3.1.0",
|
||||||
"gatsby-plugin-feed": "^2.13.1",
|
"gatsby-plugin-image": "^1.1.1",
|
||||||
"gatsby-plugin-lunr": "^1.5.2",
|
"gatsby-plugin-lunr": "^1.5.2",
|
||||||
"gatsby-plugin-manifest": "^2.12.0",
|
"gatsby-plugin-manifest": "^3.1.0",
|
||||||
"gatsby-plugin-matomo": "^0.9.0",
|
"gatsby-plugin-matomo": "^0.9.0",
|
||||||
"gatsby-plugin-meta-redirect": "^1.1.1",
|
"gatsby-plugin-meta-redirect": "^1.1.1",
|
||||||
"gatsby-plugin-offline": "^3.10.2",
|
"gatsby-plugin-offline": "^4.1.0",
|
||||||
"gatsby-plugin-react-helmet": "^3.10.0",
|
"gatsby-plugin-react-helmet": "^4.1.0",
|
||||||
"gatsby-plugin-sass": "^3.2.0",
|
"gatsby-plugin-sharp": "^3.1.1",
|
||||||
"gatsby-plugin-sharp": "^2.14.3",
|
"gatsby-plugin-sitemap": "^3.1.0",
|
||||||
"gatsby-plugin-sitemap": "^2.12.0",
|
"gatsby-plugin-svgr": "^3.0.0-beta.0",
|
||||||
"gatsby-plugin-svgr": "^2.1.0",
|
"gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.21",
|
||||||
"gatsby-plugin-use-dark-mode": "^1.2.0",
|
"gatsby-plugin-webpack-size": "^2.0.1",
|
||||||
"gatsby-plugin-webpack-size": "^1.0.0",
|
|
||||||
"gatsby-redirect-from": "^0.3.0",
|
"gatsby-redirect-from": "^0.3.0",
|
||||||
"gatsby-remark-autolink-headers": "^2.11.0",
|
"gatsby-remark-autolink-headers": "^3.1.0",
|
||||||
"gatsby-remark-breaks": "^1.0.0",
|
"gatsby-remark-breaks": "^1.0.0",
|
||||||
"gatsby-remark-copy-linked-files": "^2.10.0",
|
"gatsby-remark-copy-linked-files": "^3.1.0",
|
||||||
"gatsby-remark-images": "^3.11.1",
|
"gatsby-remark-images": "^4.1.0",
|
||||||
"gatsby-remark-images-medium-zoom": "^1.7.0",
|
"gatsby-remark-images-medium-zoom": "^1.7.0",
|
||||||
"gatsby-remark-smartypants": "^2.10.0",
|
"gatsby-remark-smartypants": "^3.1.0",
|
||||||
"gatsby-remark-vscode": "^3.2.0",
|
"gatsby-remark-vscode": "^3.2.1",
|
||||||
"gatsby-source-filesystem": "^2.11.1",
|
"gatsby-source-filesystem": "^3.1.0",
|
||||||
"gatsby-source-graphql": "^2.14.0",
|
"gatsby-source-graphql": "^3.1.0",
|
||||||
"gatsby-transformer-remark": "^2.16.1",
|
"gatsby-transformer-remark": "^3.1.0",
|
||||||
"gatsby-transformer-sharp": "^2.12.0",
|
"gatsby-transformer-sharp": "^3.1.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"pigeon-maps": "^0.17.1",
|
"nord-visual-studio-code": "github:arcticicestudio/nord-visual-studio-code",
|
||||||
|
"pigeon-maps": "^0.19.5",
|
||||||
"pigeon-marker": "^0.3.4",
|
"pigeon-marker": "^0.3.4",
|
||||||
"react": "^16.14.0",
|
"react": "^17.0.1",
|
||||||
"react-clipboard.js": "^2.0.16",
|
"react-clipboard.js": "^2.0.16",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^17.0.1",
|
||||||
"react-feather": "^2.0.9",
|
"react-feather": "^2.0.9",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-qr-svg": "^2.3.0",
|
"react-qr-svg": "^2.3.0",
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"remark": "^13.0.0",
|
"remark": "^13.0.0",
|
||||||
"remark-react": "^8.0.0",
|
"remark-react": "^8.0.0",
|
||||||
"slugify": "^1.4.7",
|
"slugify": "^1.5.0"
|
||||||
"use-dark-mode": "^2.3.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
"@testing-library/jest-dom": "^5.11.9",
|
"@testing-library/jest-dom": "^5.11.9",
|
||||||
"@testing-library/react": "^11.2.5",
|
"@testing-library/react": "^11.2.5",
|
||||||
"@types/classnames": "^2.2.11",
|
"@types/classnames": "^2.2.11",
|
||||||
"@types/fs-extra": "^9.0.7",
|
"@types/fs-extra": "^9.0.8",
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^26.0.21",
|
||||||
"@types/loadable__component": "^5.13.3",
|
"@types/loadable__component": "^5.13.3",
|
||||||
"@types/lunr": "^2.3.3",
|
"@types/lunr": "^2.3.3",
|
||||||
"@types/node": "^14.14.28",
|
"@types/node": "^14.14.35",
|
||||||
"@types/node-fetch": "^2.5.8",
|
"@types/node-fetch": "^2.5.8",
|
||||||
"@types/react": "^17.0.2",
|
"@types/react": "^17.0.3",
|
||||||
"@types/react-dom": "^17.0.1",
|
"@types/react-dom": "^17.0.2",
|
||||||
"@types/react-helmet": "^6.1.0",
|
"@types/react-helmet": "^6.1.0",
|
||||||
"@types/react-transition-group": "^4.4.0",
|
"@types/react-transition-group": "^4.4.0",
|
||||||
"@types/shortid": "^0.0.29",
|
"@types/shortid": "^0.0.29",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.15.2",
|
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||||
"@typescript-eslint/parser": "^4.15.2",
|
"@typescript-eslint/parser": "^4.18.0",
|
||||||
"@welldone-software/why-did-you-render": "^6.0.5",
|
"@welldone-software/why-did-you-render": "^6.1.1",
|
||||||
"eslint": "^7.21.0",
|
"eslint": "^7.22.0",
|
||||||
"eslint-config-prettier": "^8.1.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
"eslint-loader": "^4.0.2",
|
|
||||||
"eslint-plugin-graphql": "^4.0.0",
|
"eslint-plugin-graphql": "^4.0.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"eslint-plugin-prettier": "^3.3.1",
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
"eslint-plugin-react": "^7.22.0",
|
"eslint-plugin-react": "^7.22.0",
|
||||||
"eslint-plugin-testing-library": "^3.10.1",
|
"eslint-plugin-testing-library": "^3.10.2",
|
||||||
"fs-extra": "^9.1.0",
|
"fs-extra": "^9.1.0",
|
||||||
"gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.20",
|
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"markdownlint-cli": "^0.26.0",
|
"markdownlint-cli": "^0.27.1",
|
||||||
"node-iptc": "^1.0.5",
|
"node-iptc": "^1.0.5",
|
||||||
"node-sass": "^5.0.0",
|
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"ora": "^5.3.0",
|
"ora": "^5.4.0",
|
||||||
|
"postcss": "^8.2.8",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"shortid": "^2.2.16",
|
"shortid": "^2.2.16",
|
||||||
"stylelint": "^13.11.0",
|
"stylelint": "^13.12.0",
|
||||||
"stylelint-config-css-modules": "^2.2.0",
|
"stylelint-config-css-modules": "^2.2.0",
|
||||||
"stylelint-config-prettier": "^8.0.2",
|
"stylelint-config-prettier": "^8.0.2",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"stylelint-config-standard": "^21.0.0",
|
||||||
"stylelint-prettier": "^1.2.0",
|
"stylelint-prettier": "^1.2.0",
|
||||||
"ts-node": "^9.1.1",
|
"ts-node": "^9.1.1",
|
||||||
"typescript": "^4.2.2"
|
"typescript": "^4.2.3",
|
||||||
|
"typescript-plugin-css-modules": "^3.2.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
15
src/@types/Image.d.ts
vendored
15
src/@types/Image.d.ts
vendored
@ -1,17 +1,14 @@
|
|||||||
import { FixedObject, FluidObject } from 'gatsby-image'
|
import { GatsbyImageProps, IGatsbyImageData } from 'gatsby-plugin-image'
|
||||||
|
|
||||||
export interface ImageProps {
|
export interface ImageProps extends GatsbyImageProps {
|
||||||
title?: string
|
title?: string
|
||||||
fluid?: FluidObject
|
|
||||||
fixed?: FixedObject
|
|
||||||
alt?: string
|
|
||||||
original?: { src: string }
|
original?: { src: string }
|
||||||
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImageNode {
|
export interface ImageNode extends IGatsbyImageData {
|
||||||
childImageSharp: ImageProps
|
fields?: {
|
||||||
fields: {
|
exif?: Exif
|
||||||
exif: Exif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
src/@types/css.d.ts
vendored
Normal file
4
src/@types/css.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
declare module '*.module.css' {
|
||||||
|
const classes: { [key: string]: string }
|
||||||
|
export default classes
|
||||||
|
}
|
10
src/@types/global.d.ts
vendored
10
src/@types/global.d.ts
vendored
@ -1,13 +1,3 @@
|
|||||||
declare module '*.module.css' {
|
|
||||||
const classes: { [key: string]: string }
|
|
||||||
export default classes
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '*.module.scss' {
|
|
||||||
const classes: { [key: string]: string }
|
|
||||||
export default classes
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '*.svg' {
|
declare module '*.svg' {
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
export const ReactComponent: React.FunctionComponent<
|
export const ReactComponent: React.FunctionComponent<
|
||||||
|
75
src/components/Layout.module.css
Normal file
75
src/components/Layout.module.css
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
.content {
|
||||||
|
padding: 0 calc(var(--spacer) / 1.5);
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--maxWidthContainer);
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* topbar and footer as fixed
|
||||||
|
site background
|
||||||
|
///////////////////////////////////// */
|
||||||
|
|
||||||
|
.document {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: var(--spacer);
|
||||||
|
background-color: var(--body-background-color);
|
||||||
|
padding-bottom: calc(var(--spacer) * 2);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
transition: 0.4s var(--easing);
|
||||||
|
transition-property: transform, background, border, box-shadow;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.85);
|
||||||
|
box-shadow: 0 1px 10px rgba(1, 85, 101, 0.1),
|
||||||
|
0 -1px 4px rgba(1, 85, 101, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.has-menu-open) .document {
|
||||||
|
transform: translate3d(0, calc(var(--spacer) * 2), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .document {
|
||||||
|
border-top-color: rgba(255, 255, 255, 0.05);
|
||||||
|
box-shadow: 0 1px 8px rgba(0, 7, 8, 0.3), 0 -1px 4px rgba(0, 21, 25, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 60rem) {
|
||||||
|
.document {
|
||||||
|
padding-top: calc(var(--spacer) * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) and (min-height: 500px) {
|
||||||
|
.document {
|
||||||
|
margin-top: calc(var(--spacer) * 2.5);
|
||||||
|
|
||||||
|
/* height of footer */
|
||||||
|
margin-bottom: calc(var(--spacer) * 12);
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
min-height: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--maxWidthContent);
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wide {
|
||||||
|
composes: container;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breakout {
|
||||||
|
margin-left: calc(-50vw + 50%);
|
||||||
|
margin-right: calc(-50vw + 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
.breakout {
|
||||||
|
margin-left: -8rem;
|
||||||
|
margin-right: -8rem;
|
||||||
|
}
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
#___gatsby {
|
|
||||||
// display: flex;
|
|
||||||
// min-height: 100vh;
|
|
||||||
// flex-direction: column;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 0 $spacer / $line-height;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
@media (min-width: $screen-sm) {
|
|
||||||
padding: 0 ($spacer * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// topbar and footer as fixed
|
|
||||||
// site background
|
|
||||||
/////////////////////////////////////
|
|
||||||
|
|
||||||
.document {
|
|
||||||
width: 100%;
|
|
||||||
padding-top: $spacer;
|
|
||||||
background-color: $body-background-color;
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.7);
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.7);
|
|
||||||
padding-bottom: $spacer * 2;
|
|
||||||
box-shadow: 0 1px 4px rgba($brand-main, 0.1),
|
|
||||||
0 -1px 4px rgba($brand-main, 0.2);
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
transition: 0.4s $easing;
|
|
||||||
transition-property: transform, background;
|
|
||||||
|
|
||||||
:global(.has-menu-open) & {
|
|
||||||
transform: translate3d(0, ($spacer * 3.5), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
background-color: $body-background-color--dark;
|
|
||||||
color: $text-color--dark;
|
|
||||||
border-top-color: darken($brand-grey, 15%);
|
|
||||||
border-bottom-color: darken($body-background-color--dark, 3%);
|
|
||||||
box-shadow: 0 1px 8px rgba(darken($brand-main, 15%), 0.1),
|
|
||||||
0 -1px 4px darken($brand-main, 15%);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $screen-md) {
|
|
||||||
padding-top: $spacer * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $screen-sm) and (min-height: 500px) {
|
|
||||||
margin-top: $spacer * 2.65;
|
|
||||||
margin-bottom: $spacer * 18; // height of footer
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
min-height: 500px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Container from './atoms/Container'
|
|
||||||
import Typekit from './atoms/Typekit'
|
import Typekit from './atoms/Typekit'
|
||||||
import Header from './organisms/Header'
|
import Header from './organisms/Header'
|
||||||
import Footer from './organisms/Footer'
|
import Footer from './organisms/Footer'
|
||||||
import styles from './Layout.module.scss'
|
import { document, content } from './Layout.module.css'
|
||||||
|
|
||||||
// if (process.env.NODE_ENV !== 'production') {
|
// if (process.env.NODE_ENV !== 'production') {
|
||||||
// // eslint-disable-next-line
|
// // eslint-disable-next-line
|
||||||
@ -17,10 +16,8 @@ export default function Layout({ children }: { children: any }): ReactElement {
|
|||||||
<Typekit />
|
<Typekit />
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<main className={styles.document} id="document">
|
<main className={document} id="document">
|
||||||
<div className={styles.content}>
|
<div className={content}>{children}</div>
|
||||||
<Container>{children}</Container>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
88
src/components/atoms/Changelog.module.css
Normal file
88
src/components/atoms/Changelog.module.css
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
.title {
|
||||||
|
margin-top: calc(var(--spacer) * 3);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: var(--spacer);
|
||||||
|
padding-left: calc(var(--spacer) / 2);
|
||||||
|
margin-left: calc(var(--spacer) / 2);
|
||||||
|
border-left: var(--stroke-width) solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2,
|
||||||
|
.content h3,
|
||||||
|
.content h4 {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: calc(var(--spacer) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2::before,
|
||||||
|
.content h3::before,
|
||||||
|
.content h4::before {
|
||||||
|
content: '';
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--color-headings);
|
||||||
|
position: absolute;
|
||||||
|
left: -1.275rem;
|
||||||
|
top: calc(var(--font-size-large) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2 + blockquote,
|
||||||
|
.content h3 + blockquote,
|
||||||
|
.content h4 + blockquote {
|
||||||
|
padding-left: 0;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2 + blockquote::before,
|
||||||
|
.content h3 + blockquote::before,
|
||||||
|
.content h4 + blockquote::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2,
|
||||||
|
.content h3 {
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: calc(var(--spacer) / 8);
|
||||||
|
margin-bottom: calc(var(--spacer) / var(--line-height));
|
||||||
|
}
|
||||||
|
|
||||||
|
.content ul {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
margin-left: calc(var(--spacer) / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content ul li {
|
||||||
|
margin-bottom: calc(var(--spacer) / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.source {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
font-family: var(--font-family-base);
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
padding-bottom: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.source,
|
||||||
|
.source a {
|
||||||
|
color: var(--text-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.source a {
|
||||||
|
margin-left: calc(var(--spacer) / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.source code {
|
||||||
|
font-size: calc(var(--font-size-mini) * 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.source a:hover {
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
@ -1,92 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-top: $spacer * 3;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding-top: $spacer;
|
|
||||||
padding-left: $spacer / 2;
|
|
||||||
margin-left: $spacer / 2;
|
|
||||||
border-left: 1px solid $brand-grey-dimmed;
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
border-left-color: rgba($color-headings--dark, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: $spacer / 4;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
width: 0.5rem;
|
|
||||||
height: 0.5rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: inline-block;
|
|
||||||
background: $color-headings;
|
|
||||||
position: absolute;
|
|
||||||
left: -($spacer / 1.5);
|
|
||||||
top: $font-size-large / 3;
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
background: $color-headings--dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+ blockquote {
|
|
||||||
padding-left: 0;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
font-size: $font-size-large;
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
margin-left: 0;
|
|
||||||
margin-top: $spacer / 8;
|
|
||||||
margin-bottom: $spacer / $line-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
margin-left: $spacer / 8;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: $spacer / 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.source {
|
|
||||||
font-size: $font-size-mini;
|
|
||||||
font-family: $font-family-base;
|
|
||||||
font-weight: $font-weight-base;
|
|
||||||
padding-bottom: $spacer / 2;
|
|
||||||
|
|
||||||
&,
|
|
||||||
a {
|
|
||||||
color: $brand-grey-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
margin-left: $spacer / 8;
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-size: ($font-size-mini * 0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $link-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
|
|||||||
import { graphql, useStaticQuery } from 'gatsby'
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
import remark from 'remark'
|
import remark from 'remark'
|
||||||
import remarkReact from 'remark-react'
|
import remarkReact from 'remark-react'
|
||||||
import styles from './Changelog.module.scss'
|
import { title, content, source } from './Changelog.module.css'
|
||||||
import { GitHub, GitHubRepo } from '../../@types/GitHub'
|
import { GitHub, GitHubRepo } from '../../@types/GitHub'
|
||||||
|
|
||||||
export function PureChangelog({
|
export function PureChangelog({
|
||||||
@ -30,22 +30,20 @@ export function PureChangelog({
|
|||||||
const filePathDisplay = `${owner.login}/${repo}:CHANGELOG.md`
|
const filePathDisplay = `${owner.login}/${repo}:CHANGELOG.md`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.changelog}>
|
<>
|
||||||
<h2 className={styles.title} id="changelog">
|
<h2 className={title} id="changelog">
|
||||||
Changelog
|
Changelog
|
||||||
</h2>
|
</h2>
|
||||||
<div className={styles.content}>
|
<div className={content}>
|
||||||
<p className={styles.source}>
|
{changelogHtml}
|
||||||
<em>
|
<p className={source}>
|
||||||
sourced from{' '}
|
sourced from{' '}
|
||||||
<a href={filePathUrl}>
|
<a href={filePathUrl}>
|
||||||
<code>{filePathDisplay}</code>
|
<code>{filePathDisplay}</code>
|
||||||
</a>
|
</a>
|
||||||
</em>
|
|
||||||
</p>
|
</p>
|
||||||
{changelogHtml}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
.container {
|
|
||||||
max-width: 37rem;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import React, { ReactElement } from 'react'
|
|
||||||
import styles from './Container.module.scss'
|
|
||||||
|
|
||||||
export default function Container({
|
|
||||||
children
|
|
||||||
}: {
|
|
||||||
children: any
|
|
||||||
}): ReactElement {
|
|
||||||
return <section className={styles.container}>{children}</section>
|
|
||||||
}
|
|
30
src/components/atoms/Copy.module.css
Normal file
30
src/components/atoms/Copy.module.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
.button {
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
background: rgba(var(--brand-grey), 0.3);
|
||||||
|
padding: calc(var(--spacer) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button svg {
|
||||||
|
stroke: var(--text-color-light);
|
||||||
|
transition: 0.15s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copied {
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copied svg {
|
||||||
|
stroke: var(--text-color-dimmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover svg {
|
||||||
|
stroke: var(--text-color-dimmed);
|
||||||
|
}
|
@ -1,35 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.button {
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
border: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
background: rgba($brand-grey, 0.3);
|
|
||||||
padding: $spacer / 3;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
stroke: $brand-grey-light;
|
|
||||||
transition: 0.15s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
svg {
|
|
||||||
stroke: $brand-grey-dimmed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.copied {
|
|
||||||
background: green;
|
|
||||||
|
|
||||||
// stylelint-disable-next-line no-descending-specificity
|
|
||||||
svg {
|
|
||||||
stroke: $brand-grey-dimmed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,12 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import loadable from '@loadable/component'
|
import loadable from '@loadable/component'
|
||||||
import styles from './Copy.module.scss'
|
import { copied, button } from './Copy.module.css'
|
||||||
import Icon from './Icon'
|
import Icon from './Icon'
|
||||||
|
|
||||||
const Clipboard = loadable(() => import('react-clipboard.js'))
|
const Clipboard = loadable(() => import('react-clipboard.js'))
|
||||||
|
|
||||||
const onCopySuccess = (e: any) => {
|
const onCopySuccess = (e: any) => {
|
||||||
e.trigger.classList.add(styles.copied)
|
e.trigger.classList.add(copied)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Copy({ text }: { text: string }): ReactElement {
|
export default function Copy({ text }: { text: string }): ReactElement {
|
||||||
@ -15,7 +15,7 @@ export default function Copy({ text }: { text: string }): ReactElement {
|
|||||||
data-clipboard-text={text}
|
data-clipboard-text={text}
|
||||||
button-title="Copy to clipboard"
|
button-title="Copy to clipboard"
|
||||||
onSuccess={(e: ClipboardJS.Event) => onCopySuccess(e)}
|
onSuccess={(e: ClipboardJS.Event) => onCopySuccess(e)}
|
||||||
className={styles.button}
|
className={button}
|
||||||
>
|
>
|
||||||
<Icon name="Copy" />
|
<Icon name="Copy" />
|
||||||
</Clipboard>
|
</Clipboard>
|
||||||
|
18
src/components/atoms/Divider.module.css
Normal file
18
src/components/atoms/Divider.module.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.divider {
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1px dashed var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
height: 1px;
|
||||||
|
bottom: -2px;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 1px dashed #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .divider::before {
|
||||||
|
border-bottom-color: var(--brand-grey);
|
||||||
|
}
|
69
src/components/atoms/Exif.module.css
Normal file
69
src/components/atoms/Exif.module.css
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
.exif {
|
||||||
|
margin-top: -3rem;
|
||||||
|
margin-bottom: calc(var(--spacer) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
color: var(--text-color-light);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data span {
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: var(--spacer) calc(var(--spacer) / 1.5);
|
||||||
|
border-bottom: 1px dashed var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data span svg {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: calc(var(--spacer) / 8);
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data span:first-child {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.data {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data span {
|
||||||
|
border-left: 1px dashed var(--border-color);
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data span:first-child {
|
||||||
|
flex-basis: auto;
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.map {
|
||||||
|
composes: breakout from '../Layout.module.css';
|
||||||
|
composes: frame from './Image.module.css';
|
||||||
|
height: 180px;
|
||||||
|
margin-top: calc(var(--spacer) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.map {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.map img {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map > div:first-child {
|
||||||
|
background: none !important;
|
||||||
|
}
|
@ -1,84 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.exif {
|
|
||||||
margin-top: -($spacer * $line-height);
|
|
||||||
margin-bottom: $spacer * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data {
|
|
||||||
@include breakoutviewport;
|
|
||||||
|
|
||||||
font-size: $font-size-mini;
|
|
||||||
color: $brand-grey-light;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: block;
|
|
||||||
flex: 1 1 20%;
|
|
||||||
white-space: nowrap;
|
|
||||||
padding: $spacer;
|
|
||||||
border-bottom: 1px dashed $brand-grey-dimmed;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: $spacer / 8;
|
|
||||||
opacity: 0.65;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
flex-basis: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
border-bottom-color: rgba($brand-grey, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $screen-sm) {
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
|
|
||||||
span {
|
|
||||||
border-left: 1px dashed $brand-grey-dimmed;
|
|
||||||
border-bottom: 0;
|
|
||||||
|
|
||||||
&,
|
|
||||||
&:first-child {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
border-left-color: rgba($brand-grey, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.map {
|
|
||||||
@include breakoutviewport;
|
|
||||||
@include media-frame;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
height: 220px;
|
|
||||||
margin-top: $spacer * 2;
|
|
||||||
|
|
||||||
@media (min-width: $screen-sm) {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div:first-child {
|
|
||||||
background: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import ExifMap from './ExifMap'
|
import ExifMap from './ExifMap'
|
||||||
import styles from './Exif.module.scss'
|
import { exif as styleExif, data, map } from './Exif.module.css'
|
||||||
import { Exif as ExifMeta } from '../../@types/Image'
|
import { Exif as ExifMeta } from '../../@types/Image'
|
||||||
import Icon from './Icon'
|
import Icon from './Icon'
|
||||||
|
|
||||||
@ -31,8 +31,8 @@ export default function Exif({ exif }: { exif: ExifMeta }): ReactElement {
|
|||||||
} = exif.formatted
|
} = exif.formatted
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className={styles.exif}>
|
<aside className={styleExif}>
|
||||||
<div className={styles.data}>
|
<div className={data}>
|
||||||
{model && <ExifData title="Camera model" value={model} icon="Camera" />}
|
{model && <ExifData title="Camera model" value={model} icon="Camera" />}
|
||||||
{focalLength && (
|
{focalLength && (
|
||||||
<ExifData title="Focal length" value={focalLength} icon="Crosshair" />
|
<ExifData title="Focal length" value={focalLength} icon="Crosshair" />
|
||||||
@ -49,7 +49,7 @@ export default function Exif({ exif }: { exif: ExifMeta }): ReactElement {
|
|||||||
{iso && <ExifData title="ISO" value={iso} icon="Maximize" />}
|
{iso && <ExifData title="ISO" value={iso} icon="Maximize" />}
|
||||||
</div>
|
</div>
|
||||||
{gps && gps.latitude && (
|
{gps && gps.latitude && (
|
||||||
<div className={styles.map}>
|
<div className={map}>
|
||||||
<ExifMap gps={gps} />
|
<ExifMap gps={gps} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import Map from 'pigeon-maps'
|
import { Map } from 'pigeon-maps'
|
||||||
import Marker from 'pigeon-marker'
|
import Marker from 'pigeon-marker'
|
||||||
import useDarkMode from 'use-dark-mode'
|
import useDarkMode from '../../hooks/useDarkMode'
|
||||||
|
|
||||||
const mapbox = (mapboxId: string) => (
|
const mapbox = (mapboxId: string) => (
|
||||||
x: string,
|
x: string,
|
||||||
@ -23,10 +23,7 @@ export default function ExifMap({
|
|||||||
}: {
|
}: {
|
||||||
gps: { latitude: string; longitude: string }
|
gps: { latitude: string; longitude: string }
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { value } = useDarkMode(false, {
|
const { value } = useDarkMode()
|
||||||
classNameDark: 'dark',
|
|
||||||
classNameLight: 'light'
|
|
||||||
})
|
|
||||||
const isDarkMode = value
|
const isDarkMode = value
|
||||||
const [zoom, setZoom] = useState(12)
|
const [zoom, setZoom] = useState(12)
|
||||||
|
|
||||||
@ -40,7 +37,7 @@ export default function ExifMap({
|
|||||||
<Map
|
<Map
|
||||||
center={[latitude, longitude]}
|
center={[latitude, longitude]}
|
||||||
zoom={zoom}
|
zoom={zoom}
|
||||||
height={220}
|
height={180}
|
||||||
dprs={[1, 2]}
|
dprs={[1, 2]}
|
||||||
attribution={false}
|
attribution={false}
|
||||||
provider={isDarkMode ? providers['dark'] : providers['light']}
|
provider={isDarkMode ? providers['dark'] : providers['light']}
|
||||||
|
70
src/components/atoms/Hamburger.module.css
Normal file
70
src/components/atoms/Hamburger.module.css
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
.hamburger {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: var(--stroke-width) solid var(--text-color-light);
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
transition: 0.2s var(--easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
.line:nth-child(1) {
|
||||||
|
top: 0;
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line:nth-child(2) {
|
||||||
|
top: 7px;
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line:nth-child(3) {
|
||||||
|
top: 14px;
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open state */
|
||||||
|
:global(.has-menu-open) .line:nth-child(1) {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.has-menu-open) .line:nth-child(2) {
|
||||||
|
width: 0%;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.has-menu-open) .line:nth-child(3) {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: calc(var(--spacer) / 2);
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: -1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover,
|
||||||
|
.button:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover .line,
|
||||||
|
.button:focus .line {
|
||||||
|
border-color: var(--link-color);
|
||||||
|
}
|
@ -1,75 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.hamburger {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
transform: rotate(0deg);
|
|
||||||
cursor: pointer;
|
|
||||||
margin-top: $spacer / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hamburgerLine {
|
|
||||||
@include transition;
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
height: 1px;
|
|
||||||
width: 100%;
|
|
||||||
border-bottom: $stroke-width solid $brand-grey-light;
|
|
||||||
opacity: 1;
|
|
||||||
left: 0;
|
|
||||||
transform: rotate(0deg);
|
|
||||||
|
|
||||||
&:nth-child(1) {
|
|
||||||
top: 0;
|
|
||||||
transform-origin: left center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
top: 7px;
|
|
||||||
transform-origin: left center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(3) {
|
|
||||||
top: 14px;
|
|
||||||
transform-origin: left center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open state
|
|
||||||
:global(.has-menu-open) & {
|
|
||||||
&:nth-child(1) {
|
|
||||||
transform: rotate(45deg);
|
|
||||||
top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
width: 0%;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(3) {
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
top: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hamburgerButton {
|
|
||||||
padding: $spacer / 2;
|
|
||||||
vertical-align: middle;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
margin-right: -($spacer / 2);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
outline: 0;
|
|
||||||
|
|
||||||
.hamburgerLine {
|
|
||||||
border-color: $brand-cyan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Hamburger.module.scss'
|
import { button, hamburger, line } from './Hamburger.module.css'
|
||||||
|
|
||||||
export default function Hamburger({
|
export default function Hamburger({
|
||||||
onClick
|
onClick
|
||||||
@ -7,16 +7,11 @@ export default function Hamburger({
|
|||||||
onClick(): void
|
onClick(): void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
return (
|
return (
|
||||||
<button
|
<button type="button" title="Menu" className={button} onClick={onClick}>
|
||||||
type="button"
|
<span className={hamburger}>
|
||||||
title="Menu"
|
<span className={line} />
|
||||||
className={styles.hamburgerButton}
|
<span className={line} />
|
||||||
onClick={onClick}
|
<span className={line} />
|
||||||
>
|
|
||||||
<span className={styles.hamburger}>
|
|
||||||
<span className={styles.hamburgerLine} />
|
|
||||||
<span className={styles.hamburgerLine} />
|
|
||||||
<span className={styles.hamburgerLine} />
|
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
stroke: currentColor;
|
stroke: currentColor;
|
||||||
stroke-width: $stroke-width;
|
stroke-width: var(--stroke-width);
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
stroke-linejoin: round;
|
stroke-linejoin: round;
|
||||||
fill: none;
|
fill: none;
|
@ -27,7 +27,7 @@ import {
|
|||||||
import { ReactComponent as Jsonfeed } from '../../images/jsonfeed.svg'
|
import { ReactComponent as Jsonfeed } from '../../images/jsonfeed.svg'
|
||||||
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'
|
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'
|
||||||
import { ReactComponent as Stopwatch } from '../../images/stopwatch.svg'
|
import { ReactComponent as Stopwatch } from '../../images/stopwatch.svg'
|
||||||
import styles from './Icon.module.scss'
|
import { icon } from './Icon.module.css'
|
||||||
|
|
||||||
const components: any = {
|
const components: any = {
|
||||||
Download: ArrowDownCircle,
|
Download: ArrowDownCircle,
|
||||||
@ -55,12 +55,12 @@ const components: any = {
|
|||||||
Crosshair
|
Crosshair
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon = ({ name }: { name: string }): ReactElement => {
|
const Icon = ({ name, ...props }: { name: string }): ReactElement => {
|
||||||
const IconMapped = components[name]
|
const IconMapped = components[name]
|
||||||
// const IconFeather = (Feather as any)[name]
|
// const IconFeather = (Feather as any)[name]
|
||||||
if (!IconMapped) return null
|
if (!IconMapped) return null
|
||||||
|
|
||||||
return <IconMapped className={styles.icon} />
|
return <IconMapped className={icon} {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Icon
|
export default Icon
|
||||||
|
53
src/components/atoms/Image.module.css
Normal file
53
src/components/atoms/Image.module.css
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
.frame {
|
||||||
|
border: var(--stroke-width) solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: 0.2s ease-out;
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover .frame,
|
||||||
|
a:focus .frame {
|
||||||
|
border-color: var(--link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
composes: frame;
|
||||||
|
max-width: none;
|
||||||
|
margin-top: calc(var(--spacer) * var(--line-height));
|
||||||
|
margin-bottom: calc(var(--spacer) * var(--line-height));
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* single photo post teasers */
|
||||||
|
.image:only-child {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageTitle {
|
||||||
|
font-size: var(--font-size-h3);
|
||||||
|
font-family: var(--font-family-headings);
|
||||||
|
line-height: var(--line-height-headings);
|
||||||
|
font-weight: var(--font-weight-headings);
|
||||||
|
font-style: normal;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 10%;
|
||||||
|
padding: calc(var(--spacer) / 3) var(--spacer);
|
||||||
|
background: var(--link-color);
|
||||||
|
color: #fff !important;
|
||||||
|
text-shadow: 0 1px 0 #000;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(0, -20px, 0);
|
||||||
|
transition: 0.2s var(--easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover .imageTitle,
|
||||||
|
a:focus .imageTitle {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.imageTitle {
|
|
||||||
@include transition();
|
|
||||||
|
|
||||||
font-size: $font-size-h3;
|
|
||||||
font-family: $font-family-headings;
|
|
||||||
line-height: $line-height-headings;
|
|
||||||
font-weight: $font-weight-headings;
|
|
||||||
font-style: normal;
|
|
||||||
text-align: left;
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 10%;
|
|
||||||
padding: $spacer / 3 $spacer;
|
|
||||||
background: rgba($link-color, 0.85);
|
|
||||||
color: #fff !important;
|
|
||||||
text-shadow: 0 1px 0 #000;
|
|
||||||
left: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transform: translate3d(0, -20px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
|
||||||
@include media-frame;
|
|
||||||
@include breakoutviewport;
|
|
||||||
|
|
||||||
max-width: none;
|
|
||||||
margin-top: $spacer * $line-height;
|
|
||||||
margin-bottom: $spacer * $line-height;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
a & {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// single photo post teasers
|
|
||||||
&:only-child {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover &,
|
|
||||||
a:focus & {
|
|
||||||
border-color: $link-color !important;
|
|
||||||
|
|
||||||
.imageTitle {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +1,22 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { graphql } from 'gatsby'
|
import { graphql } from 'gatsby'
|
||||||
import Img from 'gatsby-image'
|
import { GatsbyImage } from 'gatsby-plugin-image'
|
||||||
import { ImageProps } from '../../@types/Image'
|
import { ImageProps } from '../../@types/Image'
|
||||||
import styles from './Image.module.scss'
|
import { image as styleImage, imageTitle } from './Image.module.css'
|
||||||
|
|
||||||
export const Image = ({
|
export const Image = ({
|
||||||
title,
|
title,
|
||||||
fluid,
|
image,
|
||||||
fixed,
|
|
||||||
alt,
|
alt,
|
||||||
original
|
original,
|
||||||
|
className
|
||||||
}: ImageProps): ReactElement => (
|
}: ImageProps): ReactElement => (
|
||||||
<figure className={styles.image} data-original={original && original.src}>
|
<figure
|
||||||
<Img backgroundColor="transparent" fluid={fluid} fixed={fixed} alt={alt} />
|
className={`${styleImage} ${className ? className : ''}`}
|
||||||
{title && <figcaption className={styles.imageTitle}>{title}</figcaption>}
|
data-original={original?.src}
|
||||||
|
>
|
||||||
|
<GatsbyImage image={image} alt={alt} />
|
||||||
|
{title && <figcaption className={imageTitle}>{title}</figcaption>}
|
||||||
</figure>
|
</figure>
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,9 +25,7 @@ export const imageSizeDefault = graphql`
|
|||||||
original {
|
original {
|
||||||
src
|
src
|
||||||
}
|
}
|
||||||
fluid(maxWidth: 940, quality: 85) {
|
gatsbyImageData(layout: CONSTRAINED, width: 1040, quality: 85)
|
||||||
...GatsbyImageSharpFluid_withWebp_noBase64
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -33,9 +34,13 @@ export const imageSizeThumb = graphql`
|
|||||||
original {
|
original {
|
||||||
src
|
src
|
||||||
}
|
}
|
||||||
fluid(maxWidth: 420, maxHeight: 140, quality: 85, cropFocus: CENTER) {
|
gatsbyImageData(
|
||||||
...GatsbyImageSharpFluid_withWebp_noBase64
|
layout: CONSTRAINED
|
||||||
}
|
width: 480
|
||||||
|
height: 180
|
||||||
|
quality: 85
|
||||||
|
transformOptions: { cropFocus: CENTER }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -44,8 +49,12 @@ export const photoSizeThumb = graphql`
|
|||||||
original {
|
original {
|
||||||
src
|
src
|
||||||
}
|
}
|
||||||
fluid(maxWidth: 300, maxHeight: 300, quality: 85, cropFocus: CENTER) {
|
gatsbyImageData(
|
||||||
...GatsbyImageSharpFluid_withWebp_noBase64
|
layout: CONSTRAINED
|
||||||
}
|
width: 316
|
||||||
|
height: 316
|
||||||
|
quality: 85
|
||||||
|
transformOptions: { cropFocus: CENTER }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
32
src/components/atoms/Input.module.css
Normal file
32
src/components/atoms/Input.module.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--padding-base-vertical) var(--padding-base-horizontal);
|
||||||
|
font-size: var(--input-font-size);
|
||||||
|
font-weight: var(--input-font-weight);
|
||||||
|
line-height: var(--line-height);
|
||||||
|
color: var(--input-color);
|
||||||
|
background-color: var(--input-bg);
|
||||||
|
background-image: none;
|
||||||
|
border: var(--stroke-width) solid transparent;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: none;
|
||||||
|
transition: all ease-in-out 0.15s;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input::-moz-placeholder,
|
||||||
|
.input::-webkit-input-placeholder,
|
||||||
|
.input:-ms-input-placeholder {
|
||||||
|
color: var(--input-color-placeholder);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:focus {
|
||||||
|
border-color: var(--input-border-focus);
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input[disabled] {
|
||||||
|
color: var(--text-color-dimmed);
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.input {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding: $padding-base-vertical $padding-base-horizontal;
|
|
||||||
font-size: $input-font-size;
|
|
||||||
font-weight: $input-font-weight;
|
|
||||||
line-height: $line-height;
|
|
||||||
color: $input-color;
|
|
||||||
background-color: $input-bg;
|
|
||||||
background-image: none; // Reset unusual Firefox-on-Android default style
|
|
||||||
border: 0;
|
|
||||||
border-radius: $input-border-radius;
|
|
||||||
box-shadow: none;
|
|
||||||
transition: all ease-in-out 0.15s;
|
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
@include placeholder();
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
color: $input-color--dark;
|
|
||||||
background-color: $input-bg--dark;
|
|
||||||
@include placeholder($input-color-placeholder--dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border-color: $input-border-focus;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[disabled] {
|
|
||||||
color: $brand-grey-dimmed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Input.module.scss'
|
import { input } from './Input.module.css'
|
||||||
|
|
||||||
export default function Input(props: any): ReactElement {
|
export default function Input({ className, ...props }: any): ReactElement {
|
||||||
return <input className={styles.input} {...props} />
|
return <input className={`${input} ${className || ''}`} {...props} />
|
||||||
}
|
}
|
||||||
|
17
src/components/atoms/Qr.module.css
Normal file
17
src/components/atoms/Qr.module.css
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.qr {
|
||||||
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code {
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
padding-right: 2rem;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code code {
|
||||||
|
padding: calc(var(--spacer) / 2);
|
||||||
|
font-size: 0.65rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.qr {
|
|
||||||
margin-bottom: $spacer / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
margin: 0 auto;
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
padding-right: 2rem;
|
|
||||||
width: fit-content;
|
|
||||||
|
|
||||||
code {
|
|
||||||
padding: $spacer / 2;
|
|
||||||
font-size: 0.65rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Suspense } from 'react'
|
import React, { Suspense } from 'react'
|
||||||
import { render, fireEvent, waitForElement } from '@testing-library/react'
|
import { render, fireEvent, waitFor } from '@testing-library/react'
|
||||||
|
|
||||||
import Qr from './Qr'
|
import Qr from './Qr'
|
||||||
|
|
||||||
@ -10,10 +10,8 @@ describe('Qr', () => {
|
|||||||
<Qr address="xxx" />
|
<Qr address="xxx" />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
const lazyElement = await waitForElement(() =>
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
container.querySelector('button')
|
await waitFor(() => container.querySelector('button'))
|
||||||
)
|
|
||||||
expect(lazyElement).toBeInTheDocument()
|
|
||||||
fireEvent.click(container.querySelector('button'))
|
fireEvent.click(container.querySelector('button'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { QRCode } from 'react-qr-svg'
|
import { QRCode } from 'react-qr-svg'
|
||||||
import styles from './Qr.module.scss'
|
import { qr, code } from './Qr.module.css'
|
||||||
import Copy from './Copy'
|
import Copy from './Copy'
|
||||||
|
|
||||||
export default function Qr({
|
export default function Qr({
|
||||||
@ -19,10 +19,10 @@ export default function Qr({
|
|||||||
level="Q"
|
level="Q"
|
||||||
style={{ width: 120 }}
|
style={{ width: 120 }}
|
||||||
value={address}
|
value={address}
|
||||||
className={styles.qr}
|
className={qr}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<pre className={styles.code}>
|
<pre className={code}>
|
||||||
<code>{address}</code>
|
<code>{address}</code>
|
||||||
<Copy text={address} />
|
<Copy text={address} />
|
||||||
</pre>
|
</pre>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { graphql, useStaticQuery } from 'gatsby'
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
|
import { getSrc } from 'gatsby-plugin-image'
|
||||||
import { useSiteMetadata } from '../../../hooks/use-site-metadata'
|
import { useSiteMetadata } from '../../../hooks/use-site-metadata'
|
||||||
import { Post } from '../../../@types/Post'
|
import { Post } from '../../../@types/Post'
|
||||||
import MetaTags from './MetaTags'
|
import MetaTags from './MetaTags'
|
||||||
@ -39,9 +39,7 @@ export default function SEO({
|
|||||||
const postMeta = post.frontmatter
|
const postMeta = post.frontmatter
|
||||||
title = `${postMeta.title} ¦ ${siteTitle}`
|
title = `${postMeta.title} ¦ ${siteTitle}`
|
||||||
description = postMeta.description ? postMeta.description : post.excerpt
|
description = postMeta.description ? postMeta.description : post.excerpt
|
||||||
image = postMeta.image
|
image = postMeta.image ? getSrc(postMeta.image) : `/${logo}`
|
||||||
? postMeta.image.childImageSharp.fluid.src
|
|
||||||
: `/${logo}`
|
|
||||||
postURL = `${siteUrl}${slug}`
|
postURL = `${siteUrl}${slug}`
|
||||||
} else {
|
} else {
|
||||||
title = `${siteTitle} ¦ ${siteDescription}`
|
title = `${siteTitle} ¦ ${siteDescription}`
|
||||||
|
20
src/components/atoms/Tag.module.css
Normal file
20
src/components/atoms/Tag.module.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.tag {
|
||||||
|
color: var(--text-color-light);
|
||||||
|
margin-left: calc(var(--spacer) / 2);
|
||||||
|
margin-right: calc(var(--spacer) / 2);
|
||||||
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag::before {
|
||||||
|
content: '#';
|
||||||
|
margin-right: 1px;
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
margin-left: calc(var(--spacer) / 6);
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
color: $brand-grey-light;
|
|
||||||
margin-left: $spacer / 2;
|
|
||||||
margin-right: $spacer / 2;
|
|
||||||
margin-bottom: $spacer / 2;
|
|
||||||
white-space: nowrap;
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '#';
|
|
||||||
margin-right: 1px;
|
|
||||||
opacity: 0.65;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.count {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
margin-left: $spacer / 6;
|
|
||||||
opacity: 0.65;
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import styles from './Tag.module.scss'
|
import { tag, count as styleCount } from './Tag.module.css'
|
||||||
|
|
||||||
export default function Tag({
|
export default function Tag({
|
||||||
name,
|
name,
|
||||||
@ -14,9 +14,9 @@ export default function Tag({
|
|||||||
style?: any
|
style?: any
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
return (
|
return (
|
||||||
<Link className={styles.tag} to={url} style={style}>
|
<Link className={tag} to={url} style={style}>
|
||||||
{name}
|
{name}
|
||||||
{count && <span className={styles.count}>{count}</span>}
|
{count && <span className={styleCount}>{count}</span>}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
45
src/components/molecules/Menu.module.css
Normal file
45
src/components/molecules/Menu.module.css
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
.menu {
|
||||||
|
margin-top: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li {
|
||||||
|
list-style: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu a {
|
||||||
|
color: var(--text-color);
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0 calc(var(--spacer) / 4);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
text-shadow: 0 1px 0 rgba(255 255 255 0.5);
|
||||||
|
padding: var(--padding-base-horizontal);
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu::-webkit-scrollbar,
|
||||||
|
.menu::-moz-scrollbar,
|
||||||
|
.menu::-webkit-scrollbar-thumb,
|
||||||
|
.menu::-webkit-scrollbar-track {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -1,66 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
@include divider-top;
|
|
||||||
|
|
||||||
margin-top: 0;
|
|
||||||
padding-top: $spacer / 2;
|
|
||||||
position: absolute;
|
|
||||||
top: calc(100% + #{$spacer / 1.5});
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
text-align: center;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
list-style: none;
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $text-color;
|
|
||||||
line-height: 1;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin: 0 $spacer / 4;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
text-shadow: 0 1px 0 rgba(#fff, 0.5);
|
|
||||||
padding: $padding-base-horizontal;
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: $link-color-hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
color: $text-color--dark;
|
|
||||||
text-shadow: 0 -1px 0 rgba(#000, 0.5);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: $link-color-hover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar,
|
|
||||||
&::-moz-scrollbar,
|
|
||||||
&::-webkit-scrollbar-thumb,
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ import React, { ReactElement, useState } from 'react'
|
|||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import Hamburger from '../atoms/Hamburger'
|
import Hamburger from '../atoms/Hamburger'
|
||||||
import styles from './Menu.module.scss'
|
import { menu as styleMenu } from './Menu.module.css'
|
||||||
import { useSiteMetadata } from '../../hooks/use-site-metadata'
|
import { useSiteMetadata } from '../../hooks/use-site-metadata'
|
||||||
import { MenuItem } from '../../@types/Site'
|
import { MenuItem } from '../../@types/Site'
|
||||||
|
|
||||||
@ -10,17 +10,19 @@ export default function Menu(): ReactElement {
|
|||||||
const [menuOpen, setMenuOpen] = useState(false)
|
const [menuOpen, setMenuOpen] = useState(false)
|
||||||
const { menu } = useSiteMetadata()
|
const { menu } = useSiteMetadata()
|
||||||
|
|
||||||
const toggleMenu = () => {
|
function toggleMenu(): void {
|
||||||
setMenuOpen(!menuOpen)
|
setMenuOpen(!menuOpen)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MenuItems = menu.map((item: MenuItem) => (
|
const MenuItems = menu.map(
|
||||||
|
(item: MenuItem): JSX.Element => (
|
||||||
<li key={item.title}>
|
<li key={item.title}>
|
||||||
<Link onClick={toggleMenu} to={item.link}>
|
<Link onClick={toggleMenu} to={item.link}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -28,7 +30,7 @@ export default function Menu(): ReactElement {
|
|||||||
<html className={menuOpen ? 'has-menu-open' : undefined} lang="en" />
|
<html className={menuOpen ? 'has-menu-open' : undefined} lang="en" />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<Hamburger onClick={toggleMenu} />
|
<Hamburger onClick={toggleMenu} />
|
||||||
<nav className={styles.menu}>
|
<nav className={styleMenu}>
|
||||||
<ul>{MenuItems}</ul>
|
<ul>{MenuItems}</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</>
|
</>
|
||||||
|
25
src/components/molecules/Networks.module.css
Normal file
25
src/components/molecules/Networks.module.css
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
.link {
|
||||||
|
text-align: center;
|
||||||
|
width: 2rem;
|
||||||
|
padding: calc(var(--spacer) / 4);
|
||||||
|
margin-left: calc(var(--spacer) / 4);
|
||||||
|
margin-right: calc(var(--spacer) / 4);
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--text-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
transition: stroke 0.3s ease-in-out;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.link {
|
|
||||||
text-align: center;
|
|
||||||
width: 2rem;
|
|
||||||
padding: $spacer / 4;
|
|
||||||
margin-left: $spacer / 4;
|
|
||||||
margin-right: $spacer / 4;
|
|
||||||
display: inline-block;
|
|
||||||
color: $text-color-light;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
transition: stroke 0.3s ease-in-out;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Icon from '../atoms/Icon'
|
import Icon from '../atoms/Icon'
|
||||||
import styles from './Networks.module.scss'
|
import { link as styleLink } from './Networks.module.css'
|
||||||
|
|
||||||
function NetworkIcon({ link }: { link: string }) {
|
function NetworkIcon({ link }: { link: string }) {
|
||||||
let IconComp
|
let IconComp
|
||||||
@ -28,7 +28,7 @@ export default function IconLinks({
|
|||||||
return (
|
return (
|
||||||
<p>
|
<p>
|
||||||
{links.map((link: string) => (
|
{links.map((link: string) => (
|
||||||
<a key={link} className={styles.link} href={link} title={link}>
|
<a key={link} className={styleLink} href={link} title={link}>
|
||||||
<NetworkIcon link={link} />
|
<NetworkIcon link={link} />
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
|
41
src/components/molecules/Pagination.module.css
Normal file
41
src/components/molecules/Pagination.module.css
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
.pagination {
|
||||||
|
margin-top: calc(var(--spacer) * 3);
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number {
|
||||||
|
text-align: center;
|
||||||
|
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 2);
|
||||||
|
border: 1px solid var(--text-color-dimmed);
|
||||||
|
font-variant-numeric: lining-nums;
|
||||||
|
margin-left: -1px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 2.5rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.number:hover,
|
||||||
|
.number:focus {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.number:first-child {
|
||||||
|
border-top-left-radius: var(--border-radius);
|
||||||
|
border-bottom-left-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.number:last-child {
|
||||||
|
border-top-right-radius: var(--border-radius);
|
||||||
|
border-bottom-right-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
composes: number;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
color: var(--text-color-dimmed);
|
||||||
|
}
|
@ -1,58 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
@include breakoutviewport;
|
|
||||||
|
|
||||||
margin-top: $spacer * 3;
|
|
||||||
margin-bottom: $spacer;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
text-align: center;
|
|
||||||
padding: $spacer / 6 $spacer / 2;
|
|
||||||
border: 1px solid $brand-grey-dimmed;
|
|
||||||
font-variant-numeric: lining-nums;
|
|
||||||
margin-left: -1px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-width: 2.5rem;
|
|
||||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
||||||
color: $brand-grey;
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
color: $brand-grey-dimmed;
|
|
||||||
border-color: darken($body-background-color--dark, 5%);
|
|
||||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-top-left-radius: $border-radius;
|
|
||||||
border-bottom-left-radius: $border-radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-top-right-radius: $border-radius;
|
|
||||||
border-bottom-right-radius: $border-radius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.current {
|
|
||||||
composes: number;
|
|
||||||
cursor: default;
|
|
||||||
pointer-events: none;
|
|
||||||
color: $brand-grey-dimmed;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
color: $brand-grey-light;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,15 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import styles from './Pagination.module.scss'
|
|
||||||
import shortid from 'shortid'
|
import shortid from 'shortid'
|
||||||
import { PageContext } from '../../@types/Post'
|
import { PageContext } from '../../@types/Post'
|
||||||
import Icon from '../atoms/Icon'
|
import Icon from '../atoms/Icon'
|
||||||
|
import {
|
||||||
|
current as styleCurrent,
|
||||||
|
number as styleNumber,
|
||||||
|
pagination
|
||||||
|
} from './Pagination.module.css'
|
||||||
|
|
||||||
const PageNumber = ({
|
function PageNumber({
|
||||||
i,
|
i,
|
||||||
slug,
|
slug,
|
||||||
current
|
current
|
||||||
@ -13,8 +17,8 @@ const PageNumber = ({
|
|||||||
i: number
|
i: number
|
||||||
slug: string
|
slug: string
|
||||||
current?: boolean
|
current?: boolean
|
||||||
}) => {
|
}): JSX.Element {
|
||||||
const classes = current ? styles.current : styles.number
|
const classes = current ? styleCurrent : styleNumber
|
||||||
const link = i === 0 ? slug : `${slug}page/${i + 1}`
|
const link = i === 0 ? slug : `${slug}page/${i + 1}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -30,13 +34,13 @@ function PrevNext({
|
|||||||
}: {
|
}: {
|
||||||
prevPagePath?: string
|
prevPagePath?: string
|
||||||
nextPagePath?: string
|
nextPagePath?: string
|
||||||
}) {
|
}): JSX.Element {
|
||||||
const link = prevPagePath || nextPagePath
|
const link = prevPagePath || nextPagePath
|
||||||
const rel = prevPagePath ? 'prev' : 'next'
|
const rel = prevPagePath ? 'prev' : 'next'
|
||||||
const title = prevPagePath ? 'Newer Posts' : 'Older Posts'
|
const title = prevPagePath ? 'Newer Posts' : 'Older Posts'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={link} rel={rel} title={title} className={styles.number}>
|
<Link to={link} rel={rel} title={title} className={styleNumber}>
|
||||||
{prevPagePath ? (
|
{prevPagePath ? (
|
||||||
<Icon name="ChevronLeft" />
|
<Icon name="ChevronLeft" />
|
||||||
) : (
|
) : (
|
||||||
@ -62,7 +66,7 @@ export default function Pagination({
|
|||||||
const isLast = currentPageNumber === numPages
|
const isLast = currentPageNumber === numPages
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.pagination}>
|
<div className={pagination}>
|
||||||
{!isFirst && <PrevNext prevPagePath={prevPagePath} />}
|
{!isFirst && <PrevNext prevPagePath={prevPagePath} />}
|
||||||
{Array.from({ length: numPages }, (_, i) => (
|
{Array.from({ length: numPages }, (_, i) => (
|
||||||
<PageNumber
|
<PageNumber
|
||||||
|
6
src/components/molecules/PostDate.module.css
Normal file
6
src/components/molecules/PostDate.module.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.time {
|
||||||
|
font-style: italic;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
color: var(--text-color-light);
|
||||||
|
margin-bottom: calc(var(--spacer) / var(--line-height));
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.time {
|
|
||||||
font-style: italic;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
color: $text-color-light;
|
|
||||||
margin-bottom: $spacer / $line-height;
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Time from '../atoms/Time'
|
import Time from '../atoms/Time'
|
||||||
import styles from './PostDate.module.scss'
|
import { time } from './PostDate.module.css'
|
||||||
|
|
||||||
export default function PostDate({
|
export default function PostDate({
|
||||||
date,
|
date,
|
||||||
@ -10,7 +10,7 @@ export default function PostDate({
|
|||||||
updated?: string
|
updated?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div className={styles.time}>
|
<div className={time}>
|
||||||
<Time date={date} />
|
<Time date={date} />
|
||||||
{updated && ' • updated '}
|
{updated && ' • updated '}
|
||||||
{updated && <Time date={updated} />}
|
{updated && <Time date={updated} />}
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
padding-left: 0.2rem;
|
padding-left: 0.2rem;
|
||||||
padding-right: 0.2rem;
|
padding-right: 0.2rem;
|
||||||
margin-top: $spacer / 2;
|
margin-top: calc(var(--spacer) / 3);
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
font-size: $font-size-base;
|
font-size: var(--font-size-base);
|
||||||
transition: color 0.2s ease-out;
|
transition: color 0.2s ease-out;
|
||||||
color: $text-color-light;
|
}
|
||||||
|
|
||||||
|
.title + div {
|
||||||
|
padding-left: 0.2rem;
|
||||||
|
padding-right: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
figure {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.post:hover {
|
||||||
font-style: italic;
|
text-decoration: none;
|
||||||
font-size: $font-size-small;
|
}
|
||||||
color: $text-color-light;
|
|
||||||
padding-left: 0.2rem;
|
.post:hover .title {
|
||||||
padding-right: 0.2rem;
|
color: var(--link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post figure {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
@include media-frame;
|
composes: frame from '../atoms/Image.module.css';
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
min-height: 95px;
|
min-height: 95px;
|
||||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACaADAAQAAAABAAAACQAAAAAvQpmhAAAAHElEQVQYGWNgoBL4T8gcggoIGcBA0ASCCmhsBQBhFwX7u70C8QAAAABJRU5ErkJggg==);
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACaADAAQAAAABAAAACQAAAAAvQpmhAAAAHElEQVQYGWNgoBL4T8gcggoIGcBA0ASCCmhsBQBhFwX7u70C8QAAAABJRU5ErkJggg==);
|
||||||
|
}
|
||||||
a:hover & {
|
|
||||||
border-color: $link-color;
|
a:hover .empty {
|
||||||
}
|
border-color: var(--link-color);
|
||||||
}
|
}
|
@ -3,7 +3,11 @@ import { Link, graphql } from 'gatsby'
|
|||||||
import { Image } from '../atoms/Image'
|
import { Image } from '../atoms/Image'
|
||||||
import { Post } from '../../@types/Post'
|
import { Post } from '../../@types/Post'
|
||||||
import PostTitle from '../templates/Post/Title'
|
import PostTitle from '../templates/Post/Title'
|
||||||
import styles from './PostTeaser.module.scss'
|
import {
|
||||||
|
post as stylePost,
|
||||||
|
empty,
|
||||||
|
title as styleTitle
|
||||||
|
} from './PostTeaser.module.css'
|
||||||
|
|
||||||
export const postTeaserQuery = graphql`
|
export const postTeaserQuery = graphql`
|
||||||
fragment PostTeaser on MarkdownRemark {
|
fragment PostTeaser on MarkdownRemark {
|
||||||
@ -42,21 +46,24 @@ export default function PostTeaser({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={styles.post}
|
className={stylePost}
|
||||||
to={slug}
|
to={slug}
|
||||||
onClick={toggleSearch && toggleSearch}
|
onClick={toggleSearch && toggleSearch}
|
||||||
>
|
>
|
||||||
{image ? (
|
{image ? (
|
||||||
<Image fluid={image.childImageSharp.fluid} alt={title} />
|
<Image
|
||||||
|
image={(image as any).childImageSharp.gatsbyImageData}
|
||||||
|
alt={title}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className={styles.empty} />
|
<span className={empty} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PostTitle
|
<PostTitle
|
||||||
title={title}
|
title={title}
|
||||||
date={hideDate ? null : date}
|
date={hideDate ? null : date}
|
||||||
updated={updated}
|
updated={updated}
|
||||||
className={styles.title}
|
className={styleTitle}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
|
61
src/components/molecules/RelatedPosts.module.css
Normal file
61
src/components/molecules/RelatedPosts.module.css
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
.title {
|
||||||
|
font-size: var(--font-size-h3);
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts ul {
|
||||||
|
display: grid;
|
||||||
|
gap: calc(var(--spacer) / 2);
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.relatedPosts ul {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: var(--spacer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts li {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts li::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts figure {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts img {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts a > div {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts a h4 {
|
||||||
|
color: var(--text-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.relatedPosts a:hover h4,
|
||||||
|
.relatedPosts a:focus h4 {
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--text-color-light);
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-left: calc(var(--spacer) / 2);
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: $font-size-h3;
|
|
||||||
margin-bottom: $spacer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.relatedPosts {
|
|
||||||
margin-top: -($spacer * 2);
|
|
||||||
|
|
||||||
@media (min-width: $screen-md) {
|
|
||||||
@include breakoutviewport;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: grid;
|
|
||||||
gap: $spacer / 2;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
@media (min-width: $screen-sm) {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: $spacer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
figure {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
color: $text-color-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
h4 {
|
|
||||||
color: $link-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
font-size: $font-size-mini;
|
|
||||||
display: inline-block;
|
|
||||||
color: $brand-grey-light;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin-left: $spacer / 2;
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { graphql, useStaticQuery } from 'gatsby'
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
import PostTeaser from './PostTeaser'
|
import PostTeaser from './PostTeaser'
|
||||||
import styles from './RelatedPosts.module.scss'
|
import { relatedPosts, title, button } from './RelatedPosts.module.css'
|
||||||
import { Post, Frontmatter } from '../../@types/Post'
|
import { Post, Frontmatter } from '../../@types/Post'
|
||||||
import { PhotoThumb } from '../templates/Photos'
|
import { PhotoThumb } from '../templates/Photos'
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ function postsWithDataFilter(
|
|||||||
posts: [{ node: Post }],
|
posts: [{ node: Post }],
|
||||||
key: keyof Frontmatter,
|
key: keyof Frontmatter,
|
||||||
valuesToFind: string[]
|
valuesToFind: string[]
|
||||||
) {
|
): { node: Post }[] {
|
||||||
const newArray = posts
|
const newArray = posts
|
||||||
.filter(({ node }: { node: Post }) => {
|
.filter(({ node }: { node: Post }) => {
|
||||||
const frontmatterKey = node.frontmatter[key] as []
|
const frontmatterKey = node.frontmatter[key] as []
|
||||||
@ -39,7 +39,7 @@ function postsWithDataFilter(
|
|||||||
return newArray
|
return newArray
|
||||||
}
|
}
|
||||||
|
|
||||||
function photosWithDataFilter(posts: []) {
|
function photosWithDataFilter(posts: [{ node: Post }]): { node: Post }[] {
|
||||||
const newArray = posts
|
const newArray = posts
|
||||||
.filter((post: { node: Post }) => {
|
.filter((post: { node: Post }) => {
|
||||||
const { fileAbsolutePath } = post.node
|
const { fileAbsolutePath } = post.node
|
||||||
@ -78,10 +78,10 @@ export default function RelatedPosts({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className={styles.relatedPosts}>
|
<aside className={relatedPosts}>
|
||||||
<h1 className={styles.title}>
|
<h1 className={title}>
|
||||||
Related {isPhotos ? 'Photos' : 'Posts'}{' '}
|
Related {isPhotos ? 'Photos' : 'Posts'}{' '}
|
||||||
<button className={styles.button} onClick={() => refreshPosts()}>
|
<button className={button} onClick={() => refreshPosts()}>
|
||||||
Refresh
|
Refresh
|
||||||
</button>
|
</button>
|
||||||
</h1>
|
</h1>
|
||||||
|
22
src/components/molecules/Search/SearchButton.module.css
Normal file
22
src/components/molecules/Search/SearchButton.module.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.searchButton {
|
||||||
|
padding: calc(var(--spacer) / 2);
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: calc(var(--spacer) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchButton:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchButton svg {
|
||||||
|
stroke: var(--text-color-light);
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchButton:hover svg,
|
||||||
|
.searchButton:focus svg {
|
||||||
|
stroke: var(--link-color);
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.searchButton {
|
|
||||||
padding: $spacer / 2;
|
|
||||||
vertical-align: middle;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
margin-right: $spacer / 4;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
stroke: $brand-grey-light;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
svg {
|
|
||||||
stroke: $brand-cyan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
svg {
|
|
||||||
stroke: darken($brand-cyan, 30%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,13 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './SearchButton.module.scss'
|
import { searchButton } from './SearchButton.module.css'
|
||||||
import Icon from '../../atoms/Icon'
|
import Icon from '../../atoms/Icon'
|
||||||
|
|
||||||
const SearchButton = (props: any): ReactElement => (
|
const SearchButton = ({ onClick }: { onClick: () => void }): ReactElement => (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
title="Search"
|
title="Search"
|
||||||
className={styles.searchButton}
|
className={searchButton}
|
||||||
{...props}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<Icon name="Search" />
|
<Icon name="Search" />
|
||||||
</button>
|
</button>
|
||||||
|
20
src/components/molecules/Search/SearchInput.module.css
Normal file
20
src/components/molecules/Search/SearchInput.module.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.searchInput::-webkit-search-cancel-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInputClose {
|
||||||
|
position: absolute;
|
||||||
|
right: calc(var(--spacer) / 2);
|
||||||
|
top: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInputClose svg {
|
||||||
|
stroke: var(--brand-grey-light);
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchInputClose:hover svg,
|
||||||
|
.searchInputClose:focus svg {
|
||||||
|
stroke: var(--link-color);
|
||||||
|
}
|
@ -1,28 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.searchInput {
|
|
||||||
composes: input from '../../atoms/Input.module.scss';
|
|
||||||
|
|
||||||
&::-webkit-search-cancel-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchInputClose {
|
|
||||||
position: absolute;
|
|
||||||
right: $spacer / 2;
|
|
||||||
top: $spacer / 2;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
stroke: $brand-grey-light;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
svg {
|
|
||||||
stroke: $link-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import Input from '../../atoms/Input'
|
import Input from '../../atoms/Input'
|
||||||
import Icon from '../../atoms/Icon'
|
import Icon from '../../atoms/Icon'
|
||||||
import styles from './SearchInput.module.scss'
|
import { searchInput, searchInputClose } from './SearchInput.module.css'
|
||||||
|
|
||||||
export default function SearchInput({
|
export default function SearchInput({
|
||||||
value,
|
value,
|
||||||
@ -15,7 +15,7 @@ export default function SearchInput({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
className={styles.searchInput}
|
className={searchInput}
|
||||||
type="search"
|
type="search"
|
||||||
placeholder="Search everything"
|
placeholder="Search everything"
|
||||||
autoFocus // eslint-disable-line
|
autoFocus // eslint-disable-line
|
||||||
@ -23,7 +23,7 @@ export default function SearchInput({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className={styles.searchInputClose}
|
className={searchInputClose}
|
||||||
onClick={onToggle}
|
onClick={onToggle}
|
||||||
title="Close search"
|
title="Close search"
|
||||||
>
|
>
|
||||||
|
72
src/components/molecules/Search/SearchResults.module.css
Normal file
72
src/components/molecules/Search/SearchResults.module.css
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
.searchResults {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--body-background-color);
|
||||||
|
animation: fadein 0.3s;
|
||||||
|
overflow: scroll;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
height: 91vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
composes: container from '../../Layout.module.css';
|
||||||
|
padding: var(--spacer) calc(var(--spacer) / 2);
|
||||||
|
margin-bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 60rem) {
|
||||||
|
.results {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.results li {
|
||||||
|
display: block;
|
||||||
|
flex: 0 0 48%;
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.results li {
|
||||||
|
flex-basis: 31%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.results li::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results img {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results a > div {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results a:hover h4,
|
||||||
|
.results a:focus h4 {
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadein {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
@ -1,79 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
@import 'mixins';
|
|
||||||
|
|
||||||
.searchResults {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 10;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba($body-background-color, 0.95);
|
|
||||||
backdrop-filter: blur(5px);
|
|
||||||
animation: fadein 0.3s;
|
|
||||||
overflow: scroll;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
height: 91vh;
|
|
||||||
|
|
||||||
:global(.dark) & {
|
|
||||||
background: rgba($body-background-color--dark, 0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
@include breakoutviewport;
|
|
||||||
|
|
||||||
padding: $spacer $spacer / 2;
|
|
||||||
margin-bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
@media (min-width: $screen-md) {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: block;
|
|
||||||
flex: 0 0 48%;
|
|
||||||
margin-bottom: $spacer;
|
|
||||||
|
|
||||||
@media (min-width: $screen-sm) {
|
|
||||||
flex-basis: 31%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
h4 {
|
|
||||||
color: $link-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadein {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,12 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import { graphql, useStaticQuery } from 'gatsby'
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
import Container from '../../atoms/Container'
|
|
||||||
import PostTeaser from '../PostTeaser'
|
import PostTeaser from '../PostTeaser'
|
||||||
import SearchResultsEmpty from './SearchResultsEmpty'
|
import SearchResultsEmpty from './SearchResultsEmpty'
|
||||||
import styles from './SearchResults.module.scss'
|
import {
|
||||||
|
searchResults,
|
||||||
|
results as styleResults
|
||||||
|
} from './SearchResults.module.css'
|
||||||
import { Post } from '../../../@types/Post'
|
import { Post } from '../../../@types/Post'
|
||||||
|
|
||||||
export interface Results {
|
export interface Results {
|
||||||
@ -35,10 +37,9 @@ function SearchResultsPure({
|
|||||||
toggleSearch(): void
|
toggleSearch(): void
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.searchResults}>
|
<div className={searchResults}>
|
||||||
<Container>
|
|
||||||
{results.length > 0 ? (
|
{results.length > 0 ? (
|
||||||
<ul>
|
<ul className={styleResults}>
|
||||||
{results.map((page: { slug: string }) =>
|
{results.map((page: { slug: string }) =>
|
||||||
posts
|
posts
|
||||||
.filter(
|
.filter(
|
||||||
@ -54,7 +55,6 @@ function SearchResultsPure({
|
|||||||
) : (
|
) : (
|
||||||
<SearchResultsEmpty searchQuery={searchQuery} results={results} />
|
<SearchResultsEmpty searchQuery={searchQuery} results={results} />
|
||||||
)}
|
)}
|
||||||
</Container>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
.empty {
|
||||||
|
padding-top: 15vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyMessage {
|
||||||
|
color: var(--text-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyMessageText {
|
||||||
|
margin-bottom: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyMessageText::after {
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
animation: ellipsis steps(4, end) 1s infinite;
|
||||||
|
|
||||||
|
/* ascii code for the ellipsis character */
|
||||||
|
content: '\2026';
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 101%;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ellipsis {
|
||||||
|
to {
|
||||||
|
width: 1rem;
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
padding-top: 15vh;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.emptyMessage {
|
|
||||||
color: $brand-grey-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.emptyMessageText {
|
|
||||||
margin-bottom: 0;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
overflow: hidden;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: bottom;
|
|
||||||
animation: ellipsis steps(4, end) 1s infinite;
|
|
||||||
content: '\2026'; // ascii code for the ellipsis character
|
|
||||||
width: 0;
|
|
||||||
position: absolute;
|
|
||||||
left: 101%;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ellipsis {
|
|
||||||
to {
|
|
||||||
width: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,9 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './SearchResultsEmpty.module.scss'
|
import {
|
||||||
|
empty,
|
||||||
|
emptyMessage,
|
||||||
|
emptyMessageText
|
||||||
|
} from './SearchResultsEmpty.module.css'
|
||||||
import { Results } from './SearchResults'
|
import { Results } from './SearchResults'
|
||||||
|
|
||||||
const SearchResultsEmpty = ({
|
const SearchResultsEmpty = ({
|
||||||
@ -9,9 +13,9 @@ const SearchResultsEmpty = ({
|
|||||||
searchQuery: string
|
searchQuery: string
|
||||||
results: Results[]
|
results: Results[]
|
||||||
}): ReactElement => (
|
}): ReactElement => (
|
||||||
<div className={styles.empty}>
|
<div className={empty}>
|
||||||
<header className={styles.emptyMessage}>
|
<header className={emptyMessage}>
|
||||||
<p className={styles.emptyMessageText}>
|
<p className={emptyMessageText}>
|
||||||
{searchQuery.length > 0 && results.length === 0
|
{searchQuery.length > 0 && results.length === 0
|
||||||
? 'No results found'
|
? 'No results found'
|
||||||
: 'Awaiting your input'}
|
: 'Awaiting your input'}
|
||||||
|
46
src/components/molecules/Search/index.module.css
Normal file
46
src/components/molecules/Search/index.module.css
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
.search {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(var(--spacer) / 2);
|
||||||
|
right: calc(var(--spacer) / 2);
|
||||||
|
top: calc(var(--spacer) / 4);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.search {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.appear,
|
||||||
|
.enter {
|
||||||
|
opacity: 0.01;
|
||||||
|
transform: translate3d(0, -100px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.appearActive,
|
||||||
|
.enterActive {
|
||||||
|
opacity: 1;
|
||||||
|
transition: 0.2s ease-out;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.exitActive {
|
||||||
|
opacity: 0.01;
|
||||||
|
transition: 0.2s ease-in;
|
||||||
|
transform: translate3d(0, -100px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.hasSearchOpen) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
@import 'variables';
|
|
||||||
|
|
||||||
.search {
|
|
||||||
position: absolute;
|
|
||||||
left: $spacer / 2;
|
|
||||||
right: $spacer / 2;
|
|
||||||
top: $spacer / 4;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: $screen-md) {
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.appear,
|
|
||||||
.enter {
|
|
||||||
opacity: 0.01;
|
|
||||||
transform: translate3d(0, -100px, 0);
|
|
||||||
|
|
||||||
&.appearActive,
|
|
||||||
&.enterActive {
|
|
||||||
opacity: 1;
|
|
||||||
transition: 0.2s ease-out;
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.exit {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
|
|
||||||
&.exitActive {
|
|
||||||
opacity: 0.01;
|
|
||||||
transition: 0.2s ease-in;
|
|
||||||
transform: translate3d(0, -100px, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.hasSearchOpen) {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user