pagy 7.0.11 → 8.6.3

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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/apps/calendar.ru +745 -0
  3. data/apps/demo.ru +435 -0
  4. data/apps/rails.ru +212 -0
  5. data/apps/repro.ru +177 -0
  6. data/apps/tmp/calendar.sqlite3 +0 -0
  7. data/apps/tmp/calendar.sqlite3-shm +0 -0
  8. data/apps/tmp/calendar.sqlite3-wal +0 -0
  9. data/apps/tmp/local_secret.txt +1 -0
  10. data/apps/tmp/pagy-keyset.sqlite3 +0 -0
  11. data/apps/tmp/pagy-keyset.sqlite3-shm +0 -0
  12. data/apps/tmp/pagy-keyset.sqlite3-wal +0 -0
  13. data/bin/pagy +100 -0
  14. data/{lib/config → config}/pagy.rb +31 -73
  15. data/javascripts/pagy-module.js +100 -0
  16. data/javascripts/pagy.js +4 -0
  17. data/javascripts/pagy.min.js +4 -0
  18. data/javascripts/pagy.min.js.map +10 -0
  19. data/javascripts/pagy.mjs +100 -0
  20. data/lib/optimist.rb +1022 -0
  21. data/lib/pagy/backend.rb +8 -3
  22. data/lib/pagy/calendar/day.rb +4 -3
  23. data/lib/pagy/calendar/month.rb +4 -3
  24. data/lib/pagy/calendar/quarter.rb +4 -3
  25. data/lib/pagy/calendar/unit.rb +106 -0
  26. data/lib/pagy/calendar/week.rb +3 -3
  27. data/lib/pagy/calendar/year.rb +4 -3
  28. data/lib/pagy/calendar.rb +55 -99
  29. data/lib/pagy/console.rb +2 -2
  30. data/lib/pagy/countless.rb +15 -10
  31. data/lib/pagy/extras/arel.rb +1 -1
  32. data/lib/pagy/extras/array.rb +1 -1
  33. data/lib/pagy/extras/bootstrap.rb +52 -63
  34. data/lib/pagy/extras/bulma.rb +49 -64
  35. data/lib/pagy/extras/calendar.rb +35 -5
  36. data/lib/pagy/extras/countless.rb +2 -2
  37. data/lib/pagy/extras/foundation.rb +52 -62
  38. data/lib/pagy/extras/gearbox.rb +28 -27
  39. data/lib/pagy/extras/headers.rb +1 -1
  40. data/lib/pagy/extras/i18n.rb +1 -1
  41. data/lib/pagy/extras/items.rb +21 -18
  42. data/lib/pagy/extras/{frontend_helpers.rb → js_tools.rb} +9 -6
  43. data/lib/pagy/extras/jsonapi.rb +2 -2
  44. data/lib/pagy/extras/materialize.rb +56 -52
  45. data/lib/pagy/extras/metadata.rb +6 -2
  46. data/lib/pagy/extras/overflow.rb +5 -4
  47. data/lib/pagy/extras/pagy.rb +82 -0
  48. data/lib/pagy/extras/semantic.rb +50 -51
  49. data/lib/pagy/extras/size.rb +40 -0
  50. data/lib/pagy/extras/standalone.rb +2 -2
  51. data/lib/pagy/extras/trim.rb +12 -12
  52. data/lib/pagy/extras/uikit.rb +51 -50
  53. data/lib/pagy/frontend.rb +39 -53
  54. data/lib/pagy/url_helpers.rb +9 -10
  55. data/lib/pagy.rb +51 -82
  56. data/{lib/locales → locales}/ar.yml +10 -11
  57. data/{lib/locales → locales}/be.yml +5 -5
  58. data/{lib/locales → locales}/bg.yml +5 -5
  59. data/{lib/locales → locales}/bs.yml +5 -5
  60. data/locales/ca.yml +21 -0
  61. data/locales/ckb.yml +18 -0
  62. data/{lib/locales → locales}/cs.yml +5 -5
  63. data/locales/da.yml +21 -0
  64. data/{lib/locales → locales}/de.yml +5 -5
  65. data/{lib/locales → locales}/en.yml +5 -5
  66. data/{lib/locales → locales}/es.yml +3 -3
  67. data/{lib/locales → locales}/fr.yml +5 -5
  68. data/{lib/locales → locales}/hr.yml +5 -5
  69. data/{lib/locales → locales}/id.yml +5 -5
  70. data/{lib/locales → locales}/it.yml +5 -5
  71. data/{lib/locales → locales}/ja.yml +5 -5
  72. data/{lib/locales → locales}/km.yml +5 -5
  73. data/locales/ko.yml +17 -0
  74. data/{lib/locales → locales}/nb.yml +5 -5
  75. data/{lib/locales → locales}/nl.yml +5 -5
  76. data/{lib/locales → locales}/nn.yml +5 -5
  77. data/{lib/locales → locales}/pl.yml +5 -5
  78. data/{lib/locales → locales}/pt-BR.yml +3 -3
  79. data/{lib/locales → locales}/pt.yml +3 -3
  80. data/locales/ru.yml +25 -0
  81. data/{lib/locales → locales}/sr.yml +5 -5
  82. data/{lib/locales → locales}/sv-SE.yml +5 -5
  83. data/{lib/locales → locales}/sv.yml +5 -5
  84. data/{lib/locales → locales}/sw.yml +5 -5
  85. data/{lib/locales → locales}/ta.yml +5 -5
  86. data/{lib/locales → locales}/tr.yml +5 -5
  87. data/{lib/locales → locales}/uk.yml +5 -5
  88. data/locales/vi.yml +17 -0
  89. data/{lib/locales → locales}/zh-CN.yml +5 -5
  90. data/{lib/locales → locales}/zh-HK.yml +5 -5
  91. data/{lib/locales → locales}/zh-TW.yml +5 -5
  92. data/{lib/stylesheets → stylesheets}/pagy.css +19 -34
  93. data/{lib/stylesheets → stylesheets}/pagy.scss +17 -19
  94. data/stylesheets/pagy.tailwind.css +21 -0
  95. metadata +76 -53
  96. data/lib/javascripts/pagy-dev.js +0 -112
  97. data/lib/javascripts/pagy-module.js +0 -111
  98. data/lib/javascripts/pagy.js +0 -1
  99. data/lib/locales/ca.yml +0 -23
  100. data/lib/locales/ckb.yml +0 -18
  101. data/lib/locales/da.yml +0 -23
  102. data/lib/locales/ko.yml +0 -19
  103. data/lib/locales/ru.yml +0 -27
  104. data/lib/locales/vi.yml +0 -17
  105. data/lib/pagy/calendar/helper.rb +0 -65
  106. data/lib/pagy/extras/navs.rb +0 -51
  107. data/lib/pagy/extras/support.rb +0 -40
  108. data/lib/stylesheets/pagy.tailwind.scss +0 -24
  109. /data/{lib/javascripts/pagy-module.d.ts → javascripts/pagy.d.ts} +0 -0
