pagy 6.0.0 → 7.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/lib/config/pagy.rb +56 -44
  4. data/lib/javascripts/pagy-dev.js +3 -3
  5. data/lib/javascripts/pagy-module.js +2 -2
  6. data/lib/javascripts/pagy.js +1 -1
  7. data/lib/locales/ar.yml +15 -11
  8. data/lib/locales/be.yml +25 -0
  9. data/lib/locales/bg.yml +10 -11
  10. data/lib/locales/bs.yml +12 -11
  11. data/lib/locales/ca.yml +12 -11
  12. data/lib/locales/cs.yml +13 -10
  13. data/lib/locales/da.yml +14 -13
  14. data/lib/locales/de.yml +10 -11
  15. data/lib/locales/en.yml +10 -11
  16. data/lib/locales/es.yml +10 -11
  17. data/lib/locales/fr.yml +10 -11
  18. data/lib/locales/hr.yml +12 -11
  19. data/lib/locales/id.yml +10 -11
  20. data/lib/locales/it.yml +10 -11
  21. data/lib/locales/ja.yml +10 -11
  22. data/lib/locales/km.yml +10 -10
  23. data/lib/locales/ko.yml +10 -11
  24. data/lib/locales/nb.yml +10 -11
  25. data/lib/locales/nl.yml +10 -11
  26. data/lib/locales/nn.yml +10 -11
  27. data/lib/locales/pl.yml +12 -11
  28. data/lib/locales/pt-BR.yml +10 -11
  29. data/lib/locales/pt.yml +10 -11
  30. data/lib/locales/ru.yml +14 -11
  31. data/lib/locales/sr.yml +12 -10
  32. data/lib/locales/sv-SE.yml +10 -12
  33. data/lib/locales/sv.yml +10 -12
  34. data/lib/locales/sw.yml +12 -11
  35. data/lib/locales/ta.yml +12 -11
  36. data/lib/locales/tr.yml +10 -11
  37. data/lib/locales/uk.yml +12 -11
  38. data/lib/locales/vi.yml +17 -0
  39. data/lib/locales/zh-CN.yml +10 -11
  40. data/lib/locales/zh-HK.yml +10 -11
  41. data/lib/locales/zh-TW.yml +10 -11
  42. data/lib/pagy/backend.rb +10 -4
  43. data/lib/pagy/calendar/day.rb +4 -3
  44. data/lib/pagy/calendar/helper.rb +15 -11
  45. data/lib/pagy/calendar/month.rb +4 -3
  46. data/lib/pagy/calendar/quarter.rb +4 -3
  47. data/lib/pagy/calendar/week.rb +1 -1
  48. data/lib/pagy/calendar/year.rb +3 -2
  49. data/lib/pagy/calendar.rb +33 -7
  50. data/lib/pagy/console.rb +1 -1
  51. data/lib/pagy/countless.rb +1 -1
  52. data/lib/pagy/exceptions.rb +1 -1
  53. data/lib/pagy/extras/arel.rb +1 -1
  54. data/lib/pagy/extras/array.rb +1 -1
  55. data/lib/pagy/extras/bootstrap.rb +45 -32
  56. data/lib/pagy/extras/bulma.rb +41 -32
  57. data/lib/pagy/extras/calendar.rb +17 -13
  58. data/lib/pagy/extras/countless.rb +1 -1
  59. data/lib/pagy/extras/elasticsearch_rails.rb +8 -8
  60. data/lib/pagy/extras/foundation.rb +48 -33
  61. data/lib/pagy/extras/frontend_helpers.rb +11 -11
  62. data/lib/pagy/extras/gearbox.rb +1 -1
  63. data/lib/pagy/extras/headers.rb +8 -8
  64. data/lib/pagy/extras/i18n.rb +5 -5
  65. data/lib/pagy/extras/items.rb +19 -12
  66. data/lib/pagy/extras/jsonapi.rb +79 -0
  67. data/lib/pagy/extras/materialize.rb +42 -30
  68. data/lib/pagy/extras/meilisearch.rb +12 -11
  69. data/lib/pagy/extras/metadata.rb +2 -2
  70. data/lib/pagy/extras/navs.rb +22 -34
  71. data/lib/pagy/extras/overflow.rb +9 -7
  72. data/lib/pagy/extras/searchkick.rb +8 -8
  73. data/lib/pagy/extras/semantic.rb +41 -28
  74. data/lib/pagy/extras/standalone.rb +10 -16
  75. data/lib/pagy/extras/support.rb +19 -33
  76. data/lib/pagy/extras/trim.rb +4 -4
  77. data/lib/pagy/extras/uikit.rb +44 -31
  78. data/lib/pagy/frontend.rb +57 -26
  79. data/lib/pagy/i18n.rb +3 -3
  80. data/lib/pagy/url_helpers.rb +15 -12
  81. data/lib/pagy.rb +57 -41
  82. metadata +7 -19
  83. data/lib/templates/bootstrap_nav.html.erb +0 -24
  84. data/lib/templates/bootstrap_nav.html.haml +0 -34
  85. data/lib/templates/bootstrap_nav.html.slim +0 -34
  86. data/lib/templates/bulma_nav.html.erb +0 -24
  87. data/lib/templates/bulma_nav.html.haml +0 -32
  88. data/lib/templates/bulma_nav.html.slim +0 -32
  89. data/lib/templates/foundation_nav.html.erb +0 -24
  90. data/lib/templates/foundation_nav.html.haml +0 -34
  91. data/lib/templates/foundation_nav.html.slim +0 -34
  92. data/lib/templates/nav.html.erb +0 -22
  93. data/lib/templates/nav.html.haml +0 -30
  94. data/lib/templates/nav.html.slim +0 -29
  95. data/lib/templates/uikit_nav.html.erb +0 -15
  96. data/lib/templates/uikit_nav.html.haml +0 -28
  97. data/lib/templates/uikit_nav.html.slim +0 -28
