1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-12-22 17:23:50 +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',
link: '/goodies'
link: '/tags/goodies'
}
]
}

View File

@ -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)

View File

@ -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())
}

View File

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

View File

@ -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 (
<Link className={classes} to={link}>
@ -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 (
<div className={styles.pagination}>
<div>{!isFirst && <PrevNext prevPagePath={prevPagePath} />}</div>
<div>
{Array.from({ length: numPages }, (_, i) => (
<PageNumber
key={shortid.generate()}
i={i}
current={currentPageNumber === i + 1}
/>
<PageNumber i={i} slug={slug} current={currentPageNumber === i + 1} />
))}
</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 {
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;
}
}

View File

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

View File

@ -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({
<Layout location={location}>
<SEO />
{location.pathname === '/' && <Featured />}
{tag && <h1 className={styles.archiveTitle}>#{tag}</h1>}
{currentPageNumber > 1 && (
<h1
className={styles.archiveTitle}
>{`Page ${currentPageNumber} / ${numPages}`}</h1>
{tag && (
<h1 className={styles.archiveTitle}>
<span>#</span>
{tag}
</h1>
)}
{numPages > 1 && currentPageNumber > 1 && (
<h2
className={styles.paginationTitle}
>{`Page ${currentPageNumber} / ${numPages}`}</h2>
)}
{PostsList}
{nextPage && nextPage > 1 && <Pagination pageContext={pageContext} />}
{numPages > 1 && <Pagination pageContext={pageContext} />}
</Layout>
)
}
export const indexQuery = graphql`
export const postsQuery = graphql`
query($tag: String, $skip: Int, $limit: Int) {
allMarkdownRemark(
filter: { frontmatter: { tags: { eq: $tag } } }