pagy 6.0.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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