@@ -1,25 +1,25 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/semantic
2
1
  # frozen_string_literal: true
3
2
 
4
- require 'pagy/extras/frontend_helpers'
3
+ warn '[PAGY WARNING] The semantic 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 SemanticExtra
10
12
  # 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: '',
12
- nav_aria_label: nil, nav_i18n_key: nil, **vars)
13
- p_id = %( id="#{pagy_id}") if pagy_id
14
- link = pagy_link_proc(pagy, link_extra:)
13
+ def pagy_semantic_nav(pagy, id: nil, aria_label: nil, **vars)
14
+ id = %( id="#{id}") if id
15
+ a = pagy_anchor(pagy)
15
16
 
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
+ html = %(<div#{id} role="navigation" class="pagy-semantic nav ui pagination menu" #{
18
+ nav_aria_label(pagy, aria_label:)}>#{semantic_prev_html(pagy, a)})
19
19
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
20
20
  html << case item
21
21
  when Integer
22
- link.call(item, pagy.label_for(item), %(class="item"))
22
+ a.(item, pagy.label_for(item), classes: 'item')
23
23
  when String
24
24
  %(<a role="link" class="item active" aria-current="page" aria-disabled="true">#{pagy.label_for(item)}</a>)
25
25
  when :gap
@@ -28,66 +28,65 @@ class Pagy # :nodoc:
28
28
  raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
29
29
  end
30
30
  end
31
- html << semantic_next_html(pagy, link)
32
- html << %(</div>)
31
+ html << %(#{semantic_next_html(pagy, a)}</div>)
33
32
  end
34
33
 
35
- # Javascript pagination for semantic: it returns a nav and a JSON tag used by the pagy.js file
36
- def pagy_semantic_nav_js(pagy, pagy_id: nil, link_extra: '',
37
- nav_aria_label: nil, nav_i18n_key: nil, **vars)
34
+ # Javascript pagination for semantic: it returns a nav with a data-pagy attribute used by the pagy.js file
35
+ def pagy_semantic_nav_js(pagy, id: nil, aria_label: nil, **vars)
38
36
  sequels = pagy.sequels(**vars)
39
- p_id = %( id="#{pagy_id}") if pagy_id
40
- link = pagy_link_proc(pagy, link_extra:)
41
- tags = { 'before' => semantic_prev_html(pagy, link),
42
- 'link' => link.call(PAGE_TOKEN, LABEL_TOKEN, %(class="item")),
43
- 'active' => %(<a role="link" class="item active" aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a>),
44
- 'gap' => %(<div class="disabled item">#{pagy_t('pagy.gap')}</div>),
45
- 'after' => semantic_next_html(pagy, link) }
37
+ id = %( id="#{id}") if id
38
+ a = pagy_anchor(pagy)
39
+ tokens = { 'before' => semantic_prev_html(pagy, a),
40
+ 'a' => a.(PAGE_TOKEN, LABEL_TOKEN, classes: 'item'),
41
+ 'current' => %(<a role="link" class="item active" aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a>),
42
+ 'gap' => %(<div class="disabled item">#{pagy_t('pagy.gap')}</div>),
43
+ 'after' => semantic_next_html(pagy, a) }
46
44
 
47
- %(<div#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-semantic-nav-js ui pagination menu" role="navigation" #{
48
- nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)} #{
49
- pagy_data(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></div>)
45
+ %(<div#{id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-semantic nav-js ui pagination menu" role="navigation" #{
46
+ nav_aria_label(pagy, aria_label:)} #{
47
+ pagy_data(pagy, :nav, tokens, sequels, pagy.label_sequels(sequels))
48
+ }></div>)
50
49
  end
51
50
 
52
- # Combo pagination for semantic: it returns a nav and a JSON tag used by the pagy.js file
53
- def pagy_semantic_combo_nav_js(pagy, pagy_id: nil, link_extra: '',
54
- nav_aria_label: nil, nav_i18n_key: nil)
55
- p_id = %( id="#{pagy_id}") if pagy_id
56
- link = pagy_link_proc(pagy, link_extra: %(class="item" #{link_extra}))
57
- p_page = pagy.page
58
- p_pages = pagy.pages
59
- input = %(<input name="page" type="number" min="1" max="#{p_pages}" value="#{
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">)
51
+ # Combo pagination for semantic: it returns a nav with a data-pagy attribute used by the pagy.js file
52
+ def pagy_semantic_combo_nav_js(pagy, id: nil, aria_label: nil)
53
+ id = %( id="#{id}") if id
54
+ a = pagy_anchor(pagy)
55
+ pages = pagy.pages
56
+
57
+ page_input = %(<input name="page" type="number" min="1" max="#{pages}" value="#{pagy.page}" aria-current="page") <<
58
+ %(style="text-align: center; width: #{pages.to_s.length + 1}rem; padding: 0; margin: 0 0.3rem">) <<
59
+ JSTools::A_TAG
62
60
 
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)} #{
65
- pagy_data(pagy, :combo, pagy_url_for(pagy, PAGE_TOKEN))}>#{
66
- semantic_prev_html pagy, link
67
- }<div class="pagy-combo-input item">#{
68
- pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
69
- }</div> #{
70
- semantic_next_html pagy, link
61
+ %(<div#{id} class="pagy-semantic combo-nav-js ui compact menu" role="navigation" #{
62
+ nav_aria_label(pagy, aria_label:)} #{
63
+ pagy_data(pagy, :combo, pagy_url_for(pagy, PAGE_TOKEN))
64
+ }>#{
65
+ semantic_prev_html(pagy, a)
66
+ }<div class="item"><label>#{
67
+ pagy_t('pagy.combo_nav_js', page_input:, pages:)
68
+ }</label></div> #{
69
+ semantic_next_html(pagy, a)
71
70
  }</div>)
