pagy 7.0.11 → 43.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/apps/calendar.ru +741 -0
  4. data/apps/demo.ru +513 -0
  5. data/apps/enable_rails_page_segment.rb +54 -0
  6. data/apps/index.rb +9 -0
  7. data/apps/keynav+root_key.ru +316 -0
  8. data/apps/keynav.ru +255 -0
  9. data/apps/keyset.ru +219 -0
  10. data/apps/keyset_sequel.ru +212 -0
  11. data/apps/rails.ru +216 -0
  12. data/apps/repro.ru +185 -0
  13. data/bin/pagy +5 -0
  14. data/config/pagy.rb +46 -0
  15. data/javascripts/ai_widget.js +90 -0
  16. data/javascripts/pagy.js +168 -0
  17. data/javascripts/pagy.min.js +2 -0
  18. data/javascripts/pagy.mjs +161 -0
  19. data/javascripts/wand.js +1172 -0
  20. data/lib/pagy/classes/calendar/calendar.rb +101 -0
  21. data/lib/pagy/{calendar → classes/calendar}/day.rb +9 -12
  22. data/lib/pagy/{calendar → classes/calendar}/month.rb +7 -11
  23. data/lib/pagy/{calendar → classes/calendar}/quarter.rb +12 -16
  24. data/lib/pagy/classes/calendar/unit.rb +93 -0
  25. data/lib/pagy/{calendar → classes/calendar}/week.rb +7 -11
  26. data/lib/pagy/{calendar → classes/calendar}/year.rb +9 -9
  27. data/lib/pagy/classes/exceptions.rb +26 -0
  28. data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
  29. data/lib/pagy/classes/keyset/adapters/sequel.rb +62 -0
  30. data/lib/pagy/classes/keyset/keynav.rb +85 -0
  31. data/lib/pagy/classes/keyset/keyset.rb +150 -0
  32. data/lib/pagy/classes/offset/countish.rb +17 -0
  33. data/lib/pagy/classes/offset/countless.rb +63 -0
  34. data/lib/pagy/classes/offset/offset.rb +63 -0
  35. data/lib/pagy/classes/offset/search.rb +34 -0
  36. data/lib/pagy/classes/request.rb +48 -0
  37. data/lib/pagy/cli.rb +122 -0
  38. data/lib/pagy/console.rb +5 -20
  39. data/lib/pagy/deprecated.rb +84 -0
  40. data/lib/pagy/modules/abilities/configurable.rb +37 -0
  41. data/lib/pagy/modules/abilities/countable.rb +23 -0
  42. data/lib/pagy/modules/abilities/linkable.rb +72 -0
  43. data/lib/pagy/modules/abilities/rangeable.rb +14 -0
  44. data/lib/pagy/modules/abilities/shiftable.rb +12 -0
  45. data/lib/pagy/modules/b64.rb +35 -0
  46. data/lib/pagy/modules/console.rb +33 -0
  47. data/lib/pagy/modules/i18n/i18n.rb +72 -0
  48. data/lib/pagy/modules/i18n/p11n/arabic.rb +30 -0
  49. data/lib/pagy/modules/i18n/p11n/east_slavic.rb +27 -0
  50. data/lib/pagy/modules/i18n/p11n/one_other.rb +15 -0
  51. data/lib/pagy/modules/i18n/p11n/one_upto_two_other.rb +15 -0
  52. data/lib/pagy/modules/i18n/p11n/other.rb +13 -0
  53. data/lib/pagy/modules/i18n/p11n/polish.rb +27 -0
  54. data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
  55. data/lib/pagy/modules/i18n/p11n.rb +16 -0
  56. data/lib/pagy/modules/searcher.rb +20 -0
  57. data/lib/pagy/next.rb +25 -0
  58. data/lib/pagy/tasks/sync.rb +20 -0
  59. data/lib/pagy/toolbox/helpers/anchor_tags.rb +21 -0
  60. data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +28 -0
  61. data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +19 -0
  62. data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +32 -0
  63. data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +24 -0
  64. data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +25 -0
  65. data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +20 -0
  66. data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +31 -0
  67. data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +23 -0
  68. data/lib/pagy/toolbox/helpers/data_hash.rb +29 -0
  69. data/lib/pagy/toolbox/helpers/headers_hash.rb +30 -0
  70. data/lib/pagy/toolbox/helpers/info_tag.rb +30 -0
  71. data/lib/pagy/toolbox/helpers/input_nav_js.rb +22 -0
  72. data/lib/pagy/toolbox/helpers/limit_tag_js.rb +25 -0
  73. data/lib/pagy/toolbox/helpers/loaders.rb +55 -0
  74. data/lib/pagy/toolbox/helpers/page_url.rb +16 -0
  75. data/lib/pagy/toolbox/helpers/series_nav.rb +30 -0
  76. data/lib/pagy/toolbox/helpers/series_nav_js.rb +20 -0
  77. data/lib/pagy/toolbox/helpers/support/a_lambda.rb +36 -0
  78. data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +19 -0
  79. data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +10 -0
  80. data/lib/pagy/toolbox/helpers/support/series.rb +37 -0
  81. data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +19 -0
  82. data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +17 -0
  83. data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +42 -0
  84. data/lib/pagy/toolbox/helpers/urls_hash.rb +12 -0
  85. data/lib/pagy/toolbox/paginators/calendar.rb +34 -0
  86. data/lib/pagy/toolbox/paginators/countish.rb +38 -0
  87. data/lib/pagy/toolbox/paginators/countless.rb +22 -0
  88. data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +56 -0
  89. data/lib/pagy/toolbox/paginators/keynav_js.rb +26 -0
  90. data/lib/pagy/toolbox/paginators/keyset.rb +15 -0
  91. data/lib/pagy/toolbox/paginators/meilisearch.rb +34 -0
  92. data/lib/pagy/toolbox/paginators/method.rb +38 -0
  93. data/lib/pagy/toolbox/paginators/offset.rb +24 -0
  94. data/lib/pagy/toolbox/paginators/searchkick.rb +34 -0
  95. data/lib/pagy/toolbox/paginators/typesense_rails.rb +34 -0
  96. data/lib/pagy.rb +67 -131
  97. data/locales/ar.yml +32 -0
  98. data/locales/be.yml +28 -0
  99. data/locales/bg.yml +24 -0
  100. data/locales/bs.yml +28 -0
  101. data/locales/ca.yml +24 -0
  102. data/locales/ckb.yml +20 -0
  103. data/locales/cs.yml +26 -0
  104. data/locales/da.yml +24 -0
  105. data/locales/de.yml +24 -0
  106. data/locales/dz.yml +20 -0
  107. data/locales/en.yml +24 -0
  108. data/{lib/locales → locales}/es.yml +9 -6
  109. data/locales/fr.yml +24 -0
  110. data/locales/hr.yml +28 -0
  111. data/locales/id.yml +20 -0
  112. data/locales/it.yml +24 -0
  113. data/locales/ja.yml +20 -0
  114. data/locales/km.yml +20 -0
  115. data/locales/ko.yml +20 -0
  116. data/locales/nb.yml +24 -0
  117. data/locales/nl.yml +24 -0
  118. data/locales/nn.yml +24 -0
  119. data/locales/pl.yml +28 -0
  120. data/{lib/locales → locales}/pt-BR.yml +10 -7
  121. data/{lib/locales → locales}/pt.yml +10 -7
  122. data/locales/ru.yml +28 -0
  123. data/locales/sk.yml +26 -0
  124. data/locales/sr.yml +28 -0
  125. data/locales/sv-SE.yml +24 -0
  126. data/locales/sv.yml +24 -0
  127. data/locales/sw.yml +28 -0
  128. data/locales/ta.yml +24 -0
  129. data/locales/tr.yml +24 -0
  130. data/locales/uk.yml +28 -0
  131. data/locales/vi.yml +20 -0
  132. data/locales/zh-CN.yml +20 -0
  133. data/locales/zh-HK.yml +20 -0
  134. data/locales/zh-TW.yml +20 -0
  135. data/stylesheets/pagy-tailwind.css +68 -0
  136. data/stylesheets/pagy.css +83 -0
  137. metadata +185 -94
  138. data/lib/config/pagy.rb +0 -258
  139. data/lib/javascripts/pagy-dev.js +0 -112
  140. data/lib/javascripts/pagy-module.js +0 -111
  141. data/lib/javascripts/pagy.js +0 -1
  142. data/lib/locales/ar.yml +0 -30
  143. data/lib/locales/be.yml +0 -25
  144. data/lib/locales/bg.yml +0 -21
  145. data/lib/locales/bs.yml +0 -25
  146. data/lib/locales/ca.yml +0 -23
  147. data/lib/locales/ckb.yml +0 -18
  148. data/lib/locales/cs.yml +0 -23
  149. data/lib/locales/da.yml +0 -23
  150. data/lib/locales/de.yml +0 -21
  151. data/lib/locales/en.yml +0 -21
  152. data/lib/locales/fr.yml +0 -21
  153. data/lib/locales/hr.yml +0 -25
  154. data/lib/locales/id.yml +0 -19
  155. data/lib/locales/it.yml +0 -21
  156. data/lib/locales/ja.yml +0 -19
  157. data/lib/locales/km.yml +0 -19
  158. data/lib/locales/ko.yml +0 -19
  159. data/lib/locales/nb.yml +0 -21
  160. data/lib/locales/nl.yml +0 -21
  161. data/lib/locales/nn.yml +0 -21
  162. data/lib/locales/pl.yml +0 -25
  163. data/lib/locales/ru.yml +0 -27
  164. data/lib/locales/sr.yml +0 -25
  165. data/lib/locales/sv-SE.yml +0 -21
  166. data/lib/locales/sv.yml +0 -21
  167. data/lib/locales/sw.yml +0 -23
  168. data/lib/locales/ta.yml +0 -23
  169. data/lib/locales/tr.yml +0 -19
  170. data/lib/locales/uk.yml +0 -25
  171. data/lib/locales/vi.yml +0 -17
  172. data/lib/locales/zh-CN.yml +0 -19
  173. data/lib/locales/zh-HK.yml +0 -19
  174. data/lib/locales/zh-TW.yml +0 -19
  175. data/lib/pagy/backend.rb +0 -39
  176. data/lib/pagy/calendar/helper.rb +0 -65
  177. data/lib/pagy/calendar.rb +0 -126
  178. data/lib/pagy/countless.rb +0 -37
  179. data/lib/pagy/exceptions.rb +0 -25
  180. data/lib/pagy/extras/arel.rb +0 -36
  181. data/lib/pagy/extras/array.rb +0 -24
  182. data/lib/pagy/extras/bootstrap.rb +0 -108
  183. data/lib/pagy/extras/bulma.rb +0 -105
  184. data/lib/pagy/extras/calendar.rb +0 -53
  185. data/lib/pagy/extras/countless.rb +0 -37
  186. data/lib/pagy/extras/elasticsearch_rails.rb +0 -80
  187. data/lib/pagy/extras/foundation.rb +0 -105
  188. data/lib/pagy/extras/frontend_helpers.rb +0 -67
  189. data/lib/pagy/extras/gearbox.rb +0 -54
  190. data/lib/pagy/extras/headers.rb +0 -53
  191. data/lib/pagy/extras/i18n.rb +0 -26
  192. data/lib/pagy/extras/items.rb +0 -61
  193. data/lib/pagy/extras/jsonapi.rb +0 -79
  194. data/lib/pagy/extras/materialize.rb +0 -96
  195. data/lib/pagy/extras/meilisearch.rb +0 -65
  196. data/lib/pagy/extras/metadata.rb +0 -38
  197. data/lib/pagy/extras/navs.rb +0 -51
  198. data/lib/pagy/extras/overflow.rb +0 -80
  199. data/lib/pagy/extras/searchkick.rb +0 -67
  200. data/lib/pagy/extras/semantic.rb +0 -95
  201. data/lib/pagy/extras/standalone.rb +0 -60
  202. data/lib/pagy/extras/support.rb +0 -40
  203. data/lib/pagy/extras/trim.rb +0 -29
  204. data/lib/pagy/extras/uikit.rb +0 -97
  205. data/lib/pagy/frontend.rb +0 -114
  206. data/lib/pagy/i18n.rb +0 -165
  207. data/lib/pagy/url_helpers.rb +0 -27
  208. data/lib/stylesheets/pagy.css +0 -61
  209. data/lib/stylesheets/pagy.scss +0 -50
  210. data/lib/stylesheets/pagy.tailwind.scss +0 -24
  211. /data/{lib/javascripts/pagy-module.d.ts → javascripts/pagy.d.ts} +0 -0
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/searcher'
4
+
5
+ class Pagy
6
+ module ElasticsearchRailsPaginator
7
+ module_function
8
+
9
+ def paginate(search, options)
10
+ if search.is_a?(Search::Arguments) # Active mode
11
+
12
+ Searcher.wrap(search, options) do
13
+ model, arguments, search_options = search
14
+
15
+ search_options[:size] = options[:limit]
16
+ search_options[:from] = options[:limit] * ((options[:page] || 1) - 1)
17
+
18
+ method = options[:search_method] || ElasticsearchRails::DEFAULT[:search_method]
19
+ response_object = model.send(method, *arguments, **search_options)
20
+ options[:count] = total_count_from(response_object)
21
+
22
+ [ElasticsearchRails.new(**options), response_object]
23
+ end
24
+
25
+ else # Passive mode
26
+ from, size = pagination_params_from(search)
27
+ options[:limit] = size
28
+ options[:page] = ((from || 0) / options[:limit]) + 1
29
+ options[:count] = total_count_from(search)
30
+
31
+ ElasticsearchRails.new(**options)
32
+ end
33
+ end
34
+
35
+ # Support different versions of ElasticsearchRails
36
+ def pagination_params_from(response_object)
37
+ definition = response_object.search.definition
38
+ definition = definition.to_hash if definition.respond_to?(:to_hash)
39
+ container = (definition.is_a?(Hash) && (definition[:body] || definition)) || response_object.search.options
40
+ from = (container[:from] || container['from']).to_i
41
+ size = (container[:size] || container['size']).to_i
42
+ size = 10 if size.zero?
43
+
44
+ [from, size]
45
+ end
46
+
47
+ # Support different versions of ElasticsearchRails
48
+ def total_count_from(response_object)
49
+ total = response_object.instance_eval do
50
+ respond_to?(:response) ? response['hits']['total'] : raw_response['hits']['total']
51
+ end
52
+
53
+ total.is_a?(Hash) ? total['value'] : total
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../pagy/modules/b64'
4
+
5
+ class Pagy
6
+ module KeynavJsPaginator
7
+ module_function
8
+
9
+ def paginate(set, options)
10
+ page = options[:request].resolve_page(force_integer: false) # allow nil
11
+
12
+ return CountlessPaginator.paginate(set, page:, **options) if page&.match(' ') # countless fallback
13
+
14
+ if page.is_a?(String) # keynav page param
15
+ page_arguments = JSON.parse(B64.urlsafe_decode(page))
16
+ # Restart the pagination from page 1/nil if the url has been requested from another browser
17
+ options[:page] = page_arguments if options[:request].cookie == page_arguments.shift
18
+ end
19
+
20
+ options[:limit] = options[:request].resolve_limit
21
+
22
+ pagy = Keyset::Keynav.new(set, **options)
23
+ [pagy, pagy.records]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module KeysetPaginator
5
+ module_function
6
+
7
+ def paginate(set, options)
8
+ options[:page] ||= options[:request].resolve_page(force_integer: false) # allow nil
9
+ options[:limit] = options[:request].resolve_limit
10
+
11
+ pagy = Keyset.new(set, **options)
12
+ [pagy, pagy.records]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/searcher'
4
+
5
+ class Pagy
6
+ module MeilisearchPaginator
7
+ module_function
8
+
9
+ def paginate(search, options)
10
+ if search.is_a?(Search::Arguments) # Active mode
11
+
12
+ Searcher.wrap(search, options) do
13
+ model, arguments, search_options = search
14
+
15
+ search_options[:hits_per_page] = options[:limit]
16
+ search_options[:page] = options[:page]
17
+
18
+ method = options[:search_method] || Meilisearch::DEFAULT[:search_method]
19
+ results = model.send(method, *arguments, search_options)
20
+ options[:count] = results.raw_answer['totalHits']
21
+
22
+ [Meilisearch.new(**options), results]
23
+ end
24
+
25
+ else # Passive mode
26
+ options[:limit] = search.raw_answer['hitsPerPage']
27
+ options[:page] = search.raw_answer['page']
28
+ options[:count] = search.raw_answer['totalHits']
29
+
30
+ Meilisearch.new(**options)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../classes/request'
4
+
5
+ class Pagy
6
+ paginators = { offset: :OffsetPaginator,
7
+ countless: :CountlessPaginator,
8
+ countish: :CountishPaginator,
9
+ keyset: :KeysetPaginator,
10
+ keynav_js: :KeynavJsPaginator,
11
+ calendar: :CalendarPaginator,
12
+ elasticsearch_rails: :ElasticsearchRailsPaginator,
13
+ meilisearch: :MeilisearchPaginator,
14
+ searchkick: :SearchkickPaginator,
15
+ typesense_rails: :TypesenseRailsPaginator }.freeze
16
+
17
+ path = Pathname.new(__dir__)
18
+ paginators.each { |symbol, name| autoload name, path.join(symbol.to_s) }
19
+
20
+ # Pagy::Method defines the #pagy method to be included in the app controller/view.
21
+ Method = Module.new do
22
+ protected
23
+
24
+ define_method :pagy do |paginator = :offset, collection, **options|
25
+ arguments = if paginator == :calendar
26
+ [self, collection, options]
27
+ else
28
+ [collection, options = Pagy::OPTIONS.merge(options)]
29
+ end
30
+
31
+ options[:root_key] = 'page' if options[:jsonapi] # enforce 'page' root_key for JSON:API
32
+ options[:request] ||= request # custom request Hash or self.request
33
+ options[:request] = Request.new(options) # Pagy::Request
34
+
35
+ Pagy.const_get(paginators[paginator]).paginate(*arguments)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/abilities/countable'
4
+
5
+ class Pagy
6
+ module OffsetPaginator
7
+ module_function
8
+
9
+ def paginate(collection, options)
10
+ options[:page] ||= options[:request].resolve_page
11
+ options[:limit] = options[:request].resolve_limit
12
+ options[:count] ||= Countable.get_count(collection, options)
13
+
14
+ pagy = Offset.new(**options)
15
+ records = if collection.instance_of?(Array)
16
+ collection[pagy.offset, pagy.limit]
17
+ else
18
+ pagy.records(collection)
19
+ end
20
+
21
+ [pagy, records]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/searcher'
4
+
5
+ class Pagy
6
+ module SearchkickPaginator
7
+ module_function
8
+
9
+ def paginate(search, options)
10
+ if search.is_a?(Search::Arguments) # Active mode
11
+
12
+ Searcher.wrap(search, options) do
13
+ model, arguments, search_options, block = search
14
+
15
+ search_options[:per_page] = options[:limit]
16
+ search_options[:page] = options[:page]
17
+
18
+ method = options[:search_method] || Searchkick::DEFAULT[:search_method]
19
+ results = model.send(method, *arguments || '*', **search_options, &block)
20
+ options[:count] = results.total_count
21
+
22
+ [Searchkick.new(**options), results]
23
+ end
24
+
25
+ else # Passive mode
26
+ options[:limit] = search.respond_to?(:options) ? search.options[:per_page] : search.per_page
27
+ options[:page] = search.respond_to?(:options) ? search.options[:page] : search.current_page
28
+ options[:count] = search.total_count
29
+
30
+ Searchkick.new(**options)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/searcher'
4
+
5
+ class Pagy
6
+ module TypesenseRailsPaginator
7
+ module_function
8
+
9
+ def paginate(search, options)
10
+ if search.is_a?(Search::Arguments) # Active mode
11
+
12
+ Searcher.wrap(search, options) do
13
+ model, arguments, search_options = search
14
+
15
+ search_options[:per_page] = options[:limit]
16
+ search_options[:page] = options[:page]
17
+
18
+ method = options[:search_method] || TypesenseRails::DEFAULT[:search_method]
19
+ results = model.send(method, *arguments, search_options)
20
+ options[:count] = results.raw_answer['found']
21
+
22
+ [TypesenseRails.new(**options), results]
23
+ end
24
+
25
+ else # Passive mode
26
+ options[:limit] = search.raw_answer['request_params']['per_page']
27
+ options[:page] = search.raw_answer['page']
28
+ options[:count] = search.raw_answer['found']
29
+
30
+ TypesenseRails.new(**options)
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/pagy.rb CHANGED
@@ -1,151 +1,87 @@
1
- # See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/pagy
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'pathname'
5
4
 