@@ -1,4 +1,4 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/semantic
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/semantic
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'pagy/extras/frontend_helpers'
@@ -8,73 +8,86 @@ class Pagy # :nodoc:
8
8
  # The resulting code may not look very elegant, but produces the best benchmarks
9
9
  module SemanticExtra
10
10
  # Pagination for semantic: it returns the html with the series of links to the pages
11
- def pagy_semantic_nav(pagy, pagy_id: nil, link_extra: '', **vars)
11
+ def pagy_semantic_nav(pagy, pagy_id: nil, link_extra: '',
12
+ nav_aria_label: nil, nav_i18n_key: nil, **vars)
12
13
  p_id = %( id="#{pagy_id}") if pagy_id
13
- link = pagy_link_proc(pagy, link_extra: %(class="item" #{link_extra}))
14
+ link = pagy_link_proc(pagy, link_extra:)
14
15
 
15
- html = +%(<div#{p_id} class="pagy-semantic-nav ui pagination menu">)
16
- html << pagy_semantic_prev_html(pagy, link)
16
+ html = +%(<div#{p_id} role="navigation" class="pagy-semantic-nav ui pagination menu" #{
17
+ nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)}>)
18
+ html << semantic_prev_html(pagy, link)
17
19
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
18
20
  html << case item
19
- when Integer then link.call item
20
- when String then %(<a class="item active">#{pagy.label_for(item)}</a>)
21
- when :gap then %(<div class="disabled item">#{pagy_t 'pagy.nav.gap'}</div>)
22
- else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
21
+ when Integer
22
+ link.call(item, pagy.label_for(item), %(class="item"))
23
+ when String
24
+ link.call(item, pagy.label_for(item), %(class="item active"))
25
+ when :gap
26
+ %(<div class="disabled item">#{pagy_t 'pagy.gap'}</div>)
27
+ else
28
+ raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
23
29
  end
24
30
  end
25
- html << pagy_semantic_next_html(pagy, link)
31
+ html << semantic_next_html(pagy, link)
26
32
  html << %(</div>)
27
33
  end
28
34
 
29
35
  # Javascript pagination for semantic: it returns a nav and a JSON tag used by the pagy.js file
30
- def pagy_semantic_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
36
+ def pagy_semantic_nav_js(pagy, pagy_id: nil, link_extra: '',
37
+ nav_aria_label: nil, nav_i18n_key: nil, **vars)
31
38
  sequels = pagy.sequels(**vars)
32
39
  p_id = %( id="#{pagy_id}") if pagy_id
33
- link = pagy_link_proc(pagy, link_extra: %(class="item" #{link_extra}))
34
- tags = { 'before' => pagy_semantic_prev_html(pagy, link),
35
- 'link' => link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER),
36
- 'active' => %(<a class="item active">#{LABEL_PLACEHOLDER}</a>),
37
- 'gap' => %(<div class="disabled item">#{pagy_t('pagy.nav.gap')}</div>),
38
- 'after' => pagy_semantic_next_html(pagy, link) }
40
+ link = pagy_link_proc(pagy, link_extra:)
41
+ tags = { 'before' => semantic_prev_html(pagy, link),
42
+ 'link' => link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER, %(class="item")),
43
+ 'active' => link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER, %(class="item active")),
44
+ 'gap' => %(<div class="disabled item">#{pagy_t('pagy.gap')}</div>),
45
+ 'after' => semantic_next_html(pagy, link) }
39
46
 
40
47
  %(<div#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-semantic-nav-js ui pagination menu" role="navigation" #{
41
- pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></div>)
48
+ nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)} #{
49
+ pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></div>)
42
50
  end
43
51
 
44
52
  # Combo pagination for semantic: it returns a nav and a JSON tag used by the pagy.js file
45
- def pagy_semantic_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
53
+ def pagy_semantic_combo_nav_js(pagy, pagy_id: nil, link_extra: '',
54
+ nav_aria_label: nil, nav_i18n_key: nil)
46
55
  p_id = %( id="#{pagy_id}") if pagy_id
47
56
  link = pagy_link_proc(pagy, link_extra: %(class="item" #{link_extra}))
48
57
  p_page = pagy.page
49
58
  p_pages = pagy.pages
50
59
  input = %(<input type="number" min="1" max="#{p_pages}" value="#{
51
- p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length + 1}rem; margin: 0 0.3rem">)
60
+ p_page}" style="padding: 0; text-align: center; width: #{
61
+ p_pages.to_s.length + 1}rem; margin: 0 0.3rem" aria-current="page">)
52
62
 
53
63
  %(<div#{p_id} class="pagy-semantic-combo-nav-js ui compact menu" role="navigation" #{
64
+ nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)} #{
54
65
  pagy_data(pagy, :combo, pagy_marked_link(link))}>#{
55
- pagy_semantic_prev_html pagy, link
66
+ semantic_prev_html pagy, link
56
67
  }<div class="pagy-combo-input item">#{
