pagy 3.10.0 → 5.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/lib/config/pagy.rb +121 -52
  4. data/lib/javascripts/pagy-dev.js +117 -0
  5. data/lib/javascripts/pagy.js +1 -106
  6. data/lib/javascripts/pagy.mjs +118 -0
  7. data/lib/locales/ar.yml +26 -0
  8. data/lib/locales/bg.yml +2 -2
  9. data/lib/locales/bs.yml +24 -0
  10. data/lib/locales/ca.yml +2 -2
  11. data/lib/locales/cs.yml +2 -2
  12. data/lib/locales/da.yml +2 -2
  13. data/lib/locales/de.yml +2 -2
  14. data/lib/locales/en.yml +2 -2
  15. data/lib/locales/es.yml +2 -2
  16. data/lib/locales/fr.yml +2 -2
  17. data/lib/locales/hr.yml +24 -0
  18. data/lib/locales/id.yml +2 -2
  19. data/lib/locales/it.yml +2 -2
  20. data/lib/locales/ja.yml +2 -2
  21. data/lib/locales/km.yml +2 -2
  22. data/lib/locales/ko.yml +2 -2
  23. data/lib/locales/nb.yml +2 -2
  24. data/lib/locales/nl.yml +2 -2
  25. data/lib/locales/pl.yml +2 -2
  26. data/lib/locales/pt-BR.yml +2 -2
  27. data/lib/locales/pt.yml +2 -2
  28. data/lib/locales/ru.yml +2 -2
  29. data/lib/locales/sr.yml +23 -0
  30. data/lib/locales/sv-SE.yml +2 -2
  31. data/lib/locales/sv.yml +2 -2
  32. data/lib/locales/sw.yml +22 -0
  33. data/lib/locales/ta.yml +22 -0
  34. data/lib/locales/tr.yml +2 -2
  35. data/lib/locales/uk.yml +24 -0
  36. data/lib/locales/zh-CN.yml +2 -2
  37. data/lib/locales/zh-HK.yml +2 -2
  38. data/lib/locales/zh-TW.yml +3 -3
  39. data/lib/pagy/backend.rb +11 -12
  40. data/lib/pagy/calendar/day.rb +29 -0
  41. data/lib/pagy/calendar/month.rb +16 -0
  42. data/lib/pagy/calendar/month_mixin.rb +49 -0
  43. data/lib/pagy/calendar/quarter.rb +23 -0
  44. data/lib/pagy/calendar/week.rb +39 -0
  45. data/lib/pagy/calendar/year.rb +29 -0
  46. data/lib/pagy/calendar.rb +90 -0
  47. data/lib/pagy/console.rb +23 -0
  48. data/lib/pagy/countless.rb +23 -19
  49. data/lib/pagy/exceptions.rb +16 -13
  50. data/lib/pagy/extras/arel.rb +12 -7
  51. data/lib/pagy/extras/array.rb +10 -9
  52. data/lib/pagy/extras/bootstrap.rb +77 -39
  53. data/lib/pagy/extras/bulma.rb +77 -43
  54. data/lib/pagy/extras/calendar.rb +66 -0
  55. data/lib/pagy/extras/countless.rb +17 -17
  56. data/lib/pagy/extras/elasticsearch_rails.rb +66 -37
  57. data/lib/pagy/extras/foundation.rb +74 -41
  58. data/lib/pagy/extras/frontend_helpers.rb +70 -0
  59. data/lib/pagy/extras/gearbox.rb +42 -0
  60. data/lib/pagy/extras/headers.rb +32 -18
  61. data/lib/pagy/extras/i18n.rb +18 -17
  62. data/lib/pagy/extras/items.rb +42 -53
  63. data/lib/pagy/extras/materialize.rb +68 -43
  64. data/lib/pagy/extras/meilisearch.rb +61 -0
  65. data/lib/pagy/extras/metadata.rb +27 -26
  66. data/lib/pagy/extras/navs.rb +54 -29
  67. data/lib/pagy/extras/overflow.rb +57 -52
  68. data/lib/pagy/extras/searchkick.rb +54 -36
  69. data/lib/pagy/extras/semantic.rb +66 -39
  70. data/lib/pagy/extras/standalone.rb +64 -0
  71. data/lib/pagy/extras/support.rb +34 -17
  72. data/lib/pagy/extras/trim.rb +18 -12
  73. data/lib/pagy/extras/uikit.rb +66 -44
  74. data/lib/pagy/frontend.rb +61 -53
  75. data/lib/pagy/i18n.rb +164 -0
  76. data/lib/pagy/url_helpers.rb +38 -0
  77. data/lib/pagy.rb +96 -30
  78. data/lib/templates/bootstrap_nav.html.erb +1 -1
  79. data/lib/templates/bootstrap_nav.html.haml +1 -1
  80. data/lib/templates/bootstrap_nav.html.slim +1 -1
  81. data/lib/templates/foundation_nav.html.erb +1 -1
  82. data/lib/templates/foundation_nav.html.haml +1 -1
  83. data/lib/templates/foundation_nav.html.slim +1 -1
  84. data/lib/templates/uikit_nav.html.erb +2 -2
  85. data/lib/templates/uikit_nav.html.haml +1 -1
  86. data/lib/templates/uikit_nav.html.slim +2 -2
  87. metadata +37 -16
  88. data/lib/locales/README.md +0 -35
  89. data/lib/locales/utils/i18n.rb +0 -25
  90. data/lib/locales/utils/loader.rb +0 -34
  91. data/lib/locales/utils/p11n.rb +0 -88
  92. data/lib/pagy/extras/pagy_search.rb +0 -18
  93. data/lib/pagy/extras/shared.rb +0 -53
  94. data/pagy.gemspec +0 -16
