gitea源码

paginator.go 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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. package paginator
  5. import "code.gitea.io/gitea/modules/util"
  6. /*
  7. In template:
  8. ```html
  9. {{if not .Page.IsFirst}}[First](1){{end}}
  10. {{if .Page.HasPrevious}}[Previous]({{.Page.Previous}}){{end}}
  11. {{range .Page.Pages}}
  12. {{if eq .Num -1}}
  13. ...
  14. {{else}}
  15. {{.Num}}{{if .IsCurrent}}(current){{end}}
  16. {{end}}
  17. {{end}}
  18. {{if .Page.HasNext}}[Next]({{.Page.Next}}){{end}}
  19. {{if not .Page.IsLast}}[Last]({{.Page.TotalPages}}){{end}}
  20. ```
  21. Output:
  22. ```
  23. [First](1) [Previous](2) ... 2 3(current) 4 ... [Next](4) [Last](5)
  24. ```
  25. */
  26. // Paginator represents a set of results of pagination calculations.
  27. type Paginator struct {
  28. total int // total rows count, -1 means unknown
  29. totalPages int // total pages count, -1 means unknown
  30. current int // current page number
  31. curRows int // current page rows count
  32. pagingNum int // how many rows in one page
  33. numPages int // how many pages to show on the UI
  34. }
  35. // New initialize a new pagination calculation and returns a Paginator as result.
  36. func New(total, pagingNum, current, numPages int) *Paginator {
  37. pagingNum = max(pagingNum, 1)
  38. totalPages := util.Iif(total == -1, -1, (total+pagingNum-1)/pagingNum)
  39. if total >= 0 {
  40. current = min(current, totalPages)
  41. }
  42. current = max(current, 1)
  43. return &Paginator{
  44. total: total,
  45. totalPages: totalPages,
  46. current: current,
  47. pagingNum: pagingNum,
  48. numPages: numPages,
  49. }
  50. }
  51. func (p *Paginator) SetCurRows(rows int) {
  52. // For "unlimited paging", we need to know the rows of current page to determine if there is a next page.
  53. // There is still an edge case: when curRows==pagingNum, then the "next page" will be an empty page.
  54. // Ideally we should query one more row to determine if there is really a next page, but it's impossible in current framework.
  55. p.curRows = rows
  56. if p.total == -1 && p.current == 1 && !p.HasNext() {
  57. // if there is only one page for the "unlimited paging", set total rows/pages count
  58. // then the tmpl could decide to hide the nav bar.
  59. p.total = rows
  60. p.totalPages = util.Iif(p.total == 0, 0, 1)
  61. }
  62. }
  63. // IsFirst returns true if current page is the first page.
  64. func (p *Paginator) IsFirst() bool {
  65. return p.current == 1
  66. }
  67. // HasPrevious returns true if there is a previous page relative to current page.
  68. func (p *Paginator) HasPrevious() bool {
  69. return p.current > 1
  70. }
  71. func (p *Paginator) Previous() int {
  72. if !p.HasPrevious() {
  73. return p.current
  74. }
  75. return p.current - 1
  76. }
  77. // HasNext returns true if there is a next page relative to current page.
  78. func (p *Paginator) HasNext() bool {
  79. if p.total == -1 {
  80. return p.curRows >= p.pagingNum
  81. }
  82. return p.current*p.pagingNum < p.total
  83. }
  84. func (p *Paginator) Next() int {
  85. if !p.HasNext() {
  86. return p.current
  87. }
  88. return p.current + 1
  89. }
  90. // IsLast returns true if current page is the last page.
  91. func (p *Paginator) IsLast() bool {
  92. return !p.HasNext()
  93. }
  94. // Total returns number of total rows.
  95. func (p *Paginator) Total() int {
  96. return p.total
  97. }
  98. // TotalPages returns number of total pages.
  99. func (p *Paginator) TotalPages() int {
  100. return p.totalPages
  101. }
  102. // Current returns current page number.
  103. func (p *Paginator) Current() int {
  104. return p.current
  105. }
  106. // PagingNum returns number of page size.
  107. func (p *Paginator) PagingNum() int {
  108. return p.pagingNum
  109. }
  110. // Page presents a page in the paginator.
  111. type Page struct {
  112. num int
  113. isCurrent bool
  114. }
  115. func (p *Page) Num() int {
  116. return p.num
  117. }
  118. func (p *Page) IsCurrent() bool {
  119. return p.isCurrent
  120. }
  121. func getMiddleIdx(numPages int) int {
  122. return (numPages + 1) / 2
  123. }
  124. // Pages returns a list of nearby page numbers relative to current page.
  125. // If value is -1 means "..." that more pages are not showing.
  126. func (p *Paginator) Pages() []*Page {
  127. if p.numPages == 0 {
  128. return nil
  129. } else if p.total == -1 || (p.numPages == 1 && p.TotalPages() == 1) {
  130. // Only show current page.
  131. return []*Page{{p.current, true}}
  132. }
  133. // Total page number is less or equal.
  134. if p.TotalPages() <= p.numPages {
  135. pages := make([]*Page, p.TotalPages())
  136. for i := range pages {
  137. pages[i] = &Page{i + 1, i+1 == p.current}
  138. }
  139. return pages
  140. }
  141. numPages := p.numPages
  142. offsetIdx := 0
  143. hasMoreNext := false
  144. // Check more previous and next pages.
  145. previousNum := getMiddleIdx(p.numPages) - 1
  146. if previousNum > p.current-1 {
  147. previousNum -= previousNum - (p.current - 1)
  148. }
  149. nextNum := p.numPages - previousNum - 1
  150. if p.current+nextNum > p.TotalPages() {
  151. delta := nextNum - (p.TotalPages() - p.current)
  152. nextNum -= delta
  153. previousNum += delta
  154. }
  155. offsetVal := p.current - previousNum
  156. if offsetVal > 1 {
  157. numPages++
  158. offsetIdx = 1
  159. }
  160. if p.current+nextNum < p.TotalPages() {
  161. numPages++
  162. hasMoreNext = true
  163. }
  164. pages := make([]*Page, numPages)
  165. // There are more previous pages.
  166. if offsetIdx == 1 {
  167. pages[0] = &Page{-1, false}
  168. }
  169. // There are more next pages.
  170. if hasMoreNext {
  171. pages[len(pages)-1] = &Page{-1, false}
  172. }
  173. // Check previous pages.
  174. for i := 0; i < previousNum; i++ {
  175. pages[offsetIdx+i] = &Page{i + offsetVal, false}
  176. }
  177. pages[offsetIdx+previousNum] = &Page{p.current, true}
  178. // Check next pages.
  179. for i := 1; i <= nextNum; i++ {
  180. pages[offsetIdx+previousNum+i] = &Page{p.current + i, false}
  181. }
  182. return pages
  183. }