pagy 5.7.5 → 8.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/apps/calendar.ru +745 -0
  4. data/apps/demo.ru +435 -0
  5. data/apps/rails.ru +213 -0
  6. data/apps/repro.ru +177 -0
  7. data/apps/tmp/calendar.sqlite3 +0 -0
  8. data/apps/tmp/calendar.sqlite3-shm +0 -0
  9. data/apps/tmp/calendar.sqlite3-wal +0 -0
  10. data/apps/tmp/local_secret.txt +1 -0
  11. data/bin/pagy +100 -0
  12. data/{lib/config → config}/pagy.rb +72 -96
  13. data/javascripts/pagy-module.js +100 -0
  14. data/javascripts/pagy.d.ts +5 -0
  15. data/javascripts/pagy.js +4 -0
  16. data/javascripts/pagy.min.js +4 -0
  17. data/javascripts/pagy.min.js.map +10 -0
  18. data/javascripts/pagy.mjs +100 -0
  19. data/lib/optimist.rb +1022 -0
  20. data/lib/pagy/backend.rb +16 -4
  21. data/lib/pagy/calendar/day.rb +22 -10
  22. data/lib/pagy/calendar/month.rb +35 -9
  23. data/lib/pagy/calendar/quarter.rb +36 -10
  24. data/lib/pagy/calendar/unit.rb +106 -0
  25. data/lib/pagy/calendar/week.rb +16 -16
  26. data/lib/pagy/calendar/year.rb +15 -9
  27. data/lib/pagy/calendar.rb +60 -68
  28. data/lib/pagy/console.rb +3 -3
  29. data/lib/pagy/countless.rb +17 -12
  30. data/lib/pagy/exceptions.rb +1 -1
  31. data/lib/pagy/extras/arel.rb +2 -2
  32. data/lib/pagy/extras/array.rb +2 -2
  33. data/lib/pagy/extras/bootstrap.rb +57 -53
  34. data/lib/pagy/extras/bulma.rb +53 -57
  35. data/lib/pagy/extras/calendar.rb +54 -37
  36. data/lib/pagy/extras/countless.rb +3 -3
  37. data/lib/pagy/extras/elasticsearch_rails.rb +12 -11
  38. data/lib/pagy/extras/foundation.rb +59 -54
  39. data/lib/pagy/extras/gearbox.rb +32 -19
  40. data/lib/pagy/extras/headers.rb +11 -11
  41. data/lib/pagy/extras/i18n.rb +5 -5
  42. data/lib/pagy/extras/items.rb +34 -24
  43. data/lib/pagy/extras/{frontend_helpers.rb → js_tools.rb} +24 -24
  44. data/lib/pagy/extras/jsonapi.rb +79 -0
  45. data/lib/pagy/extras/materialize.rb +63 -47
  46. data/lib/pagy/extras/meilisearch.rb +26 -22
  47. data/lib/pagy/extras/metadata.rb +13 -9
  48. data/lib/pagy/extras/overflow.rb +13 -10
  49. data/lib/pagy/extras/pagy.rb +82 -0
  50. data/lib/pagy/extras/searchkick.rb +12 -11
  51. data/lib/pagy/extras/semantic.rb +57 -45
  52. data/lib/pagy/extras/size.rb +40 -0
  53. data/lib/pagy/extras/standalone.rb +12 -16
  54. data/lib/pagy/extras/trim.rb +13 -13
  55. data/lib/pagy/extras/uikit.rb +60 -46
  56. data/lib/pagy/frontend.rb +62 -45
  57. data/lib/pagy/i18n.rb +4 -3
  58. data/lib/pagy/url_helpers.rb +13 -25
  59. data/lib/pagy.rb +61 -59
  60. data/locales/ar.yml +28 -0
  61. data/locales/be.yml +25 -0
  62. data/locales/bg.yml +21 -0
  63. data/locales/bs.yml +25 -0
  64. data/locales/ca.yml +21 -0
  65. data/locales/ckb.yml +18 -0
  66. data/locales/cs.yml +23 -0
  67. data/locales/da.yml +21 -0
  68. data/locales/de.yml +21 -0
  69. data/locales/en.yml +21 -0
  70. data/locales/es.yml +21 -0
  71. data/locales/fr.yml +21 -0
  72. data/locales/hr.yml +25 -0
  73. data/locales/id.yml +19 -0
  74. data/locales/it.yml +21 -0
  75. data/locales/ja.yml +19 -0
  76. data/locales/km.yml +19 -0
  77. data/locales/ko.yml +17 -0
  78. data/locales/nb.yml +21 -0
  79. data/locales/nl.yml +21 -0
  80. data/locales/nn.yml +21 -0
  81. data/locales/pl.yml +25 -0
  82. data/locales/pt-BR.yml +21 -0
  83. data/locales/pt.yml +21 -0
  84. data/locales/ru.yml +25 -0
  85. data/locales/sr.yml +25 -0
  86. data/locales/sv-SE.yml +21 -0
  87. data/locales/sv.yml +21 -0
  88. data/locales/sw.yml +23 -0
  89. data/locales/ta.yml +23 -0
  90. data/locales/tr.yml +19 -0
  91. data/locales/uk.yml +25 -0
  92. data/locales/vi.yml +17 -0
  93. data/locales/zh-CN.yml +19 -0
  94. data/locales/zh-HK.yml +19 -0
  95. data/locales/zh-TW.yml +19 -0
  96. data/stylesheets/pagy.css +46 -0
  97. data/stylesheets/pagy.scss +48 -0
  98. data/stylesheets/pagy.tailwind.css +21 -0
  99. metadata +79 -66
  100. data/lib/javascripts/pagy-dev.js +0 -118
  101. data/lib/javascripts/pagy-module.d.ts +0 -30
  102. data/lib/javascripts/pagy-module.js +0 -117
  103. data/lib/javascripts/pagy.js +0 -1
  104. data/lib/locales/ar.yml +0 -26
  105. data/lib/locales/bg.yml +0 -22
  106. data/lib/locales/bs.yml +0 -24
  107. data/lib/locales/ca.yml +0 -22
  108. data/lib/locales/cs.yml +0 -22
  109. data/lib/locales/da.yml +0 -22
  110. data/lib/locales/de.yml +0 -22
  111. data/lib/locales/en.yml +0 -22
  112. data/lib/locales/es.yml +0 -22
  113. data/lib/locales/fr.yml +0 -22
  114. data/lib/locales/hr.yml +0 -24
  115. data/lib/locales/id.yml +0 -20
  116. data/lib/locales/it.yml +0 -22
  117. data/lib/locales/ja.yml +0 -20
  118. data/lib/locales/km.yml +0 -19
  119. data/lib/locales/ko.yml +0 -20
  120. data/lib/locales/nb.yml +0 -22
  121. data/lib/locales/nl.yml +0 -22
  122. data/lib/locales/pl.yml +0 -24
  123. data/lib/locales/pt-BR.yml +0 -22
  124. data/lib/locales/pt.yml +0 -22
  125. data/lib/locales/ru.yml +0 -24
  126. data/lib/locales/sr.yml +0 -23
  127. data/lib/locales/sv-SE.yml +0 -23
  128. data/lib/locales/sv.yml +0 -23
  129. data/lib/locales/sw.yml +0 -22
  130. data/lib/locales/ta.yml +0 -22
  131. data/lib/locales/tr.yml +0 -20
  132. data/lib/locales/uk.yml +0 -24
  133. data/lib/locales/zh-CN.yml +0 -20
  134. data/lib/locales/zh-HK.yml +0 -20
  135. data/lib/locales/zh-TW.yml +0 -20
  136. data/lib/pagy/calendar/month_mixin.rb +0 -49
  137. data/lib/pagy/extras/navs.rb +0 -63
  138. data/lib/pagy/extras/support.rb +0 -54
  139. data/lib/templates/bootstrap_nav.html.erb +0 -24
  140. data/lib/templates/bootstrap_nav.html.haml +0 -34
  141. data/lib/templates/bootstrap_nav.html.slim +0 -34
  142. data/lib/templates/bulma_nav.html.erb +0 -24
  143. data/lib/templates/bulma_nav.html.haml +0 -32
  144. data/lib/templates/bulma_nav.html.slim +0 -32
  145. data/lib/templates/foundation_nav.html.erb +0 -24
  146. data/lib/templates/foundation_nav.html.haml +0 -34
  147. data/lib/templates/foundation_nav.html.slim +0 -34
  148. data/lib/templates/nav.html.erb +0 -22
  149. data/lib/templates/nav.html.haml +0 -30
  150. data/lib/templates/nav.html.slim +0 -29
  151. data/lib/templates/uikit_nav.html.erb +0 -15
  152. data/lib/templates/uikit_nav.html.haml +0 -28
  153. data/lib/templates/uikit_nav.html.slim +0 -28
