diff --git a/config.js b/config.js index 9afbf64f..ae3990b3 100644 --- a/config.js +++ b/config.js @@ -28,7 +28,7 @@ module.exports = { }, { title: 'Goodies', - link: '/goodies' + link: '/tags/goodies' } ] } diff --git a/gatsby-node.js b/gatsby-node.js index 08d79243..2a7190a0 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -7,7 +7,6 @@ const { generateRedirectPages } = require('./gatsby/createPages') const { generateJsonFeed } = require('./gatsby/feeds') -const { itemsPerPage } = require('./config') exports.onCreateNode = ({ node, actions, getNode, createNodeId }) => { // Markdown files @@ -26,7 +25,7 @@ exports.createPages = async ({ graphql, actions }) => { const result = await graphql(` { - allMarkdownRemark { + posts: allMarkdownRemark { edges { node { fields { @@ -38,19 +37,26 @@ exports.createPages = async ({ graphql, actions }) => { } } } + + tags: allMarkdownRemark { + group(field: frontmatter___tags) { + tag: fieldValue + totalCount + } + } } `) if (result.errors) throw result.errors - const posts = result.data.allMarkdownRemark.edges - const numPages = Math.ceil(posts.length / itemsPerPage) + const posts = result.data.posts.edges + const tags = result.data.tags.group // Generate posts & posts index - generatePostPages(createPage, posts, numPages) + generatePostPages(createPage, posts) // Generate tag pages - generateTagPages(createPage, posts, numPages) + generateTagPages(createPage, tags) // Create manual redirects generateRedirectPages(createRedirect) diff --git a/gatsby/createPages.js b/gatsby/createPages.js index 653a097e..9d7fd6e4 100644 --- a/gatsby/createPages.js +++ b/gatsby/createPages.js @@ -1,12 +1,29 @@ const path = require('path') const postsTemplate = path.resolve('src/templates/Posts.tsx') +const { itemsPerPage } = require('../config') const redirects = [ { f: '/feed', t: '/feed.xml' }, - { f: '/feed/', t: '/feed.xml' } + { f: '/feed/', t: '/feed.xml' }, + { f: '/goodies/', t: '/tags/goodies/' } ] -exports.generatePostPages = (createPage, posts, numPages) => { +function getPaginationData(i, numPages, slug) { + const currentPage = i + 1 + const prevPageNumber = currentPage <= 1 ? null : currentPage - 1 + const nextPageNumber = currentPage + 1 > numPages ? null : currentPage + 1 + const prevPagePath = prevPageNumber + ? prevPageNumber === 1 + ? slug + : `${slug}page/${prevPageNumber}/` + : null + const nextPagePath = nextPageNumber ? `${slug}page/${nextPageNumber}/` : null + const path = i === 0 ? slug : `${slug}page/${i + 1}` + + return { prevPagePath, nextPagePath, path } +} + +exports.generatePostPages = (createPage, posts) => { const postTemplate = path.resolve('src/templates/Post/index.tsx') // Create Post pages @@ -21,33 +38,59 @@ exports.generatePostPages = (createPage, posts, numPages) => { }) // Create paginated Blog index pages + const numPages = Math.ceil(posts.length / itemsPerPage) + const slug = `/` + Array.from({ length: numPages }).forEach((_, i) => { + const { prevPagePath, nextPagePath, path } = getPaginationData( + i, + numPages, + slug + ) + createPage({ - path: i === 0 ? '/' : `/page/${i + 1}`, + path, component: postsTemplate, context: { - limit: numPages, - skip: i * numPages, + slug, + limit: itemsPerPage, + skip: i * itemsPerPage, numPages, currentPageNumber: i + 1, - prevPage: i - 1, - nextPage: i + 2 + prevPagePath, + nextPagePath } }) }) } -exports.generateTagPages = (createPage, posts) => { - const tagList = arrayReducer(posts, 'tags') +exports.generateTagPages = (createPage, tags) => { + tags.forEach(({ tag, totalCount }) => { + // Create paginated tag pages + const numPages = Math.ceil(totalCount / itemsPerPage) + const slug = `/tags/${tag}/` - tagList.forEach(tag => { - if (tag === 'goodies') return + Array.from({ length: numPages }).forEach((_, i) => { + const { prevPagePath, nextPagePath, path } = getPaginationData( + i, + numPages, + slug + ) - // Create tag pages - createPage({ - path: `/tags/${tag}/`, - component: postsTemplate, - context: { tag } + createPage({ + path, + component: postsTemplate, + context: { + tag, + slug, + limit: itemsPerPage, + skip: i * itemsPerPage, + numPages, + currentPageNumber: i + 1, + prevPagePath, + nextPagePath + } + }) }) }) } @@ -61,21 +104,3 @@ exports.generateRedirectPages = createRedirect => { }) }) } - -// -// ---------------------- -// -// https://www.adamjberkowitz.com/tags-and-categories-in-gatsby-js/ -const arrayReducer = (postsArray, type) => { - return (postsArray = postsArray - .map(({ node }) => { - return node.frontmatter[type] - }) - .reduce((a, b) => { - return a.concat(b) - }, []) - .filter((type, index, array) => { - return array.indexOf(type) === index - }) - .sort()) -} diff --git a/src/components/molecules/Pagination.module.scss b/src/components/molecules/Pagination.module.scss index 2e48ef90..de982858 100644 --- a/src/components/molecules/Pagination.module.scss +++ b/src/components/molecules/Pagination.module.scss @@ -4,7 +4,7 @@ display: flex; margin-top: $spacer * 2; margin-bottom: $spacer; - justify-content: center; + justify-content: space-between; > div { &:first-child { @@ -40,4 +40,8 @@ pointer-events: none; border: 1px solid darken($brand-grey-dimmed, 5%); color: $brand-grey-light; + + :global(.dark) & { + border-color: darken($brand-grey-light, 15%); + } } diff --git a/src/components/molecules/Pagination.tsx b/src/components/molecules/Pagination.tsx index b1fc066a..89469d25 100644 --- a/src/components/molecules/Pagination.tsx +++ b/src/components/molecules/Pagination.tsx @@ -1,11 +1,18 @@ import React from 'react' import { Link } from 'gatsby' -import shortid from 'shortid' import styles from './Pagination.module.scss' -function PageNumber({ i, current }: { i: number; current?: boolean }) { +const PageNumber = ({ + i, + slug, + current +}: { + i: number + slug: string + current?: boolean +}) => { const classes = current ? styles.current : styles.number - const link = i === 0 ? '' : `/page/${i + 1}` + const link = i === 0 ? slug : `${slug}page/${i + 1}` return ( @@ -36,28 +43,29 @@ export default function Pagination({ pageContext }: { pageContext: { + slug: string currentPageNumber: number numPages: number - prevPage?: number - nextPage?: number + prevPagePath?: string + nextPagePath?: string } }) { - const { currentPageNumber, numPages, prevPage, nextPage } = pageContext + const { + slug, + currentPageNumber, + numPages, + prevPagePath, + nextPagePath + } = pageContext const isFirst = currentPageNumber === 1 const isLast = currentPageNumber === numPages - const prevPagePath = currentPageNumber === 2 ? '/' : `/page/${prevPage}` - const nextPagePath = `/page/${nextPage}` return (
{!isFirst && }
{Array.from({ length: numPages }, (_, i) => ( - + ))}
{!isLast && }
diff --git a/src/pages/goodies.module.scss b/src/pages/goodies.module.scss deleted file mode 100644 index 814d77af..00000000 --- a/src/pages/goodies.module.scss +++ /dev/null @@ -1,37 +0,0 @@ -@import 'variables'; -@import 'mixins'; - -.image { - @include breakoutviewport; - - max-width: none; -} - -.goodie { - @include divider; - - padding-top: $spacer; - padding-bottom: $spacer * 2; - - &:first-of-type { - padding-top: 0; - } - - &:last-child { - border-bottom: 0; - padding-bottom: 0; - - &::before { - display: none; - } - } - - a { - display: block; - } -} - -.title { - margin-top: 0; - margin-bottom: $spacer; -} diff --git a/src/pages/goodies.tsx b/src/pages/goodies.tsx deleted file mode 100644 index 62d70744..00000000 --- a/src/pages/goodies.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react' -import { graphql, Link } from 'gatsby' -import PostImage from '../templates/Post/PostImage' -import Page from '../templates/Page' -import styles from './goodies.module.scss' -import { Post } from '../@types/Post' - -const page = { - frontmatter: { - title: 'Goodies', - description: - 'Goodies released by designer & developer Matthias Kretschmann.' - } -} - -const GoodiesThumb = ({ post }: { post: Post }) => { - const { title, image } = post.frontmatter - const { slug } = post.fields - - return ( -
- {image && ( - -

{title}

- - - )} -
- ) -} - -export default function Goodies({ - data, - location -}: { - data: { goodies: { edges: [{ node: Post }] } } - location: Location -}) { - return ( - - {data.goodies.edges.map(({ node }) => ( - - ))} - - ) -} - -export const goodiesQuery = graphql` - query { - goodies: allMarkdownRemark( - filter: { frontmatter: { tags: { eq: "goodies" } } } - sort: { order: DESC, fields: [fields___date] } - ) { - edges { - node { - id - frontmatter { - title - image { - childImageSharp { - ...ImageFluid - } - } - } - fields { - slug - } - } - } - } - } -` diff --git a/src/templates/Post/PostMeta.module.scss b/src/templates/Post/PostMeta.module.scss index 83fa3339..793429a0 100644 --- a/src/templates/Post/PostMeta.module.scss +++ b/src/templates/Post/PostMeta.module.scss @@ -70,7 +70,7 @@ } .tag { - color: $text-color; + color: $brand-grey-light; margin-left: $spacer / 2; margin-right: $spacer / 2; margin-bottom: $spacer / 2; @@ -78,16 +78,8 @@ display: inline-block; &::before { - color: $brand-grey-light; content: '#'; margin-right: 1px; - } - - :global(.dark) & { - color: $brand-grey-light; - - &::before { - color: $brand-grey; - } + opacity: 0.65; } } diff --git a/src/templates/Posts.module.scss b/src/templates/Posts.module.scss index 913835ba..417aac1c 100644 --- a/src/templates/Posts.module.scss +++ b/src/templates/Posts.module.scss @@ -15,7 +15,20 @@ } .archiveTitle { + @include divider; + font-size: $font-size-h3; margin-top: 0; margin-bottom: 0; + padding-bottom: $spacer * $line-height; + + span { + color: $brand-grey-light; + padding-right: $spacer / 12; + font-size: $font-size-base; + } +} + +.paginationTitle { + composes: archiveTitle; } diff --git a/src/templates/Posts.tsx b/src/templates/Posts.tsx index 740a25cf..13fe2d3d 100644 --- a/src/templates/Posts.tsx +++ b/src/templates/Posts.tsx @@ -23,13 +23,13 @@ export default function Posts({ location: Location pageContext: { tag: string + slug: string currentPageNumber: number numPages: number - nextPage: number } }) { const edges = data.allMarkdownRemark.edges - const { tag, currentPageNumber, numPages, nextPage } = pageContext + const { tag, currentPageNumber, numPages } = pageContext const PostsList = edges.map(({ node }: { node: Post }) => { const { type, linkurl, title, image } = node.frontmatter @@ -75,19 +75,24 @@ export default function Posts({ {location.pathname === '/' && } - {tag &&

#{tag}

} - {currentPageNumber > 1 && ( -

{`Page ${currentPageNumber} / ${numPages}`}

+ {tag && ( +

+ # + {tag} +

+ )} + {numPages > 1 && currentPageNumber > 1 && ( +

{`Page ${currentPageNumber} / ${numPages}`}

)} {PostsList} - {nextPage && nextPage > 1 && } + {numPages > 1 && }
) } -export const indexQuery = graphql` +export const postsQuery = graphql` query($tag: String, $skip: Int, $limit: Int) { allMarkdownRemark( filter: { frontmatter: { tags: { eq: $tag } } }