website/pkg/models/pagination.go

158 lines
3.5 KiB
Go
Raw Normal View History

package models
import (
"fmt"
"math"
"math/rand"
"net/http"
"strconv"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/encryption"
)
// Pagination result object.
type Pagination struct {
Page int // provide <0 to mean "last page"
PerPage int
Total int64
Sort string
// privates
lastPage bool
}
// Load the page from form or query parameters.
func (p *Pagination) ParsePage(r *http.Request) {
raw := r.FormValue("page")
a, err := strconv.Atoi(raw)
if err == nil {
2022-08-14 00:42:42 +00:00
if a <= 0 {
p.lastPage = true
a = 1
}
p.Page = a
2022-08-14 00:42:42 +00:00
} else {
p.Page = 1
}
}
// Page for Iter.
type Page struct {
Page int // label to show in the button
IsCurrent bool // highlight the currently selected page button
// this "button" is a drop-down menu of page numbers in the middle
IsOverflow bool
overflowFrom int
overflowTo int
}
// Iter the pages, for templates.
//
// If there are fewer than 7 pages (configurable) this returns a simple slice of buttons
// for each of the 7 pages to be drawn. If there are a LOT of pages, the middle button of
// the pager should be a drop-down menu to pick from the pages within.
func (p *Pagination) Iter() []Page {
var (
pages = []Page{}
total = p.Pages()
)
for i := 1; i <= total; i++ {
pages = append(pages, Page{
Page: i,
IsCurrent: i == p.Page,
})
}
// Do we have A LOT of pages?
if len(pages) > config.PagerButtonLimit+1 {
// The left half of the buttons should be pages 1 thru N.
// The right half are the final pages M thru Last.
// In the middle will be the overflow drop-down of middle pages.
var (
endLength = config.PagerButtonLimit / 2
start = endLength + 1
end = len(pages) - endLength
overflow = Page{
Page: start,
IsCurrent: p.Page >= start && p.Page <= end,
IsOverflow: true,
overflowFrom: start,
overflowTo: end,
}
result = []Page{}
)
// If we are currently selected on an overflow page, set the label to match.
if overflow.IsCurrent {
overflow.Page = p.Page
}
result = pages[:endLength]
result = append(result, overflow)
result = append(result, pages[end:]...)
return result
}
return pages
}
// IterOverflow: if the Page represents an overflow drop-down menu, iterate the members of the menu
// for easy template integration.
func (p Page) IterOverflow() []Page {
var result = []Page{}
for i := p.overflowFrom; i <= p.overflowTo; i++ {
result = append(result, Page{
Page: i,
IsCurrent: p.Page == i,
})
}
return result
}
// UniqueSerialID will return a unique JavaScript ID.
//
// It is used in front-end pages such as for Pagination drop-down menus, which may appear multiple times
// on a page and each use needs a unique ID attribute to connect the button to the dropdown.
func (p *Pagination) UniqueSerialID() string {
return encryption.Hash([]byte(fmt.Sprintf("%d", rand.Intn(9000000))))
}
2023-06-16 02:40:40 +00:00
func (p Pagination) Pages() int {
if p.PerPage == 0 {
return 0
}
return int(math.Ceil(float64(p.Total) / float64(p.PerPage)))
}
func (p *Pagination) GetOffset() int {
// Are we looking for the FINAL page?
if p.lastPage && p.Pages() >= 1 {
p.Page = p.Pages()
}
return (p.Page - 1) * p.PerPage
}
func (p *Pagination) HasNext() bool {
return p.Page < p.Pages()
}
func (p *Pagination) HasPrevious() bool {
return p.Page > 1
}
func (p *Pagination) Next() int {
if p.Page >= p.Pages() {
return p.Pages()
}
return p.Page + 1
}
func (p *Pagination) Previous() int {
if p.Page > 1 {
return p.Page - 1
}
return 1
}