6
- # Core class
7
- class Pagy
8
- VERSION = '7.0.11'
9
-
10
- # Root pathname to get the path of Pagy files like templates or dictionaries
11
- def self.root
12
- @root ||= Pathname.new(__dir__).freeze
13
- end
14
-
15
- # Default core vars: constant for easy access, but mutable for customizable defaults
16
- DEFAULT = { page: 1, # rubocop:disable Style/MutableConstant
17
- items: 20,
18
- outset: 0,
19
- size: 7,
20
- page_param: :page,
21
- params: {},
22
- fragment: '',
23
- link_extra: '',
24
- item_i18n_key: 'pagy.item_name',
25
- nav_i18n_key: 'pagy.aria_label.nav',
26
- cycle: false,
27
- request_path: '',
28
- count_args: [:all] } # AR friendly
29
-
30
- attr_reader :count, :page, :items, :vars, :pages, :last, :offset, :in, :from, :to, :prev, :next, :params, :request_path
31
-
32
- # Merge and validate the options, do some simple arithmetic and set the instance variables
33
- def initialize(vars)
34
- normalize_vars(vars)
35
- setup_vars(count: 0, page: 1, outset: 0)
36
- setup_items_var
37
- setup_pages_var
38
- setup_offset_var
39
- setup_params_var
40
- setup_request_path_var
41
- raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
42
-
43
- @from = [@offset - @outset + 1, @count].min
44
- @to = [@offset - @outset + @items, @count].min
45
- @in = [@to - @from + 1, @count].min
46
- @prev = (@page - 1 unless @page == 1)
47
- @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
48
- end
49
-
50
- # Return the array of page numbers and :gap items e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
51
- def series(size: @vars[:size], **_)
52
- series = []
53
- if size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier
54
- # This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3)
55
- left_gap_start = 1 + size[0]
56
- left_gap_end = @page - size[1] - 1
57
- right_gap_start = @page + size[2] + 1
58
- right_gap_end = @last - size[3]
59
- left_gap_end = right_gap_end if left_gap_end > right_gap_end
60
- right_gap_start = left_gap_start if left_gap_start > right_gap_start
61
- start = 1
62
- if (left_gap_end - left_gap_start).positive?
63
- series.push(*start...left_gap_start, :gap)
64
- start = left_gap_end + 1
65
- end
66
- if (right_gap_end - right_gap_start).positive?
67
- series.push(*start...right_gap_start, :gap)
68
- start = right_gap_end + 1
69
- end
70
- series.push(*start..@last)
71
- elsif size.is_a?(Integer) && size.positive? # only central series
72
- # The simplest and fastest algorithm
73
- size = @pages if size > @pages # reduce the max size to @pages
74
- left = ((size - 1) / 2.0).floor # left half might be 1 page shorter for even size
75
- start = if @page <= left # beginning pages
76
- 1
77
- elsif @page > @pages - (size - left) # end pages
78
- @pages - size + 1
79
- else # intermediate pages
80
- @page - left
81
- end
82
- series = (start..start + size - 1).to_a
83
- else
84
- return [] if size.empty?
85
-
86
- raise VariableError.new(self, :size, 'to be a single positive Integer or an Array of 4', size)
87
- end
88
- series[series.index(@page)] = @page.to_s
89
- series
90
- end
91
-
92
- # Label for any page. Allow the customization of the output (overridden by the calendar extra)
93
- def label_for(page)
94
- page.to_s
95
- end
5
+ require_relative 'pagy/classes/exceptions'
6
+ require_relative 'pagy/modules/abilities/linkable'
7
+ require_relative 'pagy/modules/abilities/configurable'
8
+ require_relative 'pagy/toolbox/helpers/loaders'
96
9
 
