0db69983fe
The pager widget will now show a dropdown menu of overflow pages in the middle. This allows easy access to the First and Last pages and an ability to select from any of the middle pages to jump to quickly.
158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
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 {
|
|
if a <= 0 {
|
|
p.lastPage = true
|
|
a = 1
|
|
}
|
|
p.Page = a
|
|
} 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))))
|
|
}
|
|
|
|
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
|
|
}
|