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,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