@@ -0,0 +1,40 @@
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/size
2
+ # frozen_string_literal: true
3
+
4
+ class Pagy # :nodoc:
5
+ # Implement the legacy bar using the array size.
6
+ # Unless you have very specific requirements, use the faster and better looking default bar.
7
+ module SizeExtra
8
+ # Setup @items based on the :gearbox_items variable
9
+ def series(size: @vars[:size], **_)
10
+ return super unless size.is_a?(Array)
11
+ return [] if size == []
12
+ raise VariableError.new(self, :size, 'to be an Array of 4 Integers or []', size) \
13
+ unless size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier
14
+
15
+ [].tap do |series|
16
+ # This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3)
17
+ # However the behavior of the legacy nav bar was taken straight from WillPaginate and Kaminari:
18
+ # it's ill-concieved and complicates the experience of devs and users.
19
+ left_gap_start = 1 + size[0]
20
+ left_gap_end = @page - size[1] - 1
21
+ right_gap_start = @page + size[2] + 1
22
+ right_gap_end = @last - size[3]
23
+ left_gap_end = right_gap_end if left_gap_end > right_gap_end
24
+ right_gap_start = left_gap_start if left_gap_start > right_gap_start
25
+ start = 1
26
+ if (left_gap_end - left_gap_start).positive?
27
+ series.push(*start...left_gap_start, :gap)
28
+ start = left_gap_end + 1
29
+ end
30
+ if (right_gap_end - right_gap_start).positive?
31
+ series.push(*start...right_gap_start, :gap)
32
+ start = right_gap_end + 1
33
+ end
34
+ series.push(*start..@last)
35
+ series[series.index(@page)] = @page.to_s
36
+ end
37
+ end
38
+ end
39
+ prepend SizeExtra
40
+ 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'
@@ -8,6 +8,7 @@ class Pagy # :nodoc:
8
8
  # even in the irb/rails console without any app or config.
