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