pagy 3.8.0 → 4.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/lib/config/pagy.rb +36 -25
  4. data/lib/javascripts/pagy.js +104 -93
  5. data/lib/locales/ar.yml +26 -0
  6. data/lib/locales/bg.yml +2 -2
  7. data/lib/locales/bs.yml +24 -0
  8. data/lib/locales/ca.yml +2 -2
  9. data/lib/locales/cs.yml +22 -0
  10. data/lib/locales/da.yml +2 -2
  11. data/lib/locales/de.yml +2 -2
  12. data/lib/locales/en.yml +2 -2
  13. data/lib/locales/es.yml +2 -2
  14. data/lib/locales/fr.yml +2 -2
  15. data/lib/locales/hr.yml +24 -0
  16. data/lib/locales/id.yml +2 -2
  17. data/lib/locales/it.yml +2 -2
  18. data/lib/locales/ja.yml +2 -2
  19. data/lib/locales/km.yml +2 -2
  20. data/lib/locales/ko.yml +2 -2
  21. data/lib/locales/nb.yml +2 -2
  22. data/lib/locales/nl.yml +2 -2
  23. data/lib/locales/pl.yml +2 -2
  24. data/lib/locales/pt-BR.yml +2 -2
  25. data/lib/locales/pt.yml +22 -0
  26. data/lib/locales/ru.yml +2 -2
  27. data/lib/locales/sr.yml +23 -0
  28. data/lib/locales/sv-SE.yml +2 -2
  29. data/lib/locales/sv.yml +2 -2
  30. data/lib/locales/sw.yml +22 -0
  31. data/lib/locales/tr.yml +2 -2
  32. data/lib/locales/uk.yml +24 -0
  33. data/lib/locales/utils/i18n.rb +3 -11
  34. data/lib/locales/utils/loader.rb +7 -10
  35. data/lib/locales/utils/p11n.rb +49 -17
  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.rb +50 -26
  40. data/lib/pagy/backend.rb +5 -3
  41. data/lib/pagy/console.rb +21 -0
  42. data/lib/pagy/countless.rb +13 -10
  43. data/lib/pagy/deprecation.rb +27 -0
  44. data/lib/pagy/exceptions.rb +7 -2
  45. data/lib/pagy/extras/arel.rb +4 -3
  46. data/lib/pagy/extras/array.rb +4 -3
  47. data/lib/pagy/extras/bootstrap.rb +61 -30
  48. data/lib/pagy/extras/bulma.rb +60 -38
  49. data/lib/pagy/extras/countless.rb +7 -8
  50. data/lib/pagy/extras/elasticsearch_rails.rb +28 -17
  51. data/lib/pagy/extras/foundation.rb +61 -32
  52. data/lib/pagy/extras/headers.rb +13 -9
  53. data/lib/pagy/extras/i18n.rb +11 -5
  54. data/lib/pagy/extras/items.rb +21 -34
  55. data/lib/pagy/extras/materialize.rb +58 -35
  56. data/lib/pagy/extras/meilisearch.rb +55 -0
  57. data/lib/pagy/extras/metadata.rb +27 -21
  58. data/lib/pagy/extras/navs.rb +44 -20
  59. data/lib/pagy/extras/overflow.rb +52 -48
  60. data/lib/pagy/extras/searchkick.rb +27 -16
  61. data/lib/pagy/extras/semantic.rb +55 -30
  62. data/lib/pagy/extras/shared.rb +14 -15
  63. data/lib/pagy/extras/standalone.rb +62 -0
  64. data/lib/pagy/extras/support.rb +26 -9
  65. data/lib/pagy/extras/trim.rb +12 -11
  66. data/lib/pagy/extras/uikit.rb +54 -35
  67. data/lib/pagy/frontend.rb +64 -32
  68. metadata +16 -7
  69. data/lib/locales/README.md +0 -35
  70. data/lib/pagy/extras/pagy_search.rb +0 -18
  71. data/pagy.gemspec +0 -16
@@ -1,5 +1,4 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/semantic
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
4
  require 'pagy/extras/shared'
@@ -8,48 +7,74 @@ class Pagy
8
7
  module Frontend
9
8
 
10
9
  # Pagination for semantic: it returns the html with the series of links to the pages