9
9
  module StandaloneExtra
10
10
  # Extracted from Rack::Utils and reformatted for rubocop
11
+ # :nocov:
11
12
  module QueryUtils
12
13
  module_function
13
14
 
@@ -24,36 +25,31 @@ class Pagy # :nodoc:
24
25
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
25
26
  end.delete_if(&:empty?).join('&')
26
27
  when nil
27
- prefix
28
+ escape(prefix)
28
29
  else
29
30
  raise ArgumentError, 'value must be a Hash' if prefix.nil?
30
31
 
31
- "#{prefix}=#{escape(value)}"
32
+ "#{escape(prefix)}=#{escape(value)}"
32
33
  end
33
34
  end
34
35
  end
36
+ # :nocov:
35
37
 
36
38
  # Return the URL for the page. If there is no pagy.vars[:url]
37
39
  # it works exactly as the regular #pagy_url_for, relying on the params method and Rack.
38
40
  # If there is a defined pagy.vars[:url] variable it does not need the params method nor Rack.
39
- def pagy_url_for(pagy, page, absolute: nil)
41
+ def pagy_url_for(pagy, page, absolute: false, **_)
40
42
  return super unless pagy.vars[:url]
41
43
 
42
- vars = pagy.vars
43
- page_param = vars[:page_param].to_s
44
- items_param = vars[:items_param].to_s
45
- params = pagy.params.is_a?(Hash) ? pagy.params.clone : {} # safe when it gets reused
46
- params[page_param] = page
47
- params[items_param] = vars[:items] if vars[:items_extra]
48
- query_string = "?#{QueryUtils.build_nested_query(pagy_deprecated_params(pagy, params))}" # remove in 6.0
49
- # params = pagy.params.call(params) if pagy.params.is_a?(Proc) # add in 6.0
50
- # query_string = "?#{Rack::Utils.build_nested_query(params)}" # add in 6.0
44
+ vars = pagy.vars
45
+ params = vars[:params].is_a?(Hash) ? vars[:params].clone : {} # safe when it gets reused
46
+ pagy_set_query_params(page, vars, params)
47
+ params = vars[:params].(params) if vars[:params].is_a?(Proc)
48
+ query_string = "?#{QueryUtils.build_nested_query(params)}"
51
49
  "#{vars[:url]}#{query_string}#{vars[:fragment]}"