@@ -1,65 +1,54 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/items
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- require 'pagy/extras/shared'
4
+ require 'pagy/extras/frontend_helpers'
6
5
 
7
- 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
8
10
 
9
- # Default variables for this extra
10
- VARS[:items_param] = :items
11
- VARS[:max_items] = 100
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
12
16
 
13
- ITEMS_PLACEHOLDER = '__pagy_items__'
17
+ # Set the items variable considering the params and other pagy variables
18
+ def pagy_set_items_from_params(vars)
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
14
22
 
15
- # Handle a custom number of :items from params
16
- module Backend ; private
17
-
18
- def pagy_with_items(vars)
19
- vars[:items] ||= (items = params[vars[:items_param] || VARS[:items_param]]) && # :items from :items_param
20
- [items.to_i, vars.key?(:max_items) ? vars[:max_items] : VARS[:max_items]].compact.min # :items capped to :max_items
21
- end
22
-
23
- # add the pagy*_get_vars alias-chained methods for frontend, and defined/required extras
24
- [nil, 'countless', 'elasticsearch_rails', 'searchkick'].each do |name|
25
- prefix, if_start, if_end = "_#{name}", "if defined?(Pagy::#{name.upcase})", "end" if name
26
- module_eval <<-RUBY
27
- #{if_start}
28
- alias_method :pagy#{prefix}_get_vars_without_items, :pagy#{prefix}_get_vars
29
- def pagy#{prefix}_get_vars_with_items(collection, vars)
30
- pagy_with_items(vars)
31
- pagy#{prefix}_get_vars_without_items(collection, vars)
32
- end
33
- alias_method :pagy#{prefix}_get_vars, :pagy#{prefix}_get_vars_with_items
34
- #{if_end}
35
- RUBY
36
- end
37
-
38
- end
39
-
40
- module Frontend
41
-
42
- alias_method :pagy_url_for_without_items, :pagy_url_for
43
- def pagy_url_for_with_items(page, pagy, url=false)
44
- p_vars = pagy.vars; params = request.GET.merge(p_vars[:params]); params[p_vars[:page_param].to_s] = page
45
- params[p_vars[:items_param].to_s] = p_vars[:items]
46
- "#{request.base_url if url}#{request.path}?#{Rack::Utils.build_nested_query(pagy_get_params(params))}#{p_vars[:anchor]}"
23
+ vars[:items] = [items.to_i, vars.key?(:max_items) ? vars[:max_items] : DEFAULT[:max_items]].compact.min
24
+ end
47
25
  end
48
- alias_method :pagy_url_for, :pagy_url_for_with_items
49
26
 
