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,38 +1,37 @@
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/countless
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require 'pagy'
4
5
 
5
6
  class Pagy
6
-
7
+ # No need to know the count to paginate
7
8
  class Countless < Pagy
8
-
9
- INSTANCE_VARS_MIN = { items: 1, page: 1, outset: 0 }.freeze
10
-
11
9
  # Merge and validate the options, do some simple arithmetic and set a few instance variables
12
- def initialize(vars={}) # rubocop:disable Lint/MissingSuper
13
- @vars = VARS.merge(vars.delete_if{|_,v| v.nil? || v == '' }) # default vars + cleaned vars (can be overridden)
14
- @vars[:fragment] = Pagy.deprecated_var(:anchor, @vars[:anchor], :fragment, @vars[:fragment]) if @vars[:anchor]
15
-
16
- INSTANCE_VARS_MIN.each do |k,min| # validate instance variables
17
- raise VariableError.new(self), "expected :#{k} >= #{min}; got #{@vars[k].inspect}" \
18
- unless @vars[k] && instance_variable_set(:"@#{k}", @vars[k].to_i) >= min
19
- end
20
- @offset = @items * (@page - 1) + @outset # pagination offset + outset (initial offset)
10
+ def initialize(vars = {}) # rubocop:disable Lint/MissingSuper
11
+ normalize_vars(vars)
12
+ setup_vars(page: 1, outset: 0)
13
+ setup_items_var
14
+ setup_offset_var
15
+ setup_params_var
21
16
  end
22
17
 
23
- # Finalize the instance variables based on the fetched items
24
- def finalize(fetched)
25
- raise OverflowError.new(self), "page #{@page} got no items" \
26
- if fetched.zero? && @page > 1
18
+ # Finalize the instance variables based on the fetched size
19
+ def finalize(fetched_size)
20
+ raise OverflowError.new(self, :page, "to be < #{@page}", @page) if fetched_size.zero? && @page > 1
27
21
 
28
- @pages = @last = (fetched > @items ? @page + 1 : @page) # set the @pages and @last
29
- @items = fetched if fetched < @items && fetched.positive? # adjust items for last non-empty page
30
- @from = fetched.zero? ? 0 : @offset + 1 - @outset # page begins from item
31
- @to = fetched.zero? ? 0 : @offset + @items - @outset # page ends to item
32
- @prev = (@page-1 unless @page == 1) # nil if no prev page
33
- @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1 # nil if no next page, 1 if :cycle
22
+ @pages = @last = (fetched_size > @items ? @page + 1 : @page)
23
+ @in = [fetched_size, @items].min
24
+ @from = @in.zero? ? 0 : @offset - @outset + 1
25
+ @to = @offset - @outset + @in
26
+ @prev = (@page - 1 unless @page == 1)
27
+ @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
34
28
  self
35
29
  end
36
30
 
31
+ # Override the original series.
32
+ # Return nil if :countless_minimal is enabled
33
+ def series(*, **)
34
+ super unless @vars[:countless_minimal]
35
+ end
37
36
  end
38
37
  end
@@ -1,27 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Pagy
4
-
5
- # generic variable error
4
+ # Generic variable error
6
5
  class VariableError < ArgumentError
7
- attr_reader :pagy
8
-
9
- def initialize(pagy)
10
- super
11
- @pagy = pagy
12
- end
6
+ attr_reader :pagy, :variable, :value
13
7
 
14
- def variable
15
- message =~ /expected :(\w+)/
16
- Regexp.last_match(1)&.to_sym
17
- end
18
-
19
- def value
20
- pagy.vars[variable]
8
+ # Set the variables and prepare the message
9
+ def initialize(pagy, variable, description, value)
10
+ @pagy = pagy
11
+ @variable = variable
12
+ @value = value
13
+ super "expected :#{@variable} #{description}; got #{@value.inspect}"
21
14
  end
22
15
  end
23
16
 
24
- # specific overflow error
17
+ # Specific overflow error
25
18
  class OverflowError < VariableError; end
26
19
 
20
+ # I18n configuration error
21
+ class I18nError < StandardError; end
22
+
23
+ # Generic internal error
24
+ class InternalError < StandardError; end
27
25
  end
@@ -1,22 +1,26 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/arel
2
2
  # frozen_string_literal: true
3
3
 
4
- class Pagy
5
- module Backend
4
+ class Pagy # :nodoc:
5
+ # Better performance of grouped ActiveRecord collections
6
+ module ArelExtra
6
7
  private
