pagination.go
package helpers
import (
"net/http"
"strconv"
)
// DefaultPageSize is the default number of items per page.
const DefaultPageSize = 50
// Pagination holds pagination state for list views.
type Pagination struct {
Page int // Current page (1-indexed)
PageSize int // Items per page
TotalItems int // Total number of items
TotalPages int // Total number of pages
HasPrev bool // Has previous page
HasNext bool // Has next page
Offset int // SQL offset
}
// NewPagination creates pagination state from a request.
func NewPagination(r *http.Request, totalItems int) *Pagination {
return NewPaginationWithSize(r, totalItems, DefaultPageSize)
}
// NewPaginationWithSize creates pagination state with custom page size.
func NewPaginationWithSize(r *http.Request, totalItems, pageSize int) *Pagination {
page := 1
if p := r.URL.Query().Get("page"); p != "" {
if parsed, err := strconv.Atoi(p); err == nil && parsed > 0 {
page = parsed
}
}
totalPages := (totalItems + pageSize - 1) / pageSize
if totalPages < 1 {
totalPages = 1
}
// Clamp page to valid range
if page > totalPages {
page = totalPages
}
return &Pagination{
Page: page,
PageSize: pageSize,
TotalItems: totalItems,
TotalPages: totalPages,
HasPrev: page > 1,
HasNext: page < totalPages,
Offset: (page - 1) * pageSize,
}
}
// PrevPage returns the previous page number.
func (p *Pagination) PrevPage() int {
if p.Page > 1 {
return p.Page - 1
}
return 1
}
// NextPage returns the next page number.
func (p *Pagination) NextPage() int {
if p.Page < p.TotalPages {
return p.Page + 1
}
return p.TotalPages
}
// Pages returns a list of page numbers to display in pagination.
// Shows up to 5 pages around the current page.
func (p *Pagination) Pages() []int {
if p.TotalPages <= 7 {
// Show all pages if 7 or fewer
pages := make([]int, p.TotalPages)
for i := range pages {
pages[i] = i + 1
}
return pages
}
// Show: 1 ... (current-1) current (current+1) ... last
pages := []int{1}
start := max(2, p.Page-1)
end := min(p.TotalPages-1, p.Page+1)
if start > 2 {
pages = append(pages, -1) // -1 represents "..."
}
for i := start; i <= end; i++ {
pages = append(pages, i)
}
if end < p.TotalPages-1 {
pages = append(pages, -1)
}
pages = append(pages, p.TotalPages)
return pages
}