72
71
  end
73
72
 
74
73
  private
75
74
 
76
- def semantic_prev_html(pagy, link)
75
+ def semantic_prev_html(pagy, a)
77
76
  if (p_prev = pagy.prev)
78
- link.call(p_prev, pagy_t('pagy.prev'), %(#{prev_aria_label_attr} class="item"))
77
+ a.(p_prev, pagy_t('pagy.prev'), classes: 'item', aria_label: pagy_t('pagy.aria_label.prev'))
79
78
  else
80
- +%(<div class="item disabled" role="link" aria-disabled="true" #{
81
- prev_aria_label_attr}>#{pagy_t('pagy.prev')}</div>)
79
+ %(<div class="item disabled" role="a" aria-disabled="true" aria-label="#{
80
+ pagy_t('pagy.aria_label.prev')}">#{pagy_t('pagy.prev')}</div>)
82
81
  end
83
82
  end
84
83
 
85
- def semantic_next_html(pagy, link)
84
+ def semantic_next_html(pagy, a)
86
85
  if (p_next = pagy.next)
87
- link.call(p_next, pagy_t('pagy.next'), %(#{next_aria_label_attr} class="item"))
86
+ a.(p_next, pagy_t('pagy.next'), classes: 'item', aria_label: pagy_t('pagy.aria_label.next'))
88
87
  else
89
- +%(<div class="item disabled" role="link" aria-disabled="true" #{
90
- next_aria_label_attr}>#{pagy_t('pagy.next')}</div>)
88
+ %(<div class="item disabled" role="link" aria-disabled="true" aria=label="#{
89
+ pagy_t('pagy.aria_label.prev')}">#{pagy_t('pagy.next')}</div>)
91
90
  end
92
91
  end
93
92
  end
@@ -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
@@ -42,9 +42,9 @@ class Pagy # :nodoc:
42
42
  return super unless pagy.vars[:url]
43
43
 
44
44
  vars = pagy.vars
45
- params = pagy.params.is_a?(Hash) ? pagy.params.clone : {} # safe when it gets reused
45
+ params = vars[:params].is_a?(Hash) ? vars[:params].clone : {} # safe when it gets reused
46
46
  pagy_set_query_params(page, vars, params)
47
- params = pagy.params.call(params) if pagy.params.is_a?(Proc)
47
+ params = vars[:params].(params) if vars[:params].is_a?(Proc)
48
48
  query_string = "?#{QueryUtils.build_nested_query(params)}"
49
49
  "#{vars[:url]}#{query_string}#{vars[:fragment]}"
50
50
  end
@@ -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:)
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.to_s == '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,25 +1,26 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/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: '',
12
- nav_aria_label: nil, nav_i18n_key: nil, **vars)
13
- p_id = %( id="#{pagy_id}") if pagy_id
14
- link = pagy_link_proc(pagy, link_extra:)
15
-
16
- html = +%(<ul#{p_id} class="pagy-uikit-nav uk-pagination uk-flex-center" role="navigation" #{
13
+ def pagy_uikit_nav(pagy, id: nil, aria_label: nil, **vars)
14
+ id = %( id="#{id}") if id
15
+ a = pagy_anchor(pagy)
17
16
 
18
- nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)}>#{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)})
19
20
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
20
21
  html << case item
21
22
  when Integer
22
- %(<li>#{link.call(item)}</li>)
23
+ %(<li>#{a.(item)}</li>)
23
24
  when String
24
25
  %(<li class="uk-active"><span role="link" aria-current="page" aria-disabled="true">#{
25
26
  pagy.label_for(item)}</span></li>)
@@ -29,67 +30,67 @@ class Pagy # :nodoc:
29
30
  raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
30
31
  end
31
32
  end
32
- html << uikit_next_html(pagy, link)
33
- html << %(</ul>)
33
+ html << %(#{uikit_next_html(pagy, a)}</ul>)
34
34
  end
35
35
 
36
- # Javascript pagination for uikit: it returns a nav and a JSON tag used by the pagy.js file
37
- def pagy_uikit_nav_js(pagy, pagy_id: nil, link_extra: '',
38
- nav_aria_label: nil, nav_i18n_key: nil, **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)
39
38
  sequels = pagy.sequels(**vars)
40
- p_id = %( id="#{pagy_id}") if pagy_id
41
- link = pagy_link_proc(pagy, link_extra:)
42
- tags = { 'before' => uikit_prev_html(pagy, link),
43
- 'link' => %(<li>#{link.call(PAGE_TOKEN, LABEL_TOKEN)}</li>),
44
- 'active' => %(<li class="uk-active"><span role="link" aria-current="page" aria-disabled="true">#{
45
- LABEL_TOKEN}</span></li>),
46
- 'gap' => %(<li class="uk-disabled"><span>#{pagy_t 'pagy.gap'}</span></li>),
47
- 'after' => 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) }
48
47
 
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>)
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>)
52
52
  end
53
53
 
54
- # Javascript combo pagination for uikit: it returns a nav and a JSON tag used by the pagy.js file
55
- def pagy_uikit_combo_nav_js(pagy, pagy_id: nil, link_extra: '',
56
- nav_aria_label: nil, nav_i18n_key: nil)
57
- p_id = %( id="#{pagy_id}") if pagy_id
58
- link = pagy_link_proc(pagy, link_extra:)
59
- p_page = pagy.page
60
- p_pages = pagy.pages
61
- input = %(<input name="page" type="number" min="1" max="#{p_pages}" value="#{
62
- p_page}" style="text-align: center; width: #{p_pages.to_s.length + 1}rem;" aria-current="page">)
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})
63
62
 
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)} #{
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:)} #{
66
65
  pagy_data(pagy, :combo, pagy_url_for(pagy, PAGE_TOKEN))