7
8
 
8
- def pagy_arel(collection, vars={})
9
+ # Return Pagy object and paginated collection/results
10
+ def pagy_arel(collection, vars = {})
9
11
  pagy = Pagy.new(pagy_arel_get_vars(collection, vars))
10
- [ pagy, pagy_get_items(collection, pagy) ]
12
+ [pagy, pagy_get_items(collection, pagy)]
11
13
  end
12
14
 
15
+ # Sub-method called only by #pagy_arel: here for easy customization of variables by overriding
13
16
  def pagy_arel_get_vars(collection, vars)
14
- pagy_set_items_from_params(vars) if defined?(UseItemsExtra)
17
+ pagy_set_items_from_params(vars) if defined?(ItemsExtra)
15
18
  vars[:count] ||= pagy_arel_count(collection)
16
- vars[:page] ||= params[ vars[:page_param] || VARS[:page_param] ]
19
+ vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
17
20
  vars
18
21
  end
19
22
 
23
+ # Count using Arel when grouping
20
24
  def pagy_arel_count(collection)
21
25
  if collection.group_values.empty?
22
26
  # COUNT(*)
@@ -27,6 +31,6 @@ class Pagy
27
31
  collection.unscope(:order).limit(1).pluck(sql).first.to_i
28
32
  end
29
33
  end
30
-
31
34
  end
35
+ Backend.prepend ArelExtra
32
36
  end
@@ -1,24 +1,24 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/array
2
2
  # frozen_string_literal: true
3
3
 
4
- class Pagy
5
- # Add specialized backend methods to paginate array collections
6
- module Backend
4
+ class Pagy # :nodoc:
5
+ # Paginate arrays efficiently, avoiding expensive array-wrapping and without overriding
6
+ module ArrayExtra
7
7
  private
8
8
 
9
- # Return Pagy object and items
10
- def pagy_array(array, vars={})
9
+ # Return Pagy object and paginated items
10
+ def pagy_array(array, vars = {})
11
11
  pagy = Pagy.new(pagy_array_get_vars(array, vars))
12
- [ pagy, array[pagy.offset, pagy.items] ]
12
+ [pagy, array[pagy.offset, pagy.items]]
13
13
  end
14
14
 
15
15
  # Sub-method called only by #pagy_array: here for easy customization of variables by overriding
16
16
  def pagy_array_get_vars(array, vars)
17
- pagy_set_items_from_params(vars) if defined?(UseItemsExtra)
17
+ pagy_set_items_from_params(vars) if defined?(ItemsExtra)
18
18
  vars[:count] ||= array.size
19
- vars[:page] ||= params[ vars[:page_param] || VARS[:page_param] ]
19
+ vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
20
20
  vars
21
21
  end
22
-
23
22
  end
23
+ Backend.prepend ArrayExtra
24
24
  end
@@ -1,86 +1,93 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/bootstrap
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 BootstrapExtra
9
10
  # Pagination for bootstrap: it returns the html with the series of links to the pages
