pagy 4.11.0 → 6.0.0

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/lib/config/pagy.rb +119 -58
  4. data/lib/javascripts/pagy-dev.js +114 -0
  5. data/lib/javascripts/pagy-module.d.ts +5 -0
  6. data/lib/javascripts/pagy-module.js +113 -0
  7. data/lib/javascripts/pagy.js +1 -121
  8. data/lib/locales/de.yml +1 -1
  9. data/lib/locales/ko.yml +1 -1
  10. data/lib/locales/nn.yml +22 -0
  11. data/lib/locales/ta.yml +22 -0
  12. data/lib/pagy/backend.rb +10 -13
  13. data/lib/pagy/calendar/day.rb +39 -0
  14. data/lib/pagy/calendar/helper.rb +61 -0
  15. data/lib/pagy/calendar/month.rb +40 -0
  16. data/lib/pagy/calendar/quarter.rb +47 -0
  17. data/lib/pagy/calendar/week.rb +39 -0
  18. data/lib/pagy/calendar/year.rb +33 -0
  19. data/lib/pagy/calendar.rb +100 -0
  20. data/lib/pagy/console.rb +6 -4
  21. data/lib/pagy/countless.rb +22 -23
  22. data/lib/pagy/exceptions.rb +14 -16
  23. data/lib/pagy/extras/arel.rb +11 -7
  24. data/lib/pagy/extras/array.rb +9 -9
  25. data/lib/pagy/extras/bootstrap.rb +45 -38
  26. data/lib/pagy/extras/bulma.rb +50 -38
  27. data/lib/pagy/extras/calendar.rb +49 -0
  28. data/lib/pagy/extras/countless.rb +15 -18
  29. data/lib/pagy/extras/elasticsearch_rails.rb +67 -48
  30. data/lib/pagy/extras/foundation.rb +39 -35
  31. data/lib/pagy/extras/frontend_helpers.rb +72 -0
  32. data/lib/pagy/extras/gearbox.rb +54 -0
  33. data/lib/pagy/extras/headers.rb +30 -20
  34. data/lib/pagy/extras/i18n.rb +15 -13
  35. data/lib/pagy/extras/items.rb +42 -40
  36. data/lib/pagy/extras/materialize.rb +40 -38
  37. data/lib/pagy/extras/meilisearch.rb +53 -44
  38. data/lib/pagy/extras/metadata.rb +15 -20
  39. data/lib/pagy/extras/navs.rb +35 -34
  40. data/lib/pagy/extras/overflow.rb +62 -61
  41. data/lib/pagy/extras/searchkick.rb +54 -46
  42. data/lib/pagy/extras/semantic.rb +42 -40
  43. data/lib/pagy/extras/standalone.rb +50 -46
  44. data/lib/pagy/extras/support.rb +24 -16
  45. data/lib/pagy/extras/trim.rb +15 -14
  46. data/lib/pagy/extras/uikit.rb +41 -38
  47. data/lib/pagy/frontend.rb +36 -59
  48. data/lib/pagy/i18n.rb +164 -0
  49. data/lib/pagy/url_helpers.rb +24 -0
  50. data/lib/pagy.rb +90 -31
  51. data/lib/templates/bootstrap_nav.html.erb +2 -2
  52. data/lib/templates/bootstrap_nav.html.haml +2 -2
  53. data/lib/templates/bootstrap_nav.html.slim +2 -2
  54. data/lib/templates/foundation_nav.html.erb +1 -1
  55. data/lib/templates/foundation_nav.html.haml +1 -1
  56. data/lib/templates/foundation_nav.html.slim +1 -1
  57. data/lib/templates/nav.html.erb +1 -1
  58. data/lib/templates/nav.html.haml +1 -1
  59. data/lib/templates/nav.html.slim +1 -1
  60. data/lib/templates/uikit_nav.html.erb +2 -2
  61. data/lib/templates/uikit_nav.html.haml +1 -1
  62. data/lib/templates/uikit_nav.html.slim +2 -2
  63. metadata +29 -13
  64. data/lib/locales/utils/i18n.rb +0 -17
  65. data/lib/locales/utils/loader.rb +0 -31
  66. data/lib/locales/utils/p11n.rb +0 -112
  67. data/lib/pagy/deprecation.rb +0 -27
  68. data/lib/pagy/extras/shared.rb +0 -52