11
- def pagy_semantic_nav(pagy)
12
- link, p_prev, p_next = pagy_link_proc(pagy, 'class="item"'), pagy.prev, pagy.next
10
+ def pagy_semantic_nav(pagy, pagy_id: nil, link_extra: '')
11
+ p_id = %( id="#{pagy_id}") if pagy_id
12
+ link = pagy_link_proc(pagy, link_extra: %(class="item" #{link_extra}))
13
13
 
14
- html = EMPTY + (p_prev ? %(#{link.call p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"'})
15
- : %(<div class="item disabled"><i class="left small chevron icon"></i></div>))
14
+ html = +%(<div#{p_id} class="pagy-semantic-nav ui pagination menu">)
15
+ html << pagy_semantic_prev_html(pagy, link)
16
16
  pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
- html << if item.is_a?(Integer); %(#{link.call item}) # page link
18
- elsif item.is_a?(String) ; %(<a class="item active">#{item}</a>) # current page
19
- elsif item == :gap ; %(<div class="disabled item">...</div>) # page gap
17
+ html << case item
18
+ when Integer then link.call item # page link
19
+ when String then %(<a class="item active">#{item}</a>) # current page
20
+ when :gap then %(<div class="disabled item">#{pagy_t 'pagy.nav.gap'}</div>) # page gap
20
21
  end
21
22
  end
22
- html << (p_next ? %(#{link.call p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"'})
23
- : %(<div class="item disabled"><i class="right small chevron icon"></i></div>))
24
- %(<div class="pagy-semantic-nav ui pagination menu" aria-label="pager">#{html}</div>)
23
+ html << pagy_semantic_next_html(pagy, link)
24
+ html << %(</div>)
25
25
  end
26
26
 
27
27
  # Javascript pagination for semantic: it returns a nav and a JSON tag used by the Pagy.nav javascript
28
- def pagy_semantic_nav_js(pagy, id=pagy_id)
29
- link, p_prev, p_next = pagy_link_proc(pagy, 'class="item"'), pagy.prev, pagy.next
30
- tags = { 'before' => (p_prev ? %(#{link.call(p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"')})
31
- : %(<div class="item disabled"><i class="left small chevron icon"></i></div>)),
32
- 'link' => %(#{link.call(PAGE_PLACEHOLDER)}),
28
+ def pagy_semantic_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
30
+ p_id = %( id="#{pagy_id}") if pagy_id
31
+ link = pagy_link_proc(pagy, link_extra: %(class="item" #{link_extra}))
32
+ tags = { 'before' => pagy_semantic_prev_html(pagy, link),
33
+ 'link' => link.call(PAGE_PLACEHOLDER),
33
34
  'active' => %(<a class="item active">#{pagy.page}</a>),
34
35
  'gap' => %(<div class="disabled item">#{pagy_t('pagy.nav.gap')}</div>),
35
- 'after' => (p_next ? %(#{link.call(p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"')})
36
- : %(<div class="item disabled"><i class="right small chevron icon"></i></div>)) }
37
- %(<div id="#{id}" class="pagy-semantic-nav-js ui pagination menu" role="navigation" aria-label="pager"></div>#{pagy_json_tag(:nav, id, tags, pagy.sequels, defined?(TRIM) && pagy.vars[:page_param])})
36
+ 'after' => pagy_semantic_next_html(pagy, link) }
37
+
38
+ %(<div#{p_id} class="pagy-njs pagy-semantic-nav-js ui pagination menu" role="navigation" #{pagy_json_attr(pagy, :nav, tags, pagy.sequels(steps))}></div>)
38
39
  end
39
40
 
40
41
  # Combo pagination for semantic: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
41
- def pagy_semantic_combo_nav_js(pagy, id=pagy_id)
42
- link, p_prev, p_next, p_page, p_pages = pagy_link_proc(pagy, 'class="item"'), pagy.prev, pagy.next, pagy.page, pagy.pages
43
-
44
- html = EMPTY + %(<div id="#{id}" class="pagy-semantic-combo-nav-js ui compact menu" role="navigation" aria-label="pager">)
45
- html << (p_prev ? %(#{link.call p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"'})
46
- : %(<div class="item disabled"><i class="left small chevron icon"></i></div>))
47
- 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; margin: 0 0.3rem">)
48
- html << %(<div class="pagy-combo-input item">#{pagy_t('pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages)}</div> )
49
- html << (p_next ? %(#{link.call p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"'})
50
- : %(<div class="item disabled"><i class="right small chevron icon"></i></div>))
51
- html << %(</div>#{pagy_json_tag(:combo_nav, id, p_page, pagy_marked_link(link), defined?(TRIM) && pagy.vars[:page_param])})
42
+ def pagy_semantic_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
+ p_id = %( id="#{pagy_id}") if pagy_id
45
+ link = pagy_link_proc(pagy, link_extra: %(class="item" #{link_extra}))
46
+ p_page = pagy.page
47
+ p_pages = pagy.pages
48
+ 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; margin: 0 0.3rem">)
49
+
50
+ %(<div#{p_id} class="pagy-semantic-combo-nav-js ui compact menu" role="navigation" #{
51
+ pagy_json_attr pagy, :combo_nav, p_page, pagy_marked_link(link)
52
+ }>#{
53
+ pagy_semantic_prev_html pagy, link
54
+ }<div class="pagy-combo-input item">#{
55
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
56
+ }</div> #{
57
+ pagy_semantic_next_html pagy, link
58
+ }</div>)
52
59
  end
53
60
 
61
+ private
62
+
63
+ def pagy_semantic_prev_html(pagy, link)
64
+ if (p_prev = pagy.prev)
65
+ link.call p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"'
66
+ else
67
+ +%(<div class="item disabled"><i class="left small chevron icon"></i></div>)
68
+ end
69
+ end
70
+
71
+ def pagy_semantic_next_html(pagy, link)
72
+ if (p_next = pagy.next)
73
+ link.call p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"'
74
+ else
75
+ +%(<div class="item disabled"><i class="right small chevron icon"></i></div>)
76
+ end
77
+ end
78
+
54
79
  end
55
80
  end
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'digest'
@@ -17,35 +16,35 @@ class Pagy
17
16
  # "350" => [1, 2, :gap, 17, 18, 19, "20", 21, 22, 23, :gap, 49, 50],
18
17
  # "550" => [1, 2, 3, :gap, 16, 17, 18, 19, "20", 21, 22, 23, 24, :gap, 48, 49, 50] }
19
18
  # Notice: if :steps is false it will use the single {0 => @vars[:size]} size
20
- def sequels
21
- steps = @vars[:steps] || {0 => @vars[:size]}
22
- steps.key?(0) or raise(VariableError.new(self), "expected :steps to define the 0 width; got #{steps.inspect}")
23
- sequels = {}; steps.each {|width, size| sequels[width.to_s] = series(size)}; sequels
19
+ def sequels(steps=nil)
20
+ steps ||= @vars[:steps] || {0 => @vars[:size]}
21
+ raise VariableError.new(self), "expected :steps to define the 0 width; got #{steps.inspect}" \
22
+ unless steps.key?(0)
23
+ {}.tap do |sequels|
24
+ steps.each {|width, size| sequels[width.to_s] = series(size)}
25
+ end
24
26
  end
25
27
 
26
28
  module Frontend
27
29
 
28
30
  if defined?(Oj)
29
31
  # it returns a script tag with the JSON-serialized args generated with the faster oj gem
30
- def pagy_json_tag(*args)
31
- %(<script type="application/json" class="pagy-json">#{Oj.dump(args, mode: :strict)}</script>)
32
+ def pagy_json_attr(pagy, *args)
33
+ args << pagy.vars[:page_param] if pagy.vars[:enable_trim_extra]
34
+ %(data-pagy-json="#{Oj.dump(args, mode: :strict).gsub('"', '&quot;')}")
32
35
  end
33
36
  else
34
37
  require 'json'
35
38
  # it returns a script tag with the JSON-serialized args generated with the slower to_json
36
- def pagy_json_tag(*args)
37
- %(<script type="application/json" class="pagy-json">#{args.to_json}</script>)
39
+ def pagy_json_attr(pagy, *args)
40
+ args << pagy.vars[:page_param] if pagy.vars[:enable_trim_extra]
41
+ %(data-pagy-json="#{args.to_json.gsub('"', '&quot;')}")
38
42
  end
39
43
  end
40
44
 
41
- # it returns the SHA1 (fastest on modern ruby) string used as default `id` attribute by all the `*_js` tags
42
- def pagy_id
43
- "pagy-#{Digest::SHA1.hexdigest(caller(2..2)[0].split(':in')[0])}"
44
- end
45
-
46
45
  # it returns the marked link to used by pagy.js
47
46
  def pagy_marked_link(link)
48
- link.call(PAGE_PLACEHOLDER, '', 'style="display: none;"')
47
+ link.call PAGE_PLACEHOLDER, '', 'style="display: none;"'
49
48
  end
50
49
 
51
50
  end
@@ -0,0 +1,62 @@
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/standalone
2
+ # frozen_string_literal: true
3
+
4
+ require 'uri'
5
+ class Pagy
6
+
7
+ # extracted from Rack::Utils and reformatted for rubocop
8
+ module QueryUtils
9
+ module_function
10
+ def escape(str)
11
+ URI.encode_www_form_component(str)
12
+ end
13
+ def build_nested_query(value, prefix = nil)
14
+ case value
15
+ when Array
16
+ value.map { |v| build_nested_query(v, "#{prefix}[]") }.join('&')
17
+ when Hash
18
+ value.map { |k, v| build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) }.delete_if(&:empty?).join('&')
19
+ when nil
20
+ prefix
21
+ else
22
+ raise ArgumentError, 'value must be a Hash' if prefix.nil?
23
+ "#{prefix}=#{escape(value)}"
24
+ end
25
+ end
26
+ end
27
+
28
+ module UseStandaloneExtra
29
+ # without any :url var it works exactly as the regular #pagy_url_for;
30
+ # with a defined :url variable it does not use rack/request
31
+ def pagy_url_for(pagy, page, deprecated_url=nil, absolute: nil)
32
+ absolute = Pagy.deprecated_arg(:url, deprecated_url, :absolute, absolute) if deprecated_url
33
+ pagy, page = Pagy.deprecated_order(pagy, page) if page.is_a?(Pagy)
34
+ p_vars = pagy.vars
35
+ if p_vars[:url]
36
+ url_string = p_vars[:url]
37
+ params = {}
38
+ else
39
+ url_string = "#{request.base_url if absolute}#{request.path}"
40
+ params = request.GET
41
+ end
42
+ params = params.merge(p_vars[:params])
43
+ params[p_vars[:page_param].to_s] = page
44
+ params[p_vars[:items_param].to_s] = p_vars[:items] if defined?(UseItemsExtra)
45
+ query_string = "?#{QueryUtils.build_nested_query(pagy_get_params(params))}" unless params.empty?
46
+ "#{url_string}#{query_string}#{p_vars[:fragment]}"
47
+ end
48
+ end
49
+
50
+ # single line in order to avoid complicating simplecov already tested with other GitHub Actions
51
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') \
52
+ && Helpers.prepend(UseStandaloneExtra) \
53
+ || ( Frontend.prepend(UseStandaloneExtra); Backend.prepend(UseStandaloneExtra) if defined?(Pagy::Backend::METADATA) ) # rubocop:disable Style/Semicolon
54
+
55
+ # defines a dummy #params method if not already defined in the including module
56
+ module Backend
57
+ def self.included(controller)
58
+ controller.define_method(:params){{}} unless controller.method_defined?(:params)
59
+ end
60
+ end
61
+
62
+ end
@@ -1,5 +1,4 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/support
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
4
  class Pagy
@@ -7,21 +6,39 @@ class Pagy
7
6
  module Frontend
8
7
 
9
8
  def pagy_prev_url(pagy)
10
- pagy_url_for(pagy.prev, pagy) if pagy.prev
9
+ pagy_url_for(pagy, pagy.prev) if pagy.prev
11
10
  end
12
11
 
13
12
  def pagy_next_url(pagy)
14
- pagy_url_for(pagy.next, pagy) if pagy.next
13
+ pagy_url_for(pagy, pagy.next) if pagy.next
15
14
  end
16
15
 
17
- def pagy_prev_link(pagy, text = pagy_t('pagy.nav.prev'), link_extra = '')
18
- pagy.prev ? %(<span class="page prev"><a href="#{pagy_prev_url(pagy)}" rel="next" aria-label="next" #{pagy.vars[:link_extra]} #{link_extra}>#{text}</a></span>)
19
- : %(<span class="page prev disabled">#{text}</span>)
16
+ def pagy_prev_link(pagy, deprecated_text=nil, deprecated_link_extra=nil, text: pagy_t('pagy.nav.prev'), link_extra: '')
17
+ text = Pagy.deprecated_arg(:text, deprecated_text, :text, text) if deprecated_text
18
+ link_extra = Pagy.deprecated_arg(:link_extra, deprecated_link_extra, :link_extra, link_extra) if deprecated_link_extra
19
+ if pagy.prev
20
+ %(<span class="page prev"><a href="#{pagy_url_for(pagy, pagy.prev)}" rel="prev" aria-label="previous" #{pagy.vars[:link_extra]} #{link_extra}>#{text}</a></span>)
21
+ else
22
+ %(<span class="page prev disabled">#{text}</span>)
23
+ end
20
24
  end
21
25
 
22
- def pagy_next_link(pagy, text = pagy_t('pagy.nav.next'), link_extra = '')
23
- pagy.next ? %(<span class="page next"><a href="#{pagy_next_url(pagy)}" rel="next" aria-label="next" #{pagy.vars[:link_extra]} #{link_extra}>#{text}</a></span>)
24
- : %(<span class="page next disabled">#{text}</span>)
26
+ def pagy_next_link(pagy, deprecated_text=nil, deprecated_link_extra=nil, text: pagy_t('pagy.nav.next'), link_extra: '')
27
+ text = Pagy.deprecated_arg(:text, deprecated_text, :text, text) if deprecated_text
28
+ link_extra = Pagy.deprecated_arg(:link_extra, deprecated_link_extra, :link_extra, link_extra) if deprecated_link_extra
29
+ if pagy.next
30
+ %(<span class="page next"><a href="#{pagy_url_for(pagy, pagy.next)}" rel="next" aria-label="next" #{pagy.vars[:link_extra]} #{link_extra}>#{text}</a></span>)
31
+ else
32
+ %(<span class="page next disabled">#{text}</span>)
33
+ end
34
+ end
35
+
36
+ def pagy_prev_link_tag(pagy)
37
+ %(<link href="#{pagy_url_for(pagy, pagy.prev)}" rel="prev"/>) if pagy.prev
38
+ end
39
+
40
+ def pagy_next_link_tag(pagy)
41
+ %(<link href="#{pagy_url_for(pagy, pagy.next)}" rel="next"/>) if pagy.next
25
42
  end
26
43
 
27
44
  end
@@ -1,23 +1,24 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/trim
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
4
  class Pagy
6
5
 
7
- module Frontend
6
+ VARS[:enable_trim_extra] = true
8
7
 
9
- TRIM = true # boolean used by *_js helpers
8
+ module UseTrimExtra
10
9
 
11
- alias_method :pagy_link_proc_without_trim, :pagy_link_proc
12
- def pagy_link_proc_with_trim(pagy, link_extra='')
13
- link_proc = pagy_link_proc_without_trim(pagy, link_extra)
14
- re = /[?&]#{pagy.vars[:page_param]}=1\b(?!&)|\b#{pagy.vars[:page_param]}=1&/
15
- lambda do |n, text=n, extra=''|
16
- link = link_proc.call(n, text, extra)
17
- n == 1 ? link.sub(re, '') : link
10
+ def pagy_link_proc(pagy, deprecated_link_extra=nil, link_extra: '')
11
+ link_extra = Pagy.deprecated_arg(:link_extra, deprecated_link_extra, :link_extra, link_extra) if deprecated_link_extra
12
+ link_proc = super(pagy, link_extra: link_extra)
13
+ return link_proc unless pagy.vars[:enable_trim_extra]
14
+ lambda do |num, text=num, extra=''|
15
+ link = +link_proc.call(num, text, extra)
16
+ return link unless num == 1
17
+ link.sub!(/[?&]#{pagy.vars[:page_param]}=1\b(?!&)|\b#{pagy.vars[:page_param]}=1&/, '')
18
18
  end
19
19
  end
20
- alias_method :pagy_link_proc, :pagy_link_proc_with_trim
21
20
 
22
21
  end
22
+ Frontend.prepend UseTrimExtra
23
+
23
24
  end
@@ -1,5 +1,4 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/uikit
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
4
  require 'pagy/extras/shared'
@@ -8,55 +7,75 @@ class Pagy
8
7
  module Frontend
9
8
 
10
9
  # Pagination for uikit: it returns the html with the series of links to the pages
11
- def pagy_uikit_nav(pagy)
12
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
10
+ def pagy_uikit_nav(pagy, pagy_id: nil, link_extra: '')
11
+ p_id = %( id="#{pagy_id}") if pagy_id
12
+ link = pagy_link_proc(pagy, link_extra: link_extra)
13
13
 
14
- previous_span = "<span uk-pagination-previous>#{pagy_t('pagy.nav.prev')}</span>"
15
- html = EMPTY + (p_prev ? %(<li>#{link.call p_prev, previous_span}</li>)
16
- : %(<li class="uk-disabled"><a href="#">#{previous_span}</a></li>))
14
+ html = +%(<ul#{p_id} class="pagy-uikit-nav uk-pagination uk-flex-center">#{pagy_uikit_prev_html pagy, link})
17
15
  pagy.series.each do |item|
18
- html << if item.is_a?(Integer); %(<li>#{link.call item}</li>)
19
- elsif item.is_a?(String) ; %(<li class="uk-active"><span>#{item}</span></li>)
20
- elsif item == :gap ; %(<li class="uk-disabled"><span>#{pagy_t('pagy.nav.gap')}</span></li>)
16
+ html << case item
17
+ when Integer then %(<li>#{link.call item}</li>)
18
+ when String then %(<li class="uk-active"><span>#{item}</span></li>)
19
+ when :gap then %(<li class="uk-disabled"><span>#{pagy_t 'pagy.nav.gap'}</span></li>)
21
20
  end
22
21
  end
23
- next_span = "<span uk-pagination-next>#{pagy_t('pagy.nav.next')}</span>"
24
- html << (p_next ? %(<li>#{link.call p_next, next_span}</li>)
25
- : %(<li class="uk-disabled"><a href="#">#{next_span}</a></li>))
26
- %(<ul class="pagy-uikit-nav uk-pagination uk-flex-center">#{html}</ul>)
22
+ html << pagy_uikit_next_html(pagy, link)
23
+ html << %(</ul>)
27
24
  end
28
25
 
29
26
  # Javascript pagination for uikit: it returns a nav and a JSON tag used by the Pagy.nav javascript
30
- def pagy_uikit_nav_js(pagy, id=pagy_id)
31
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
32
- previous_span = "<span uk-pagination-previous>#{pagy_t('pagy.nav.prev')}</span>"
33
- next_span = "<span uk-pagination-next>#{pagy_t('pagy.nav.next')}</span>"
34
- tags = { 'before' => p_prev ? %(<li>#{link.call p_prev, previous_span}</li>)
35
- : %(<li class="uk-disabled"><a href="#">#{previous_span}</a></li>),
27
+ def pagy_uikit_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
29
+ p_id = %( id="#{pagy_id}") if pagy_id
30
+ link = pagy_link_proc(pagy, link_extra: link_extra)
31
+ tags = { 'before' => pagy_uikit_prev_html(pagy, link),
36
32
  'link' => %(<li>#{link.call(PAGE_PLACEHOLDER)}</li>),
37
33
  'active' => %(<li class="uk-active"><span>#{PAGE_PLACEHOLDER}</span></li>),
38
- 'gap' => %(<li class="uk-disabled"><span>#{pagy_t('pagy.nav.gap')}</span></li>),
39
- 'after' => p_next ? %(<li>#{link.call p_next, next_span}</li>)
40
- : %(<li class="uk-disabled"><a href="#">#{next_span}</a></li>) }
41
- %(<ul id="#{id}" class="pagy-uikit-nav-js uk-pagination uk-flex-center"></ul>#{pagy_json_tag(:nav, id, tags, pagy.sequels, defined?(TRIM) && pagy.vars[:page_param])})
34
+ 'gap' => %(<li class="uk-disabled"><span>#{pagy_t 'pagy.nav.gap'}</span></li>),
35
+ 'after' => pagy_uikit_next_html(pagy, link) }
36
+
37
+ %(<ul#{p_id} class="pagy-njs pagy-uikit-nav-js uk-pagination uk-flex-center" #{pagy_json_attr(pagy, :nav, tags, pagy.sequels(steps))}></ul>)
42
38
  end
43
39
 
44
40
  # Javascript combo pagination for uikit: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
45
- def pagy_uikit_combo_nav_js(pagy, id=pagy_id)
46
- link, p_prev, p_next, p_page, p_pages = pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
41
+ def pagy_uikit_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
43
+ p_id = %( id="#{pagy_id}") if pagy_id
44
+ link = pagy_link_proc(pagy, link_extra: link_extra)
45
+ p_page = pagy.page
46
+ p_pages = pagy.pages
47
+ input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" style="text-align: center; width: #{p_pages.to_s.length+1}rem;">)
48
+
49
+ %(<ul#{p_id} class="pagy-uikit-combo-nav-js uk-button-group uk-pagination uk-flex-center" #{
50
+ pagy_json_attr pagy, :combo_nav, p_page, pagy_marked_link(link)
51
+ }>#{
52
+ pagy_uikit_prev_html pagy, link
53
+ }<li>#{
54
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
55
+ }</li>#{
56
+ pagy_uikit_next_html pagy, link
57
+ }</ul>)
58
+ end
47
59
 
48
- html = %(<div id="#{id}" class="pagy-uikit-combo-nav-js uk-button-group">)
49
- html = html + (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'class="uk-button uk-button-default"')
50
- : %(<button class="uk-button uk-button-default" disabled>#{pagy_t('pagy.nav.prev')}</button>))
60
+ private
51
61
 
52
- html << %(<div class="uk-text-middle uk-margin-left uk-margin-right">)
53
- input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" class="uk-input" style="padding: 0; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
54
- html << pagy_t('pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages) + '</div>'
62
+ def pagy_uikit_prev_html(pagy, link)
63
+ previous_span = %(<span uk-pagination-previous>#{pagy_t 'pagy.nav.prev'}</span>)
64
+ if (p_prev = pagy.prev)
65
+ %(<li>#{link.call p_prev, previous_span}</li>)
66
+ else
67
+ %(<li class="uk-disabled"><a href="#">#{previous_span}</a></li>)
68
+ end
69
+ end
55
70
 
56
- html << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'class="uk-button uk-button-default"')
57
- : %(<button class="uk-button uk-button-default" disabled>#{pagy_t('pagy.nav.next')}</button>))
71
+ def pagy_uikit_next_html(pagy, link)
72
+ next_span = %(<span uk-pagination-next>#{pagy_t 'pagy.nav.next'}</span>)
73
+ if (p_next = pagy.next)
74
+ %(<li>#{link.call p_next, next_span}</li>)
75
+ else
76
+ %(<li class="uk-disabled"><a href="#">#{next_span}</a></li>)
77
+ end
78
+ end
58
79
 
59
- html << %(</div>#{pagy_json_tag(:combo_nav, id, p_page, pagy_marked_link(link), defined?(TRIM) && pagy.vars[:page_param])})
60
- end
61
80
  end
62
81
  end
data/lib/pagy/frontend.rb CHANGED
@@ -1,26 +1,33 @@
1
1
  # See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/api/frontend
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
4
  require 'yaml'
6
5
 
7
6
  class Pagy
8
7
 
9
- PAGE_PLACEHOLDER = '__pagy_page__' # string used for search and replace, hardcoded also in the pagy.js file
8
+ PAGE_PLACEHOLDER = '__pagy_page__' # string used for search and replace, hardcoded also in the pagy.js file
10
9
 
11
10
  # I18n static hash loaded at startup, used as default alternative to the i18n gem.
12
11
  # see https://ddnexus.github.io/pagy/api/frontend#i18n
13
- I18n = eval(Pagy.root.join('locales', 'utils', 'i18n.rb').read) #rubocop:disable Security/Eval
12
+ I18n = eval Pagy.root.join('locales', 'utils', 'i18n.rb').read #rubocop:disable Security/Eval
14
13
 
15
14
  module Helpers
16
15
  # This works with all Rack-based frameworks (Sinatra, Padrino, Rails, ...)
17
- def pagy_url_for(page, pagy, url=false)
18
- p_vars = pagy.vars; params = request.GET.merge(p_vars[:params]); params[p_vars[:page_param].to_s] = page
19
- "#{request.base_url if url}#{request.path}?#{Rack::Utils.build_nested_query(pagy_get_params(params))}#{p_vars[:anchor]}"
16
+ def pagy_url_for(pagy, page, deprecated_url=nil, absolute: nil)
17
+ absolute = Pagy.deprecated_arg(:url, deprecated_url, :absolute, absolute) if deprecated_url
18
+ pagy, page = Pagy.deprecated_order(pagy, page) if page.is_a?(Pagy)
19
+ p_vars = pagy.vars
20
+ params = request.GET.merge(p_vars[:params])
21
+ params[p_vars[:page_param].to_s] = page
22
+ params[p_vars[:items_param].to_s] = p_vars[:items] if defined?(UseItemsExtra)
23
+ query_string = "?#{Rack::Utils.build_nested_query(pagy_get_params(params))}" unless params.empty?
24
+ "#{request.base_url if absolute}#{request.path}#{query_string}#{p_vars[:fragment]}"
20
25
  end
21
26
 
22
27
  # Sub-method called only by #pagy_url_for: here for easy customization of params by overriding
23
- def pagy_get_params(params) params end
28
+ def pagy_get_params(params)
29
+ params
30
+ end
24
31
  end
25
32
 
26
33
  # All the code here has been optimized for performance: it may not look very pretty
@@ -29,46 +36,71 @@ class Pagy
29
36
 
30
37
  include Helpers
31
38
 
32
- EMPTY = '' # EMPTY + 'string' is almost as fast as +'string' but is also 1.9 compatible
33
- MARK = PAGE_PLACEHOLDER # backward compatibility in case of helper-overriding in legacy apps
34
-
35
39
  # Generic pagination: it returns the html with the series of links to the pages
36
- def pagy_nav(pagy)
37
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
40
+ def pagy_nav(pagy, pagy_id: nil, link_extra: '')
41
+ p_id = %( id="#{pagy_id}") if pagy_id
42
+ link = pagy_link_proc(pagy, link_extra: link_extra)
43
+ p_prev = pagy.prev
44
+ p_next = pagy.next
38
45
 
39
- html = EMPTY + (p_prev ? %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
40
- : %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> ))
46
+ html = +%(<nav#{p_id} class="pagy-nav pagination" aria-label="pager">)
47
+ html << if p_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
41
52
  pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
42
- html << if item.is_a?(Integer); %(<span class="page">#{link.call item}</span> ) # page link
43
- elsif item.is_a?(String) ; %(<span class="page active">#{item}</span> ) # current page
44
- elsif item == :gap ; %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> ) # page gap
53
+ html << case item
54
+ when Integer then %(<span class="page">#{link.call item}</span> ) # page link
55
+ when String then %(<span class="page active">#{item}</span> ) # current page
56
+ when :gap then %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> ) # page gap
45
57
  end
46
58
  end
47
- html << (p_next ? %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
48
- : %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>))
49
- %(<nav class="pagy-nav pagination" role="navigation" aria-label="pager">#{html}</nav>)
59
+ html << if p_next
60
+ %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
61
+ else
62
+ %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>)
63
+ end
64
+ html << %(</nav>)
50
65
  end
51
66
 
52
67
  # Return examples: "Displaying items 41-60 of 324 in total" of "Displaying Products 41-60 of 324 in total"
53
- def pagy_info(pagy)
54
- path = if (count = pagy.count) == 0 ; 'pagy.info.no_items'
55
- else pagy.pages == 1 ? 'pagy.info.single_page' : 'pagy.info.multiple_pages'
56
- end
57
- pagy_t(path, item_name: pagy_t(pagy.vars[:i18n_key], count: count), count: count, from: pagy.from, to: pagy.to)
68
+ def pagy_info(pagy, deprecated_item_name=nil, pagy_id: nil, item_name: nil, i18n_key: nil)
69
+ p_id = %( id="#{pagy_id}") if pagy_id
70
+ item_name = Pagy.deprecated_arg(:item_name, deprecated_item_name, :item_name, item_name) if deprecated_item_name
71
+ p_count = pagy.count
72
+ key = if p_count.zero? then 'pagy.info.no_items'
73
+ elsif pagy.pages == 1 then 'pagy.info.single_page'
74
+ else 'pagy.info.multiple_pages'
75
+ end
76
+
77
+ %(<span#{p_id} class="pagy-info">#{
78
+ pagy_t key, item_name: item_name || pagy_t(i18n_key || pagy.vars[:i18n_key], count: p_count),
79
+ count: p_count, from: pagy.from, to: pagy.to
80
+ }</span>)
58
81
  end
59
82
 
60
83
  # Returns a performance optimized proc to generate the HTML links
61
84
  # Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
62
- def pagy_link_proc(pagy, link_extra='')
63
- p_prev, p_next = pagy.prev, pagy.next
64
- a, b = %(<a href="#{pagy_url_for(PAGE_PLACEHOLDER, pagy)}" #{pagy.vars[:link_extra]} #{link_extra}).split(PAGE_PLACEHOLDER, 2)
65
- lambda {|n, text=n, extra=''| "#{a}#{n}#{b}#{ if n == p_prev ; ' rel="prev"'
66
- elsif n == p_next ; ' rel="next"'
67
- else '' end } #{extra}>#{text}</a>"}
85
+ def pagy_link_proc(pagy, deprecated_link_extra=nil, link_extra: '')
86
+ link_extra = Pagy.deprecated_arg(:link_extra, deprecated_link_extra, :link_extra, link_extra) if deprecated_link_extra
87
+ p_prev = pagy.prev
88
+ p_next = pagy.next
89
+ left, right = %(<a href="#{pagy_url_for pagy, PAGE_PLACEHOLDER}" #{pagy.vars[:link_extra]} #{link_extra}).split(PAGE_PLACEHOLDER, 2)
90
+ lambda do |num, text=num, extra_attrs=''|
91
+ %(#{left}#{num}#{right}#{ case num
92
+ when p_prev then ' rel="prev"'
93
+ when p_next then ' rel="next"'
94
+ else ''
95
+ end } #{extra_attrs}>#{text}</a>)
96
+ end
68
97
  end
69
98
 
70
99
  # Similar to I18n.t: just ~18x faster using ~10x less memory
71
- def pagy_t(path, vars={}) Pagy::I18n.t(@pagy_locale||=nil, path, vars) end
100
+ # (@pagy_locale explicitly initialized in order to avoid warning)
101
+ def pagy_t(key, **opts)
102
+ Pagy::I18n.t @pagy_locale||=nil, key, **opts
103
+ end
72
104
 
73
105
  end
74
106
  end