10
- def pagy_bootstrap_nav(pagy, pagy_id: nil, link_extra: '')
11
+ def pagy_bootstrap_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: %(class="page-link" #{link_extra}))
13
14
 
14
- html = +%(<nav#{p_id} class="pagy-bootstrap-nav" aria-label="pager"><ul class="pagination">)
15
+ html = +%(<nav#{p_id} class="pagy-bootstrap-nav"><ul class="pagination">)
15
16
  html << pagy_bootstrap_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
- when Integer then %(<li class="page-item">#{link.call item}</li>)
19
- when String then %(<li class="page-item active">#{link.call item}</li>)
20
- when :gap then %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.gap'}</a></li>)
19
+ when Integer
20
+ %(<li class="page-item">#{link.call item}</li>)
21
+ when String
22
+ %(<li class="page-item active">#{link.call item}</li>)
23
+ when :gap
24
+ %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.gap'}</a></li>)
25
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
21
26
  end
22
27
  end
23
28
  html << pagy_bootstrap_next_html(pagy, link)
24
29
  html << %(</ul></nav>)
25
30
  end
26
31
 
27
- # Javascript pagination for bootstrap: it returns a nav and a JSON tag used by the Pagy.nav javascript
28
- def pagy_bootstrap_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
32
+ # Javascript pagination for bootstrap: it returns a nav and a JSON tag used by the pagy.js file
33
+ def pagy_bootstrap_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
34
+ sequels = pagy.sequels(**vars)
30
35
  p_id = %( id="#{pagy_id}") if pagy_id
31
36
  link = pagy_link_proc(pagy, link_extra: %(class="page-link" #{link_extra}))
32
37
  tags = { 'before' => %(<ul class="pagination">#{pagy_bootstrap_prev_html pagy, link}),
33
- 'link' => %(<li class="page-item">#{mark = link.call(PAGE_PLACEHOLDER)}</li>),
38
+ 'link' => %(<li class="page-item">#{mark = link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER)}</li>),
34
39
  'active' => %(<li class="page-item active">#{mark}</li>),
35
40
  'gap' => %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.gap'}</a></li>),
36
41
  'after' => %(#{pagy_bootstrap_next_html pagy, link}</ul>) }
37
42
 
38
- %(<nav#{p_id} class="pagy-njs pagy-bootstrap-nav-js" aria-label="pager" #{pagy_json_attr(pagy, :nav, tags, pagy.sequels(steps))}></nav>)
43
+ %(<nav#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-bootstrap-nav-js" #{
44
+ pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></nav>)
39
45
  end
40
46
 
41
- # Javascript combo pagination for bootstrap: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
42
- def pagy_bootstrap_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
47
+ # Javascript combo pagination for bootstrap: it returns a nav and a JSON tag used by the pagy.js file
48
+ def pagy_bootstrap_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
44
49
  p_id = %( id="#{pagy_id}") if pagy_id
45
50
  link = pagy_link_proc(pagy, link_extra: link_extra)
46
51
  p_page = pagy.page
47
52
  p_pages = pagy.pages
48
- input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" class="text-primary" style="padding: 0; border: none; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
53
+ input = %(<input type="number" min="1" max="#{p_pages}" value="#{
54
+ p_page}" class="text-primary" style="padding: 0; border: none; text-align: center; width: #{
55
+ p_pages.to_s.length + 1}rem;">)
49
56
 
50
- %(<nav#{p_id} class="pagy-bootstrap-combo-nav-js pagination" aria-label="pager"><div class="btn-group" role="group" #{
51
- pagy_json_attr pagy, :combo_nav, p_page, pagy_marked_link(link)
52
- }>#{
57
+ %(<nav#{p_id} class="pagy-bootstrap-combo-nav-js pagination"><div class="btn-group" role="group" #{
58
+ pagy_data(pagy, :combo, pagy_marked_link(link))}>#{
53
59
  if (p_prev = pagy.prev)
54
60
  link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous" class="prev btn btn-primary"'
55
61
  else
56
62
  %(<a class="prev btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.prev')}</a>)
57
63
  end
58
- }<div class="pagy-combo-input btn btn-primary disabled" style="white-space: nowrap;">#{pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</div>#{
64
+ }<div class="pagy-combo-input btn btn-primary disabled" style="white-space: nowrap;">#{
65
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</div>#{
59
66
  if (p_next = pagy.next)
60
67
  link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next" class="next btn btn-primary"'
61
68
  else
62
- %(<a class="next btn btn-primary disabled" href="#">#{pagy_t 'pagy.nav.next' }</a>)
69
+ %(<a class="next btn btn-primary disabled" href="#">#{pagy_t 'pagy.nav.next'}</a>)
63
70
  end
64
- }</div></nav>)
71
+ }</div></nav>)
65
72
  end
66
73
 
67
74
  private
68
75
 
69
- def pagy_bootstrap_prev_html(pagy, link)
70
- if (p_prev = pagy.prev)
71
- %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
72
- else
73
- %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.prev'}</a></li>)
74
- end
76
+ def pagy_bootstrap_prev_html(pagy, link)
77
+ if (p_prev = pagy.prev)
78
+ %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
79
+ else
80
+ %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.prev'}</a></li>)
75
81
  end
82
+ end
76
83
 
77
- def pagy_bootstrap_next_html(pagy, link)
78
- if (p_next = pagy.next)
79
- %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
80
- else
81
- %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.next'}</a></li>)
82
- end
84
+ def pagy_bootstrap_next_html(pagy, link)
85
+ if (p_next = pagy.next)
86
+ %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
87
+ else
88
+ %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.next'}</a></li>)
83
89
  end
84
-
90
+ end
85
91
  end
92
+ Frontend.prepend BootstrapExtra
86
93
  end
@@ -1,82 +1,94 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/bulma
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pagy/extras/shared'
4
+ require 'pagy/extras/frontend_helpers'
5
5
 
6
- class Pagy
7
- module Frontend
8
-
9
- # Pagination for Bulma: it returns the html with the series of links to the pages
10
- def pagy_bulma_nav(pagy, pagy_id: nil, link_extra: '')
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 BulmaExtra
10
+ # Pagination for bulma: it returns the html with the series of links to the pages
11
+ def pagy_bulma_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-bulma-nav pagination is-centered" aria-label="pagination">)
15
16
  html << pagy_bulma_prev_next_html(pagy, link)
16
17
  html << %(<ul class="pagination-list">)
17
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
18
+ pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
18
19
  html << case item
19
- when Integer then %(<li>#{link.call item, item, %(class="pagination-link" aria-label="goto page #{item}") }</li>) # page link
20
- when String then %(<li>#{link.call item, item, %(class="pagination-link is-current" aria-label="page #{item}" aria-current="page")}</li>) # active page
21
- when :gap then %(<li><span class="pagination-ellipsis">#{pagy_t 'pagy.nav.gap'}</span></li>) # page gap
20
+ when Integer
21
+ %(<li>#{link.call item, pagy.label_for(item), %(class="pagination-link" aria-label="goto page #{item}")}</li>)
22
+ when String
23
+ %(<li>#{link.call item, pagy.label_for(item),
24
+ %(class="pagination-link is-current" aria-label="page #{item}" aria-current="page")}</li>)
25
+ when :gap
26
+ %(<li><span class="pagination-ellipsis">#{pagy_t 'pagy.nav.gap'}</span></li>)
27
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
22
28
  end
23
29
  end
24
30
  html << %(</ul></nav>)
25
31
  end
26
32
 
27
- def pagy_bulma_nav_js(pagy, deprecated_id=nil, pagy_id: nil, link_extra: '', steps: nil)
28
- pagy_id = Pagy.deprecated_arg(:id, deprecated_id, :pagy_id, pagy_id) if deprecated_id
33
+ # Javascript pagination for bulma: it returns a nav and a JSON tag used by the Pagy.nav javascript
34
+ def pagy_bulma_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
35
+ sequels = pagy.sequels(**vars)
29
36
  p_id = %( id="#{pagy_id}") if pagy_id
30
37
  link = pagy_link_proc(pagy, link_extra: link_extra)
31
38
  tags = { 'before' => %(#{pagy_bulma_prev_next_html(pagy, link)}<ul class="pagination-list">),
32
- 'link' => %(<li>#{link.call PAGE_PLACEHOLDER, PAGE_PLACEHOLDER, %(class="pagination-link" aria-label="goto page #{PAGE_PLACEHOLDER}")}</li>),
33
- 'active' => %(<li>#{link.call PAGE_PLACEHOLDER, PAGE_PLACEHOLDER, %(class="pagination-link is-current" aria-current="page" aria-label="page #{PAGE_PLACEHOLDER}")}</li>),
34
- 'gap' => %(<li><span class="pagination-ellipsis">#{pagy_t 'pagy.nav.gap' }</span></li>),
39
+ 'link' => %(<li>#{link.call PAGE_PLACEHOLDER, LABEL_PLACEHOLDER,
40
+ %(class="pagination-link" aria-label="goto page #{PAGE_PLACEHOLDER}")}</li>),
41
+ 'active' => %(<li>#{link.call PAGE_PLACEHOLDER, LABEL_PLACEHOLDER,
42
+ %(class="pagination-link is-current" aria-current="page" aria-label="page #{
43
+ PAGE_PLACEHOLDER}")}</li>),
44
+ 'gap' => %(<li><span class="pagination-ellipsis">#{pagy_t 'pagy.nav.gap'}</span></li>),
35
45
  'after' => '</ul>' }
36
46
 
37
- %(<nav#{p_id} class="pagy-njs pagy-bulma-nav-js pagination is-centered" aria-label="pagination" #{pagy_json_attr(pagy, :nav, tags, pagy.sequels(steps))}></nav>)
47
+ %(<nav#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-bulma-nav-js pagination is-centered" aria-label="pagination" #{
48
+ pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></nav>)
38
49
  end
39
50
 
40
- # Javascript combo pagination for Bulma: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
41
- def pagy_bulma_combo_nav_js(pagy, deprecated_id=nil, pagy_id: nil, link_extra: '')
42
- pagy_id = Pagy.deprecated_arg(:id, deprecated_id, :pagy_id, pagy_id) if deprecated_id
51
+ # Javascript combo pagination for bulma: it returns a nav and a JSON tag used by the pagy.js file
52
+ def pagy_bulma_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
43
53
  p_id = %( id="#{pagy_id}") if pagy_id
44
54
  link = pagy_link_proc(pagy, link_extra: link_extra)
45
55
  p_page = pagy.page
46
56
  p_pages = pagy.pages
47
- input = %(<input class="input" type="number" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length+1}rem; margin:0 0.3rem;">)
57
+ input = %(<input class="input" type="number" min="1" max="#{p_pages}" value="#{
58
+ p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length + 1}rem; margin:0 0.3rem;">)
48
59
 
49
- %(<nav#{p_id} class="pagy-bulma-combo-nav-js" aria-label="pagination"><div class="field is-grouped is-grouped-centered" role="group" #{
50
- pagy_json_attr pagy, :combo_nav, p_page, pagy_marked_link(link)
51
- }>#{
60
+ html = %(<nav#{p_id} class="pagy-bulma-combo-nav-js" aria-label="pagination">)
61
+ %(#{html}<div class="field is-grouped is-grouped-centered" role="group" #{
62
+ pagy_data(pagy, :combo, pagy_marked_link(link))}>#{
52
63
  if (p_prev = pagy.prev)
53
64
  %(<p class="control">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'class="button" aria-label="previous page"'}</p>)
54
65
  else
55
66
  %(<p class="control"><a class="button" disabled>#{pagy_t 'pagy.nav.prev'}</a></p>)
56
67
  end
57
- }<div class="pagy-combo-input control level is-mobile">#{pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</div>#{
68
+ }<div class="pagy-combo-input control level is-mobile">#{
69
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</div>#{
58
70
  if (p_next = pagy.next)
59
71
  %(<p class="control">#{link.call p_next, pagy_t('pagy.nav.next'), 'class="button" aria-label="next page"'}</p>)
60
72
  else
61
73
  %(<p class="control"><a class="button" disabled>#{pagy_t 'pagy.nav.next'}</a></p>)
62
74
  end
63
- }</div></nav>)
75
+ }</div></nav>)
64
76
  end
65
77
 
66
78
  private
67
79
 
68
- def pagy_bulma_prev_next_html(pagy, link)
69
- html = +if (p_prev = pagy.prev)
70
- link.call p_prev, pagy_t('pagy.nav.prev'), 'class="pagination-previous" aria-label="previous page"'
71
- else
72
- %(<a class="pagination-previous" disabled>#{pagy_t 'pagy.nav.prev'}</a>)
73
- end
74
- html << if (p_next = pagy.next)
75
- link.call p_next, pagy_t('pagy.nav.next'), 'class="pagination-next" aria-label="next page"'
76
- else
77
- %(<a class="pagination-next" disabled>#{pagy_t 'pagy.nav.next' }</a>)
78
- end
79
- end
80
-
80
+ def pagy_bulma_prev_next_html(pagy, link)
81
+ html = +if (p_prev = pagy.prev)
82
+ link.call p_prev, pagy_t('pagy.nav.prev'), 'class="pagination-previous" aria-label="previous page"'
83
+ else
84
+ %(<a class="pagination-previous" disabled>#{pagy_t 'pagy.nav.prev'}</a>)
85
+ end
86
+ html << if (p_next = pagy.next)
87
+ link.call p_next, pagy_t('pagy.nav.next'), 'class="pagination-next" aria-label="next page"'
88
+ else
89
+ %(<a class="pagination-next" disabled>#{pagy_t 'pagy.nav.next'}</a>)
90
+ end
91
+ end
81
92
  end
93
+ Frontend.prepend BulmaExtra
82
94
  end
@@ -0,0 +1,49 @@
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/calendar
2
+ # frozen_string_literal: true
3
+
4
+ require 'pagy/calendar'
5
+ require 'pagy/calendar/helper'
6
+
7
+ class Pagy # :nodoc:
8
+ # Add pagination filtering by calendar unit (:year, :quarter, :month, :week, :day) to the regular pagination
9
+ module CalendarExtra
10
+ # Additions for the Backend module
11
+ module Backend
12
+ CONF_KEYS = (Calendar::UNITS + %i[pagy active]).freeze
13
+
14
+ private
15
+
16
+ # Take a collection and a conf Hash with keys in CONF_KEYS and return an array with 3 items: [calendar, pagy, results]
17
+ def pagy_calendar(collection, conf)
18
+ unless conf.is_a?(Hash) && (conf.keys - CONF_KEYS).empty? && conf.all? { |k, v| v.is_a?(Hash) || k == :active }
19
+ raise ArgumentError, "keys must be in #{CONF_KEYS.inspect} and object values must be Hashes; got #{conf.inspect}"
20
+ end
21
+
22
+ conf[:pagy] = {} unless conf[:pagy] # use default Pagy object when omitted
23
+ unless conf.key?(:active) && !conf[:active]
24
+ calendar, from, to = Calendar::Helper.send(:init, conf, pagy_calendar_period(collection), params)
25
+ collection = pagy_calendar_filter(collection, from, to)
26
+ end
27
+ pagy, results = send(conf[:pagy][:backend] || :pagy, collection, conf[:pagy]) # use backend: :pagy when omitted
28
+ [calendar, pagy, results]
29
+ end
30
+
31
+ def pagy_calendar_url_at(calendar, time)
32
+ pagy_url_for(calendar.send(:last_object_at, time), 1)
33
+ end
34
+
35
+ # This method must be implemented by the application
36
+ def pagy_calendar_period(*)
37
+ raise NoMethodError, 'the pagy_calendar_period method must be implemented by the application ' \
38
+ '(see https://ddnexus.github.io/pagy/extras/calendar#pagy_calendar_periodcollection)'
39
+ end
40
+
41
+ # This method must be implemented by the application
42
+ def pagy_calendar_filter(*)
43
+ raise NoMethodError, 'the pagy_calendar_filter method must be implemented by the application ' \
44
+ '(see https://ddnexus.github.io/pagy/extras/calendar#pagy_calendar_filtercollection-from-to)'
45
+ end
46
+ end
47
+ end
48
+ Backend.prepend CalendarExtra::Backend
49
+ end
@@ -3,38 +3,35 @@
3
3
 
4
4
  require 'pagy/countless'
5
5
 
6
- class Pagy
6
+ class Pagy # :nodoc:
7
+ DEFAULT[:countless_minimal] = false
7
8
 
8
- VARS[:countless_minimal] = false
9
-
10
- module Backend
11
- private # the whole module is private so no problem with including it in a controller
9
+ # Paginate without the need of any count, saving one query per rendering
10
+ module CountlessExtra
11
+ private
12
12
 
13
13
  # Return Pagy object and items
14
- def pagy_countless(collection, vars={})
15
- pagy = Pagy::Countless.new(pagy_countless_get_vars(collection, vars))
16
- [ pagy, pagy_countless_get_items(collection, pagy) ]
14
+ def pagy_countless(collection, vars = {})
15
+ pagy = Countless.new(pagy_countless_get_vars(collection, vars))
16
+ [pagy, pagy_countless_get_items(collection, pagy)]
17
17
  end
18
18
 
19
19
  # Sub-method called only by #pagy_countless: here for easy customization of variables by overriding
20
20
  def pagy_countless_get_vars(_collection, vars)
21
- pagy_set_items_from_params(vars) if defined?(UseItemsExtra)
22
- vars[:page] ||= params[ vars[:page_param] || VARS[:page_param] ]
21
+ pagy_set_items_from_params(vars) if defined?(ItemsExtra)
22
+ vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
23
23
  vars
24
24
  end
25
25
 
26
26
  # Sub-method called only by #pagy_countless: here for easy customization of record-extraction by overriding
27
+ # You may need to override this method for collections without offset|limit
27
28
  def pagy_countless_get_items(collection, pagy)
28
- # This should work with ActiveRecord, Sequel, Mongoid...
29
29
  return collection.offset(pagy.offset).limit(pagy.items) if pagy.vars[:countless_minimal]
30
30
 
31
- items = collection.offset(pagy.offset).limit(pagy.items + 1).to_a
32
- items_size = items.size
33
- items.pop if items_size == pagy.items + 1
34
- # finalize may adjust pagy.items, so must be used after checking the size
35
- pagy.finalize(items_size)
36
- items
31
+ fetched = collection.offset(pagy.offset).limit(pagy.items + 1).to_a # eager load items + 1
32
+ pagy.finalize(fetched.size) # finalize the pagy object
33
+ fetched[0, pagy.items] # ignore eventual extra item
37
34
  end
38
-
39
35
  end
36
+ Backend.prepend CountlessExtra
40
37
  end