@@ -1,61 +1,80 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/elasticsearch_rails
2
2
  # frozen_string_literal: true
3
3
 
4
- class Pagy
4
+ class Pagy # :nodoc:
5
+ DEFAULT[:elasticsearch_rails_search] ||= :search
6
+ DEFAULT[:elasticsearch_rails_pagy_search] ||= :pagy_search
5
7
 
6
- VARS[:elasticsearch_rails_search_method] ||= :pagy_search
8
+ # Paginate ElasticsearchRails response objects
9
+ module ElasticsearchRailsExtra
10
+ module_function
7
11
 
8
- module ElasticsearchRails
9
- # returns an array used to delay the call of #search
10
- # after the pagination variables are merged to the options
11
- # it also pushes to the same array an eventually called method
12
- def pagy_elasticsearch_rails(query_or_payload, **options)
13
- [self, query_or_payload, options].tap do |args|
14
- args.define_singleton_method(:method_missing){|*a| args += a}
15
- end
12
+ # Get the count from different version of ElasticsearchRails
13
+ def total_count(response)
14
+ total = if response.respond_to?(:raw_response)
15
+ response.raw_response['hits']['total']
16
+ else
17
+ response.response['hits']['total']
18
+ end
19
+ total.is_a?(Hash) ? total['value'] : total
16
20
  end
17
- alias_method VARS[:elasticsearch_rails_search_method], :pagy_elasticsearch_rails
18
- end
19
-
20
- # create a Pagy object from an Elasticsearch::Model::Response::Response object
21
- def self.new_from_elasticsearch_rails(response, vars={})
22
- vars[:items] = response.search.options[:size] || 10
23
- vars[:page] = (response.search.options[:from] || 0) / vars[:items] + 1
24
- total = response.respond_to?(:raw_response) ? response.raw_response['hits']['total'] : response.response['hits']['total']
25
- vars[:count] = total.is_a?(Hash) ? total['value'] : total
26
- new(vars)
27
- end
28
21
 
29
- # Add specialized backend methods to paginate ElasticsearchRails searches
30
- module Backend
31
- private
32
-
33
- # Return Pagy object and items
34
- def pagy_elasticsearch_rails(pagy_search_args, vars={})
35
- model, query_or_payload, options, *called = pagy_search_args
36
- vars = pagy_elasticsearch_rails_get_vars(nil, vars)
37
- options[:size] = vars[:items]
38
- options[:from] = vars[:items] * (vars[:page] - 1)
39
- response = model.search(query_or_payload, **options)
40
- total = response.respond_to?(:raw_response) ? response.raw_response['hits']['total'] : response.response['hits']['total']
41
- vars[:count] = total.is_a?(Hash) ? total['value'] : total
42
-
43
- pagy = Pagy.new(vars)
44
- # with :last_page overflow we need to re-run the method in order to get the hits
45
- return pagy_elasticsearch_rails(pagy_search_args, vars.merge(page: pagy.page)) \
46
- if defined?(Pagy::UseOverflowExtra) && pagy.overflow? && pagy.vars[:overflow] == :last_page
47
-
48
- [ pagy, called.empty? ? response : response.send(*called) ]
22
+ module ElasticsearchRails # :nodoc:
23
+ # Return an array used to delay the call of #search
24
+ # after the pagination variables are merged to the options.
25
+ # It also pushes to the same array an optional method call.
26
+ def pagy_elasticsearch_rails(query_or_payload, **options)
27
+ [self, query_or_payload, options].tap do |args|
28
+ args.define_singleton_method(:method_missing) { |*a| args += a }
29
+ end
30
+ end
31
+ alias_method Pagy::DEFAULT[:elasticsearch_rails_pagy_search], :pagy_elasticsearch_rails
49
32
  end
50
33
 
51
- # Sub-method called only by #pagy_elasticsearch_rails: here for easy customization of variables by overriding
52
- # the _collection argument is not available when the method is called
53
- def pagy_elasticsearch_rails_get_vars(_collection, vars)
54
- pagy_set_items_from_params(vars) if defined?(UseItemsExtra)
55
- vars[:items] ||= VARS[:items]
56
- vars[:page] ||= (params[ vars[:page_param] || VARS[:page_param] ] || 1).to_i
57
- vars
34
+ # Additions for the Pagy class
35
+ module Pagy
36
+ # Create a Pagy object from an Elasticsearch::Model::Response::Response object
37
+ def new_from_elasticsearch_rails(response, vars = {})
38
+ vars[:items] = response.search.options[:size] || 10
39
+ vars[:page] = ((response.search.options[:from] || 0) / vars[:items]) + 1
40
+ vars[:count] = ElasticsearchRailsExtra.total_count(response)
41
+ new(vars)
42
+ end
58
43
  end