97
- # Label for the current page. Allow the customization of the output (overridden by the calendar extra)
98
- def label
99
- @page.to_s
100
- end
10
+ # Top superclass
11
+ class Pagy
12
+ VERSION = '43.5.1'
13
+ ROOT = Pathname.new(__dir__).parent.freeze
14
+ DEFAULT = { limit: 20, limit_key: 'limit', page_key: 'page' }.freeze
15
+ PAGE_TOKEN = EscapedValue.new('P ')
16
+ LIMIT_TOKEN = EscapedValue.new('L ')
17
+ LABEL_TOKEN = 'L'
18
+ A_TAG = '<a style="display: none;">#</a>'
19
+
20
+ path = Pathname.new(__FILE__).sub_ext('')
21
+ autoload :Method, path.join('toolbox/paginators/method')
22
+ autoload :I18n, path.join('modules/i18n/i18n')
23
+ autoload :Console, path.join('modules/console')
24
+ autoload :Calendar, path.join('classes/calendar/calendar')
25
+ autoload :Offset, path.join('classes/offset/offset')
26
+ autoload :Search, path.join('classes/offset/search')
27
+ autoload :ElasticsearchRails, path.join('classes/offset/search')
28
+ autoload :Meilisearch, path.join('classes/offset/search')
29
+ autoload :Searchkick, path.join('classes/offset/search')
30
+ autoload :TypesenseRails, path.join('classes/offset/search')
31
+ autoload :Keyset, path.join('classes/keyset/keyset')
32
+ autoload :SyncTask, path.join('tasks/sync')
33
+
34
+ OPTIONS = {} # rubocop:disable Style/MutableConstant
35
+
36
+ extend Configurable
37
+ include Linkable
38
+ include HelperLoader
39
+
40
+ attr_reader :page, :next, :in, :limit, :options
101
41
 
