pagy 9.4.0 → 43.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/apps/calendar.ru +548 -552
  3. data/apps/demo.ru +224 -181
  4. data/apps/index.rb +3 -1
  5. data/apps/keynav+root_key.ru +321 -0
  6. data/apps/keynav.ru +260 -0
  7. data/apps/{keyset_ar.ru → keyset.ru} +26 -32
  8. data/apps/{keyset_s.ru → keyset_sequel.ru} +23 -29
  9. data/apps/rails.ru +51 -48
  10. data/apps/repro.ru +51 -47
  11. data/bin/pagy +20 -21
  12. data/config/pagy.rb +35 -207
  13. data/javascripts/ai_widget.js +90 -0
  14. data/javascripts/pagy.js +153 -0
  15. data/javascripts/pagy.js.map +10 -0
  16. data/javascripts/pagy.min.js +2 -4
  17. data/javascripts/pagy.mjs +113 -63
  18. data/javascripts/wand.js +1172 -0
  19. data/lib/pagy/classes/calendar/calendar.rb +96 -0
  20. data/lib/pagy/{calendar → classes/calendar}/day.rb +7 -11
  21. data/lib/pagy/{calendar → classes/calendar}/month.rb +5 -10
  22. data/lib/pagy/{calendar → classes/calendar}/quarter.rb +10 -15
  23. data/lib/pagy/classes/calendar/unit.rb +93 -0
  24. data/lib/pagy/{calendar → classes/calendar}/week.rb +5 -9
  25. data/lib/pagy/{calendar → classes/calendar}/year.rb +5 -6
  26. data/lib/pagy/classes/exceptions.rb +25 -0
  27. data/lib/pagy/classes/keyset/active_record.rb +11 -0
  28. data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
  29. data/lib/pagy/classes/keyset/adapters/sequel.rb +63 -0
  30. data/lib/pagy/classes/keyset/keynav/active_record.rb +13 -0
  31. data/lib/pagy/classes/keyset/keynav/sequel.rb +13 -0
  32. data/lib/pagy/classes/keyset/keynav.rb +79 -0
  33. data/lib/pagy/classes/keyset/keyset.rb +126 -0
  34. data/lib/pagy/classes/keyset/sequel.rb +11 -0
  35. data/lib/pagy/classes/offset/countish.rb +17 -0
  36. data/lib/pagy/classes/offset/countless.rb +61 -0
  37. data/lib/pagy/classes/offset/offset.rb +56 -0
  38. data/lib/pagy/classes/offset/search.rb +32 -0
  39. data/lib/pagy/classes/request.rb +36 -0
  40. data/lib/pagy/console.rb +3 -20
  41. data/lib/pagy/modules/abilities/configurable.rb +36 -0
  42. data/lib/pagy/modules/abilities/countable.rb +23 -0
  43. data/lib/pagy/modules/abilities/linkable.rb +60 -0
  44. data/lib/pagy/modules/abilities/rangeable.rb +16 -0
  45. data/lib/pagy/modules/abilities/shiftable.rb +12 -0
  46. data/lib/pagy/{b64.rb → modules/b64.rb} +3 -7
  47. data/lib/pagy/modules/console.rb +24 -0
  48. data/lib/pagy/modules/i18n/i18n.rb +48 -0
  49. data/lib/pagy/modules/i18n/p11n/arabic.rb +29 -0
  50. data/lib/pagy/modules/i18n/p11n/east_slavic.rb +26 -0
  51. data/lib/pagy/modules/i18n/p11n/one_other.rb +15 -0
  52. data/lib/pagy/modules/i18n/p11n/one_upto_two_other.rb +15 -0
  53. data/lib/pagy/modules/i18n/p11n/other.rb +13 -0
  54. data/lib/pagy/modules/i18n/p11n/polish.rb +26 -0
  55. data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
  56. data/lib/pagy/modules/i18n/p11n.rb +16 -0
  57. data/lib/pagy/modules/searcher.rb +17 -0
  58. data/lib/pagy/toolbox/helpers/anchor_tags.rb +25 -0
  59. data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +24 -0
  60. data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +18 -0
  61. data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +29 -0
  62. data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +21 -0
  63. data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +21 -0
  64. data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +19 -0
  65. data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +28 -0
  66. data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +20 -0
  67. data/lib/pagy/toolbox/helpers/data_hash.rb +27 -0
  68. data/lib/pagy/toolbox/helpers/headers_hash.rb +29 -0
  69. data/lib/pagy/toolbox/helpers/info_tag.rb +26 -0
  70. data/lib/pagy/toolbox/helpers/input_nav_js.rb +18 -0
  71. data/lib/pagy/toolbox/helpers/limit_tag_js.rb +23 -0
  72. data/lib/pagy/toolbox/helpers/loader.rb +33 -0
  73. data/lib/pagy/toolbox/helpers/page_url.rb +18 -0
  74. data/lib/pagy/toolbox/helpers/series_nav.rb +28 -0
  75. data/lib/pagy/toolbox/helpers/series_nav_js.rb +18 -0
  76. data/lib/pagy/toolbox/helpers/support/a_lambda.rb +35 -0
  77. data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +15 -0
  78. data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +12 -0
  79. data/lib/pagy/toolbox/helpers/support/series.rb +38 -0
  80. data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +20 -0
  81. data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +17 -0
  82. data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +37 -0
  83. data/lib/pagy/toolbox/helpers/urls_hash.rb +13 -0
  84. data/lib/pagy/toolbox/paginators/calendar.rb +29 -0
  85. data/lib/pagy/toolbox/paginators/countish.rb +37 -0
  86. data/lib/pagy/toolbox/paginators/countless.rb +20 -0
  87. data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +50 -0
  88. data/lib/pagy/toolbox/paginators/keynav_js.rb +26 -0
  89. data/lib/pagy/toolbox/paginators/keyset.rb +15 -0
  90. data/lib/pagy/toolbox/paginators/meilisearch.rb +31 -0
  91. data/lib/pagy/toolbox/paginators/method.rb +35 -0
  92. data/lib/pagy/toolbox/paginators/offset.rb +18 -0
  93. data/lib/pagy/toolbox/paginators/searchkick.rb +31 -0
  94. data/lib/pagy.rb +59 -97
  95. data/locales/ar.yml +9 -6
  96. data/locales/be.yml +9 -6
  97. data/locales/bg.yml +9 -6
  98. data/locales/bs.yml +9 -6
  99. data/locales/ca.yml +9 -6
  100. data/locales/ckb.yml +8 -6
  101. data/locales/cs.yml +9 -6
  102. data/locales/da.yml +9 -6
  103. data/locales/de.yml +9 -6
  104. data/locales/dz.yml +9 -6
  105. data/locales/en.yml +9 -6
  106. data/locales/es.yml +9 -6
  107. data/locales/fr.yml +9 -6
  108. data/locales/hr.yml +9 -6
  109. data/locales/id.yml +10 -9
  110. data/locales/it.yml +9 -6
  111. data/locales/ja.yml +10 -9
  112. data/locales/km.yml +10 -9
  113. data/locales/ko.yml +9 -6
  114. data/locales/nb.yml +9 -6
  115. data/locales/nl.yml +9 -6
  116. data/locales/nn.yml +9 -6
  117. data/locales/pl.yml +9 -6
  118. data/locales/pt-BR.yml +10 -7
  119. data/locales/pt.yml +10 -7
  120. data/locales/ru.yml +9 -6
  121. data/locales/sk.yml +9 -6
  122. data/locales/sr.yml +9 -6
  123. data/locales/sv-SE.yml +9 -6
  124. data/locales/sv.yml +9 -6
  125. data/locales/sw.yml +12 -9
  126. data/locales/ta.yml +9 -6
  127. data/locales/tr.yml +17 -12
  128. data/locales/uk.yml +9 -6
  129. data/locales/vi.yml +9 -6
  130. data/locales/zh-CN.yml +9 -6
  131. data/locales/zh-HK.yml +9 -6
  132. data/locales/zh-TW.yml +9 -6
  133. data/stylesheets/pagy-tailwind.css +64 -0
  134. data/stylesheets/pagy.css +63 -27
  135. metadata +118 -52
  136. data/javascripts/pagy.min.js.map +0 -10
  137. data/lib/pagy/backend.rb +0 -44
  138. data/lib/pagy/calendar/unit.rb +0 -103
  139. data/lib/pagy/calendar.rb +0 -84
  140. data/lib/pagy/countless.rb +0 -38
  141. data/lib/pagy/exceptions.rb +0 -25
  142. data/lib/pagy/extras/arel.rb +0 -28
  143. data/lib/pagy/extras/array.rb +0 -19
  144. data/lib/pagy/extras/bootstrap.rb +0 -97
  145. data/lib/pagy/extras/bulma.rb +0 -93
  146. data/lib/pagy/extras/calendar.rb +0 -79
  147. data/lib/pagy/extras/countless.rb +0 -32
  148. data/lib/pagy/extras/elasticsearch_rails.rb +0 -71
  149. data/lib/pagy/extras/gearbox.rb +0 -55
  150. data/lib/pagy/extras/headers.rb +0 -54
  151. data/lib/pagy/extras/i18n.rb +0 -26
  152. data/lib/pagy/extras/js_tools.rb +0 -70
  153. data/lib/pagy/extras/jsonapi.rb +0 -88
  154. data/lib/pagy/extras/keyset.rb +0 -30
  155. data/lib/pagy/extras/limit.rb +0 -63
  156. data/lib/pagy/extras/meilisearch.rb +0 -57
  157. data/lib/pagy/extras/metadata.rb +0 -42
  158. data/lib/pagy/extras/overflow.rb +0 -81
  159. data/lib/pagy/extras/pagy.rb +0 -82
  160. data/lib/pagy/extras/searchkick.rb +0 -59
  161. data/lib/pagy/extras/size.rb +0 -40
  162. data/lib/pagy/extras/standalone.rb +0 -60
  163. data/lib/pagy/extras/trim.rb +0 -29
  164. data/lib/pagy/frontend.rb +0 -99
  165. data/lib/pagy/i18n.rb +0 -167
  166. data/lib/pagy/keyset/active_record.rb +0 -44
  167. data/lib/pagy/keyset/sequel.rb +0 -57
  168. data/lib/pagy/keyset.rb +0 -118
  169. data/lib/pagy/shared_methods.rb +0 -26
  170. data/lib/pagy/url_helpers.rb +0 -26
  171. data/stylesheets/pagy.scss +0 -48
  172. data/stylesheets/pagy.tailwind.css +0 -21
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module I18n
5
+ module P11n
6
+ module OneOther
7
+ module_function
8
+
9
+ def plural_for(n = 0)
10
+ n == 1 ? :one : :other
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module I18n
5
+ module P11n
6
+ module OneUptoTwoOther
7
+ module_function
8
+
9
+ def plural_for(n = 0)
10
+ n >= 0 && n < 2 ? :one : :other
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module I18n
5
+ module P11n
6
+ module Other
7
+ module_function
8
+
9
+ def plural_for(*) = :other
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module I18n
5
+ module P11n
6
+ module Polish
7
+ module_function
8
+
9
+ def plural_for(n = 0)
10
+ mod10 = n % 10
11
+ mod100 = n % 100
12
+ case
13
+ when n == 1
14
+ :one
15
+ when [2, 3, 4].include?(mod10) && ![12, 13, 14].include?(mod100)
16
+ :few
17
+ when [0, 1, 5, 6, 7, 8, 9].include?(mod10) || [12, 13, 14].include?(mod100)
18
+ :many
19
+ else
20
+ :other
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module I18n
5
+ module P11n
6
+ module WestSlavic
7
+ module_function
8
+
9
+ def plural_for(n = 0)
10
+ case n
11
+ when 1
12
+ :one
13
+ when 2, 3, 4
14
+ :few
15
+ else
16
+ :other
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module I18n
5
+ module P11n
6
+ path = ROOT.join('lib/pagy/modules/i18n/p11n')
7
+ autoload :Arabic, path.join('arabic')
8
+ autoload :EastSlavic, path.join('east_slavic')
9
+ autoload :OneOther, path.join('one_other')
10
+ autoload :OneUptoTwoOther, path.join('one_upto_two_other')
11
+ autoload :Other, path.join('other')
12
+ autoload :Polish, path.join('polish')
13
+ autoload :WestSlavic, path.join('west_slavic')
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ # Relegate internal functions. Make overriding search classes easier.
5
+ module Searcher
6
+ module_function
7
+
8
+ # Common search logic
9
+ def wrap(pagy_search_args, options)
10
+ options[:page] ||= options[:request].resolve_page
11
+ options[:limit] = options[:request].resolve_limit
12
+ pagy, results = yield
13
+ calling = pagy_search_args[4..]
14
+ [pagy, calling.empty? ? results : results.send(*calling)]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support/a_lambda' # inheritable
4
+
5
+ class Pagy
6
+ # Return the enabled/disabled previous page anchor tag
7
+ def previous_tag(a = nil, text: I18n.translate('pagy.previous'),
8
+ aria_label: I18n.translate('pagy.aria_label.previous'), **)
9
+ if @previous
10
+ (a || a_lambda(**)).(@previous, text, aria_label:)
11
+ else
12
+ %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
13
+ end
14
+ end
15
+
16
+ # Return the enabled/disabled next page anchor tag
17
+ def next_tag(a = nil, text: I18n.translate('pagy.next'),
18
+ aria_label: I18n.translate('pagy.aria_label.next'), **)
19
+ if @next
20
+ (a || a_lambda(**)).(@next, text, aria_label:)
21
+ else
22
+ %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'previous_next_html'
4
+ require_relative '../support/wrap_input_nav_js'
5
+
6
+ class Pagy
7
+ private
8
+
9
+ # Javascript combo pagination for bootstrap: it returns a nav with a data-pagy attribute used by the pagy.js file
10
+ def bootstrap_input_nav_js(classes: 'pagination', **)
11
+ a_lambda = a_lambda(**)
12
+ input = %(<input name="page" type="number" min="1" max="#{last}" value="#{@page}" aria-current="page" ) +
13
+ %(style="text-align: center; width: #{@page.to_s.length + 1}rem; padding: 0; border-radius: .25rem; ) +
14
+ %(border: none; display: inline-block;" class="page-link active">#{A_TAG})
15
+ html = %(<ul class="#{classes}">#{
16
+ bootstrap_html_for(:previous, a_lambda)
17
+ }<li class="page-item"><label class="page-link">#{
18
+ I18n.translate('pagy.input_nav_js', page_input: input, pages: @last)
19
+ }</label></li>#{
20
+ bootstrap_html_for(:next, a_lambda)
21
+ }</ul>)
22
+ wrap_input_nav_js(html, 'pagy-bootstrap input-nav-js', **)
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ private
5
+
6
+ # Return the enabled/disabled previous/next page anchor tag, embedded in the li tag
7
+ def bootstrap_html_for(which, a_lambda)
8
+ if send(which)
9
+ %(<li class="page-item #{which}">#{
10
+ a_lambda.(send(which), I18n.translate("pagy.#{which}"),
11
+ classes: 'page-link',
12
+ aria_label: I18n.translate("pagy.aria_label.#{which}"))}</li>)
13
+ else
14
+ %(<li class="page-item #{which} disabled"><a role="link" class="page-link" aria-disabled="true" aria-label="#{
15
+ I18n.translate("pagy.aria_label.#{which}")}">#{I18n.translate("pagy.#{which}")}</a></li>)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'previous_next_html'
4
+ require_relative '../support/wrap_series_nav'
5
+
6
+ class Pagy
7
+ private
8
+
9
+ # Pagination for bootstrap: it returns the html with the series of links to the pages
10
+ def bootstrap_series_nav(classes: 'pagination', **)
11
+ a_lambda = a_lambda(**)
12
+ html = %(<ul class="#{classes}">#{bootstrap_html_for(:previous, a_lambda)})
13
+ series(**).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
14
+ html << case item
15
+ when Integer
16
+ %(<li class="page-item">#{a_lambda.(item, classes: 'page-link')}</li>)
17
+ when String
18
+ %(<li class="page-item active"><a role="link" class="page-link" aria-current="page" aria-disabled="true">#{
19
+ page_label(item)}</a></li>)
20
+ when :gap
21
+ %(<li class="page-item gap disabled"><a role="link" class="page-link" aria-disabled="true">#{
22
+ I18n.translate('pagy.gap')}</a></li>)
23
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
24
+ end
25
+ end
26
+ html << %(#{bootstrap_html_for(:next, a_lambda)}</ul>)
27
+ wrap_series_nav(html, 'pagy-bootstrap series-nav', **)
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'previous_next_html'
4
+ require_relative '../support/wrap_series_nav_js'
5
+
6
+ class Pagy
7
+ private
8
+
9
+ # Javascript pagination for bootstrap: it returns a nav with a data-pagy attribute used by the pagy.js file
10
+ def bootstrap_series_nav_js(classes: 'pagination', **)
11
+ a_lambda = a_lambda(**)
12
+ tokens = { before: %(<ul class="#{classes}">#{bootstrap_html_for(:previous, a_lambda)}),
13
+ anchor: %(<li class="page-item">#{a_lambda.(PAGE_TOKEN, LABEL_TOKEN, classes: 'page-link')}</li>),
14
+ current: %(<li class="page-item active"><a role="link" class="page-link" ) +
15
+ %(aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a></li>),
16
+ gap: %(<li class="page-item gap disabled"><a role="link" class="page-link" aria-disabled="true">#{
17
+ I18n.translate('pagy.gap')}</a></li>),
18
+ after: %(#{bootstrap_html_for(:next, a_lambda)}</ul>) }
19
+ wrap_series_nav_js(tokens, 'pagy-bootstrap series-nav-js', **)
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'previous_next_html'
4
+ require_relative '../support/wrap_input_nav_js'
5
+
6
+ class Pagy
7
+ private
8
+
9
+ # Javascript combo pagination for bulma: it returns a nav with a data-pagy attribute used by the pagy.js file
10
+ def bulma_input_nav_js(classes: 'pagination', **)
11
+ a_lambda = a_lambda(**)
12
+ input = %(<input name="page" type="number" min="1" max="#{@last}" value="#{@page}" aria-current="page") +
13
+ %(style="text-align: center; width: #{@page.to_s.length + 1}rem; line-height: 1.2rem; ) +
14
+ %(border: none; border-radius: .25rem; padding: .0625rem; color: white; ) +
15
+ %(background-color: #485fc7;">#{A_TAG})
16
+ html = %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)}<li class="pagination-link"><label>#{
17
+ I18n.translate('pagy.input_nav_js', page_input: input, pages: @last)
18
+ }</label></li>#{bulma_html_for(:next, a_lambda)}</ul>)
19
+ wrap_input_nav_js(html, "pagy-bulma input-nav-js #{classes}", **)
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ private
5
+
6
+ # Return the enabled/disabled previous/next page anchor tag
7
+ def bulma_html_for(which, a_lambda)
8
+ %(<li>#{
9
+ if send(which)
10
+ a_lambda.(send(which), I18n.translate("pagy.#{which}"),
11
+ classes: "pagination-#{which}",
12
+ aria_label: I18n.translate("pagy.aria_label.#{which}"))
13
+ else
14
+ %(<a role="link" class="pagination-#{which}" disabled aria-disabled="true" aria-label="#{
15
+ I18n.translate("pagy.aria_label.#{which}")}">#{I18n.translate("pagy.#{which}")}</a>)
16
+ end
17
+ }</li>)
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'previous_next_html'
4
+ require_relative '../support/wrap_series_nav'
5
+
6
+ class Pagy
7
+ private
8
+
9
+ # Pagination for bulma: it returns the html with the series of links to the pages
10
+ def bulma_series_nav(classes: 'pagination', **)
11
+ a_lambda = a_lambda(**)
12
+ html = %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)})
13
+ series(**).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
14
+ html << case item
15
+ when Integer
16
+ %(<li>#{a_lambda.(item, page_label(item), classes: 'pagination-link')}</li>)
17
+ when String
18
+ %(<li><a role="link" class="pagination-link is-current" aria-current="page" aria-disabled="true">#{
19
+ page_label(item)}</a></li>)
20
+ when :gap
21
+ %(<li><span class="pagination-ellipsis">#{I18n.translate('pagy.gap')}</span></li>)
22
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
23
+ end
24
+ end
25
+ html << %(#{bulma_html_for(:next, a_lambda)}</ul>)
26
+ wrap_series_nav(html, "pagy-bulma series-nav #{classes}", **)
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'previous_next_html'
4
+ require_relative '../support/wrap_series_nav_js'
5
+
6
+ class Pagy
7
+ private
8
+
9
+ # Javascript pagination for bulma: it returns a nav with a data-pagy attribute used by the Pagy.nav javascript
10
+ def bulma_series_nav_js(classes: 'pagination', **)
11
+ a_lambda = a_lambda(**)
12
+ tokens = { before: %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)}),
13
+ anchor: %(<li>#{a_lambda.(PAGE_TOKEN, LABEL_TOKEN, classes: 'pagination-link')}</li>),
14
+ current: %(<li><a role="link" class="pagination-link is-current" ) +
15
+ %(aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a></li>),
16
+ gap: %(<li><span class="pagination-ellipsis">#{I18n.translate('pagy.gap')}</span></li>),
17
+ after: %(#{bulma_html_for(:next, a_lambda)}</ul>) }
18
+ wrap_series_nav_js(tokens, "pagy-bulma series-nav-js #{classes}", **)
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ DEFAULT_DATA_KEYS = %i[url_template first_url previous_url page_url next_url last_url
5
+ count page limit last in from to previous next options].freeze
6
+
7
+ # Generate a hash of the wanted internal data
8
+ def data_hash(data_keys: @options[:data_keys] || DEFAULT_DATA_KEYS, **)
9
+ data_keys -= %i[count limit] if calendar?
10
+ template = compose_page_url(PAGE_TOKEN, **)
11
+ to_url = ->(page) { template.sub(PAGE_TOKEN, page.to_s) }
12
+
13
+ data_keys.each_with_object({}) do |key, data|
14
+ data[key] = case key
15
+ when :url_template then template
16
+ when :first_url then compose_page_url(nil, **)
17
+ when :previous_url then to_url.(@previous)
18
+ when :page_url then to_url.(@page)
19
+ when :next_url then to_url.(@next)
20
+ when :last_url then to_url.(@last)
21
+ else send(key)
22
+ end
23
+ rescue NoMethodError
24
+ raise OptionError.new(self, :data, 'to contain known keys', key)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'urls_hash'
4
+
5
+ # Add pagination response headers
6
+ class Pagy
7
+ DEFAULT_HEADERS_MAP = { page: 'current-page',
8
+ limit: 'page-limit',
9
+ count: 'total-count',
10
+ pages: 'total-pages' }.freeze
11
+
12
+ # Generate a hash of RFC-8288-compliant http headers
13
+ def headers_hash(headers_map: @options[:headers_map] || DEFAULT_HEADERS_MAP, **)
14
+ links = urls_hash(**, absolute: true).map { %(<#{_2}>; rel="#{_1}") }.join(', ')
15
+ headers_map.each_with_object('link' => links) do |(key, name), hash|
16
+ next unless name
17
+
18
+ # :nocov:
19
+ value = case key
20
+ when :page then @page
21
+ when :limit then @limit unless calendar?
22
+ when :pages then @last if @count
23
+ when :count then @count
24
+ end
25
+ # :nocov:
26
+ hash[name] = value.to_s if value
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ # Instances with count return "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
5
+ # Instances with no count return only page info: "Page 3 of 100"
6
+ def info_tag(id: nil, item_name: nil)
7
+ i18n_key = if @count.nil?
8
+ 'pagy.info_tag.no_count'
9
+ elsif @count.zero?
10
+ 'pagy.info_tag.no_items'
11
+ elsif @in == @count
12
+ 'pagy.info_tag.single_page'
13
+ else
14
+ 'pagy.info_tag.multiple_pages'
15
+ end
16
+ info_data = if @count.nil?
17
+ { page: @page, pages: @last }
18
+ else
19
+ { item_name: item_name || I18n.translate('pagy.item_name', count: @count),
20
+ count: @count,
21
+ from: @from,
22
+ to: @to }
23
+ end
24
+ %(<span#{%( id="#{id}") if id} class="pagy info">#{I18n.translate(i18n_key, **info_data)}</span>)
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support/wrap_input_nav_js'
4
+
5
+ class Pagy
6
+ # JavaScript input pagination: it returns a nav with a data-pagy attribute used by the pagy.js file
7
+ def input_nav_js(style = nil, **)
8
+ return send(:"#{style}_input_nav_js", **) if style
9
+
10
+ a_lambda = a_lambda(**)
11
+ input = %(<input name="page" type="number" min="1" max="#{@last}" value="#{@page}" aria-current="page" ) +
12
+ %(style="text-align: center; width: #{@page.to_s.length + 1}rem; padding: 0;">#{A_TAG})
13
+ html = %(#{previous_tag(a_lambda)}<label>#{
14
+ I18n.translate('pagy.input_nav_js', page_input: input, pages: @last)}</label>#{
15
+ next_tag(a_lambda)})
16
+ wrap_input_nav_js(html, 'pagy input-nav-js', **)
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support/data_pagy_attribute'
4
+
5
+ class Pagy
6
+ # Return the limit selector HTML. For example "Show [20] items per page"
7
+ def limit_tag_js(id: nil, item_name: nil, client_max_limit: @options[:client_max_limit], **)
8
+ raise OptionError.new(self, :client_max_limit, 'to be truthy', client_max_limit) unless client_max_limit
9
+
10
+ limit_input = %(<input name="limit" type="number" min="1" max="#{@options[:client_max_limit]}" value="#{
11
+ @limit}" style="padding: 0; text-align: center; width: #{@limit.to_s.length + 1}rem;">#{A_TAG})
12
+ url_token = compose_page_url(PAGE_TOKEN, limit: LIMIT_TOKEN)
13
+
14
+ %(<span#{%( id="#{id}") if id} class="pagy limit-tag-js" #{
15
+ data_pagy_attribute(:ltj, @from, url_token)
16
+ }><label>#{
17
+ I18n.translate('pagy.limit_tag_js',
18
+ item_name: item_name || I18n.translate('pagy.item_name', count: @limit),
19
+ limit_input:,
20
+ count: @limit)
21
+ }</label></span>)
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ module Loader
5
+ paths = { public: { page_url: 'page_url',
6
+ data_hash: 'data_hash',
7
+ headers_hash: 'headers_hash',
8
+ urls_hash: 'urls_hash',
9
+ next_tag: 'anchor_tags',
10
+ previous_tag: 'anchor_tags',
11
+ input_nav_js: 'input_nav_js',
12
+ info_tag: 'info_tag',
13
+ limit_tag_js: 'limit_tag_js',
14
+ series_nav: 'series_nav',
15
+ series_nav_js: 'series_nav_js' },
16
+ protected: { bootstrap_series_nav: 'bootstrap/series_nav',
17
+ bootstrap_series_nav_js: 'bootstrap/series_nav_js',
18
+ bootstrap_input_nav_js: 'bootstrap/input_nav_js',
19
+ bulma_series_nav: 'bulma/series_nav',
20
+ bulma_series_nav_js: 'bulma/series_nav_js',
21
+ bulma_input_nav_js: 'bulma/input_nav_js' } }.freeze
22
+
23
+ paths.each do |visibility, methods|
24
+ send(visibility)
25
+ # Load the method, overriding its own alias. Next requests will call the method directly.
26
+ define_method(:"load_#{visibility}") do |*args, **kwargs|
27
+ require_relative methods[__callee__]
28
+ send(__callee__, *args, **kwargs)
29
+ end
30
+ methods.each_key { |method| alias_method method, :"load_#{visibility}" }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ # Return the page url for any page
5
+ # :nocov:
6
+ def page_url(page, **)
7
+ target = case page
8
+ when :first then nil
9
+ when :current then @page
10
+ when :previous then @previous
11
+ when :next then @next
12
+ when :last then @last
13
+ else page
14
+ end
15
+ compose_page_url(target, **) if target || page == :first
16
+ end
17
+ # :nocov:
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support/wrap_series_nav'
4
+
5
+ class Pagy
6
+ # Return the HTML with the series of links to the pages
7
+ def series_nav(style = nil, **)
8
+ return send(:"#{style}_series_nav", **) if style
9
+
10
+ a_lambda = a_lambda(**)
11
+ html = previous_tag(a_lambda)
12
+ series(**).each do |item|
13
+ # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
14
+ html << case item
15
+ when Integer
16
+ a_lambda.(item)
17
+ when String
18
+ %(<a role="link" aria-disabled="true" aria-current="page">#{page_label(item)}</a>)
19
+ when :gap
20
+ %(<a role="separator" aria-disabled="true">#{I18n.translate('pagy.gap')}</a>)
21
+ else
22
+ raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
23
+ end
24
+ end
25
+ html << next_tag(a_lambda)
26
+ wrap_series_nav(html, 'pagy series-nav', **)
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support/wrap_series_nav_js'
4
+
5
+ class Pagy
6
+ # Return a nav with a data-pagy attribute used by the pagy.js file
7
+ def series_nav_js(style = nil, **)
8
+ return send(:"#{style}_series_nav_js", **) if style
9
+
10
+ a_lambda = a_lambda(**)
11
+ tokens = { before: previous_tag(a_lambda),
12
+ anchor: a_lambda.(PAGE_TOKEN, LABEL_TOKEN),
13
+ current: %(<a role="link" aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a>),
14
+ gap: %(<a role="separator" aria-disabled="true">#{I18n.translate('pagy.gap')}</a>),
15
+ after: next_tag(a_lambda) }
16
+ wrap_series_nav_js(tokens, 'pagy series-nav-js', **)
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ protected
5
+
6
+ # Label for any page. Allow the customization of the output
7
+ def page_label(page, **options)
8
+ return page.to_s unless calendar?
9
+
10
+ options[:format] ||= @options[:format]
11
+ localize(starting_time_for(page.to_i), **options) # page could be a string
12
+ end
13
+
14
+ # Return a performance optimized lambda to generate the anchor tag
15
+ # Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
16
+ def a_lambda(anchor_string: nil, **)
17
+ left, right = %(<a href="#{compose_page_url(PAGE_TOKEN, **)}"#{
18
+ %( #{anchor_string}) if anchor_string}).split(PAGE_TOKEN, 2)
19
+
20
+ lambda do |page, text = page_label(page), classes: nil, aria_label: nil|
21
+ title = if (counts = @options[:counts]) # only for calendar with counts
22
+ count = counts[page - 1]
23
+ classes = classes ? "#{classes} empty-page" : 'empty-page' if count.zero?
24
+ info_key = count.zero? ? 'pagy.info_tag.no_items' : 'pagy.info_tag.single_page'
25
+ %( title="#{I18n.translate(info_key, item_name: I18n.translate('pagy.item_name', count:), count:)}")
26
+ end
27
+ rel = case page
28
+ when @previous then %( rel="prev")
29
+ when @next then %( rel="next")
30
+ end
31
+ %(#{left}#{page}#{right}#{title}#{
32
+ %( class="#{classes}") if classes}#{rel}#{%( aria-label="#{aria_label}") if aria_label}>#{text}</a>)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative '../../../modules/b64'
5
+
6
+ # Relegate internal functions. Make overriding navs easier.
7
+ class Pagy
8
+ private
9
+
10
+ # Compose the data-pagy attribute, with the base64 encoded JSON-serialized args. Use the faster oj gem if defined.
11
+ def data_pagy_attribute(*args)
12
+ data = defined?(Oj) ? Oj.dump(args, mode: :compat) : JSON.dump(args)
13
+ %(data-pagy="#{B64.encode(data)}")
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Relegate internal functions. Make overriding navs easier.
4
+ class Pagy
5
+ private
6
+
7
+ # Compose the aria label attribute for the nav
8
+ def nav_aria_label_attribute(aria_label: nil)
9
+ aria_label ||= I18n.translate('pagy.aria_label.nav', count: @last)
10
+ %(aria-label="#{aria_label}")
11
+ end
12
+ end