52
50
  end
53
51
  end
54
- # In ruby 3+ `UrlHelpers.prepend StandaloneExtra` would be enough instead of using the next 2 lines
55
- Frontend.prepend StandaloneExtra
56
- Backend.prepend StandaloneExtra
52
+ UrlHelpers.prepend StandaloneExtra
57
53
 
58
54
  # Define a dummy params method if it's not already defined in the including module
59
55
  module Backend
@@ -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:
@@ -6,23 +6,23 @@ class Pagy # :nodoc:
6
6
 
7
7
  # Remove the page=1 param from the first page link
8
8
  module TrimExtra
9
- # Override the original pagy_link_proc.
10
- # Call the pagy_trim method if the trim_extra is enabled.
11
- def pagy_link_proc(pagy, link_extra: '')
12
- link_proc = super(pagy, link_extra: link_extra)
13
- return link_proc unless pagy.vars[:trim_extra]
9
+ # Override the original pagy_a_proc.
10
+ # Call the pagy_trim method for page 1 if the trim_extra is enabled
11
+ def pagy_anchor(pagy)
12
+ a_proc = super
13
+ return a_proc unless pagy.vars[:trim_extra]
14
14
 
15
- lambda do |page, text = pagy.label_for(page), extra = ''|
16
- link = +link_proc.call(page, text, extra)
17
- return link unless page == 1
15
+ lambda do |page, text = pagy.label_for(page), **opts|
16
+ a = +a_proc.(page, text, **opts)
17
+ return a unless page.to_s == '1'
18
18
 
19
- pagy_trim(pagy, link)
19
+ pagy_trim(pagy, a) # in method for isolated testing
20
20
  end
21
21
  end
22
22
 