50
- # Return the items selector HTML. For example "Show [20] items per page"
51
- def pagy_items_selector_js(pagy, id=pagy_id)
52
- p_vars = pagy.vars
53
- p_items = p_vars[:items]
54
- p_vars[:items] = ITEMS_PLACEHOLDER
55
- link = pagy_marked_link(pagy_link_proc(pagy))
56
- p_vars[:items] = p_items # restore the items
57
-
58
- html = EMPTY + %(<span id="#{id}">)
59
- 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;">)
60
- html << %(#{pagy_t('pagy.items_selector_js', item_name: pagy_t(p_vars[:i18n_key], count: p_items), items_input: input, count: p_items)})
61
- html << %(</span>#{pagy_json_tag(:items_selector, id, pagy.from, link, defined?(TRIM) && p_vars[:page_param])})
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_json_attr 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
62
50
  end
63
-
64
51
  end
52
+ Backend.prepend ItemsExtra::Backend
53
+ Frontend.prepend ItemsExtra::Frontend
65
54
  end
@@ -1,59 +1,84 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/materialize
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- require 'pagy/extras/shared'
6
-
7
- class Pagy
8
- module Frontend
4
+ require 'pagy/extras/frontend_helpers'
9
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 MaterializeExtra
10
10
  # Pagination for materialize: it returns the html with the series of links to the pages
11
- def pagy_materialize_nav(pagy)
12
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
13
- html = EMPTY + (p_prev ? %(<li class="waves-effect prev">#{link.call p_prev, '<i class="material-icons">chevron_left</i>', 'aria-label="previous"'}</li>)
14
- : %(<li class="prev disabled"><a href="#"><i class="material-icons">chevron_left</i></a></li>))
15
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
16
- html << if item.is_a?(Integer); %(<li class="waves-effect">#{link.call item}</li>) # page link
17
- elsif item.is_a?(String) ; %(<li class="active">#{link.call item}</li>) # active page
18
- elsif item == :gap ; %(<li class="gap disabled"><a href="#">#{pagy_t('pagy.nav.gap')}</a></li>) # page gap
11
+ def pagy_materialize_nav(pagy, pagy_id: nil, link_extra: '', **vars)
12
+ p_id = %( id="#{pagy_id}") if pagy_id
13
+ link = pagy_link_proc(pagy, link_extra: link_extra)
14
+
15
+ html = +%(<div#{p_id} class="pagy-materialize-nav pagination" role="navigation" aria-label="pager"><ul class="pagination">)
16
+ html << pagy_materialize_prev_html(pagy, link)
17
+ pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
18
+ html << case item
19
+ when Integer then %(<li class="waves-effect">#{link.call item}</li>) # page link
20
+ when String then %(<li class="active">#{link.call item}</li>) # active page
21
+ when :gap then %(<li class="gap disabled"><a href="#">#{pagy_t 'pagy.nav.gap'}</a></li>) # page gap
22
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
19
23
  end
20
24
  end
21
- html << (p_next ? %(<li class="waves-effect next">#{link.call p_next, '<i class="material-icons">chevron_right</i>', 'aria-label="next"'}</li>)
22
- : %(<li class="next disabled"><a href="#"><i class="material-icons">chevron_right</i></a></li>))
23
- %(<div class="pagy-materialize-nav pagination" role="navigation" aria-label="pager"><ul class="pagination">#{html}</ul></div>)
25
+ html << pagy_materialize_next_html(pagy, link)
26
+ html << %(</ul></div>)
24
27
  end
25
28
 
26
- # Javascript pagination for materialize: it returns a nav and a JSON tag used by the Pagy.nav javascript
27
- def pagy_materialize_nav_js(pagy, id=pagy_id)
28
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
29
- tags = { 'before' => ( '<ul class="pagination">' \
30
- + (p_prev ? %(<li class="waves-effect prev">#{link.call(p_prev, '<i class="material-icons">chevron_left</i>', 'aria-label="previous"')}</li>)
31
- : %(<li class="prev disabled"><a href="#"><i class="material-icons">chevron_left</i></a></li>)) ),
32
- 'link' => %(<li class="waves-effect">#{mark = link.call(PAGE_PLACEHOLDER)}</li>),
29
+ # Javascript pagination for materialize: it returns a nav and a JSON tag used by the pagy.js file
30
+ def pagy_materialize_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
31
+ sequels = pagy.sequels(**vars)
32
+ p_id = %( id="#{pagy_id}") if pagy_id
33
+ link = pagy_link_proc(pagy, link_extra: link_extra)
34
+
35
+ tags = { 'before' => %(<ul class="pagination">#{pagy_materialize_prev_html pagy, link}),
36
+ 'link' => %(<li class="waves-effect">#{mark = link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER)}</li>),
33
37
  'active' => %(<li class="active">#{mark}</li>),
34
- 'gap' => %(<li class="gap disabled"><a href="#">#{pagy_t('pagy.nav.gap')}</a></li>),
35
- 'after' => ( (p_next ? %(<li class="waves-effect next">#{link.call(p_next, '<i class="material-icons">chevron_right</i>', 'aria-label="next"')}</li>)
36
- : %(<li class="next disabled"><a href="#"><i class="material-icons">chevron_right</i></a></li>)) \
37
- + '</ul>' ) }
38
- %(<div id="#{id}" class="pagy-materialize-nav-js" role="navigation" aria-label="pager"></div>#{pagy_json_tag(:nav, id, tags, pagy.sequels, defined?(TRIM) && pagy.vars[:page_param])})
38
+ 'gap' => %(<li class="gap disabled"><a href="#">#{pagy_t 'pagy.nav.gap'}</a></li>),
39
+ 'after' => %(#{pagy_materialize_next_html pagy, link}</ul>) }
40
+
41
+ %(<div#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-materialize-nav-js" role="navigation" aria-label="pager" #{
42
+ pagy_json_attr(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></div>)
39
43
  end
40
44
 
41
- # Javascript combo pagination for materialize: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
42
- def pagy_materialize_combo_nav_js(pagy, id=pagy_id)
43
- link, p_prev, p_next, p_page, p_pages = pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
44
-
45
- html = %(<div id="#{id}" class="pagy-materialize-combo-nav-js pagination" role="navigation" aria-label="pager">) \
46
- + %(<div class="pagy-compact-chip role="group" style="height: 35px; border-radius: 18px; background: #e4e4e4; display: inline-block;">)
47
- html << '<ul class="pagination" style="margin: 0px;">'
48
- li_style = 'style="vertical-align: middle;"'
49
- html << (p_prev ? %(<li class="waves-effect prev" #{li_style}>#{link.call p_prev, '<i class="material-icons">chevron_left</i>', 'aria-label="previous"'}</li>)
50
- : %(<li class="prev disabled" #{li_style}><a href="#"><i class="material-icons">chevron_left</i></a></li>))
51
- input = %(<input type="number" class="browser-default" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 2px; border: none; border-radius: 2px; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
52
- html << %(<div class="pagy-combo-input btn-flat" style="cursor: default; padding: 0px">#{pagy_t('pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages)}</div>)
53
- html << (p_next ? %(<li class="waves-effect next" #{li_style}>#{link.call p_next, '<i class="material-icons">chevron_right</i>', 'aria-label="next"'}</li>)
54
- : %(<li class="next disabled" #{li_style}><a href="#"><i class="material-icons">chevron_right</i></a></li>))
55
- html << %(</ul></div>#{pagy_json_tag(:combo_nav, id, p_page, pagy_marked_link(link), defined?(TRIM) && pagy.vars[:page_param])})
45
+ # Javascript combo pagination for materialize: it returns a nav and a JSON tag used by the pagy.js file
46
+ def pagy_materialize_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
47
+ p_id = %( id="#{pagy_id}") if pagy_id
48
+ link = pagy_link_proc(pagy, link_extra: link_extra)
49
+ p_page = pagy.page
50
+ p_pages = pagy.pages
51
+ style = ' style="vertical-align: middle"'
52
+ input = %(<input type="number" class="browser-default" min="1" max="#{p_pages}" value="#{
53
+ p_page}" style="text-align: center; width: #{p_pages.to_s.length + 1}rem;">)
54
+
55
+ html = %(<ul#{p_id} class="pagy-materialize-combo-nav-js pagination chip" role="navigation")
56
+ %(#{html} aria-label="pager" style="padding-right: 0" #{
57
+ pagy_json_attr pagy, :combo, pagy_marked_link(link)}>#{
58
+ pagy_materialize_prev_html pagy, link, style}<li class="pagy-combo-input">#{
59
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</li>#{
60
+ pagy_materialize_next_html pagy, link, style}</ul>)
56
61
  end
57
62
 
63
+ private
64
+
65
+ def pagy_materialize_prev_html(pagy, link, style = '')
66
+ if (p_prev = pagy.prev)
67
+ %(<li class="waves-effect prev"#{style}>#{
68
+ link.call p_prev, '<i class="material-icons">chevron_left</i>', 'aria-label="previous"'}</li>)
69
+ else
70
+ %(<li class="prev disabled"#{style}><a href="#"><i class="material-icons">chevron_left</i></a></li>)
71
+ end
72
+ end
73
+
74
+ def pagy_materialize_next_html(pagy, link, style = '')
75
+ if (p_next = pagy.next)
76
+ %(<li class="waves-effect next"#{style}>#{
77
+ link.call p_next, '<i class="material-icons">chevron_right</i>', 'aria-label="next"'}</li>)
78
+ else
79
+ %(<li class="next disabled"#{style}><a href="#"><i class="material-icons">chevron_right</i></a></li>)
80
+ end
81
+ end
58
82
  end
83
+ Frontend.prepend MaterializeExtra
59
84
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy # :nodoc:
4
+ DEFAULT[:meilisearch_search_method] ||= :pagy_search
5
+
6
+ # Paginate Meilisearch results
7
+ module MeilisearchExtra
8
+ module Meilisearch # :nodoc:
9
+ # Return an array used to delay the call of #search
10
+ # after the pagination variables are merged to the options
11
+ def pagy_meilisearch(term = nil, **vars)
12
+ [self, term, vars]
13
+ end
14
+ alias_method DEFAULT[:meilisearch_search_method], :pagy_meilisearch
15
+ end
16
+
17
+ # Additions for the Pagy class
18
+ module Pagy
19
+ # Create a Pagy object from a Meilisearch results
20
+ def new_from_meilisearch(results, vars = {})
21
+ vars[:items] = results.raw_answer['limit']
22
+ vars[:page] = [results.raw_answer['offset'] / vars[:items], 1].max
23
+ vars[:count] = results.raw_answer['nbHits']
24
+ new(vars)
25
+ end
26
+ end
27
+
28
+ # Add specialized backend methods to paginate Meilisearch results
29
+ module Backend
30
+ private
31
+
32
+ # Return Pagy object and results
33
+ def pagy_meilisearch(pagy_search_args, vars = {})
34
+ model, term, options = pagy_search_args
35
+ vars = pagy_meilisearch_get_vars(nil, vars)
36
+ options[:limit] = vars[:items]
37
+ options[:offset] = (vars[:page] - 1) * vars[:items]
38
+ results = model.search(term, **options)
39
+ vars[:count] = results.raw_answer['nbHits']
40
+ pagy = ::Pagy.new(vars)
41
+ # with :last_page overflow we need to re-run the method in order to get the hits
42
+ return pagy_meilisearch(pagy_search_args, vars.merge(page: pagy.page)) \
43
+ if defined?(::Pagy::OverflowExtra) && pagy.overflow? && pagy.vars[:overflow] == :last_page
44
+
45
+ [pagy, results]
46
+ end
47
+
48
+ # Sub-method called only by #pagy_meilisearch: here for easy customization of variables by overriding.
49
+ # The _collection argument is not available when the method is called.
50
+ def pagy_meilisearch_get_vars(_collection, vars)
51
+ pagy_set_items_from_params(vars) if defined?(ItemsExtra)
52
+ vars[:items] ||= DEFAULT[:items]
53
+ vars[:page] ||= (params[vars[:page_param] || DEFAULT[:page_param]] || 1).to_i
54
+ vars
55
+ end
56
+ end
57
+ end
58
+ Meilisearch = MeilisearchExtra::Meilisearch
59
+ extend MeilisearchExtra::Pagy
60
+ Backend.prepend MeilisearchExtra::Backend
61
+ end
@@ -1,37 +1,38 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/metadata
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- class Pagy
6
- # Add a specialized backend method for pagination metadata
7
- module Backend ; private
4
+ require 'pagy/url_helpers'
8
5
 
9
- METADATA = [ :scaffold_url, :first_url, :prev_url, :page_url, :next_url, :last_url,
10
- :count, :page, :items, :vars, :pages, :last, :from, :to, :prev, :next, :series ]
11
- METADATA << :sequels if VARS.key?(:steps) # :steps gets defined along with the #sequels method
6
+ class Pagy # :nodoc:
7
+ DEFAULT[:metadata] = %i[ scaffold_url first_url prev_url page_url next_url last_url
8
+ count page items vars pages last in from to prev next series ]
12
9
 
13
- VARS[:metadata] = METADATA.dup
10
+ # Add a specialized backend method for pagination metadata
11
+ module MetadataExtra
12
+ private
14
13
 
15
- include Helpers
14
+ include UrlHelpers
16
15
 
17
- def pagy_metadata(pagy, url=false)
18
- names = pagy.vars[:metadata]
19
- (unknown = names - METADATA).empty? or raise(VariableError.new(pagy), "unknown metadata #{unknown.inspect}")
20
- scaffold_url = pagy_url_for(PAGE_PLACEHOLDER, pagy, url)
21
- metadata = {}
22
- names.each do |key|
23
- metadata[key] = case key
24
- when :scaffold_url ; scaffold_url
25
- when :first_url ; scaffold_url.sub(PAGE_PLACEHOLDER, 1.to_s)
26
- when :prev_url ; scaffold_url.sub(PAGE_PLACEHOLDER, pagy.prev.to_s)
27
- when :page_url ; scaffold_url.sub(PAGE_PLACEHOLDER, pagy.page.to_s)
28
- when :next_url ; scaffold_url.sub(PAGE_PLACEHOLDER, pagy.next.to_s)
29
- when :last_url ; scaffold_url.sub(PAGE_PLACEHOLDER, pagy.last.to_s)
30
- else pagy.send(key)
31
- end
16
+ # Return the metadata hash
17
+ def pagy_metadata(pagy, absolute: nil)
18
+ scaffold_url = pagy_url_for(pagy, PAGE_PLACEHOLDER, absolute: absolute)
19
+ {}.tap do |metadata|
20
+ keys = defined?(Calendar) && pagy.is_a?(Calendar) ? pagy.vars[:metadata] - %i[count items] : pagy.vars[:metadata]
21
+ keys.each do |key|
22
+ metadata[key] = case key
23
+ when :scaffold_url then scaffold_url
24
+ when :first_url then scaffold_url.sub(PAGE_PLACEHOLDER, 1.to_s)
25
+ when :prev_url then scaffold_url.sub(PAGE_PLACEHOLDER, pagy.prev.to_s)
26
+ when :page_url then scaffold_url.sub(PAGE_PLACEHOLDER, pagy.page.to_s)
27
+ when :next_url then scaffold_url.sub(PAGE_PLACEHOLDER, pagy.next.to_s)
28
+ when :last_url then scaffold_url.sub(PAGE_PLACEHOLDER, pagy.last.to_s)
29
+ else pagy.send(key)
30
+ end
31
+ rescue NoMethodError
32
+ raise VariableError.new(pagy, :metadata, 'to contain known keys', key)
33
+ end
32
34
  end
33
- metadata
34
35
  end
35
-
36
36
  end
37
+ Backend.prepend MetadataExtra
37
38
  end
@@ -1,38 +1,63 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/navs
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- require 'pagy/extras/shared'
6
-
7
- class Pagy
8
- module Frontend
9
-
10
- # Javascript pagination: it returns a nav and a JSON tag used by the Pagy.nav javascript
11
- def pagy_nav_js(pagy, id=pagy_id)
12
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
13
- tags = { 'before' => p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
14
- : %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ),
15
- 'link' => %(<span class="page">#{link.call(PAGE_PLACEHOLDER)}</span> ),
16
- 'active' => %(<span class="page active">#{pagy.page}</span> ),
17
- 'gap' => %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> ),
18
- 'after' => p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
19
- : %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>) }
20
- %(<nav id="#{id}" class="pagy-nav-js pagination" role="navigation" aria-label="pager"></nav>#{pagy_json_tag(:nav, id, tags, pagy.sequels, defined?(TRIM) && pagy.vars[:page_param])})
4
+ require 'pagy/extras/frontend_helpers'
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 NavsExtra
10
+ # Javascript pagination: it returns a nav and a JSON tag used by the pagy.js file
11
+ def pagy_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
12
+ sequels = pagy.sequels(**vars)
13
+ p_id = %( id="#{pagy_id}") if pagy_id
14
+ link = pagy_link_proc(pagy, link_extra: link_extra)
15
+ tags = { 'before' => pagy_nav_prev_html(pagy, link),
16
+ 'link' => %(<span class="page">#{link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER)}</span> ),
17
+ 'active' => %(<span class="page active">#{LABEL_PLACEHOLDER}</span> ),
18
+ 'gap' => %(<span class="page gap">#{pagy_t 'pagy.nav.gap'}</span> ),
19
+ 'after' => pagy_nav_next_html(pagy, link) }
20
+
21
+ %(<nav#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-nav-js pagination" aria-label="pager" #{
22
+ pagy_json_attr(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></nav>)
21
23
  end
22
24
 
23
- # Javascript combo pagination: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
24
- def pagy_combo_nav_js(pagy, id=pagy_id)
25
- link, p_prev, p_next, p_page, p_pages = pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
26
-
27
- html = EMPTY + %(<nav id="#{id}" class="pagy-combo-nav-js pagination" role="navigation" aria-label="pager">)
28
- html << (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
29
- : %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ))
30
- input = %(<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;">)
31
- html << %(<span class="pagy-combo-input" style="margin: 0 0.6rem;">#{pagy_t('pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages)}</span> )
32
- html << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
33
- : %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>))
34
- html << %(</nav>#{pagy_json_tag(:combo_nav, id, p_page, pagy_marked_link(link), defined?(TRIM) && pagy.vars[:page_param])})
25
+ # Javascript combo pagination: it returns a nav and a JSON tag used by the pagy.js file
26
+ def pagy_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
27
+ p_id = %( id="#{pagy_id}") if pagy_id
28
+ link = pagy_link_proc(pagy, link_extra: link_extra)
29
+ p_page = pagy.page
30
+ p_pages = pagy.pages
31
+ input = %(<input type="number" min="1" max="#{p_pages}" value="#{
32
+ p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length + 1}rem;">)
33
+
34
+ %(<nav#{p_id} class="pagy-combo-nav-js pagination" aria-label="pager" #{
35
+ pagy_json_attr pagy, :combo, pagy_marked_link(link)}>#{
36
+ pagy_nav_prev_html pagy, link
37
+ }<span class="pagy-combo-input" style="margin: 0 0.6rem;">#{
38
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
39
+ }</span> #{
40
+ pagy_nav_next_html pagy, link
41
+ }</nav>)
35
42
  end
36
43
 
44
+ private
45
+
46
+ def pagy_nav_prev_html(pagy, link)
47
+ if (p_prev = pagy.prev)
48
+ %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
49
+ else
50
+ %(<span class="page prev disabled">#{pagy_t 'pagy.nav.prev'}</span> )
51
+ end
52
+ end
53
+
54
+ def pagy_nav_next_html(pagy, link)
55
+ if (p_next = pagy.next)
56
+ %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
57
+ else
58
+ %(<span class="page next disabled">#{pagy_t 'pagy.nav.next'}</span>)
59
+ end
60
+ end
37
61
  end
62
+ Frontend.prepend NavsExtra
38
63
  end
@@ -1,73 +1,78 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/overflow
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- class Pagy
4
+ class Pagy # :nodoc:
5
+ DEFAULT[:overflow] = :empty_page
6
6
 
7
- OVERFLOW = true
8
-
9
- VARS[:overflow] = :empty_page
10
-
11
- def overflow?; @overflow end
7
+ # Handles OverflowError exceptions for different classes with different options
8
+ module OverflowExtra
9
+ # Support for Pagy class
10
+ module Pagy
11
+ # Is the requested page overflowing?
12
+ def overflow?
13
+ @overflow
14
+ end
12
15
 
13
- alias_method :initialize_without_overflow, :initialize
14
- def initialize_with_overflow(vars)
15
- @overflow ||= false # don't override if :last_page re-run the method after an overflow
16
- initialize_without_overflow(vars)
17
- rescue OverflowError
18
- @overflow = true # add the overflow flag
19
- case @vars[:overflow]
20
- when :exception
21
- raise # same as without the extra
22
- when :last_page
23
- initial_page = @vars[:page] # save the very initial page (even after re-run)
24
- initialize(vars.merge!(page: @last)) # re-run with the last page
25
- @vars[:page] = initial_page # restore the inital page
26
- when :empty_page
27
- @offset = @items = @from = @to = 0 # vars relative to the actual page
28
- @prev = @last # prev relative to the actual page
29
- extend(Series) # special series for :empty_page
30
- else
31
- raise VariableError.new(self), "expected :overflow variable in [:last_page, :empty_page, :exception]; got #{@vars[:overflow].inspect}"
32
- end
33
- end
34
- alias_method :initialize, :initialize_with_overflow
16
+ # Add rescue clause for different behaviors
17
+ def initialize(vars)
18
+ @overflow ||= false # still true if :last_page re-run the method after an overflow
19
+ super
20
+ rescue OverflowError
21
+ @overflow = true # add the overflow flag
22
+ case @vars[:overflow]
23
+ when :exception
24
+ raise # same as without the extra
25
+ when :last_page
26
+ requested_page = @vars[:page] # save the requested page (even after re-run)
27
+ initialize vars.merge!(page: @last) # re-run with the last page
28
+ @vars[:page] = requested_page # restore the requested page
29
+ when :empty_page
30
+ @offset = @items = @from = @to = 0 # vars relative to the actual page
31
+ if defined?(Calendar) && is_a?(Calendar) # only for Calendar instances
32
+ edge = @order == :asc ? @final : @initial # get the edge of the overflow side (neat, but any time would do)
33
+ @from = @to = edge # set both to the edge utc time (a >=&&< query will get no records)
34
+ end
35
+ @prev = @last # prev relative to the actual page
36
+ extend Series # special series for :empty_page
37
+ else
38
+ raise VariableError.new(self, :overflow, 'to be in [:last_page, :empty_page, :exception]', @vars[:overflow])
39
+ end
40
+ end
35
41
 
36
- module Series
37
- def series(size=@vars[:size])
38
- @page = @last # series for last page
39
- super(size).tap do |s| # call original series
40
- s[s.index(@page.to_s)] = @page # string to integer (i.e. no current page)
41
- @page = @vars[:page] # restore the actual page
42
+ # Special series for empty page
43
+ module Series
44
+ def series(*)
45
+ @page = @last # series for last page
46
+ super.tap do |s| # call original series
47
+ s[s.index(@page.to_s)] = @page # string to integer (i.e. no current page)
48
+ @page = @vars[:page] # restore the actual page
49
+ end
50
+ end
42
51
  end
43
52
  end
44
- end
45
53
 
46
-
47
- # support for Pagy::Countless
48
- if defined?(Pagy::Countless)
49
- class Countless
50
-
51
- alias_method :finalize_without_overflow, :finalize
52
- def finalize_with_overflow(items)
54
+ # Support for Pagy::Countless class
55
+ module Countless
56
+ # Add rescue clause for different behaviors
57
+ def finalize(items)
53
58
  @overflow = false
54
- finalize_without_overflow(items)
59
+ super
55
60
  rescue OverflowError
56
- @overflow = true # add the overflow flag
61
+ @overflow = true # add the overflow flag
57
62
  case @vars[:overflow]
58
63
  when :exception
59
- raise # same as without the extra
64
+ raise # same as without the extra
60
65
  when :empty_page
61
- @offset = @items = @from = @to = 0 # vars relative to the actual page
62
- @vars[:size] = [] # no page in the series
66
+ @offset = @items = @from = @to = 0 # vars relative to the actual page
67
+ @vars[:size] = [] # no page in the series
63
68
  self
64
69
  else
65
- raise VariableError.new(self), "expected :overflow variable in [:empty_page, :exception]; got #{@vars[:overflow].inspect}"
70
+ raise VariableError.new(self, :overflow, 'to be in [:empty_page, :exception]', @vars[:overflow])
66
71
  end
67
72
  end
68
- alias_method :finalize, :finalize_with_overflow
69
-
70
73
  end
71
74
  end
72
-
75
+ prepend OverflowExtra::Pagy
76
+ Calendar.prepend OverflowExtra::Pagy if defined?(Calendar)
77
+ Countless.prepend OverflowExtra::Countless if defined?(Countless)
73
78
  end