mirror of
https://github.com/kremalicious/blog.git
synced 2024-12-22 17:23:50 +01:00
pagination refactor
This commit is contained in:
parent
92507e4bd1
commit
c2c38346ad
@ -28,7 +28,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
title: 'Goodies',
|
||||
link: '/goodies'
|
||||
link: '/tags/goodies'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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%);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 } } }
|
||||
|
Loading…
Reference in New Issue
Block a user