23
- # Remove the the :page_param param from the first page link
24
- def pagy_trim(pagy, link)
25
- link.sub!(/[?&]#{pagy.vars[:page_param]}=1\b(?!&)|\b#{pagy.vars[:page_param]}=1&/, '')
23
+ # Remove the the :page_param param from the first page anchor
24
+ def pagy_trim(pagy, a)
25
+ a.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,82 +1,96 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/uikit
2
1
  # frozen_string_literal: true
3
2
 
4
- require 'pagy/extras/frontend_helpers'
3
+ warn '[PAGY WARNING] The uikit extra has been discontinued and it will be removed in v9 ' \
4
+ '(https://github.com/ddnexus/pagy/discussions/672#discussioncomment-9212328)'
5
+
6
+ require_relative 'js_tools'
5
7
 
6
8
  class Pagy # :nodoc:
7
9
  # Frontend modules are specially optimized for performance.
8
10
  # The resulting code may not look very elegant, but produces the best benchmarks
9
11
  module UikitExtra
10
12
  # 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)
12
- p_id = %( id="#{pagy_id}") if pagy_id
13
- link = pagy_link_proc(pagy, link_extra: link_extra)
13
+ def pagy_uikit_nav(pagy, id: nil, aria_label: nil, **vars)
14
+ id = %( id="#{id}") if id
15
+ a = pagy_anchor(pagy)
14
16
 
15
- html = +%(<ul#{p_id} class="pagy-uikit-nav uk-pagination uk-flex-center">#{pagy_uikit_prev_html pagy, link})
17
+ html = %(<ul#{id} class="pagy-uikit nav uk-pagination uk-flex-center" role="navigation" #{
18
+ nav_aria_label(pagy, aria_label:)}>#{
19
+ uikit_prev_html(pagy, a)})
16
20
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
21
  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}"
22
+ when Integer
23
+ %(<li>#{a.(item)}</li>)
24
+ when String
25
+ %(<li class="uk-active"><span role="link" aria-current="page" aria-disabled="true">#{
26
+ pagy.label_for(item)}</span></li>)
27
+ when :gap
28
+ %(<li class="uk-disabled"><span>#{pagy_t 'pagy.gap'}</span></li>)
29
+ else
30
+ raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
22
31
  end
23
32
  end
24
- html << pagy_uikit_next_html(pagy, link)
25
- html << %(</ul>)
33
+ html << %(#{uikit_next_html(pagy, a)}</ul>)
26
34
  end
27
35
 
28
- # 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)
36
+ # Javascript pagination for uikit: it returns a nav with a data-pagy attribute used by the pagy.js file
37
+ def pagy_uikit_nav_js(pagy, id: nil, aria_label: nil, **vars)
30
38
  sequels = pagy.sequels(**vars)
31
- 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),
34
- '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) }
39
+ id = %( id="#{id}") if id
40
+ a = pagy_anchor(pagy)
41
+ tokens = { 'before' => uikit_prev_html(pagy, a),
42
+ 'a' => %(<li>#{a.(PAGE_TOKEN, LABEL_TOKEN)}</li>),
43
+ 'current' => %(<li class="uk-active"><span role="link" aria-current="page" aria-disabled="true">#{
44
+ LABEL_TOKEN}</span></li>),
45
+ 'gap' => %(<li class="uk-disabled"><span>#{pagy_t 'pagy.gap'}</span></li>),
46
+ 'after' => uikit_next_html(pagy, a) }
38
47
 
39
- %(<ul#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-uikit-nav-js uk-pagination uk-flex-center" #{
40
- pagy_json_attr(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></ul>)
48
+ %(<ul#{id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-uikit nav-js uk-pagination uk-flex-center" role="navigation" #{
49
+ nav_aria_label(pagy, aria_label:)} #{
50
+ pagy_data(pagy, :nav, tokens, sequels, pagy.label_sequels(sequels))
51
+ }></ul>)
41
52
  end
42
53
 
43
- # 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: '')
45
- p_id = %( id="#{pagy_id}") if pagy_id
46
- link = pagy_link_proc(pagy, link_extra: link_extra)
47
- p_page = pagy.page
48
- p_pages = pagy.pages
49
- 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;">)
54
+ # Javascript combo pagination for uikit: it returns a nav with a data-pagy attribute used by the pagy.js file
55
+ def pagy_uikit_combo_nav_js(pagy, id: nil, aria_label: nil)
56
+ id = %( id="#{id}") if id
57
+ a = pagy_anchor(pagy)
58
+ pages = pagy.pages
59
+
60
+ page_input = %(<input name="page" type="number" min="1" max="#{pages}" value="#{pagy.page}" aria-current="page" ) <<
61
+ %(style="text-align: center; width: #{pages.to_s.length + 1}rem;">#{JSTools::A_TAG})
51
62
 
52
- %(<ul#{p_id} class="pagy-uikit-combo-nav-js uk-button-group uk-pagination uk-flex-center" #{
53
- pagy_json_attr pagy, :combo, pagy_marked_link(link)
63
+ %(<ul#{id} class="pagy-uikit combo-nav-js uk-button-group uk-pagination uk-flex-center" role="navigation" #{
64
+ nav_aria_label(pagy, aria_label:)} #{
65
+ pagy_data(pagy, :combo, pagy_url_for(pagy, PAGE_TOKEN))
54
66
  }>#{
55
- pagy_uikit_prev_html pagy, link
56
- }<li>#{
57
- pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
58
- }</li>#{
59
- pagy_uikit_next_html pagy, link
67
+ uikit_prev_html(pagy, a)
68
+ }<li><label>#{
69
+ pagy_t('pagy.combo_nav_js', page_input:, pages:)
70
+ }</label></li>#{
71
+ uikit_next_html(pagy, a)
60
72
  }</ul>)