102
42
  protected
103
43
 
104
- # Apply defaults, cleanup blanks and set @vars
105
- def normalize_vars(vars)
106
- @vars = DEFAULT.merge(vars.delete_if { |k, v| DEFAULT.key?(k) && (v.nil? || v == '') })
107
- end
44
+ # Instance identity methods, overridden by the respective classes
45
+ def offset? = false
46
+ def countless? = false
47
+ def calendar? = false
48
+ def search? = false
49
+ def keyset? = false
50
+ def keynav? = false
108
51
 
109
- # Setup and validates the passed vars: var must be present and value.to_i must be >= to min
110
- def setup_vars(name_min)
52
+ # Validate presence and min value of options
53
+ def assign_and_check(name_min)
111
54
  name_min.each do |name, min|
112
- raise VariableError.new(self, name, ">= #{min}", @vars[name]) \
113
- unless @vars[name]&.respond_to?(:to_i) && instance_variable_set(:"@#{name}", @vars[name].to_i) >= min
114
- end
115
- end
55
+ value = @options[name]
116
56
 
117
- # Setup @items (overridden by the gearbox extra)
118
- def setup_items_var
119
- setup_vars(items: 1)
57
+ if value.respond_to?(:to_i) && (integer = value.to_i) >= min
58
+ instance_variable_set(:"@#{name}", integer)
59
+ else
60
+ raise OptionError.new(self, name, ">= #{min}", value)
61
+ end
62
+ end
120
63
  end
