feat: add pagination to generated site post list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Leon Mika 2026-03-22 14:37:42 +11:00
parent 550ebf728a
commit 30884372d6
4 changed files with 74 additions and 10 deletions

View file

@ -6,3 +6,9 @@
{{ template "_post_meta.html" . }} {{ template "_post_meta.html" . }}
</div> </div>
{{ end }} {{ end }}
{{ if or .PrevURL .NextURL }}
<nav class="pagination">
{{ if .PrevURL }}<a href="{{ .PrevURL }}">← Newer posts</a>{{ end }}
{{ if .NextURL }}<a href="{{ .NextURL }}">Older posts →</a>{{ end }}
</nav>
{{ end }}

View file

@ -122,7 +122,8 @@ func (b *Builder) BuildSite(outDir string) error {
} }
func (b *Builder) renderPostListWithCategories(bctx buildContext, ctx context.Context) error { func (b *Builder) renderPostListWithCategories(bctx buildContext, ctx context.Context) error {
var posts []postSingleData // Collect all posts
var allPosts []postSingleData
for mp := range b.site.PostIter(ctx) { for mp := range b.site.PostIter(ctx) {
post, err := mp.Get() post, err := mp.Get()
if err != nil { if err != nil {
@ -132,17 +133,70 @@ func (b *Builder) renderPostListWithCategories(bctx buildContext, ctx context.Co
if err != nil { if err != nil {
return err return err
} }
posts = append(posts, rp) allPosts = append(allPosts, rp)
} }
pl := postListData{ postsPerPage := b.site.PostsPerPage
commonData: commonData{Site: b.site}, if postsPerPage < 1 {
Posts: posts, postsPerPage = 10
} }
return b.createAtPath(bctx, "", func(f io.Writer) error { totalPages := (len(allPosts) + postsPerPage - 1) / postsPerPage
return b.renderTemplate(f, tmplNamePostList, pl) if totalPages < 1 {
}) totalPages = 1
}
for page := 1; page <= totalPages; page++ {
start := (page - 1) * postsPerPage
end := start + postsPerPage
if end > len(allPosts) {
end = len(allPosts)
}
pageInfo := models.PageInfo{
CurrentPage: page,
TotalPages: totalPages,
PostsPerPage: postsPerPage,
}
var prevURL, nextURL string
if page > 1 {
if page == 2 {
prevURL = "/posts/"
} else {
prevURL = fmt.Sprintf("/posts/page/%d/", page-1)
}
}
if page < totalPages {
nextURL = fmt.Sprintf("/posts/page/%d/", page+1)
}
pl := postListData{
commonData: commonData{Site: b.site},
Posts: allPosts[start:end],
PageInfo: pageInfo,
PrevURL: prevURL,
NextURL: nextURL,
}
// Page 1 renders at both root and /posts/
var paths []string
if page == 1 {
paths = []string{"", "/posts"}
} else {
paths = []string{fmt.Sprintf("/posts/page/%d", page)}
}
for _, path := range paths {
if err := b.createAtPath(bctx, path, func(f io.Writer) error {
return b.renderTemplate(f, tmplNamePostList, pl)
}); err != nil {
return err
}
}
}
return nil
} }
func (b *Builder) renderFeeds(ctx buildContext, postIter iter.Seq[models.Maybe[*models.Post]], opts feedOptions) error { func (b *Builder) renderFeeds(ctx buildContext, postIter iter.Seq[models.Maybe[*models.Post]], opts feedOptions) error {

View file

@ -38,6 +38,7 @@ func TestBuilder_BuildSite(t *testing.T) {
} }
site := pubmodel.Site{ site := pubmodel.Site{
Site: models.Site{PostsPerPage: 10},
BaseURL: "https://example.com", BaseURL: "https://example.com",
PostIter: func(ctx context.Context) iter.Seq[models.Maybe[*models.Post]] { PostIter: func(ctx context.Context) iter.Seq[models.Maybe[*models.Post]] {
return func(yield func(models.Maybe[*models.Post]) bool) { return func(yield func(models.Maybe[*models.Post]) bool) {

View file

@ -61,7 +61,10 @@ type postSingleData struct {
type postListData struct { type postListData struct {
commonData commonData
Posts []postSingleData Posts []postSingleData
PageInfo models.PageInfo
PrevURL string
NextURL string
} }
type layoutData struct { type layoutData struct {