61
73
  end
62
74
 
63
75
  private
64
76
 
65
- def pagy_uikit_prev_html(pagy, link)
66
- previous_span = %(<span uk-pagination-previous>#{pagy_t 'pagy.nav.prev'}</span>)
77
+ def uikit_prev_html(pagy, a)
78
+ span = %(<span uk-pagination-previous></span>)
67
79
  if (p_prev = pagy.prev)
68
- %(<li>#{link.call p_prev, previous_span}</li>)
80
+ %(<li>#{a.(p_prev, span, aria_label: pagy_t('pagy.aria_label.prev'))}</li>)
69
81
  else
70
- %(<li class="uk-disabled"><a href="#">#{previous_span}</a></li>)
82
+ %(<li class="uk-disabled"><a role="link" aria-disabled="true" aria-label="#{
83
+ pagy_t('pagy.aria_label.prev')}">#{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, a)
88
+ span = %(<span uk-pagination-next></span>)
76
89
  if (p_next = pagy.next)
77
- %(<li>#{link.call p_next, next_span}</li>)
90
+ %(<li>#{a.(p_next, span, aria_label: pagy_t('pagy.aria_label.prev'))}</li>)
78
91
  else
79
- %(<li class="uk-disabled"><a href="#">#{next_span}</a></li>)
92
+ %(<li class="uk-disabled"><a role="link" aria-disabled="true" aria-label="#{
93
+ pagy_t('pagy.aria_label.next')}">#{span}</a></li>)
80
94
  end
81
95
  end
82
96
  end
data/lib/pagy/frontend.rb CHANGED
@@ -1,13 +1,13 @@
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
- require 'pagy/url_helpers'
5
- require 'pagy/i18n'
4
+ require_relative 'url_helpers'
5
+ require_relative 'i18n'
6
6
 
7
7
  class Pagy
8
8
  # Used for search and replace, hardcoded also in the pagy.js file
9
- PAGE_PLACEHOLDER = '__pagy_page__'
10
- LABEL_PLACEHOLDER = '__pagy_label__'
9
+ PAGE_TOKEN = '__pagy_page__'
10
+ LABEL_TOKEN = '__pagy_label__'
11
11
 
12
12
  # Frontend modules are specially optimized for performance.
13
13
  # The resulting code may not look very elegant, but produces the best benchmarks
@@ -15,62 +15,56 @@ 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, id: nil, aria_label: nil, **vars)
19
+ id = %( id="#{id}") if id
20
+ a = pagy_anchor(pagy)
23
21
 
24
- html = +%(<nav#{p_id} class="pagy-nav pagination" aria-label="pager">)
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
22
+ html = %(<nav#{id} class="pagy nav" #{nav_aria_label(pagy, aria_label:)}>#{
23
+ prev_a(pagy, a)})
30
24
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
31
25
  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}"
26
+ when Integer
27
+ a.(item)
28
+ when String
29
+ %(<a role="link" aria-disabled="true" aria-current="page" class="current">#{pagy.label_for(item)}</a>)
30
+ when :gap
31
+ %(<a role="link" aria-disabled="true" class="gap">#{pagy_t('pagy.gap')}</a>)
32
+ else
33
+ raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
36
34
  end
37
35
  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>)
36
+ html << %(#{next_a(pagy, a)}</nav>)
44
37
  end
45
38
 
46
39
  # 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)
48
- p_id = %( id="#{pagy_id}") if pagy_id
40
+ def pagy_info(pagy, id: nil, item_name: nil)
41
+ id = %( id="#{id}") if id
49
42
  p_count = pagy.count
50
- key = if p_count.zero? then 'pagy.info.no_items'
51
- elsif pagy.pages == 1 then 'pagy.info.single_page'
52
- else 'pagy.info.multiple_pages' # rubocop:disable Lint/ElseLayout
43
+ key = if p_count.zero?
44
+ 'pagy.info.no_items'
45
+ elsif pagy.pages == 1
46
+ 'pagy.info.single_page'
47
+ else
48
+ 'pagy.info.multiple_pages'
53
49
  end
54
50
 
55
- %(<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
+ %(<span#{id} class="pagy info">#{
52
+ pagy_t key, item_name: item_name || pagy_t('pagy.item_name', count: p_count),
57
53
  count: p_count, from: pagy.from, to: pagy.to
58
54
  }</span>)
59
55
  end
60
56
 
61
- # Return a performance optimized proc to generate the HTML links
57
+ # Return a performance optimized lambda to generate the HTML anchor element (a tag)
62
58
  # Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
63
- def pagy_link_proc(pagy, link_extra: '')
64
- p_prev = pagy.prev
65
- p_next = pagy.next
66
- left, right = %(<a href="#{pagy_url_for pagy, PAGE_PLACEHOLDER}" #{
67
- pagy.vars[:link_extra]} #{link_extra}).split(PAGE_PLACEHOLDER, 2)
68
- lambda do |page, text = pagy.label_for(page), extra_attrs = ''|
69
- %(#{left}#{page}#{right}#{ case page
70
- when p_prev then ' rel="prev"'
71
- when p_next then ' rel="next"'
72
- else ''
73
- end } #{extra_attrs}>#{text}</a>)
59
+ def pagy_anchor(pagy)
60
+ a_string = pagy.vars[:anchor_string]
61
+ a_string = %( #{a_string}) if a_string
62
+ left, right = %(<a#{a_string} href="#{pagy_url_for(pagy, PAGE_TOKEN)}").split(PAGE_TOKEN, 2)
63
+ # lambda used by all the helpers
64
+ lambda do |page, text = pagy.label_for(page), classes: nil, aria_label: nil|
65
+ classes = %( class="#{classes}") if classes
66
+ aria_label = %( aria-label="#{aria_label}") if aria_label
67
+ %(#{left}#{page}#{right}#{classes}#{aria_label}>#{text}</a>)
74
68
  end
75
69
  end
76
70
 
@@ -79,5 +73,28 @@ class Pagy
79
73
  def pagy_t(key, opts = {})
80
74
  Pagy::I18n.translate(@pagy_locale ||= nil, key, opts)
81
75
  end
76
+
77
+ private
78
+
79
+ def nav_aria_label(pagy, aria_label: nil)
80
+ aria_label ||= pagy_t('pagy.aria_label.nav', count: pagy.pages)
81
+ %(aria-label="#{aria_label}")
82
+ end
83
+
84
+ def prev_a(pagy, a, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'))
85
+ if (p_prev = pagy.prev)
86
+ a.(p_prev, text, aria_label:)
87
+ else
88
+ %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
89
+ end
90
+ end
91
+
92
+ def next_a(pagy, a, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.next'))
93
+ if (p_next = pagy.next)
94
+ a.(p_next, text, aria_label:)
95
+ else
96
+ %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
97
+ end
98
+ end
82
99
  end
83
100
  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,7 +94,9 @@ 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]
99
+ hash['ckb'] = RULE[:other]
98
100
  hash['cs'] = RULE[:west_slavic]
99
101
  hash['id'] = RULE[:other]
100
102
  hash['fr'] = RULE[:one_upto_two_other]
@@ -105,10 +107,9 @@ class Pagy
105
107
  hash['pl'] = RULE[:polish]
106
108
  hash['ru'] = RULE[:east_slavic]
107
109
  hash['sr'] = RULE[:east_slavic]
108
- hash['sv'] = RULE[:one_two_other]
109
- hash['sv-SE'] = RULE[:one_two_other]
110
110
  hash['tr'] = RULE[:other]
111
111
  hash['uk'] = RULE[:east_slavic]
112
+ hash['vi'] = RULE[:other]
112
113
  hash['zh-CN'] = RULE[:other]
113
114
  hash['zh-HK'] = RULE[:other]
114
115
  hash['zh-TW'] = RULE[:other]
@@ -6,33 +6,21 @@ 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: nil)
10
- vars = pagy.vars
11
- page_param = vars[:page_param].to_s
12
- items_param = vars[:items_param].to_s
13
- params = pagy.params.is_a?(Hash) ? pagy.params.transform_keys(&:to_s) : {}
14
- params = request.GET.merge(params)
15
- params[page_param] = page
16
- params[items_param] = vars[:items] if vars[:items_extra]
17
- query_string = "?#{Rack::Utils.build_nested_query(pagy_deprecated_params(pagy, params))}" # remove in 6.0
18
- # params = pagy.params.call(params) if pagy.params.is_a?(Proc) # add in 6.0
19
- # query_string = "?#{Rack::Utils.build_nested_query(params)}" # add in 6.0
20
- "#{request.base_url if absolute}#{request.path}#{query_string}#{vars[:fragment]}"
9
+ def pagy_url_for(pagy, page, absolute: false, **_)
10
+ vars = pagy.vars
11
+ query_params = request.GET.clone
12
+ query_params.merge!(vars[:params].transform_keys(&:to_s)) if vars[:params].is_a?(Hash)
13
+ pagy_set_query_params(page, vars, query_params)
14
+ query_params = vars[:params].(query_params) if vars[:params].is_a?(Proc)
15
+ query_string = "?#{Rack::Utils.build_nested_query(query_params)}"
16
+ "#{request.base_url if absolute}#{vars[:request_path] || request.path}#{query_string}#{vars[:fragment]}"
21
17
  end
22
18
 
23
- private
24
-
25
- # Transitional code to handle params deprecations. It will be removed in version 6.0
26
- def pagy_deprecated_params(pagy, params) # remove in 6.0
27
- if pagy.params.is_a?(Proc) # new code
28
- pagy.params.call(params)
29
- elsif respond_to?(:pagy_massage_params) # deprecated code
30
- Warning.warn '[PAGY WARNING] The pagy_massage_params method has been deprecated and it will be ignored from version 6. ' \
31
- 'Set the :params variable to a Proc with the same code as the pagy_massage_params method.'
32
- pagy_massage_params(params)
33
- else
34
- params # no massage params
35
- end
19
+ # Add the page and items params
20
+ # Overridable by the jsonapi extra
21
+ def pagy_set_query_params(page, vars, query_params)
22
+ query_params[vars[:page_param].to_s] = page
23
+ query_params[vars[:items_param].to_s] = vars[:items] if vars[:items_extra]
36
24
  end
37
25
  end
38
26
  end