57
68
  pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
58
69
  }</div> #{
59
- pagy_semantic_next_html pagy, link
70
+ semantic_next_html pagy, link
60
71
  }</div>)
61
72
  end
62
73
 
63
74
  private
64
75
 
65
- def pagy_semantic_prev_html(pagy, link)
76
+ def semantic_prev_html(pagy, link)
66
77
  if (p_prev = pagy.prev)
67
- link.call p_prev, '<i class="left small chevron icon"></i>', 'aria-label="previous"'
78
+ link.call(p_prev, pagy_t('pagy.prev'), %(#{prev_aria_label_attr} class="item"))
68
79
  else
69
- +%(<div class="item disabled"><i class="left small chevron icon"></i></div>)
80
+ +%(<div class="item disabled" role="link" aria-disabled="true" #{
81
+ prev_aria_label_attr}>#{pagy_t('pagy.prev')}</div>)
70
82
  end
71
83
  end
72
84
 
73
- def pagy_semantic_next_html(pagy, link)
85
+ def semantic_next_html(pagy, link)
74
86
  if (p_next = pagy.next)
75
- link.call p_next, '<i class="right small chevron icon"></i>', 'aria-label="next"'
87
+ link.call(p_next, pagy_t('pagy.next'), %(#{next_aria_label_attr} class="item"))
76
88
  else
77
- +%(<div class="item disabled"><i class="right small chevron icon"></i></div>)
89
+ +%(<div class="item disabled" role="link" aria-disabled="true" #{
90
+ next_aria_label_attr}>#{pagy_t('pagy.next')}</div>)
78
91
  end
79
92
  end
80
93
  end
@@ -1,4 +1,4 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/standalone
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/standalone
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'uri'
@@ -25,11 +25,11 @@ class Pagy # :nodoc:
25
25
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
26
26
  end.delete_if(&:empty?).join('&')
27
27
  when nil
28
- prefix
28
+ escape(prefix)
29
29
  else
30
30
  raise ArgumentError, 'value must be a Hash' if prefix.nil?
31
31
 
32
- "#{prefix}=#{escape(value)}"
32
+ "#{escape(prefix)}=#{escape(value)}"
33
33
  end
34
34
  end
35
35
  end
@@ -38,24 +38,18 @@ class Pagy # :nodoc:
38
38
  # Return the URL for the page. If there is no pagy.vars[:url]
39
39
  # it works exactly as the regular #pagy_url_for, relying on the params method and Rack.
40
40
  # If there is a defined pagy.vars[:url] variable it does not need the params method nor Rack.
41
- def pagy_url_for(pagy, page, absolute: false, html_escaped: false)
41
+ def pagy_url_for(pagy, page, absolute: false, **_)
42
42
  return super unless pagy.vars[:url]
43
43
 
44
- vars = pagy.vars
45
- page_param = vars[:page_param].to_s
46
- items_param = vars[:items_param].to_s
47
- params = pagy.params.is_a?(Hash) ? pagy.params.clone : {} # safe when it gets reused
48
- params[page_param] = page
49
- params[items_param] = vars[:items] if vars[:items_extra]
50
- params = pagy.params.call(params) if pagy.params.is_a?(Proc)
51
- query_string = "?#{Rack::Utils.build_nested_query(params)}"
52
- query_string = query_string.gsub('&', '&amp;') if html_escaped # the only unescaped entity
44
+ vars = pagy.vars
45
+ params = pagy.params.is_a?(Hash) ? pagy.params.clone : {} # safe when it gets reused
46
+ pagy_set_query_params(page, vars, params)
47
+ params = pagy.params.call(params) if pagy.params.is_a?(Proc)
48
+ query_string = "?#{QueryUtils.build_nested_query(params)}"
53
49
  "#{vars[:url]}#{query_string}#{vars[:fragment]}"
54
50
  end
55
51
  end
56
- # In ruby 3+ `UrlHelpers.prepend StandaloneExtra` would be enough instead of using the next 2 lines
57
- Frontend.prepend StandaloneExtra
58
- Backend.prepend StandaloneExtra
52
+ UrlHelpers.prepend StandaloneExtra
59
53
 
60
54
  # Define a dummy params method if it's not already defined in the including module
61
55
  module Backend
@@ -1,53 +1,39 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/support
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/support
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class Pagy # :nodoc:
5
5
  # Extra support for features like: incremental, auto-incremental and infinite pagination
6
6
  module SupportExtra
7
7
  # Return the previous page URL string or nil
8
- def pagy_prev_url(pagy)
9
- pagy_url_for(pagy, pagy.prev) if pagy.prev
8
+ def pagy_prev_url(pagy, absolute: false)
9
+ pagy_url_for(pagy, pagy.prev, absolute:) if pagy.prev
10
10
  end
11
11
 
12
12
  # Return the next page URL string or nil
13
- def pagy_next_url(pagy)
14
- pagy_url_for(pagy, pagy.next) if pagy.next
13
+ def pagy_next_url(pagy, absolute: false)
14
+ pagy_url_for(pagy, pagy.next, absolute:) if pagy.next
15
15
  end
16
16
 
17
- # Return the HTML string for the previous page link
18
- def pagy_prev_link(pagy, text: pagy_t('pagy.nav.prev'), link_extra: '')
19
- if pagy.prev
20
- %(<span class="page prev"><a href="#{
21
- pagy_url_for(pagy, pagy.prev, html_escaped: true)
22
- }" rel="prev" aria-label="previous" #{
23
- pagy.vars[:link_extra]
24
- } #{link_extra}>#{text}</a></span>)
25
- else
26
- %(<span class="page prev disabled">#{text}</span>)
27
- end
17
+ # Return the HTML string for the enabled/disabled previous page link
18
+ def pagy_prev_html(pagy, text: pagy_t('pagy.prev'), link_extra: '')
19
+ link = pagy_link_proc(pagy, link_extra:)
20
+ prev_html(pagy, link, text:)
28
21
  end
29
22
 
30
- # Return the HTML string for the next page link
31
- def pagy_next_link(pagy, text: pagy_t('pagy.nav.next'), link_extra: '')
32
- if pagy.next
33
- %(<span class="page next"><a href="#{
34
- pagy_url_for(pagy, pagy.next, html_escaped: true)
35
- }" rel="next" aria-label="next" #{
36
- pagy.vars[:link_extra]
37
- } #{link_extra}>#{text}</a></span>)
38
- else
39
- %(<span class="page next disabled">#{text}</span>)
40
- end
23
+ # Return the HTML string for the enabled/disabled next page link
24
+ def pagy_next_html(pagy, text: pagy_t('pagy.next'), link_extra: '')
25
+ link = pagy_link_proc(pagy, link_extra:)
26
+ next_html(pagy, link, text:)
41
27
  end
42
28
 
43
- # Return the HTML link tag for the previous page or nil
44
- def pagy_prev_link_tag(pagy)
45
- %(<link href="#{pagy_url_for(pagy, pagy.prev, html_escaped: true)}" rel="prev"/>) if pagy.prev
29
+ # Conditionally return the HTML link tag string for the previous page
30
+ def pagy_prev_link_tag(pagy, absolute: false)
31
+ %(<link href="#{pagy_url_for(pagy, pagy.prev, absolute:)}" rel="prev"/>) if pagy.prev
46
32
  end
47
33
 
48
- # Return the HTML link tag for the next page or nil
49
- def pagy_next_link_tag(pagy)
50
- %(<link href="#{pagy_url_for(pagy, pagy.next, html_escaped: true)}" rel="next"/>) if pagy.next
34
+ # Conditionally return the HTML link tag string for the next page
35
+ def pagy_next_link_tag(pagy, absolute: false)
36
+ %(<link href="#{pagy_url_for(pagy, pagy.next, absolute:)}" rel="next"/>) if pagy.next
51
37
  end
52
38
  end
53
39
  Frontend.prepend SupportExtra
@@ -1,4 +1,4 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/trim
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/trim
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class Pagy # :nodoc:
@@ -9,12 +9,12 @@ class Pagy # :nodoc:
9
9
  # Override the original pagy_link_proc.
10
10
  # Call the pagy_trim method if the trim_extra is enabled.
11
11
  def pagy_link_proc(pagy, link_extra: '')
12
- link_proc = super(pagy, link_extra: link_extra)
12
+ link_proc = super(pagy, link_extra:)
13
13
  return link_proc unless pagy.vars[:trim_extra]
14
14
 
15
15
  lambda do |page, text = pagy.label_for(page), extra = ''|
16
16
  link = +link_proc.call(page, text, extra)
17
- return link unless page == 1
17
+ return link unless page.to_s == '1'
18
18
 
19
19
  pagy_trim(pagy, link)
20
20
  end
@@ -22,7 +22,7 @@ class Pagy # :nodoc:
22
22
 
23
23
  # Remove the the :page_param param from the first page link
24
24
  def pagy_trim(pagy, link)
25
- link.sub!(/(\?|&amp;)#{pagy.vars[:page_param]}=1\b(?!&amp;)|\b#{pagy.vars[:page_param]}=1&amp;/, '')
25
+ link.sub!(/[?&]#{pagy.vars[:page_param]}=1\b(?!&)|\b#{pagy.vars[:page_param]}=1&/, '')
26
26
  end
27
27
  end
28
28
  Frontend.prepend TrimExtra
@@ -1,4 +1,4 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/uikit
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/uikit
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'pagy/extras/frontend_helpers'
@@ -8,75 +8,88 @@ class Pagy # :nodoc:
8
8
  # The resulting code may not look very elegant, but produces the best benchmarks
9
9
  module UikitExtra
10
10
  # Pagination for uikit: it returns the html with the series of links to the pages
11
- def pagy_uikit_nav(pagy, pagy_id: nil, link_extra: '', **vars)
11
+ def pagy_uikit_nav(pagy, pagy_id: nil, link_extra: '',
12
+ nav_aria_label: nil, nav_i18n_key: nil, **vars)
12
13
  p_id = %( id="#{pagy_id}") if pagy_id
13
- link = pagy_link_proc(pagy, link_extra: link_extra)
14
+ link = pagy_link_proc(pagy, link_extra:)
14
15
 
15
- html = +%(<ul#{p_id} class="pagy-uikit-nav uk-pagination uk-flex-center">#{pagy_uikit_prev_html pagy, link})
16
+ html = +%(<ul#{p_id} class="pagy-uikit-nav uk-pagination uk-flex-center" role="navigation" #{
17
+
18
+ nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)}>#{uikit_prev_html(pagy, link)})
16
19
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
20
  html << case item
18
- when Integer then %(<li>#{link.call item}</li>)
19
- when String then %(<li class="uk-active"><span>#{pagy.label_for(item)}</span></li>)
20
- when :gap then %(<li class="uk-disabled"><span>#{pagy_t 'pagy.nav.gap'}</span></li>)
21
- else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
21
+ when Integer
22
+ %(<li>#{link.call(item)}</li>)
23
+ when String
24
+ %(<li class="uk-active"><span role="link" aria-current="page" aria-disabled="true">#{
25
+ pagy.label_for(item)}</span></li>)
26
+ when :gap
27
+ %(<li class="uk-disabled"><span>#{pagy_t 'pagy.gap'}</span></li>)
28
+ else
29
+ raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
22
30
  end
23
31
  end
24
- html << pagy_uikit_next_html(pagy, link)
32
+ html << uikit_next_html(pagy, link)
25
33
  html << %(</ul>)
26
34
  end
27
35
 
28
36
  # Javascript pagination for uikit: it returns a nav and a JSON tag used by the pagy.js file
29
- def pagy_uikit_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
37
+ def pagy_uikit_nav_js(pagy, pagy_id: nil, link_extra: '',
38
+ nav_aria_label: nil, nav_i18n_key: nil, **vars)
30
39
  sequels = pagy.sequels(**vars)
31
40
  p_id = %( id="#{pagy_id}") if pagy_id
32
- link = pagy_link_proc(pagy, link_extra: link_extra)
33
- tags = { 'before' => pagy_uikit_prev_html(pagy, link),
41
+ link = pagy_link_proc(pagy, link_extra:)
42
+ tags = { 'before' => uikit_prev_html(pagy, link),
34
43
  'link' => %(<li>#{link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER)}</li>),
35
- 'active' => %(<li class="uk-active"><span>#{LABEL_PLACEHOLDER}</span></li>),
36
- 'gap' => %(<li class="uk-disabled"><span>#{pagy_t 'pagy.nav.gap'}</span></li>),
37
- 'after' => pagy_uikit_next_html(pagy, link) }
44
+ 'active' => %(<li class="uk-active"><span role="link" aria-current="page" aria-disabled="true">#{
45
+ LABEL_PLACEHOLDER}</span></li>),
46
+ 'gap' => %(<li class="uk-disabled"><span>#{pagy_t 'pagy.gap'}</span></li>),
47
+ 'after' => uikit_next_html(pagy, link) }
38
48
 
39
- %(<ul#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-uikit-nav-js uk-pagination uk-flex-center" #{
40
- pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></ul>)
49
+ %(<ul#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-uikit-nav-js uk-pagination uk-flex-center" role="navigation" #{
50
+ nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)} #{
51
+ pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></ul>)
41
52
  end
42
53
 
43
54
  # Javascript combo pagination for uikit: it returns a nav and a JSON tag used by the pagy.js file
44
- def pagy_uikit_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
55
+ def pagy_uikit_combo_nav_js(pagy, pagy_id: nil, link_extra: '',
56
+ nav_aria_label: nil, nav_i18n_key: nil)
45
57
  p_id = %( id="#{pagy_id}") if pagy_id
46
- link = pagy_link_proc(pagy, link_extra: link_extra)
58
+ link = pagy_link_proc(pagy, link_extra:)
47
59
  p_page = pagy.page
48
60
  p_pages = pagy.pages
49
61
  input = %(<input type="number" min="1" max="#{p_pages}" value="#{
50
- p_page}" style="text-align: center; width: #{p_pages.to_s.length + 1}rem;">)
62
+ p_page}" style="text-align: center; width: #{p_pages.to_s.length + 1}rem;" aria-current="page">)
51
63
 
52
- %(<ul#{p_id} class="pagy-uikit-combo-nav-js uk-button-group uk-pagination uk-flex-center" #{
64
+ %(<ul#{p_id} class="pagy-uikit-combo-nav-js uk-button-group uk-pagination uk-flex-center" role="navigation" #{
65
+ nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)} #{
53
66
  pagy_data(pagy, :combo, pagy_marked_link(link))
54
67
  }>#{
55
- pagy_uikit_prev_html pagy, link
68
+ uikit_prev_html pagy, link
56
69
  }<li>#{
57
70
  pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
58
71
  }</li>#{
59
- pagy_uikit_next_html pagy, link
72
+ uikit_next_html pagy, link
60
73
  }</ul>)
61
74
  end
62
75
 
63
76
  private
64
77
 
65
- def pagy_uikit_prev_html(pagy, link)
66
- previous_span = %(<span uk-pagination-previous>#{pagy_t 'pagy.nav.prev'}</span>)
78
+ def uikit_prev_html(pagy, link)
79
+ previous_span = %(<span uk-pagination-previous>#{pagy_t 'pagy.prev'}</span>)
67
80
  if (p_prev = pagy.prev)
68
- %(<li>#{link.call p_prev, previous_span}</li>)
81
+ %(<li>#{link.call(p_prev, previous_span, prev_aria_label_attr)}</li>)
69
82
  else
70
- %(<li class="uk-disabled"><a href="#">#{previous_span}</a></li>)
83
+ %(<li class="uk-disabled"><span role="link" aria-disabled="true" #{prev_aria_label_attr}>#{previous_span}</a></li>)
71
84
  end
72
85
  end
73
86
 
74
- def pagy_uikit_next_html(pagy, link)
75
- next_span = %(<span uk-pagination-next>#{pagy_t 'pagy.nav.next'}</span>)
87
+ def uikit_next_html(pagy, link)
88
+ next_span = %(<span uk-pagination-next>#{pagy_t 'pagy.next'}</span>)
76
89
  if (p_next = pagy.next)
77
- %(<li>#{link.call p_next, next_span}</li>)
90
+ %(<li>#{link.call(p_next, next_span, next_aria_label_attr)}</li>)
78
91
  else
79
- %(<li class="uk-disabled"><a href="#">#{next_span}</a></li>)
92
+ %(<li class="uk-disabled"><span role="link" aria-disabled="true" #{prev_aria_label_attr}>#{next_span}</span></li>)
80
93
  end
81
94
  end
82
95
  end
data/lib/pagy/frontend.rb CHANGED
@@ -1,4 +1,4 @@
1
- # See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/api/frontend
1
+ # See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/docs/api/frontend
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'pagy/url_helpers'
@@ -15,36 +15,31 @@ class Pagy
15
15
  include UrlHelpers
16
16
 
17
17
  # Generic pagination: it returns the html with the series of links to the pages
18
- def pagy_nav(pagy, pagy_id: nil, link_extra: '', **vars)
19
- p_id = %( id="#{pagy_id}") if pagy_id
20
- link = pagy_link_proc(pagy, link_extra: link_extra)
21
- p_prev = pagy.prev
22
- p_next = pagy.next
18
+ def pagy_nav(pagy, pagy_id: nil, link_extra: '',
19
+ nav_aria_label: nil, nav_i18n_key: nil, **vars)
20
+ p_id = %( id="#{pagy_id}") if pagy_id
21
+ link = pagy_link_proc(pagy, link_extra:)
23
22
 
24
- html = +%(<nav#{p_id} class="pagy-nav pagination">)
25
- html << if p_prev
26
- %(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
27
- else
28
- %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> )
29
- end
23
+ html = +%(<nav#{p_id} class="pagy-nav pagination" #{nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)}>)
24
+ html << prev_html(pagy, link)
30
25
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
31
26
  html << case item
32
- when Integer then %(<span class="page">#{link.call item}</span> )
33
- when String then %(<span class="page active">#{pagy.label_for(item)}</span> )
34
- when :gap then %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> )
35
- else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
27
+ when Integer
28
+ %(<span class="page">#{link.call(item)}</span> )
29
+ when String
30
+ %(<span class="page active">) +
31
+ %(<a role="link" aria-disabled="true" aria-current="page">#{pagy.label_for(item)}</a></span> )
32
+ when :gap
33
+ %(<span class="page gap">#{pagy_t('pagy.gap')}</span> )
34
+ else
35
+ raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
36
36
  end
37
37
  end
38
- html << if p_next
39
- %(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
40
- else
41
- %(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>)
42
- end
43
- html << %(</nav>)
38
+ html << %(#{next_html(pagy, link)}</nav>)
44
39
  end
45
40
 
46
41
  # Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
47
- def pagy_info(pagy, pagy_id: nil, item_name: nil, i18n_key: nil)
42
+ def pagy_info(pagy, pagy_id: nil, item_name: nil, item_i18n_key: nil)
48
43
  p_id = %( id="#{pagy_id}") if pagy_id
49
44
  p_count = pagy.count
50
45
  key = if p_count.zero? then 'pagy.info.no_items'
@@ -53,22 +48,25 @@ class Pagy
53
48
  end
54
49
 
55
50
  %(<span#{p_id} class="pagy-info">#{
56
- pagy_t key, item_name: item_name || pagy_t(i18n_key || pagy.vars[:i18n_key], count: p_count),
51
+ pagy_t key, item_name: item_name || pagy_t(item_i18n_key || pagy.vars[:item_i18n_key], count: p_count),
57
52
  count: p_count, from: pagy.from, to: pagy.to
58
53
  }</span>)
59
54
  end
60
55
 
61
- # Return a performance optimized proc to generate the HTML links
56
+ # Return a performance optimized lambda to generate the HTML links
62
57
  # Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
63
58
  def pagy_link_proc(pagy, link_extra: '')
64
59
  p_prev = pagy.prev
65
60
  p_next = pagy.next
66
- left, right = %(<a href="#{pagy_url_for(pagy, PAGE_PLACEHOLDER, html_escaped: true)}" #{
61
+ p_page = pagy.page.to_s
62
+ left, right = %(<a href="#{pagy_url_for(pagy, PAGE_PLACEHOLDER)}" #{
67
63
  pagy.vars[:link_extra]} #{link_extra}).split(PAGE_PLACEHOLDER, 2)
64
+ # lambda used by all the helpers
68
65
  lambda do |page, text = pagy.label_for(page), extra_attrs = ''|
69
66
  %(#{left}#{page}#{right}#{ case page
70
67
  when p_prev then ' rel="prev"'
71
68
  when p_next then ' rel="next"'
69
+ when p_page then ' aria-disabled="true" aria-current="page"'
72
70
  else ''
73
71
  end } #{extra_attrs}>#{text}</a>)
74
72
  end
@@ -79,5 +77,38 @@ class Pagy
79
77
  def pagy_t(key, opts = {})
80
78
  Pagy::I18n.translate(@pagy_locale ||= nil, key, opts)
81
79
  end
80
+
81
+ private
82
+
83
+ def nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key, count: pagy.pages)
84
+ nav_aria_label ||= pagy_t(nav_i18n_key || pagy.vars[:nav_i18n_key], count:)
85
+ %(aria-label="#{nav_aria_label}")
86
+ end
87
+
88
+ def prev_aria_label_attr
89
+ %(aria-label="#{pagy_t('pagy.aria_label.prev')}")
90
+ end
91
+
92
+ def next_aria_label_attr
93
+ %(aria-label="#{pagy_t('pagy.aria_label.next')}")
94
+ end
95
+
96
+ def prev_html(pagy, link, text: pagy_t('pagy.prev'))
97
+ if (p_prev = pagy.prev)
98
+ %(<span class="page prev">#{link.call(p_prev, text, prev_aria_label_attr)}</span> )
99
+ else
100
+ %(<span class="page prev disabled"><a role="link" aria-disabled="true" #{
101
+ prev_aria_label_attr}>#{text}</a></span> )
102
+ end
103
+ end
104
+
105
+ def next_html(pagy, link, text: pagy_t('pagy.next'))
106
+ if (p_next = pagy.next)
107
+ %(<span class="page next">#{link.call(p_next, text, next_aria_label_attr)}</span>)
108
+ else
109
+ %(<span class="page next disabled"><a role="link" aria-disabled="true" #{
110
+ next_aria_label_attr}>#{text}</a></span>)
111
+ end
112
+ end
82
113
  end
83
114
  end
data/lib/pagy/i18n.rb CHANGED
@@ -1,4 +1,4 @@
1
- # See Pagy::I18n API documentation https://ddnexus.github.io/pagy/api/i18n
1
+ # See Pagy::I18n API documentation https://ddnexus.github.io/pagy/docs/api/i18n
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'yaml'
@@ -94,6 +94,7 @@ class Pagy
94
94
  # the :one_other RULE is the default for locales missing from this list
95
95
  LOCALE = Hash.new(RULE[:one_other]).tap do |hash|
96
96
  hash['ar'] = RULE[:arabic]
97
+ hash['be'] = RULE[:east_slavic]
97
98
  hash['bs'] = RULE[:east_slavic]
98
99
  hash['cs'] = RULE[:west_slavic]
99
100
  hash['id'] = RULE[:other]
@@ -105,10 +106,9 @@ class Pagy
105
106
  hash['pl'] = RULE[:polish]
106
107
  hash['ru'] = RULE[:east_slavic]
107
108
  hash['sr'] = RULE[:east_slavic]
108
- hash['sv'] = RULE[:one_two_other]
109
- hash['sv-SE'] = RULE[:one_two_other]
110
109
  hash['tr'] = RULE[:other]
111
110
  hash['uk'] = RULE[:east_slavic]
111
+ hash['vi'] = RULE[:other]
112
112
  hash['zh-CN'] = RULE[:other]
113
113
  hash['zh-HK'] = RULE[:other]
114
114
  hash['zh-TW'] = RULE[:other]
@@ -6,19 +6,22 @@ class Pagy
6
6
  # Return the URL for the page, relying on the params method and Rack by default.
7
7
  # It supports all Rack-based frameworks (Sinatra, Padrino, Rails, ...).
8
8
  # For non-rack environments you can use the standalone extra
9
- def pagy_url_for(pagy, page, absolute: false, html_escaped: false)
10
- vars = pagy.vars
11
- request_path = vars[:request_path].to_s.empty? ? request.path : vars[:request_path]
12
- page_param = vars[:page_param].to_s
13
- items_param = vars[:items_param].to_s
14
- params = pagy.params.is_a?(Hash) ? pagy.params.transform_keys(&:to_s) : {}
15
- params = request.GET.merge(params)
16
- params[page_param] = page
17
- params[items_param] = vars[:items] if vars[:items_extra]
18
- params = pagy.params.call(params) if pagy.params.is_a?(Proc)
19
- query_string = "?#{Rack::Utils.build_nested_query(params)}"
20
- query_string = query_string.gsub('&', '&amp;') if html_escaped # the only unescaped entity
9
+ def pagy_url_for(pagy, page, absolute: false, **_)
10
+ vars = pagy.vars
11
+ request_path = vars[:request_path].to_s.empty? ? request.path : vars[:request_path]
12
+ pagy_params = pagy.params.is_a?(Hash) ? pagy.params.transform_keys(&:to_s) : {}
13
+ params = request.GET.merge(pagy_params)
14
+ pagy_set_query_params(page, vars, params)
15
+ params = pagy.params.call(params) if pagy.params.is_a?(Proc)
16
+ query_string = "?#{Rack::Utils.build_nested_query(params)}"
21
17
  "#{request.base_url if absolute}#{request_path}#{query_string}#{vars[:fragment]}"
22
18
  end
19
+
20
+ # Add the page and items params
21
+ # Overridable by the jsonapi extra
22
+ def pagy_set_query_params(page, vars, params)
23
+ params[vars[:page_param].to_s] = page
24
+ params[vars[:items_param].to_s] = vars[:items] if vars[:items_extra]
25
+ end
23
26
  end
24
27
  end