59
44
 
45
+ # Add specialized backend methods to paginate ElasticsearchRails searches
46
+ module Backend
47
+ private
48
+
49
+ # Return Pagy object and items
50
+ def pagy_elasticsearch_rails(pagy_search_args, vars = {})
51
+ model, query_or_payload,
52
+ options, *called = pagy_search_args
53
+ vars = pagy_elasticsearch_rails_get_vars(nil, vars)
54
+ options[:size] = vars[:items]
55
+ options[:from] = vars[:items] * (vars[:page] - 1)
56
+ response = model.send(DEFAULT[:elasticsearch_rails_search], query_or_payload, **options)
57
+ vars[:count] = ElasticsearchRailsExtra.total_count(response)
58
+
59
+ pagy = ::Pagy.new(vars)
60
+ # with :last_page overflow we need to re-run the method in order to get the hits
61
+ return pagy_elasticsearch_rails(pagy_search_args, vars.merge(page: pagy.page)) \
62
+ if defined?(::Pagy::OverflowExtra) && pagy.overflow? && pagy.vars[:overflow] == :last_page
63
+
64
+ [pagy, called.empty? ? response : response.send(*called)]
65
+ end
66
+
67
+ # Sub-method called only by #pagy_elasticsearch_rails: here for easy customization of variables by overriding
68
+ # the _collection argument is not available when the method is called
69
+ def pagy_elasticsearch_rails_get_vars(_collection, vars)
70
+ pagy_set_items_from_params(vars) if defined?(ItemsExtra)
71
+ vars[:items] ||= DEFAULT[:items]
72
+ vars[:page] ||= (params[vars[:page_param] || DEFAULT[:page_param]] || 1).to_i
73
+ vars
74
+ end
75
+ end
60
76
  end
77
+ ElasticsearchRails = ElasticsearchRailsExtra::ElasticsearchRails
78
+ extend ElasticsearchRailsExtra::Pagy
79
+ Backend.prepend ElasticsearchRailsExtra::Backend
61
80
  end
@@ -1,86 +1,90 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/foundation
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pagy/extras/shared'
5
-
6
- class Pagy
7
- module Frontend
4
+ require 'pagy/extras/frontend_helpers'
8
5
 
6
+ class Pagy # :nodoc:
7
+ # Frontend modules are specially optimized for performance.
8
+ # The resulting code may not look very elegant, but produces the best benchmarks
9
+ module FoundationExtra
9
10
  # Pagination for Foundation: it returns the html with the series of links to the pages
10
- def pagy_foundation_nav(pagy, pagy_id: nil, link_extra: '')
11
+ def pagy_foundation_nav(pagy, pagy_id: nil, link_extra: '', **vars)
11
12
  p_id = %( id="#{pagy_id}") if pagy_id
12
13
  link = pagy_link_proc(pagy, link_extra: link_extra)
13
14
 
