code.gitea.io/gitea@v1.19.3/modules/paginator/paginator.go (about) 1 // Copyright 2022 The Gitea Authors. 2 // Copyright 2015 https://github.com/unknwon. Licensed under the Apache License, Version 2.0 3 // SPDX-License-Identifier: Apache-2.0 4 5 package paginator 6 7 /* 8 In template: 9 10 ```html 11 {{if not .Page.IsFirst}}[First](1){{end}} 12 {{if .Page.HasPrevious}}[Previous]({{.Page.Previous}}){{end}} 13 14 {{range .Page.Pages}} 15 {{if eq .Num -1}} 16 ... 17 {{else}} 18 {{.Num}}{{if .IsCurrent}}(current){{end}} 19 {{end}} 20 {{end}} 21 22 {{if .Page.HasNext}}[Next]({{.Page.Next}}){{end}} 23 {{if not .Page.IsLast}}[Last]({{.Page.TotalPages}}){{end}} 24 ``` 25 26 Output: 27 28 ``` 29 [First](1) [Previous](2) ... 2 3(current) 4 ... [Next](4) [Last](5) 30 ``` 31 */ 32 33 // Paginator represents a set of results of pagination calculations. 34 type Paginator struct { 35 total int // total rows count 36 pagingNum int // how many rows in one page 37 current int // current page number 38 numPages int // how many pages to show on the UI 39 } 40 41 // New initialize a new pagination calculation and returns a Paginator as result. 42 func New(total, pagingNum, current, numPages int) *Paginator { 43 if pagingNum <= 0 { 44 pagingNum = 1 45 } 46 if current <= 0 { 47 current = 1 48 } 49 p := &Paginator{total, pagingNum, current, numPages} 50 if p.current > p.TotalPages() { 51 p.current = p.TotalPages() 52 } 53 return p 54 } 55 56 // IsFirst returns true if current page is the first page. 57 func (p *Paginator) IsFirst() bool { 58 return p.current == 1 59 } 60 61 // HasPrevious returns true if there is a previous page relative to current page. 62 func (p *Paginator) HasPrevious() bool { 63 return p.current > 1 64 } 65 66 func (p *Paginator) Previous() int { 67 if !p.HasPrevious() { 68 return p.current 69 } 70 return p.current - 1 71 } 72 73 // HasNext returns true if there is a next page relative to current page. 74 func (p *Paginator) HasNext() bool { 75 return p.total > p.current*p.pagingNum 76 } 77 78 func (p *Paginator) Next() int { 79 if !p.HasNext() { 80 return p.current 81 } 82 return p.current + 1 83 } 84 85 // IsLast returns true if current page is the last page. 86 func (p *Paginator) IsLast() bool { 87 if p.total == 0 { 88 return true 89 } 90 return p.total > (p.current-1)*p.pagingNum && !p.HasNext() 91 } 92 93 // Total returns number of total rows. 94 func (p *Paginator) Total() int { 95 return p.total 96 } 97 98 // TotalPages returns number of total pages. 99 func (p *Paginator) TotalPages() int { 100 if p.total == 0 { 101 return 1 102 } 103 return (p.total + p.pagingNum - 1) / p.pagingNum 104 } 105 106 // Current returns current page number. 107 func (p *Paginator) Current() int { 108 return p.current 109 } 110 111 // PagingNum returns number of page size. 112 func (p *Paginator) PagingNum() int { 113 return p.pagingNum 114 } 115 116 // Page presents a page in the paginator. 117 type Page struct { 118 num int 119 isCurrent bool 120 } 121 122 func (p *Page) Num() int { 123 return p.num 124 } 125 126 func (p *Page) IsCurrent() bool { 127 return p.isCurrent 128 } 129 130 func getMiddleIdx(numPages int) int { 131 return (numPages + 1) / 2 132 } 133 134 // Pages returns a list of nearby page numbers relative to current page. 135 // If value is -1 means "..." that more pages are not showing. 136 func (p *Paginator) Pages() []*Page { 137 if p.numPages == 0 { 138 return []*Page{} 139 } else if p.numPages == 1 && p.TotalPages() == 1 { 140 // Only show current page. 141 return []*Page{{1, true}} 142 } 143 144 // Total page number is less or equal. 145 if p.TotalPages() <= p.numPages { 146 pages := make([]*Page, p.TotalPages()) 147 for i := range pages { 148 pages[i] = &Page{i + 1, i+1 == p.current} 149 } 150 return pages 151 } 152 153 numPages := p.numPages 154 offsetIdx := 0 155 hasMoreNext := false 156 157 // Check more previous and next pages. 158 previousNum := getMiddleIdx(p.numPages) - 1 159 if previousNum > p.current-1 { 160 previousNum -= previousNum - (p.current - 1) 161 } 162 nextNum := p.numPages - previousNum - 1 163 if p.current+nextNum > p.TotalPages() { 164 delta := nextNum - (p.TotalPages() - p.current) 165 nextNum -= delta 166 previousNum += delta 167 } 168 169 offsetVal := p.current - previousNum 170 if offsetVal > 1 { 171 numPages++ 172 offsetIdx = 1 173 } 174 175 if p.current+nextNum < p.TotalPages() { 176 numPages++ 177 hasMoreNext = true 178 } 179 180 pages := make([]*Page, numPages) 181 182 // There are more previous pages. 183 if offsetIdx == 1 { 184 pages[0] = &Page{-1, false} 185 } 186 // There are more next pages. 187 if hasMoreNext { 188 pages[len(pages)-1] = &Page{-1, false} 189 } 190 191 // Check previous pages. 192 for i := 0; i < previousNum; i++ { 193 pages[offsetIdx+i] = &Page{i + offsetVal, false} 194 } 195 196 pages[offsetIdx+previousNum] = &Page{p.current, true} 197 198 // Check next pages. 199 for i := 1; i <= nextNum; i++ { 200 pages[offsetIdx+previousNum+i] = &Page{p.current + i, false} 201 } 202 203 return pages 204 }