pagy 4.11.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
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