14
15
  html = +%(<nav#{p_id} class="pagy-foundation-nav" aria-label="Pagination"><ul class="pagination">)
15
16
  html << pagy_foundation_prev_html(pagy, link)
16
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
+ pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
18
  html << case item
18
19
  when Integer then %(<li>#{link.call item}</li>) # page link
19
- when String then %(<li class="current">#{item}</li>) # active page
20
+ when String then %(<li class="current">#{pagy.label_for(item)}</li>) # active page
20
21
  when :gap then %(<li class="ellipsis gap" aria-hidden="true"></li>) # page gap
22
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
21
23
  end
22
24
  end
23
25
  html << pagy_foundation_next_html(pagy, link)
24
26
  html << %(</ul></nav>)
25
27
  end
26
28
 
27
- # Javascript pagination for foundation: it returns a nav and a JSON tag used by the Pagy.nav javascript
28
- def pagy_foundation_nav_js(pagy, deprecated_id=nil, pagy_id: nil, link_extra: '', steps: nil)
29
- pagy_id = Pagy.deprecated_arg(:id, deprecated_id, :pagy_id, pagy_id) if deprecated_id
29
+ # Javascript pagination for foundation: it returns a nav and a JSON tag used by the pagy.js file
30
+ def pagy_foundation_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
31
+ sequels = pagy.sequels(**vars)
30
32
  p_id = %( id="#{pagy_id}") if pagy_id
31
33
  link = pagy_link_proc(pagy, link_extra: link_extra)
32
34
  tags = { 'before' => %(<ul class="pagination">#{pagy_foundation_prev_html pagy, link}),
33
- 'link' => %(<li>#{link.call PAGE_PLACEHOLDER}</li>),
34
- 'active' => %(<li class="current">#{pagy.page}</li>),
35
+ 'link' => %(<li>#{link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER)}</li>),
36
+ 'active' => %(<li class="current">#{LABEL_PLACEHOLDER}</li>),
35
37
  'gap' => %(<li class="ellipsis gap" aria-hidden="true"></li>),
36
38
  'after' => %(#{pagy_foundation_next_html pagy, link}</ul>) }
37
39
 
38
- %(<nav#{p_id} class="pagy-njs pagy-foundation-nav-js" aria-label="Pagination" #{pagy_json_attr(pagy, :nav, tags, pagy.sequels(steps))}></nav>)
40
+ %(<nav#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-foundation-nav-js" aria-label="Pagination" #{
41
+ pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></nav>)
39
42
  end
40
43
 
41
- # Javascript combo pagination for Foundation: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
42
- def pagy_foundation_combo_nav_js(pagy, deprecated_id=nil, pagy_id: nil, link_extra: '')
43
- pagy_id = Pagy.deprecated_arg(:id, deprecated_id, :pagy_id, pagy_id) if deprecated_id
44
+ # Javascript combo pagination for Foundation: it returns a nav and a JSON tag used by the pagy.js file
45
+ def pagy_foundation_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
44
46
  p_id = %( id="#{pagy_id}") if pagy_id
45
47
  link = pagy_link_proc(pagy, link_extra: link_extra)
46
48
  p_page = pagy.page
47
49
  p_pages = pagy.pages
48
- input = %(<input class="input-group-field cell shrink" type="number" min="1" max="#{p_pages}" value="#{p_page}" style="width: #{p_pages.to_s.length+1}rem; padding: 0 0.3rem; margin: 0 0.3rem;">)
50
+ input = %(<input class="input-group-field cell shrink" type="number" min="1" max="#{
51
+ p_pages}" value="#{p_page}" style="width: #{
52
+ p_pages.to_s.length + 1}rem; padding: 0 0.3rem; margin: 0 0.3rem;">)
49
53
 
50
54
  %(<nav#{p_id} class="pagy-foundation-combo-nav-js" aria-label="Pagination"><div class="input-group" #{
51
- pagy_json_attr pagy, :combo_nav, p_page, pagy_marked_link(link)
52
- }>#{
55
+ pagy_data(pagy, :combo, pagy_marked_link(link))}>#{
53
56
  if (p_prev = pagy.prev)
54
- link.call p_prev, pagy_t('pagy.nav.prev'), 'style="margin-bottom: 0" aria-label="previous" class="prev button primary"'
57
+ link.call p_prev, pagy_t('pagy.nav.prev'),
58
+ 'style="margin-bottom: 0" aria-label="previous" class="prev button primary"'
55
59
  else
56
60
  %(<a style="margin-bottom: 0" class="prev button primary disabled" href="#">#{pagy_t 'pagy.nav.prev'}</a>)
57
61
  end
58
- }<span class="input-group-label">#{pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</span>#{
62
+ }<span class="input-group-label">#{pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</span>#{
59
63
  if (p_next = pagy.next)
60
64
  link.call p_next, pagy_t('pagy.nav.next'), 'style="margin-bottom: 0" aria-label="next" class="next button primary"'
61
65
  else
62
66
  %(<a style="margin-bottom: 0" class="next button primary disabled" href="#">#{pagy_t 'pagy.nav.next'}</a>)
63
67
  end
64
- }</div></nav>)
68
+ }</div></nav>)
65
69
  end
66
70
 
67
71
  private
68
72
 
69
- def pagy_foundation_prev_html(pagy, link)
70
- if (p_prev = pagy.prev)
71
- %(<li class="prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
72
- else
73
- %(<li class="prev disabled">#{pagy_t 'pagy.nav.prev' }</li>)
74
- end
73
+ def pagy_foundation_prev_html(pagy, link)
74
+ if (p_prev = pagy.prev)
75
+ %(<li class="prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
76
+ else
77
+ %(<li class="prev disabled">#{pagy_t 'pagy.nav.prev'}</li>)
75
78
  end
79
+ end
76
80
 
77
- def pagy_foundation_next_html(pagy, link)
78
- if (p_next = pagy.next)
79
- %(<li class="next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
80
- else
81
- %(<li class="next disabled">#{pagy_t 'pagy.nav.next'}</li>)
82
- end
81
+ def pagy_foundation_next_html(pagy, link)
82
+ if (p_next = pagy.next)
83
+ %(<li class="next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
84
+ else
85
+ %(<li class="next disabled">#{pagy_t 'pagy.nav.next'}</li>)
83
86
  end
84
-
87
+ end
85
88
  end
89
+ Frontend.prepend FoundationExtra
86
90
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ class Pagy # :nodoc:
6
+ DEFAULT[:steps] = false # default false will use {0 => @vars[:size]}
7
+
8
+ # Private module documented in the main classes
9
+ module FrontendHelpers
10
+ # Additions for the Pagy class
11
+ module Pagy
12
+ # `Pagy` instance method used by the `pagy*_nav_js` helpers.
13
+ # It returns the sequels of width/series generated from the :steps hash
14
+ # Example:
15
+ # >> pagy = Pagy.new(count:1000, page: 20, steps: {0 => [1,2,2,1], 350 => [2,3,3,2], 550 => [3,4,4,3]})
16
+ # >> pagy.sequels
17
+ # #=> { "0" => [1, :gap, 18, 19, "20", 21, 22, :gap, 50],
18
+ # "350" => [1, 2, :gap, 17, 18, 19, "20", 21, 22, 23, :gap, 49, 50],
19
+ # "550" => [1, 2, 3, :gap, 16, 17, 18, 19, "20", 21, 22, 23, 24, :gap, 48, 49, 50] }
20
+ # Notice: if :steps is false it will use the single {0 => @vars[:size]} size
21
+ def sequels(steps: @vars[:steps] || { 0 => @vars[:size] }, **_)
22
+ raise VariableError.new(self, :steps, 'to define the 0 width', steps) unless steps.key?(0)
23
+
24
+ {}.tap do |sequels|
25
+ steps.each { |width, step_size| sequels[width.to_s] = series(size: step_size) }
26
+ end
27
+ end
28
+
29
+ # Support for the Calendar API
30
+ def label_sequels(*); end
31
+ end
32
+
33
+ # Additions for Calendar class
34
+ module Calendar
35
+ def label_sequels(sequels = self.sequels)
36
+ {}.tap do |label_sequels|
37
+ sequels.each do |width, series|
38
+ label_sequels[width] = series.map { |item| item == :gap ? :gap : label_for(item) }
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # Additions for the Frontend
45
+ module Frontend
46
+ if defined?(Oj)
47
+ # Return a data tag with the base64 encoded JSON-serialized args generated with the faster oj gem
48
+ # Base64 encoded JSON is smaller than HTML escaped JSON
49
+ def pagy_data(pagy, *args)
50
+ args << pagy.vars[:page_param] if pagy.vars[:trim_extra]
51
+ %(data-pagy="#{Base64.strict_encode64(Oj.dump(args, mode: :strict))}")
52
+ end
53
+ else
54
+ require 'json'
55
+ # Return a data tag with the base64 encoded JSON-serialized args generated with the slower to_json
56
+ # Base64 encoded JSON is smaller than HTML escaped JSON
57
+ def pagy_data(pagy, *args)
58
+ args << pagy.vars[:page_param] if pagy.vars[:trim_extra]
59
+ %(data-pagy="#{Base64.strict_encode64(args.to_json)}")
60
+ end
61
+ end
62
+
63
+ # Return the marked link to used by pagy.js
64
+ def pagy_marked_link(link)
65
+ link.call PAGE_PLACEHOLDER, '', 'style="display: none;"'
66
+ end
67
+ end
68
+ end
69
+ prepend FrontendHelpers::Pagy
70
+ Calendar.prepend FrontendHelpers::Calendar if defined?(Calendar)
71
+ Frontend.prepend FrontendHelpers::Frontend
72
+ end
@@ -0,0 +1,54 @@
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/gearbox
2
+ # frozen_string_literal: true
3
+
4
+ class Pagy # :nodoc:
5
+ DEFAULT[:gearbox_extra] = true # extra enabled by default
6
+ DEFAULT[:gearbox_items] = [15, 30, 60, 100]
7
+
8
+ # Automatically change the number of items per page depending on the page number
9
+ # accepts an array as the :gearbox_items variable, that will determine the items for the first pages
10
+ module GearboxExtra
11
+ # Setup @items based on the :gearbox_items variable
12
+ def setup_items_var
13
+ return super if !@vars[:gearbox_extra] || @vars[:items_extra]
14
+
15
+ gearbox_items = @vars[:gearbox_items]
16
+ raise VariableError.new(self, :gearbox_items, 'to be an Array of positives', gearbox_items) \
17
+ unless gearbox_items.is_a?(Array) && gearbox_items.all? { |num| num.positive? rescue false } # rubocop:disable Style/RescueModifier
18
+
19
+ @items = gearbox_items[@page - 1] || gearbox_items.last
20
+ end
21
+
22
+ # Setup @pages and @last based on the :gearbox_items variable (not used by Pagy::Countless)
23
+ def setup_pages_var
24
+ return super if !@vars[:gearbox_extra] || @vars[:items_extra]
25
+
26
+ gearbox_items = @vars[:gearbox_items]
27
+ # This algorithm is thousands of times faster than the one in the geared_pagination gem
28
+ @pages = @last = (if @count > (sum = gearbox_items.sum)
29
+ [((@count - sum).to_f / gearbox_items.last).ceil, 1].max + gearbox_items.count
30
+ else
31
+ pages = 0
32
+ remainder = @count
33
+ while remainder.positive?
34
+ pages += 1
35
+ remainder -= gearbox_items[pages - 1]
36
+ end
37
+ [pages, 1].max
38
+ end)
39
+ end
40
+
41
+ # Setup @offset based on the :gearbox_items variable
42
+ def setup_offset_var
43
+ return super if !@vars[:gearbox_extra] || @vars[:items_extra]
44
+
45
+ gearbox_items = @vars[:gearbox_items]
46
+ @offset = if @page <= gearbox_items.count
47
+ gearbox_items[0, @page - 1].sum
48
+ else
49
+ gearbox_items.sum + (gearbox_items.last * (@page - gearbox_items.count - 1))
50
+ end + @outset
51
+ end
52
+ end
53
+ prepend GearboxExtra
54
+ end
@@ -1,43 +1,53 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/headers
2
2
  # frozen_string_literal: true
3
3
 
4
- class Pagy
5
- # Add specialized backend methods to add pagination response headers
6
- module Backend
7
- private
4
+ require 'pagy/url_helpers'
8
5
 
9
- VARS[:headers] = { page: 'Current-Page', items: 'Page-Items', count: 'Total-Count', pages: 'Total-Pages' }
6
+ class Pagy # :nodoc:
7
+ DEFAULT[:headers] = { page: 'Current-Page',
8
+ items: 'Page-Items',
9
+ count: 'Total-Count',
10
+ pages: 'Total-Pages' }
11
+ # Add specialized backend methods to add pagination response headers
12
+ module HeadersExtra
13
+ include UrlHelpers
10
14
 
11
- include Helpers
15
+ private
12
16
 
17
+ # Merge the pagy headers into the response.headers
13
18
  def pagy_headers_merge(pagy)
14
19
  response.headers.merge!(pagy_headers(pagy))
15
20
  end
16
21
 
22
+ # Generate a hash of RFC-8288 compliant http headers
17
23
  def pagy_headers(pagy)
18
24
  pagy_headers_hash(pagy).tap do |hash|
19
- hash['Link'] = hash['Link'].map{|rel, link| %(<#{link}>; rel="#{rel}")}.join(', ')
25
+ hash['Link'] = hash['Link'].map { |rel, link| %(<#{link}>; rel="#{rel}") }.join(', ')
20
26
  end
21
27
  end
22
28
 
29
+ # Generates a hash structure of the headers
23
30
  def pagy_headers_hash(pagy)
24
- countless = defined?(Pagy::Countless) && pagy.is_a?(Pagy::Countless)
25
- rels = { 'first' => 1, 'prev' => pagy.prev, 'next' => pagy.next }
26
- rels['last'] = pagy.last unless countless
27
- url_str = pagy_url_for(pagy, PAGE_PLACEHOLDER, absolute: true)
28
- hash = { 'Link' => rels.map do |rel, num| # filter_map if ruby >=2.7
29
- next unless num
30
- [ rel, url_str.sub(PAGE_PLACEHOLDER, num.to_s) ]
31
- end.compact.to_h }
32
- headers = pagy.vars[:headers]
33
- hash[headers[:page]] = pagy.page.to_s if headers[:page]
34
- hash[headers[:items]] = pagy.vars[:items].to_s if headers[:items]
31
+ countless = defined?(Countless) && pagy.is_a?(Countless)
32
+ rel = { 'first' => 1, 'prev' => pagy.prev, 'next' => pagy.next }
33
+ rel['last'] = pagy.last unless countless
34
+ url_str = pagy_url_for(pagy, PAGE_PLACEHOLDER, absolute: true)
35
+ link = rel.map do |r, num| # filter_map if ruby >=2.7
36
+ next unless num # rubocop:disable Layout/EmptyLineAfterGuardClause
37
+ [r, url_str.sub(PAGE_PLACEHOLDER, num.to_s)]
38
+ end.compact.to_h
39
+ hash = { 'Link' => link }
40
+ headers = pagy.vars[:headers]
41
+ hash[headers[:page]] = pagy.page.to_s if headers[:page]
42
+ if headers[:items] && !(defined?(Calendar) && pagy.is_a?(Calendar)) # items is not for Calendar
43
+ hash[headers[:items]] = pagy.vars[:items].to_s
44
+ end
35
45
  unless countless
36
46
  hash[headers[:pages]] = pagy.pages.to_s if headers[:pages]
37
- hash[headers[:count]] = pagy.count.to_s if headers[:count]
47
+ hash[headers[:count]] = pagy.count.to_s if pagy.count && headers[:count] # count may be nil with Calendar
38
48
  end
39
49
  hash
40
50
  end
41
-
42
51
  end
52
+ Backend.prepend HeadersExtra
43
53
  end
@@ -1,24 +1,26 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/i18n
2
2
  # frozen_string_literal: true
3
3
 
4
- class Pagy
4
+ class Pagy # :nodoc:
5
5
  # Use ::I18n gem
6
- module Frontend
7
-
8
- ::I18n.load_path += Dir[Pagy.root.join('locales', '*.yml')]
9
-
10
- # unload the pagy default constant for efficiency
11
- Pagy::I18n.clear.instance_eval do
12
- undef :load
13
- undef :t
14
- end
15
-
16
- module UseI18nGem
6
+ module I18nExtra
7
+ # Frontend overriding for translation
8
+ module Frontend
17
9
  def pagy_t(key, **opts)
18
10
  ::I18n.t(key, **opts)
19
11
  end
20
12
  end
21
- prepend UseI18nGem
22
13
 
14
+ # Calendar overriding for localization (see also the block in the calendar section of the config/pagy.rb initializer)
15
+ module Calendar
16
+ def localize(time, opts)
17
+ ::I18n.l(time, **opts)
18
+ end
19
+ end
23
20
  end
21
+ Frontend.prepend I18nExtra::Frontend
22
+ Calendar.prepend I18nExtra::Calendar if defined?(Calendar)
23
+
24
+ # Add the pagy locales to the I18n.load_path
25
+ ::I18n.load_path += Dir[Pagy.root.join('locales', '*.yml')]
24
26
  end
@@ -1,52 +1,54 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/items
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pagy/extras/shared'
4
+ require 'pagy/extras/frontend_helpers'
5
5
 
6
- class Pagy
6
+ class Pagy # :nodoc:
7
+ DEFAULT[:items_param] = :items
8
+ DEFAULT[:max_items] = 100
9
+ DEFAULT[:items_extra] = true # extra enabled by default
7
10
 
8
- # Default variables for this extra
9
- VARS[:items_param] = :items
10
- VARS[:max_items] = 100
11
-
12
- VARS[:enable_items_extra] = true
13
-
14
- ITEMS_PLACEHOLDER = '__pagy_items__'
15
-
16
- module UseItemsExtra; end
17
-
18
- module Backend
19
- private
11
+ # Allow the client to request a custom number of items per page with an optional selector UI
12
+ module ItemsExtra
13
+ # Additions for the Backend module
14
+ module Backend
15
+ private
20
16
 
17
+ # Set the items variable considering the params and other pagy variables
21
18
  def pagy_set_items_from_params(vars)
22
- return if vars[:items]
23
- return unless vars.key?(:enable_item_extra) ? vars[:enable_item_extra] : VARS[:enable_items_extra]
24
- return unless (items = params[vars[:items_param] || VARS[:items_param]]) # :items from :items_param
25
- vars[:items] = [items.to_i, vars.key?(:max_items) ? vars[:max_items] : VARS[:max_items]].compact.min # :items capped to :max_items
26
- end
19
+ return if vars[:items] # :items explicitly set
20
+ return unless vars.key?(:items_extra) ? vars[:items_extra] : DEFAULT[:items_extra] # :items_extra is false
21
+ return unless (items = params[vars[:items_param] || DEFAULT[:items_param]]) # no items from request params
27
22
 
28
- end
29
-
30
- module Frontend
31
-
32
- # Return the items selector HTML. For example "Show [20] items per page"
33
- def pagy_items_selector_js(pagy, deprecated_id=nil, pagy_id: nil, item_name: nil, i18n_key: nil, link_extra: '')
34
- return '' unless pagy.vars[:enable_items_extra]
35
- pagy_id = Pagy.deprecated_arg(:id, deprecated_id, :pagy_id, pagy_id) if deprecated_id
36
- p_id = %( id="#{pagy_id}") if pagy_id
37
- p_vars = pagy.vars
38
- p_items = p_vars[:items]
39
- p_vars[:items] = ITEMS_PLACEHOLDER
40
- link = pagy_marked_link(pagy_link_proc(pagy, link_extra: link_extra))
41
- p_vars[:items] = p_items # restore the items
42
-
43
- html = +%(<span#{p_id} class="pagy-items-selector-js" #{pagy_json_attr pagy, :items_selector, pagy.from, link}>)
44
- input = %(<input type="number" min="1" max="#{p_vars[:max_items]}" value="#{p_items}" style="padding: 0; text-align: center; width: #{p_items.to_s.length+1}rem;">)
45
- html << pagy_t('pagy.items_selector_js', item_name: item_name || pagy_t(i18n_key || p_vars[:i18n_key], count: p_items),
46
- items_input: input,
47
- count: p_items)
48
- html << %(</span>)
23
+ vars[:items] = [items.to_i, vars.key?(:max_items) ? vars[:max_items] : DEFAULT[:max_items]].compact.min
24
+ end
49
25
  end
50
26
 
27
+ # Additions for the Frontend module
28
+ module Frontend
29
+ ITEMS_PLACEHOLDER = '__pagy_items__'
30
+
31
+ # Return the items selector HTML. For example "Show [20] items per page"
32
+ def pagy_items_selector_js(pagy, pagy_id: nil, item_name: nil, i18n_key: nil, link_extra: '')
33
+ return '' unless pagy.vars[:items_extra]
34
+
35
+ p_id = %( id="#{pagy_id}") if pagy_id
36
+ p_vars = pagy.vars
37
+ p_items = p_vars[:items]
38
+ p_vars[:items] = ITEMS_PLACEHOLDER
39
+ link = pagy_marked_link(pagy_link_proc(pagy, link_extra: link_extra))
40
+ p_vars[:items] = p_items # restore the items
41
+
42
+ html = +%(<span#{p_id} class="pagy-items-selector-js" #{pagy_data(pagy, :selector, pagy.from, link)}>)
43
+ input = %(<input type="number" min="1" max="#{p_vars[:max_items]}" value="#{
44
+ p_items}" style="padding: 0; text-align: center; width: #{p_items.to_s.length + 1}rem;">)
45
+ html << pagy_t('pagy.items_selector_js', item_name: item_name || pagy_t(i18n_key || p_vars[:i18n_key], count: p_items),
46
+ items_input: input,
47
+ count: p_items)
48
+ html << %(</span>)
49
+ end
50
+ end
51
51
  end
52
+ Backend.prepend ItemsExtra::Backend
53
+ Frontend.prepend ItemsExtra::Frontend
52
54
  end