pagy 7.0.11 → 8.6.3

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