pagy 9.3.5 → 43.0.0.rc1
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.
- checksums.yaml +4 -4
- data/apps/calendar.ru +547 -551
- data/apps/demo.ru +221 -178
- data/apps/index.rb +3 -1
- data/apps/keynav.ru +258 -0
- data/apps/{keyset_ar.ru → keyset.ru} +27 -32
- data/apps/{keyset_s.ru → keyset_sequel.ru} +24 -29
- data/apps/rails.ru +51 -48
- data/apps/repro.ru +46 -43
- data/bin/pagy +20 -21
- data/config/pagy.rb +35 -207
- data/javascripts/ai_widget.js +76 -0
- data/javascripts/pagy.js +151 -0
- data/javascripts/pagy.js.map +10 -0
- data/javascripts/pagy.min.js +1 -4
- data/javascripts/pagy.mjs +111 -63
- data/javascripts/wand.js +1166 -0
- data/lib/pagy/classes/calendar/calendar.rb +98 -0
- data/lib/pagy/{calendar → classes/calendar}/day.rb +7 -11
- data/lib/pagy/{calendar → classes/calendar}/month.rb +5 -10
- data/lib/pagy/{calendar → classes/calendar}/quarter.rb +10 -15
- data/lib/pagy/classes/calendar/unit.rb +92 -0
- data/lib/pagy/{calendar → classes/calendar}/week.rb +5 -9
- data/lib/pagy/{calendar → classes/calendar}/year.rb +5 -6
- data/lib/pagy/classes/exceptions.rb +33 -0
- data/lib/pagy/classes/keyset/active_record.rb +11 -0
- data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
- data/lib/pagy/classes/keyset/adapters/sequel.rb +63 -0
- data/lib/pagy/classes/keyset/keynav/active_record.rb +13 -0
- data/lib/pagy/classes/keyset/keynav/sequel.rb +13 -0
- data/lib/pagy/classes/keyset/keynav.rb +77 -0
- data/lib/pagy/classes/keyset/keyset.rb +126 -0
- data/lib/pagy/classes/keyset/sequel.rb +11 -0
- data/lib/pagy/classes/offset/countless.rb +56 -0
- data/lib/pagy/classes/offset/offset.rb +54 -0
- data/lib/pagy/classes/offset/search.rb +38 -0
- data/lib/pagy/classes/request.rb +36 -0
- data/lib/pagy/modules/abilities/configurable.rb +36 -0
- data/lib/pagy/modules/abilities/linkable.rb +59 -0
- data/lib/pagy/modules/abilities/rangeable.rb +15 -0
- data/lib/pagy/modules/abilities/shiftable.rb +12 -0
- data/lib/pagy/{b64.rb → modules/b64.rb} +3 -7
- data/lib/pagy/modules/console.rb +38 -0
- data/lib/pagy/modules/i18n/i18n.rb +48 -0
- data/lib/pagy/modules/i18n/p11n/arabic.rb +29 -0
- data/lib/pagy/modules/i18n/p11n/east_slavic.rb +26 -0
- data/lib/pagy/modules/i18n/p11n/one_other.rb +15 -0
- data/lib/pagy/modules/i18n/p11n/one_upto_two_other.rb +15 -0
- data/lib/pagy/modules/i18n/p11n/other.rb +13 -0
- data/lib/pagy/modules/i18n/p11n/polish.rb +26 -0
- data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
- data/lib/pagy/modules/i18n/p11n.rb +16 -0
- data/lib/pagy/modules/searcher.rb +20 -0
- data/lib/pagy/toolbox/helpers/anchor_tags.rb +25 -0
- data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +24 -0
- data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +18 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +29 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +21 -0
- data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +21 -0
- data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +19 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +28 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +20 -0
- data/lib/pagy/toolbox/helpers/data_hash.rb +26 -0
- data/lib/pagy/toolbox/helpers/headers_hash.rb +20 -0
- data/lib/pagy/toolbox/helpers/info_tag.rb +26 -0
- data/lib/pagy/toolbox/helpers/input_nav_js.rb +19 -0
- data/lib/pagy/toolbox/helpers/limit_tag_js.rb +23 -0
- data/lib/pagy/toolbox/helpers/loader.rb +33 -0
- data/lib/pagy/toolbox/helpers/page_url.rb +23 -0
- data/lib/pagy/toolbox/helpers/series_nav.rb +29 -0
- data/lib/pagy/toolbox/helpers/series_nav_js.rb +19 -0
- data/lib/pagy/toolbox/helpers/support/a_lambda.rb +34 -0
- data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +15 -0
- data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +12 -0
- data/lib/pagy/toolbox/helpers/support/series.rb +38 -0
- data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +20 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +17 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +36 -0
- data/lib/pagy/toolbox/helpers/urls_hash.rb +13 -0
- data/lib/pagy/toolbox/paginators/calendar.rb +30 -0
- data/lib/pagy/toolbox/paginators/countless.rb +25 -0
- data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +32 -0
- data/lib/pagy/toolbox/paginators/keynav_js.rb +29 -0
- data/lib/pagy/toolbox/paginators/keyset.rb +18 -0
- data/lib/pagy/toolbox/paginators/meilisearch.rb +32 -0
- data/lib/pagy/toolbox/paginators/method.rb +26 -0
- data/lib/pagy/toolbox/paginators/offset.rb +33 -0
- data/lib/pagy/toolbox/paginators/searchkick.rb +32 -0
- data/lib/pagy.rb +59 -97
- data/locales/ar.yml +9 -6
- data/locales/be.yml +9 -6
- data/locales/bg.yml +9 -6
- data/locales/bs.yml +9 -6
- data/locales/ca.yml +9 -6
- data/locales/ckb.yml +8 -6
- data/locales/cs.yml +9 -6
- data/locales/da.yml +9 -6
- data/locales/de.yml +9 -6
- data/locales/dz.yml +9 -6
- data/locales/en.yml +9 -6
- data/locales/es.yml +9 -6
- data/locales/fr.yml +9 -6
- data/locales/hr.yml +9 -6
- data/locales/id.yml +9 -6
- data/locales/it.yml +9 -6
- data/locales/ja.yml +9 -6
- data/locales/km.yml +9 -6
- data/locales/ko.yml +9 -6
- data/locales/nb.yml +9 -6
- data/locales/nl.yml +9 -6
- data/locales/nn.yml +9 -6
- data/locales/pl.yml +9 -6
- data/locales/pt-BR.yml +10 -7
- data/locales/pt.yml +10 -7
- data/locales/ru.yml +9 -6
- data/locales/sr.yml +9 -6
- data/locales/sv-SE.yml +9 -6
- data/locales/sv.yml +9 -6
- data/locales/sw.yml +12 -9
- data/locales/ta.yml +12 -9
- data/locales/tr.yml +9 -6
- data/locales/uk.yml +9 -6
- data/locales/vi.yml +9 -6
- data/locales/zh-CN.yml +9 -6
- data/locales/zh-HK.yml +9 -6
- data/locales/zh-TW.yml +9 -6
- data/stylesheets/pagy-tailwind.css +64 -0
- data/stylesheets/pagy.css +63 -27
- metadata +112 -51
- data/javascripts/pagy.min.js.map +0 -10
- data/lib/pagy/backend.rb +0 -44
- data/lib/pagy/calendar/unit.rb +0 -103
- data/lib/pagy/calendar.rb +0 -84
- data/lib/pagy/console.rb +0 -23
- data/lib/pagy/countless.rb +0 -38
- data/lib/pagy/exceptions.rb +0 -25
- data/lib/pagy/extras/arel.rb +0 -28
- data/lib/pagy/extras/array.rb +0 -19
- data/lib/pagy/extras/bootstrap.rb +0 -97
- data/lib/pagy/extras/bulma.rb +0 -93
- data/lib/pagy/extras/calendar.rb +0 -79
- data/lib/pagy/extras/countless.rb +0 -32
- data/lib/pagy/extras/elasticsearch_rails.rb +0 -71
- data/lib/pagy/extras/gearbox.rb +0 -55
- data/lib/pagy/extras/headers.rb +0 -54
- data/lib/pagy/extras/i18n.rb +0 -26
- data/lib/pagy/extras/js_tools.rb +0 -70
- data/lib/pagy/extras/jsonapi.rb +0 -88
- data/lib/pagy/extras/keyset.rb +0 -30
- data/lib/pagy/extras/limit.rb +0 -63
- data/lib/pagy/extras/meilisearch.rb +0 -57
- data/lib/pagy/extras/metadata.rb +0 -42
- data/lib/pagy/extras/overflow.rb +0 -81
- data/lib/pagy/extras/pagy.rb +0 -82
- data/lib/pagy/extras/searchkick.rb +0 -59
- data/lib/pagy/extras/size.rb +0 -40
- data/lib/pagy/extras/standalone.rb +0 -60
- data/lib/pagy/extras/trim.rb +0 -29
- data/lib/pagy/frontend.rb +0 -99
- data/lib/pagy/i18n.rb +0 -166
- data/lib/pagy/keyset/active_record.rb +0 -44
- data/lib/pagy/keyset/sequel.rb +0 -57
- data/lib/pagy/keyset.rb +0 -118
- data/lib/pagy/shared_methods.rb +0 -26
- data/lib/pagy/url_helpers.rb +0 -26
- data/stylesheets/pagy.scss +0 -48
- data/stylesheets/pagy.tailwind.css +0 -21
@@ -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,20 @@
|
|
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(context, pagy_search_args, options)
|
10
|
+
context.instance_exec do
|
11
|
+
options[:request] = Request.new(options[:request] || request, options)
|
12
|
+
options[:page] ||= options[:request].resolve_page(options)
|
13
|
+
options[:limit] ||= options[:request].resolve_limit(options)
|
14
|
+
end
|
15
|
+
pagy, results = yield
|
16
|
+
calling = pagy_search_args[4..]
|
17
|
+
[pagy, calling.empty? ? results : results.send(*calling)]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
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,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Pagy
|
4
|
+
# Generate a hash of the wanted internal data
|
5
|
+
def data_hash(data_keys: @options[:data_keys] ||
|
6
|
+
%i[url_template first_url previous_url page_url next_url last_url
|
7
|
+
count page limit last in from to previous next options], **)
|
8
|
+
data_keys -= %i[count limit] if calendar?
|
9
|
+
url_template = compose_page_url(PAGE_TOKEN, **)
|
10
|
+
{}.tap do |data|
|
11
|
+
data_keys.each do |key|
|
12
|
+
data[key] = case key
|
13
|
+
when :url_template then url_template
|
14
|
+
when :first_url then compose_page_url(nil, **)
|
15
|
+
when :previous_url then url_template.sub(PAGE_TOKEN, @previous.to_s)
|
16
|
+
when :page_url then url_template.sub(PAGE_TOKEN, @page.to_s)
|
17
|
+
when :next_url then url_template.sub(PAGE_TOKEN, @next.to_s)
|
18
|
+
when :last_url then url_template.sub(PAGE_TOKEN, @last.to_s)
|
19
|
+
else send(key)
|
20
|
+
end
|
21
|
+
rescue NoMethodError
|
22
|
+
raise OptionError.new(self, :data, 'to contain known keys', key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'urls_hash'
|
4
|
+
|
5
|
+
# Add pagination response headers
|
6
|
+
class Pagy
|
7
|
+
# Generate a hash of RFC-8288-compliant http headers
|
8
|
+
def headers_hash(headers_map: @options[:headers_map] ||
|
9
|
+
{ page: 'current-page', limit: 'page-limit', count: 'total-count', pages: 'total-pages' }, **)
|
10
|
+
links = urls_hash(**, absolute: true).map { |key, url| %(<#{url}>; rel="#{key}") }.join(', ')
|
11
|
+
{ 'link' => links }.tap do |hash|
|
12
|
+
hash[headers_map[:page]] = @page.to_s if @page && headers_map[:page]
|
13
|
+
hash[headers_map[:limit]] = @limit.to_s if headers_map[:limit] && !calendar?
|
14
|
+
return hash unless @count
|
15
|
+
|
16
|
+
hash[headers_map[:pages]] = @last.to_s if headers_map[:pages]
|
17
|
+
hash[headers_map[:count]] = @count.to_s if headers_map[:count]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
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 @last == 1
|
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,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'support/wrap_input_nav_js'
|
4
|
+
|
5
|
+
class Pagy
|
6
|
+
# Javascript combo 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
|
+
alias pagy_input_nav_js input_nav_js
|
19
|
+
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
|
+
url_token = compose_page_url(PAGE_TOKEN, limit_token: LIMIT_TOKEN)
|
11
|
+
limit_input = %(<input name="limit" type="number" min="1" max="#{@options[:client_max_limit]}" value="#{
|
12
|
+
@limit}" style="padding: 0; text-align: center; width: #{@limit.to_s.length + 1}rem;">#{A_TAG})
|
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,23 @@
|
|
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
|
+
case page
|
8
|
+
when :first
|
9
|
+
compose_page_url(nil, **)
|
10
|
+
when :current
|
11
|
+
compose_page_url(@page, **) if @page
|
12
|
+
when :previous
|
13
|
+
compose_page_url(@previous, **) if @previous
|
14
|
+
when :next
|
15
|
+
compose_page_url(@next, **) if @next
|
16
|
+
when :last
|
17
|
+
compose_page_url(@last, **) if @last
|
18
|
+
when Integer, String
|
19
|
+
compose_page_url(page, **)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# :nocov:
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
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
|
+
alias pagy_series_nav series_nav
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
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
|
+
alias pagy_series_nav_js series_nav_js
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
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(**)
|
17
|
+
left, right = %(<a href="#{compose_page_url(PAGE_TOKEN, **)}").split(PAGE_TOKEN, 2)
|
18
|
+
|
19
|
+
lambda do |page, text = page_label(page), classes: nil, aria_label: nil|
|
20
|
+
title = if (counts = @options[:counts]) # only for calendar + counts
|
21
|
+
count = counts[page - 1]
|
22
|
+
classes = classes ? "#{classes} empty-page" : 'empty-page' if count.zero?
|
23
|
+
info_key = count.zero? ? 'pagy.info_tag.no_items' : 'pagy.info_tag.single_page'
|
24
|
+
%( title="#{I18n.translate(info_key, item_name: I18n.translate('pagy.item_name', count:), count:)}")
|
25
|
+
end
|
26
|
+
rel = case page
|
27
|
+
when @previous then %( rel="prev")
|
28
|
+
when @next then %( rel="next")
|
29
|
+
end
|
30
|
+
%(#{left}#{page}#{right}#{title}#{
|
31
|
+
%( class="#{classes}") if classes}#{rel}#{%( aria-label="#{aria_label}") if aria_label}>#{text}</a>)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Pagy
|
4
|
+
SERIES_SLOTS = 7
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
# Return the array of page numbers and :gap e.g. [1, :gap, 8, "9", 10, :gap, 36]
|
9
|
+
def series(slots: @options[:slots] || SERIES_SLOTS, compact: @options[:compact], **)
|
10
|
+
raise OptionError.new(self, :slots, 'to be an Integer >= 0', slots) \
|
11
|
+
unless slots.is_a?(Integer) && slots >= 0
|
12
|
+
return [] if slots.zero?
|
13
|
+
|
14
|
+
[].tap do |series|
|
15
|
+
if slots >= @last
|
16
|
+
series.push(*1..@last)
|
17
|
+
else
|
18
|
+
half = (slots - 1) / 2 # the left half might be 1 page shorter when the slots are even
|
19
|
+
start = if @page <= half # @page in the first half
|
20
|
+
1
|
21
|
+
elsif @page > (@last - slots + half) # @page in the last half
|
22
|
+
@last - slots + 1
|
23
|
+
else # @page in the middle
|
24
|
+
@page - half
|
25
|
+
end
|
26
|
+
series.push(*(start...(start + slots)))
|
27
|
+
unless compact || slots < SERIES_SLOTS # Set first, last and :gap when needed
|
28
|
+
series[0] = 1
|
29
|
+
series[1] = :gap unless series[1] == 2
|
30
|
+
series[-2] = :gap unless series[-2] == @last - 1
|
31
|
+
series[-1] = @last
|
32
|
+
end
|
33
|
+
end
|
34
|
+
current = series.index(@page)
|
35
|
+
series[current] = @page.to_s if current
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|