1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-12-23 01:30:01 +01:00

Merge pull request #183 from kremalicious/feature/pagination

pagination refactor
This commit is contained in:
Matthias Kretschmann 2019-11-06 21:07:22 +01:00 committed by GitHub
commit 6723265523
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 127 additions and 183 deletions

View File

@ -28,7 +28,7 @@ module.exports = {
}, },
{ {
title: 'Goodies', title: 'Goodies',
link: '/goodies' link: '/tags/goodies'
} }
] ]
} }

View File

@ -7,7 +7,6 @@ const {
generateRedirectPages generateRedirectPages
} = require('./gatsby/createPages') } = require('./gatsby/createPages')
const { generateJsonFeed } = require('./gatsby/feeds') const { generateJsonFeed } = require('./gatsby/feeds')
const { itemsPerPage } = require('./config')
exports.onCreateNode = ({ node, actions, getNode, createNodeId }) => { exports.onCreateNode = ({ node, actions, getNode, createNodeId }) => {
// Markdown files // Markdown files
@ -26,7 +25,7 @@ exports.createPages = async ({ graphql, actions }) => {
const result = await graphql(` const result = await graphql(`
{ {
allMarkdownRemark { posts: allMarkdownRemark {
edges { edges {
node { node {
fields { 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 if (result.errors) throw result.errors
const posts = result.data.allMarkdownRemark.edges const posts = result.data.posts.edges
const numPages = Math.ceil(posts.length / itemsPerPage) const tags = result.data.tags.group
// Generate posts & posts index // Generate posts & posts index
generatePostPages(createPage, posts, numPages) generatePostPages(createPage, posts)
// Generate tag pages // Generate tag pages
generateTagPages(createPage, posts, numPages) generateTagPages(createPage, tags)
// Create manual redirects // Create manual redirects
generateRedirectPages(createRedirect) generateRedirectPages(createRedirect)

View File

@ -1,12 +1,29 @@
const path = require('path') const path = require('path')
const postsTemplate = path.resolve('src/templates/Posts.tsx') const postsTemplate = path.resolve('src/templates/Posts.tsx')
const { itemsPerPage } = require('../config')
const redirects = [ const redirects = [
{ f: '/feed', t: '/feed.xml' }, { 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') const postTemplate = path.resolve('src/templates/Post/index.tsx')
// Create Post pages // Create Post pages
@ -21,33 +38,59 @@ exports.generatePostPages = (createPage, posts, numPages) => {
}) })
// Create paginated Blog index pages // Create paginated Blog index pages
const numPages = Math.ceil(posts.length / itemsPerPage)
const slug = `/`
Array.from({ length: numPages }).forEach((_, i) => { Array.from({ length: numPages }).forEach((_, i) => {
const { prevPagePath, nextPagePath, path } = getPaginationData(
i,
numPages,
slug
)
createPage({ createPage({
path: i === 0 ? '/' : `/page/${i + 1}`, path,
component: postsTemplate, component: postsTemplate,
context: { context: {
limit: numPages, slug,
skip: i * numPages, limit: itemsPerPage,
skip: i * itemsPerPage,
numPages, numPages,
currentPageNumber: i + 1, currentPageNumber: i + 1,
prevPage: i - 1, prevPagePath,
nextPage: i + 2 nextPagePath
} }
}) })
}) })
} }
exports.generateTagPages = (createPage, posts) => { exports.generateTagPages = (createPage, tags) => {
const tagList = arrayReducer(posts, 'tags') tags.forEach(({ tag, totalCount }) => {
// Create paginated tag pages
const numPages = Math.ceil(totalCount / itemsPerPage)
const slug = `/tags/${tag}/`
tagList.forEach(tag => { Array.from({ length: numPages }).forEach((_, i) => {
if (tag === 'goodies') return const { prevPagePath, nextPagePath, path } = getPaginationData(
i,
numPages,
slug
)
// Create tag pages
createPage({ createPage({
path: `/tags/${tag}/`, path,
component: postsTemplate, component: postsTemplate,
context: { tag } 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())
}

View File

@ -4,7 +4,7 @@
display: flex; display: flex;
margin-top: $spacer * 2; margin-top: $spacer * 2;
margin-bottom: $spacer; margin-bottom: $spacer;
justify-content: center; justify-content: space-between;
> div { > div {
&:first-child { &:first-child {
@ -40,4 +40,8 @@
pointer-events: none; pointer-events: none;
border: 1px solid darken($brand-grey-dimmed, 5%); border: 1px solid darken($brand-grey-dimmed, 5%);
color: $brand-grey-light; color: $brand-grey-light;
:global(.dark) & {
border-color: darken($brand-grey-light, 15%);
}
} }

View File

@ -1,11 +1,18 @@
import React from 'react' import React from 'react'
import { Link } from 'gatsby' import { Link } from 'gatsby'
import shortid from 'shortid'
import styles from './Pagination.module.scss' 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 classes = current ? styles.current : styles.number
const link = i === 0 ? '' : `/page/${i + 1}` const link = i === 0 ? slug : `${slug}page/${i + 1}`
return ( return (
<Link className={classes} to={link}> <Link className={classes} to={link}>
@ -36,28 +43,29 @@ export default function Pagination({
pageContext pageContext
}: { }: {
pageContext: { pageContext: {
slug: string
currentPageNumber: number currentPageNumber: number
numPages: number numPages: number
prevPage?: number prevPagePath?: string
nextPage?: number nextPagePath?: string
} }
}) { }) {
const { currentPageNumber, numPages, prevPage, nextPage } = pageContext const {
slug,
currentPageNumber,
numPages,
prevPagePath,
nextPagePath
} = pageContext
const isFirst = currentPageNumber === 1 const isFirst = currentPageNumber === 1
const isLast = currentPageNumber === numPages const isLast = currentPageNumber === numPages
const prevPagePath = currentPageNumber === 2 ? '/' : `/page/${prevPage}`
const nextPagePath = `/page/${nextPage}`
return ( return (
<div className={styles.pagination}> <div className={styles.pagination}>
<div>{!isFirst && <PrevNext prevPagePath={prevPagePath} />}</div> <div>{!isFirst && <PrevNext prevPagePath={prevPagePath} />}</div>
<div> <div>
{Array.from({ length: numPages }, (_, i) => ( {Array.from({ length: numPages }, (_, i) => (
<PageNumber <PageNumber i={i} slug={slug} current={currentPageNumber === i + 1} />
key={shortid.generate()}
i={i}
current={currentPageNumber === i + 1}
/>
))} ))}
</div> </div>
<div>{!isLast && <PrevNext nextPagePath={nextPagePath} />}</div> <div>{!isLast && <PrevNext nextPagePath={nextPagePath} />}</div>

View File

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

View File

@ -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 (
<article className={styles.goodie}>
{image && (
<Link to={slug}>
<h1 className={styles.title}>{title}</h1>
<PostImage fluid={image.childImageSharp.fluid} alt={title} />
</Link>
)}
</article>
)
}
export default function Goodies({
data,
location
}: {
data: { goodies: { edges: [{ node: Post }] } }
location: Location
}) {
return (
<Page title={page.frontmatter.title} post={page} location={location}>
{data.goodies.edges.map(({ node }) => (
<GoodiesThumb key={node.id} post={node} />
))}
</Page>
)
}
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
}
}
}
}
}
`

View File

@ -70,7 +70,7 @@
} }
.tag { .tag {
color: $text-color; color: $brand-grey-light;
margin-left: $spacer / 2; margin-left: $spacer / 2;
margin-right: $spacer / 2; margin-right: $spacer / 2;
margin-bottom: $spacer / 2; margin-bottom: $spacer / 2;
@ -78,16 +78,8 @@
display: inline-block; display: inline-block;
&::before { &::before {
color: $brand-grey-light;
content: '#'; content: '#';
margin-right: 1px; margin-right: 1px;
} opacity: 0.65;
:global(.dark) & {
color: $brand-grey-light;
&::before {
color: $brand-grey;
}
} }
} }

View File

@ -15,7 +15,20 @@
} }
.archiveTitle { .archiveTitle {
@include divider;
font-size: $font-size-h3; font-size: $font-size-h3;
margin-top: 0; margin-top: 0;
margin-bottom: 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;
} }

View File

@ -23,13 +23,13 @@ export default function Posts({
location: Location location: Location
pageContext: { pageContext: {
tag: string tag: string
slug: string
currentPageNumber: number currentPageNumber: number
numPages: number numPages: number
nextPage: number
} }
}) { }) {
const edges = data.allMarkdownRemark.edges const edges = data.allMarkdownRemark.edges
const { tag, currentPageNumber, numPages, nextPage } = pageContext const { tag, currentPageNumber, numPages } = pageContext
const PostsList = edges.map(({ node }: { node: Post }) => { const PostsList = edges.map(({ node }: { node: Post }) => {
const { type, linkurl, title, image } = node.frontmatter const { type, linkurl, title, image } = node.frontmatter
@ -75,19 +75,24 @@ export default function Posts({
<Layout location={location}> <Layout location={location}>
<SEO /> <SEO />
{location.pathname === '/' && <Featured />} {location.pathname === '/' && <Featured />}
{tag && <h1 className={styles.archiveTitle}>#{tag}</h1>} {tag && (
{currentPageNumber > 1 && ( <h1 className={styles.archiveTitle}>
<h1 <span>#</span>
className={styles.archiveTitle} {tag}
>{`Page ${currentPageNumber} / ${numPages}`}</h1> </h1>
)}
{numPages > 1 && currentPageNumber > 1 && (
<h2
className={styles.paginationTitle}
>{`Page ${currentPageNumber} / ${numPages}`}</h2>
)} )}
{PostsList} {PostsList}
{nextPage && nextPage > 1 && <Pagination pageContext={pageContext} />} {numPages > 1 && <Pagination pageContext={pageContext} />}
</Layout> </Layout>
) )
} }
export const indexQuery = graphql` export const postsQuery = graphql`
query($tag: String, $skip: Int, $limit: Int) { query($tag: String, $skip: Int, $limit: Int) {
allMarkdownRemark( allMarkdownRemark(
filter: { frontmatter: { tags: { eq: $tag } } } filter: { frontmatter: { tags: { eq: $tag } } }