121
64
 
122
- # Setup @pages and @last (overridden by the gearbox extra)
123
- def setup_pages_var
124
- @pages = @last = [(@count.to_f / @items).ceil, 1].max
125
- end
65
+ # Merge all the DEFAULT constants of the class hierarchy with the options
66
+ def assign_options(**options)
67
+ @request = options.delete(:request) # internal object
68
+ default = {}
69
+ current = self.class
126
70
 
127
- # Setup @offset based on the :gearbox_items variable
128
- def setup_offset_var
129
- @offset = (@items * (@page - 1)) + @outset # may be already set from gear_box
130
- end
71
+ loop do
72
+ default = current::DEFAULT.merge(default)
73
+ current = current.superclass
74
+ break if current == Object
75
+ end
131
76
 
132
- # Setup and validate @params
133
- def setup_params_var
134
- raise VariableError.new(self, :params, 'must be a Hash or a Proc', @params) \
135
- unless (@params = @vars[:params]).is_a?(Hash) || @params.is_a?(Proc)
77
+ clean_options = options.delete_if { |k, v| default.key?(k) && (v.nil? || v == '') }
78
+ @options = default.merge!(clean_options).freeze
136
79
  end
137
80
 
138
- def setup_request_path_var
139
- request_path = @vars[:request_path]
140
- return if request_path.to_s.empty?
141
-
142
- raise VariableError.new(self, :request_path, 'must be a bare path like "/foo"', request_path) \
143
- if !request_path.start_with?('/') || request_path.include?('?')
144
-
145
- @request_path = request_path
81
+ # Hook module for numeric UI helpers
82
+ module NumericHelpers
83
+ include NumericHelperLoader
146
84
  end
