paginater.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright 2015 Unknwon
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. // Package paginater is a helper module for custom pagination calculation.
  15. package paginater
  16. // Paginater represents a set of results of pagination calculations.
  17. type Paginater struct {
  18. total int
  19. pagingNum int
  20. current int
  21. numPages int
  22. }
  23. // New initialize a new pagination calculation and returns a Paginater as result.
  24. func New(total, pagingNum, current, numPages int) *Paginater {
  25. if pagingNum <= 0 {
  26. pagingNum = 1
  27. }
  28. if current <= 0 {
  29. current = 1
  30. }
  31. p := &Paginater{total, pagingNum, current, numPages}
  32. if p.current > p.TotalPages() {
  33. p.current = p.TotalPages()
  34. }
  35. return p
  36. }
  37. // IsFirst returns true if current page is the first page.
  38. func (p *Paginater) IsFirst() bool {
  39. return p.current == 1
  40. }
  41. // HasPrevious returns true if there is a previous page relative to current page.
  42. func (p *Paginater) HasPrevious() bool {
  43. return p.current > 1
  44. }
  45. func (p *Paginater) Previous() int {
  46. if !p.HasPrevious() {
  47. return p.current
  48. }
  49. return p.current - 1
  50. }
  51. // HasNext returns true if there is a next page relative to current page.
  52. func (p *Paginater) HasNext() bool {
  53. return p.total > p.current*p.pagingNum
  54. }
  55. func (p *Paginater) Next() int {
  56. if !p.HasNext() {
  57. return p.current
  58. }
  59. return p.current + 1
  60. }
  61. // IsLast returns true if current page is the last page.
  62. func (p *Paginater) IsLast() bool {
  63. if p.total == 0 {
  64. return true
  65. }
  66. return p.total > (p.current-1)*p.pagingNum && !p.HasNext()
  67. }
  68. // Total returns number of total rows.
  69. func (p *Paginater) Total() int {
  70. return p.total
  71. }
  72. // TotalPage returns number of total pages.
  73. func (p *Paginater) TotalPages() int {
  74. if p.total == 0 {
  75. return 1
  76. }
  77. if p.total%p.pagingNum == 0 {
  78. return p.total / p.pagingNum
  79. }
  80. return p.total/p.pagingNum + 1
  81. }
  82. // Current returns current page number.
  83. func (p *Paginater) Current() int {
  84. return p.current
  85. }
  86. // PagingNum returns number of page size.
  87. func (p *Paginater) PagingNum() int {
  88. return p.pagingNum
  89. }
  90. // Page presents a page in the paginater.
  91. type Page struct {
  92. num int
  93. isCurrent bool
  94. }
  95. func (p *Page) Num() int {
  96. return p.num
  97. }
  98. func (p *Page) IsCurrent() bool {
  99. return p.isCurrent
  100. }
  101. func getMiddleIdx(numPages int) int {
  102. if numPages%2 == 0 {
  103. return numPages / 2
  104. }
  105. return numPages/2 + 1
  106. }
  107. // Pages returns a list of nearby page numbers relative to current page.
  108. // If value is -1 means "..." that more pages are not showing.
  109. func (p *Paginater) Pages() []*Page {
  110. if p.numPages == 0 {
  111. return []*Page{}
  112. } else if p.numPages == 1 && p.TotalPages() == 1 {
  113. // Only show current page.
  114. return []*Page{{1, true}}
  115. }
  116. // Total page number is less or equal.
  117. if p.TotalPages() <= p.numPages {
  118. pages := make([]*Page, p.TotalPages())
  119. for i := range pages {
  120. pages[i] = &Page{i + 1, i+1 == p.current}
  121. }
  122. return pages
  123. }
  124. numPages := p.numPages
  125. maxIdx := numPages - 1
  126. offsetIdx := 0
  127. hasMoreNext := false
  128. // Check more previous and next pages.
  129. previousNum := getMiddleIdx(p.numPages) - 1
  130. if previousNum > p.current-1 {
  131. previousNum -= previousNum - (p.current - 1)
  132. }
  133. nextNum := p.numPages - previousNum - 1
  134. if p.current+nextNum > p.TotalPages() {
  135. delta := nextNum - (p.TotalPages() - p.current)
  136. nextNum -= delta
  137. previousNum += delta
  138. }
  139. offsetVal := p.current - previousNum
  140. if offsetVal > 1 {
  141. numPages++
  142. maxIdx++
  143. offsetIdx = 1
  144. }
  145. if p.current+nextNum < p.TotalPages() {
  146. numPages++
  147. hasMoreNext = true
  148. }
  149. pages := make([]*Page, numPages)
  150. // There are more previous pages.
  151. if offsetIdx == 1 {
  152. pages[0] = &Page{-1, false}
  153. }
  154. // There are more next pages.
  155. if hasMoreNext {
  156. pages[len(pages)-1] = &Page{-1, false}
  157. }
  158. // Check previous pages.
  159. for i := 0; i < previousNum; i++ {
  160. pages[offsetIdx+i] = &Page{i + offsetVal, false}
  161. }
  162. pages[offsetIdx+previousNum] = &Page{p.current, true}
  163. // Check next pages.
  164. for i := 1; i <= nextNum; i++ {
  165. pages[offsetIdx+previousNum+i] = &Page{p.current + i, false}
  166. }
  167. return pages
  168. }