67
66
  }>#{
68
- uikit_prev_html pagy, link
69
- }<li>#{
70
- pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages
71
- }</li>#{
72
- 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)
73
72
  }</ul>)
74
73
  end
75
74
 
76
75
  private
77
76
 
78
- def uikit_prev_html(pagy, link)
79
- previous_span = %(<span uk-pagination-previous></span>)
77
+ def uikit_prev_html(pagy, a)
78
+ span = %(<span uk-pagination-previous></span>)
80
79
  if (p_prev = pagy.prev)
81
- %(<li>#{link.call(p_prev, previous_span, prev_aria_label_attr)}</li>)
80
+ %(<li>#{a.(p_prev, span, aria_label: pagy_t('pagy.aria_label.prev'))}</li>)
82
81
  else
83
- %(<li class="uk-disabled"><span role="link" aria-disabled="true" #{prev_aria_label_attr}>#{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>)
84
84
  end
85
85
  end
86
86
 
87
- def uikit_next_html(pagy, link)
88
- next_span = %(<span uk-pagination-next></span>)
87
+ def uikit_next_html(pagy, a)
88
+ span = %(<span uk-pagination-next></span>)
89
89
  if (p_next = pagy.next)
90
- %(<li>#{link.call(p_next, next_span, next_aria_label_attr)}</li>)
90
+ %(<li>#{a.(p_next, span, aria_label: pagy_t('pagy.aria_label.prev'))}</li>)
91
91
  else
92
- %(<li class="uk-disabled"><span role="link" aria-disabled="true" #{prev_aria_label_attr}>#{next_span}</span></li>)
92
+ %(<li class="uk-disabled"><a role="link" aria-disabled="true" aria-label="#{
93
+ pagy_t('pagy.aria_label.next')}">#{span}</a></li>)
93
94
  end
94
95
  end
95
96
  end
data/lib/pagy/frontend.rb CHANGED
@@ -1,8 +1,8 @@
1
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
@@ -15,60 +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: '',
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:)
18
+ def pagy_nav(pagy, id: nil, aria_label: nil, **vars)
19
+ id = %( id="#{id}") if id
20
+ a = pagy_anchor(pagy)
22
21
 
23
- html = +%(<nav#{p_id} class="pagy pagy-nav pagination" #{nav_aria_label_attr(pagy, nav_aria_label, nav_i18n_key)}>)
24
- html << prev_html(pagy, link)
22
+ html = %(<nav#{id} class="pagy nav" #{nav_aria_label(pagy, aria_label:)}>#{
23
+ prev_a(pagy, a)})
25
24
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
26
25
  html << case item
27
26
  when Integer
28
- %(<span class="page">#{link.call(item)}</span>)
27
+ a.(item)
29
28
  when String
30
- %(<span class="page active">) +
31
- %(<a role="link" aria-disabled="true" aria-current="page">#{pagy.label_for(item)}</a></span>)
29
+ %(<a role="link" aria-disabled="true" aria-current="page" class="current">#{pagy.label_for(item)}</a>)
32
30
  when :gap
33
- %(<span class="page gap">#{pagy_t('pagy.gap')}</span>)
31
+ %(<a role="link" aria-disabled="true" class="gap">#{pagy_t('pagy.gap')}</a>)
34
32
  else
35
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 << %(#{next_html(pagy, link)}</nav>)
36
+ html << %(#{next_a(pagy, a)}</nav>)
39
37
  end
40
38
 
41
39
  # Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
42
- def pagy_info(pagy, pagy_id: nil, item_name: nil, item_i18n_key: nil)
43
- p_id = %( id="#{pagy_id}") if pagy_id
40
+ def pagy_info(pagy, id: nil, item_name: nil)
41
+ id = %( id="#{id}") if id
44
42
  p_count = pagy.count
45
- key = if p_count.zero? then 'pagy.info.no_items'
46
- elsif pagy.pages == 1 then 'pagy.info.single_page'
47
- 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'
48
49
  end
49
50
 
50
- %(<span#{p_id} class="pagy-info">#{
51
- pagy_t key, item_name: item_name || pagy_t(item_i18n_key || pagy.vars[:item_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),
52
53
  count: p_count, from: pagy.from, to: pagy.to
53
54
  }</span>)
54
55
  end
55
56
 
56
- # Return a performance optimized lambda to generate the HTML links
57
+ # Return a performance optimized lambda to generate the HTML anchor element (a tag)
57
58
  # Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
58
- def pagy_link_proc(pagy, link_extra: '')
59
- p_prev = pagy.prev
60
- p_next = pagy.next
61
- p_page = pagy.page.to_s
62
- left, right = %(<a href="#{pagy_url_for(pagy, PAGE_TOKEN)}" #{
63
- pagy.vars[:link_extra]} #{link_extra}).split(PAGE_TOKEN, 2)
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)
64
63
  # lambda used by all the helpers
65
- lambda do |page, text = pagy.label_for(page), extra_attrs = ''|
66
- %(#{left}#{page}#{right}#{ case page
67
- when p_prev then ' rel="prev"'
68
- when p_next then ' rel="next"'
69
- when p_page then ' aria-disabled="true" aria-current="page"'
70
- else ''
71
- end } #{extra_attrs}>#{text}</a>)
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>)
72
68
  end
73
69
  end
74
70
 
@@ -80,34 +76,24 @@ class Pagy
80
76
 
81
77
  private
82
78
 
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')}")
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}")
94
82
  end
95
83
 
96
- def prev_html(pagy, link, text: pagy_t('pagy.prev'))
84
+ def prev_a(pagy, a, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'))
97
85
  if (p_prev = pagy.prev)
98
- %(<span class="page prev">#{link.call(p_prev, text, prev_aria_label_attr)}</span>)
86
+ a.(p_prev, text, aria_label:)
99
87
  else
100
- %(<span class="page prev disabled"><a role="link" aria-disabled="true" #{
101
- prev_aria_label_attr}>#{text}</a></span>)
88
+ %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
102
89
  end
103
90
  end
104
91
 
105
- def next_html(pagy, link, text: pagy_t('pagy.next'))
92
+ def next_a(pagy, a, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.next'))
106
93
  if (p_next = pagy.next)
107
- %(<span class="page next">#{link.call(p_next, text, next_aria_label_attr)}</span>)
94
+ a.(p_next, text, aria_label:)
108
95
  else
109
- %(<span class="page next disabled"><a role="link" aria-disabled="true" #{
110
- next_aria_label_attr}>#{text}</a></span>)
96
+ %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
111
97
  end
112
98
  end
113
99
  end
@@ -8,20 +8,19 @@ class Pagy
8
8
  # For non-rack environments you can use the standalone extra
9
9
  def pagy_url_for(pagy, page, absolute: false, **_)
10
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)}"
17
- "#{request.base_url if absolute}#{request_path}#{query_string}#{vars[:fragment]}"
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]}"
18
17
  end
19
18
 
20
19
  # Add the page and items params
21
20
  # 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]
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]
25
24
  end
26
25
  end
27
26
  end