147
85
  end
148
86
 
149
- require 'pagy/backend'
150
- require 'pagy/frontend'
151
- require 'pagy/exceptions'
87
+ require_relative ENV['PAGY_NEXT'] == 'true' ? 'pagy/next' : 'pagy/deprecated'
data/locales/ar.yml ADDED
@@ -0,0 +1,32 @@
1
+ # See https://ddnexus.github.io/pagy/resources/i18n
2
+
3
+ ar:
4
+ pagy:
5
+ p11n: 'Arabic'
6
+ aria_label:
7
+ nav:
8
+ zero: "لا يوجد صفحات"
9
+ one: "صفحة"
10
+ two: "صفحتين"
11
+ few: "صفحات"
12
+ many: "صفحات"
13
+ other: "صفحات"
14
+ previous: "السابق"
15
+ next: "التالي"
16
+ previous: "&lt;"
17
+ next: "&gt;"
18
+ gap: "&hellip;"
19
+ item_name:
20
+ zero: "صفر"
21
+ one: "عنصر"
22
+ two: "عنصرين"
23
+ few: "قليل"
24
+ many: "كثير"
25
+ other: "عناصر"
26
+ info_tag:
27
+ no_count: "الصفحة %{page} من %{pages}"
28
+ no_items: "لا يوجد %{item_name}"
29
+ single_page: "عرض %{count} %{item_name}"
30
+ multiple_pages: "عرض %{item_name} %{from}-%{to} من اجمالي %{count}"
31
+ input_nav_js: "الصفحة %{page_input} من %{pages}"
32
+ limit_tag_js: "عرض %{limit_input} %{item_name} لكل صفحة"
data/locales/be.yml ADDED
@@ -0,0 +1,28 @@
1
+ # See https://ddnexus.github.io/pagy/resources/i18n
2
+
3
+ be:
4
+ pagy:
5
+ p11n: 'EastSlavic'
6
+ aria_label:
7
+ nav:
8
+ one: "Старонка"
9
+ few: "Старонкi"
10
+ many: "Старонкi"
11
+ other: "Старонкi"
12
+ previous: "Назад"
13
+ next: "Наперад"
14
+ previous: "&lt;"
15
+ next: "&gt;"
16
+ gap: "&hellip;"
17
+ item_name:
18
+ one: "запіс"
19
+ few: "запісы"
20
+ many: "запісаў"
21
+ other: "запісаў"
22
+ info_tag:
23
+ no_count: "Старонка %{page} з %{pages}"
24
+ no_items: "Пакуль %{item_name} няма"
25
+ single_page: "%{count} %{item_name}"
26
+ multiple_pages: "Усяго %{count} %{item_name}, паказаны з %{from} па %{to}"
27
+ input_nav_js: "Старонка %{page_input} з %{pages}"
28
+ limit_tag_js: "Паказаць %{limit_input} %{item_name} на старонцы"
data/locales/bg.yml ADDED
@@ -0,0 +1,24 @@
1
+ # See https://ddnexus.github.io/pagy/resources/i18n
2
+
3
+ bg:
4
+ pagy:
5
+ p11n: 'OneOther'
6
+ aria_label:
7
+ nav:
8
+ one: "Страница"
9
+ other: "Страници"
10
+ previous: "Предишна"
11
+ next: "Следваща"
12
+ previous: "&lt;"
13
+ next: "&gt;"
14
+ gap: "&hellip;"
15
+ item_name:
16
+ one: "резултат"
17
+ other: "резултати"
18
+ info_tag:
19
+ no_count: "Страница %{page} от %{pages}"
20
+ no_items: "Няма намерени %{item_name}"
21
+ single_page: "Показани са %{count} %{item_name}"
22
+ multiple_pages: "Показани са %{item_name} %{from}-%{to} от %{count} общо"
23
+ input_nav_js: "Страница %{page_input} от %{pages}"
24
+ limit_tag_js: "Покажи %{limit_input} %{item_name} на страница"
data/locales/bs.yml ADDED
@@ -0,0 +1,28 @@
1
+ # See https://ddnexus.github.io/pagy/resources/i18n
2
+
3
+ bs:
4
+ pagy:
5
+ p11n: 'EastSlavic'
6
+ aria_label:
7
+ nav:
8
+ one: "Stranica"
9
+ few: "Stranice"
10
+ many: "Stranica"
11
+ other: "Stranice"
12
+ previous: "Prethodna"
13
+ next: "Sljedeća"
14
+ previous: "&lt;"
15
+ next: "&gt;"
16
+ gap: "&hellip;"
17
+ item_name:
18
+ one: "stavka"
19
+ few: "stavke"
20
+ many: "stavki"
21
+ other: "stavki"
22
+ info_tag:
23
+ no_count: "Stranica %{page} od %{pages}"
24
+ no_items: "Nije pronađena %{item_name}"
25
+ single_page: "Prikazuje se %{count} %{item_name}"
26
+ multiple_pages: "Prikaz %{item_name} %{from}-%{to} od %{count} ukupno"
27
+ input_nav_js: "Stranica %{page_input} od %{pages}"
28
+ limit_tag_js: "Prikaži %{limit_input} %{item_name} po stranici"
data/locales/ca.yml ADDED
@@ -0,0 +1,24 @@
1
+ # See https://ddnexus.github.io/pagy/resources/i18n
2
+
3
+ ca:
4
+ pagy:
5
+ p11n: 'OneOther'
6
+ aria_label:
7
+ nav:
8
+ one: "Pàgina"
9
+ other: "Pàgines"
10
+ previous: "Anterior"
11
+ next: "Següent"
12
+ previous: "&lt;"
13
+ next: "&gt;"
14
+ gap: "&hellip;"
15
+ item_name:
16
+ one: "element"
17
+ other: "elements"
18
+ info_tag:
19
+ no_count: "Pàgina %{page} de %{pages}"
20
+ no_items: "No s'ha trobat cap resultat"
21
+ single_page: "Mostrant %{count} %{item_name}"
22
+ multiple_pages: "Mostrant %{item_name} %{from}-%{to} de %{count} en total"
23
+ input_nav_js: "Pàgina %{page_input} de %{pages}"
24
+ limit_tag_js: "Mostra %{limit_input} %{item_name} per pàgina"
data/locales/ckb.yml ADDED
@@ -0,0 +1,20 @@
1
+ # See https://ddnexus.github.io/pagy/resources/i18n
2
+
3
+ ckb:
4
+ pagy:
5
+ p11n: 'Other'
6
+ item_name: "بابەت"
7
+ aria_label:
8
+ nav: "پەڕەکان"
9
+ previous: "پاش"
10
+ next: "پێش"
11
+ previous: "&lt;"
12
+ next: "&gt;"
13
+ gap: "&hellip;"
14
+ info_tag:
15
+ no_count: "پەڕی %{page} لە %{pages}"
16
+ no_items: "هیچ %{item_name}ێک نەدۆزرایەوە"
17
+ single_page: "پیشاندانی %{count} %{item_name}"
18
+ multiple_pages: "پشاندانی %{item_name}ی %{from}-%{to} لە کۆی %{count} بە گشتی"
19
+ input_nav_js: "پەڕی %{page_input} لە %{pages}"
20
+ limit_tag_js: "نیشاندانی %{limit_input} %{item_name} لە هەر پەڕێک"