pagy 3.10.0 → 5.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/lib/config/pagy.rb +121 -52
- data/lib/javascripts/pagy-dev.js +117 -0
- data/lib/javascripts/pagy.js +1 -106
- data/lib/javascripts/pagy.mjs +118 -0
- data/lib/locales/ar.yml +26 -0
- data/lib/locales/bg.yml +2 -2
- data/lib/locales/bs.yml +24 -0
- data/lib/locales/ca.yml +2 -2
- data/lib/locales/cs.yml +2 -2
- data/lib/locales/da.yml +2 -2
- data/lib/locales/de.yml +2 -2
- data/lib/locales/en.yml +2 -2
- data/lib/locales/es.yml +2 -2
- data/lib/locales/fr.yml +2 -2
- data/lib/locales/hr.yml +24 -0
- data/lib/locales/id.yml +2 -2
- data/lib/locales/it.yml +2 -2
- data/lib/locales/ja.yml +2 -2
- data/lib/locales/km.yml +2 -2
- data/lib/locales/ko.yml +2 -2
- data/lib/locales/nb.yml +2 -2
- data/lib/locales/nl.yml +2 -2
- data/lib/locales/pl.yml +2 -2
- data/lib/locales/pt-BR.yml +2 -2
- data/lib/locales/pt.yml +2 -2
- data/lib/locales/ru.yml +2 -2
- data/lib/locales/sr.yml +23 -0
- data/lib/locales/sv-SE.yml +2 -2
- data/lib/locales/sv.yml +2 -2
- data/lib/locales/sw.yml +22 -0
- data/lib/locales/ta.yml +22 -0
- data/lib/locales/tr.yml +2 -2
- data/lib/locales/uk.yml +24 -0
- data/lib/locales/zh-CN.yml +2 -2
- data/lib/locales/zh-HK.yml +2 -2
- data/lib/locales/zh-TW.yml +3 -3
- data/lib/pagy/backend.rb +11 -12
- data/lib/pagy/calendar/day.rb +29 -0
- data/lib/pagy/calendar/month.rb +16 -0
- data/lib/pagy/calendar/month_mixin.rb +49 -0
- data/lib/pagy/calendar/quarter.rb +23 -0
- data/lib/pagy/calendar/week.rb +39 -0
- data/lib/pagy/calendar/year.rb +29 -0
- data/lib/pagy/calendar.rb +90 -0
- data/lib/pagy/console.rb +23 -0
- data/lib/pagy/countless.rb +23 -19
- data/lib/pagy/exceptions.rb +16 -13
- data/lib/pagy/extras/arel.rb +12 -7
- data/lib/pagy/extras/array.rb +10 -9
- data/lib/pagy/extras/bootstrap.rb +77 -39
- data/lib/pagy/extras/bulma.rb +77 -43
- data/lib/pagy/extras/calendar.rb +66 -0
- data/lib/pagy/extras/countless.rb +17 -17
- data/lib/pagy/extras/elasticsearch_rails.rb +66 -37
- data/lib/pagy/extras/foundation.rb +74 -41
- data/lib/pagy/extras/frontend_helpers.rb +70 -0
- data/lib/pagy/extras/gearbox.rb +42 -0
- data/lib/pagy/extras/headers.rb +32 -18
- data/lib/pagy/extras/i18n.rb +18 -17
- data/lib/pagy/extras/items.rb +42 -53
- data/lib/pagy/extras/materialize.rb +68 -43
- data/lib/pagy/extras/meilisearch.rb +61 -0
- data/lib/pagy/extras/metadata.rb +27 -26
- data/lib/pagy/extras/navs.rb +54 -29
- data/lib/pagy/extras/overflow.rb +57 -52
- data/lib/pagy/extras/searchkick.rb +54 -36
- data/lib/pagy/extras/semantic.rb +66 -39
- data/lib/pagy/extras/standalone.rb +64 -0
- data/lib/pagy/extras/support.rb +34 -17
- data/lib/pagy/extras/trim.rb +18 -12
- data/lib/pagy/extras/uikit.rb +66 -44
- data/lib/pagy/frontend.rb +61 -53
- data/lib/pagy/i18n.rb +164 -0
- data/lib/pagy/url_helpers.rb +38 -0
- data/lib/pagy.rb +96 -30
- data/lib/templates/bootstrap_nav.html.erb +1 -1
- data/lib/templates/bootstrap_nav.html.haml +1 -1
- data/lib/templates/bootstrap_nav.html.slim +1 -1
- data/lib/templates/foundation_nav.html.erb +1 -1
- data/lib/templates/foundation_nav.html.haml +1 -1
- data/lib/templates/foundation_nav.html.slim +1 -1
- data/lib/templates/uikit_nav.html.erb +2 -2
- data/lib/templates/uikit_nav.html.haml +1 -1
- data/lib/templates/uikit_nav.html.slim +2 -2
- metadata +37 -16
- data/lib/locales/README.md +0 -35
- data/lib/locales/utils/i18n.rb +0 -25
- data/lib/locales/utils/loader.rb +0 -34
- data/lib/locales/utils/p11n.rb +0 -88
- data/lib/pagy/extras/pagy_search.rb +0 -18
- data/lib/pagy/extras/shared.rb +0 -53
- data/pagy.gemspec +0 -16
@@ -0,0 +1,66 @@
|
|
1
|
+
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/calendar
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pagy/calendar'
|
5
|
+
|
6
|
+
class Pagy # :nodoc:
|
7
|
+
# Add pagination filtering by calendar unit (:year, :quarter, :month, :week, :day) to the regular pagination
|
8
|
+
module CalendarExtra
|
9
|
+
# Additions for the Backend module
|
10
|
+
module Backend
|
11
|
+
CONF_KEYS = (Calendar::UNITS + %i[pagy active]).freeze
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Take a collection and a conf Hash with keys in CONF_KEYS and return an array with 3 items: [calendar, pagy, results]
|
16
|
+
def pagy_calendar(collection, conf)
|
17
|
+
unless conf.is_a?(Hash) && (conf.keys - CONF_KEYS).empty? && conf.all? { |k, v| v.is_a?(Hash) || k == :active }
|
18
|
+
raise ArgumentError, "keys must be in #{CONF_KEYS.inspect} and object values must be Hashes; got #{conf.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
conf[:pagy] = {} unless conf[:pagy] # use default Pagy object when omitted
|
22
|
+
calendar, collection = pagy_setup_calendar(collection, conf) unless conf.key?(:active) && !conf[:active]
|
23
|
+
pagy, results = send(conf[:pagy][:backend] || :pagy, collection, conf[:pagy]) # use backend: :pagy when omitted
|
24
|
+
[calendar, pagy, results]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Setup and return the calendar objects and the filtered collection
|
28
|
+
def pagy_setup_calendar(collection, conf)
|
29
|
+
units = Calendar::UNITS & conf.keys # get the units in time length desc order
|
30
|
+
raise ArgumentError, 'no calendar unit found in pagy_calendar configuration' if units.empty?
|
31
|
+
|
32
|
+
page_param = conf[:pagy][:page_param] || DEFAULT[:page_param]
|
33
|
+
units.each do |unit| # set all the :page_param vars for later deletion
|
34
|
+
unit_page_param = :"#{unit}_#{page_param}"
|
35
|
+
conf[unit][:page_param] = unit_page_param
|
36
|
+
conf[unit][:page] = params[unit_page_param]
|
37
|
+
end
|
38
|
+
calendar = {}
|
39
|
+
last_obj = nil
|
40
|
+
units.each_with_index do |unit, index|
|
41
|
+
params_to_delete = units[(index + 1), units.size].map { |sub| conf[sub][:page_param] } + [page_param]
|
42
|
+
conf[unit][:params] = lambda do |params| # delete page_param from the sub-units
|
43
|
+
params_to_delete.each { |p| params.delete(p.to_s) } # Hash#except missing from ruby 2.5 baseline
|
44
|
+
params
|
45
|
+
end
|
46
|
+
conf[unit][:period] = last_obj&.send(:active_period) || pagy_calendar_period(collection)
|
47
|
+
calendar[unit] = last_obj = Calendar.send(:create, unit, conf[unit])
|
48
|
+
end
|
49
|
+
[calendar, pagy_calendar_filter(collection, last_obj.from, last_obj.to)]
|
50
|
+
end
|
51
|
+
|
52
|
+
# This method must be implemented by the application
|
53
|
+
def pagy_calendar_period(*)
|
54
|
+
raise NoMethodError, 'the pagy_calendar_period method must be implemented by the application ' \
|
55
|
+
'(see https://ddnexus.github.io/pagy/extras/calendar#pagy_calendar_periodcollection)'
|
56
|
+
end
|
57
|
+
|
58
|
+
# This method must be implemented by the application
|
59
|
+
def pagy_calendar_filter(*)
|
60
|
+
raise NoMethodError, 'the pagy_calendar_filter method must be implemented by the application ' \
|
61
|
+
'(see https://ddnexus.github.io/pagy/extras/calendar#pagy_calendar_filtercollection-from-to)'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
Backend.prepend CalendarExtra::Backend
|
66
|
+
end
|
@@ -1,37 +1,37 @@
|
|
1
1
|
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/countless
|
2
|
-
# encoding: utf-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
4
|
require 'pagy/countless'
|
6
5
|
|
7
|
-
class Pagy
|
6
|
+
class Pagy # :nodoc:
|
7
|
+
DEFAULT[:countless_minimal] = false
|
8
8
|
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
module Backend ; private # the whole module is private so no problem with including it in a controller
|
9
|
+
# Paginate without the need of any count, saving one query per rendering
|
10
|
+
module CountlessExtra
|
11
|
+
private
|
13
12
|
|
14
13
|
# Return Pagy object and items
|
15
|
-
def pagy_countless(collection, vars={})
|
16
|
-
pagy =
|
17
|
-
|
14
|
+
def pagy_countless(collection, vars = {})
|
15
|
+
pagy = Countless.new(pagy_countless_get_vars(collection, vars))
|
16
|
+
[pagy, pagy_countless_get_items(collection, pagy)]
|
18
17
|
end
|
19
18
|
|
20
19
|
# Sub-method called only by #pagy_countless: here for easy customization of variables by overriding
|
21
20
|
def pagy_countless_get_vars(_collection, vars)
|
22
|
-
vars
|
21
|
+
pagy_set_items_from_params(vars) if defined?(ItemsExtra)
|
22
|
+
vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
|
23
23
|
vars
|
24
24
|
end
|
25
25
|
|
26
26
|
# Sub-method called only by #pagy_countless: here for easy customization of record-extraction by overriding
|
27
|
+
# You may need to override this method for collections without offset|limit
|
27
28
|
def pagy_countless_get_items(collection, pagy)
|
28
|
-
|
29
|
-
items = collection.offset(pagy.offset).limit(pagy.items + 1).to_a
|
30
|
-
items_size = items.size
|
31
|
-
items.pop if items_size == pagy.items + 1
|
32
|
-
pagy.finalize(items_size) # finalize may adjust pagy.items, so must be used after checking the size
|
33
|
-
items
|
34
|
-
end
|
29
|
+
return collection.offset(pagy.offset).limit(pagy.items) if pagy.vars[:countless_minimal]
|
35
30
|
|
31
|
+
fetched = collection.offset(pagy.offset).limit(pagy.items + 1).to_a # eager load items + 1
|
32
|
+
pagy.finalize(fetched.size) # finalize the pagy object
|
33
|
+
fetched[0, pagy.items] # ignore eventual extra item
|
34
|
+
end
|
36
35
|
end
|
36
|
+
Backend.prepend CountlessExtra
|
37
37
|
end
|
@@ -1,50 +1,79 @@
|
|
1
1
|
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/elasticsearch_rails
|
2
|
-
# encoding: utf-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
|
-
|
4
|
+
class Pagy # :nodoc:
|
5
|
+
DEFAULT[:elasticsearch_rails_search_method] ||= :pagy_search
|
6
6
|
|
7
|
-
|
7
|
+
# Paginate ElasticsearchRails response objects
|
8
|
+
module ElasticsearchRailsExtra
|
9
|
+
module_function
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
new(vars)
|
19
|
-
end
|
11
|
+
# Get the count from different version of ElasticsearchRails
|
12
|
+
def total_count(response)
|
13
|
+
total = if response.respond_to?(:raw_response)
|
14
|
+
response.raw_response['hits']['total']
|
15
|
+
else
|
16
|
+
response.response['hits']['total']
|
17
|
+
end
|
18
|
+
total.is_a?(Hash) ? total['value'] : total
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
search_args[-1][:from] = vars[:items] * (vars[:page] - 1)
|
30
|
-
response = model.search(*search_args)
|
31
|
-
total = response.respond_to?(:raw_response) ? response.raw_response['hits']['total'] : response.response['hits']['total']
|
32
|
-
vars[:count] = total.is_a?(Hash) ? total['value'] : total
|
33
|
-
pagy = Pagy.new(vars)
|
34
|
-
# with :last_page overflow we need to re-run the method in order to get the hits
|
35
|
-
if defined?(OVERFLOW) && pagy.overflow? && pagy.vars[:overflow] == :last_page
|
36
|
-
return pagy_elasticsearch_rails(pagy_search_args, vars.merge(page: pagy.page))
|
21
|
+
module ElasticsearchRails # :nodoc:
|
22
|
+
# Return an array used to delay the call of #search
|
23
|
+
# after the pagination variables are merged to the options.
|
24
|
+
# It also pushes to the same array an optional method call.
|
25
|
+
def pagy_elasticsearch_rails(query_or_payload, **options)
|
26
|
+
[self, query_or_payload, options].tap do |args|
|
27
|
+
args.define_singleton_method(:method_missing) { |*a| args += a }
|
28
|
+
end
|
37
29
|
end
|
38
|
-
|
30
|
+
alias_method Pagy::DEFAULT[:elasticsearch_rails_search_method], :pagy_elasticsearch_rails
|
39
31
|
end
|
40
32
|
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
vars
|
45
|
-
|
46
|
-
|
33
|
+
# Additions for the Pagy class
|
34
|
+
module Pagy
|
35
|
+
# Create a Pagy object from an Elasticsearch::Model::Response::Response object
|
36
|
+
def new_from_elasticsearch_rails(response, vars = {})
|
37
|
+
vars[:items] = response.search.options[:size] || 10
|
38
|
+
vars[:page] = ((response.search.options[:from] || 0) / vars[:items]) + 1
|
39
|
+
vars[:count] = ElasticsearchRailsExtra.total_count(response)
|
40
|
+
new(vars)
|
41
|
+
end
|
47
42
|
end
|
48
43
|
|
44
|
+
# Add specialized backend methods to paginate ElasticsearchRails searches
|
45
|
+
module Backend
|
46
|
+
private
|
47
|
+
|
48
|
+
# Return Pagy object and items
|
49
|
+
def pagy_elasticsearch_rails(pagy_search_args, vars = {})
|
50
|
+
model, query_or_payload,
|
51
|
+
options, *called = pagy_search_args
|
52
|
+
vars = pagy_elasticsearch_rails_get_vars(nil, vars)
|
53
|
+
options[:size] = vars[:items]
|
54
|
+
options[:from] = vars[:items] * (vars[:page] - 1)
|
55
|
+
response = model.search(query_or_payload, **options)
|
56
|
+
vars[:count] = ElasticsearchRailsExtra.total_count(response)
|
57
|
+
|
58
|
+
pagy = ::Pagy.new(vars)
|
59
|
+
# with :last_page overflow we need to re-run the method in order to get the hits
|
60
|
+
return pagy_elasticsearch_rails(pagy_search_args, vars.merge(page: pagy.page)) \
|
61
|
+
if defined?(::Pagy::OverflowExtra) && pagy.overflow? && pagy.vars[:overflow] == :last_page
|
62
|
+
|
63
|
+
[pagy, called.empty? ? response : response.send(*called)]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Sub-method called only by #pagy_elasticsearch_rails: here for easy customization of variables by overriding
|
67
|
+
# the _collection argument is not available when the method is called
|
68
|
+
def pagy_elasticsearch_rails_get_vars(_collection, vars)
|
69
|
+
pagy_set_items_from_params(vars) if defined?(ItemsExtra)
|
70
|
+
vars[:items] ||= DEFAULT[:items]
|
71
|
+
vars[:page] ||= (params[vars[:page_param] || DEFAULT[:page_param]] || 1).to_i
|
72
|
+
vars
|
73
|
+
end
|
74
|
+
end
|
49
75
|
end
|
76
|
+
ElasticsearchRails = ElasticsearchRailsExtra::ElasticsearchRails
|
77
|
+
extend ElasticsearchRailsExtra::Pagy
|
78
|
+
Backend.prepend ElasticsearchRailsExtra::Backend
|
50
79
|
end
|
@@ -1,57 +1,90 @@
|
|
1
1
|
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/foundation
|
2
|
-
# encoding: utf-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
|
-
require 'pagy/extras/
|
6
|
-
|
7
|
-
class Pagy
|
8
|
-
module Frontend
|
4
|
+
require 'pagy/extras/frontend_helpers'
|
9
5
|
|
6
|
+
class Pagy # :nodoc:
|
7
|
+
# Frontend modules are specially optimized for performance.
|
8
|
+
# The resulting code may not look very elegant, but produces the best benchmarks
|
9
|
+
module FoundationExtra
|
10
10
|
# Pagination for Foundation: it returns the html with the series of links to the pages
|
11
|
-
def pagy_foundation_nav(pagy)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
def pagy_foundation_nav(pagy, pagy_id: nil, link_extra: '', **vars)
|
12
|
+
p_id = %( id="#{pagy_id}") if pagy_id
|
13
|
+
link = pagy_link_proc(pagy, link_extra: link_extra)
|
14
|
+
|
15
|
+
html = +%(<nav#{p_id} class="pagy-foundation-nav" aria-label="Pagination"><ul class="pagination">)
|
16
|
+
html << pagy_foundation_prev_html(pagy, link)
|
17
|
+
pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
18
|
+
html << case item
|
19
|
+
when Integer then %(<li>#{link.call item}</li>) # page link
|
20
|
+
when String then %(<li class="current">#{pagy.label_for(item)}</li>) # active page
|
21
|
+
when :gap then %(<li class="ellipsis gap" aria-hidden="true"></li>) # page gap
|
22
|
+
else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
|
20
23
|
end
|
21
24
|
end
|
22
|
-
html << (
|
23
|
-
|
24
|
-
%(<nav class="pagy-foundation-nav" role="navigation" aria-label="Pagination"><ul class="pagination">#{html}</ul></nav>)
|
25
|
+
html << pagy_foundation_next_html(pagy, link)
|
26
|
+
html << %(</ul></nav>)
|
25
27
|
end
|
26
28
|
|
27
|
-
# Javascript pagination for foundation: it returns a nav and a JSON tag used by the
|
28
|
-
def pagy_foundation_nav_js(pagy,
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
'link' => %(<li>#{link.call(PAGE_PLACEHOLDER)}</li>),
|
34
|
-
'active' => %(<li class="current">#{
|
29
|
+
# Javascript pagination for foundation: it returns a nav and a JSON tag used by the pagy.js file
|
30
|
+
def pagy_foundation_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
|
31
|
+
sequels = pagy.sequels(**vars)
|
32
|
+
p_id = %( id="#{pagy_id}") if pagy_id
|
33
|
+
link = pagy_link_proc(pagy, link_extra: link_extra)
|
34
|
+
tags = { 'before' => %(<ul class="pagination">#{pagy_foundation_prev_html pagy, link}),
|
35
|
+
'link' => %(<li>#{link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER)}</li>),
|
36
|
+
'active' => %(<li class="current">#{LABEL_PLACEHOLDER}</li>),
|
35
37
|
'gap' => %(<li class="ellipsis gap" aria-hidden="true"></li>),
|
36
|
-
'after' =>
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
'after' => %(#{pagy_foundation_next_html pagy, link}</ul>) }
|
39
|
+
|
40
|
+
%(<nav#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-foundation-nav-js" aria-label="Pagination" #{
|
41
|
+
pagy_json_attr(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></nav>)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Javascript combo pagination for Foundation: it returns a nav and a JSON tag used by the pagy.js file
|
45
|
+
def pagy_foundation_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
|
46
|
+
p_id = %( id="#{pagy_id}") if pagy_id
|
47
|
+
link = pagy_link_proc(pagy, link_extra: link_extra)
|
48
|
+
p_page = pagy.page
|
49
|
+
p_pages = pagy.pages
|
50
|
+
input = %(<input class="input-group-field cell shrink" type="number" min="1" max="#{
|
51
|
+
p_pages}" value="#{p_page}" style="width: #{
|
52
|
+
p_pages.to_s.length + 1}rem; padding: 0 0.3rem; margin: 0 0.3rem;">)
|
53
|
+
|
54
|
+
%(<nav#{p_id} class="pagy-foundation-combo-nav-js" aria-label="Pagination"><div class="input-group" #{
|
55
|
+
pagy_json_attr pagy, :combo, pagy_marked_link(link)}>#{
|
56
|
+
if (p_prev = pagy.prev)
|
57
|
+
link.call p_prev, pagy_t('pagy.nav.prev'),
|
58
|
+
'style="margin-bottom: 0" aria-label="previous" class="prev button primary"'
|
59
|
+
else
|
60
|
+
%(<a style="margin-bottom: 0" class="prev button primary disabled" href="#">#{pagy_t 'pagy.nav.prev'}</a>)
|
61
|
+
end
|
62
|
+
}<span class="input-group-label">#{pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</span>#{
|
63
|
+
if (p_next = pagy.next)
|
64
|
+
link.call p_next, pagy_t('pagy.nav.next'), 'style="margin-bottom: 0" aria-label="next" class="next button primary"'
|
65
|
+
else
|
66
|
+
%(<a style="margin-bottom: 0" class="next button primary disabled" href="#">#{pagy_t 'pagy.nav.next'}</a>)
|
67
|
+
end
|
68
|
+
}</div></nav>)
|
40
69
|
end
|
41
70
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
html << %(<span class="input-group-label">#{pagy_t('pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages)}</span>)
|
51
|
-
html << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'style="margin-bottom: 0px;" aria-label="next" class="next button primary"')
|
52
|
-
: %(<a style="margin-bottom: 0px;" class="next button primary disabled" href="#">#{pagy_t('pagy.nav.next')}</a>))
|
53
|
-
html << %(</div></nav>#{pagy_json_tag(:combo_nav, id, p_page, pagy_marked_link(link), defined?(TRIM) && pagy.vars[:page_param])})
|
71
|
+
private
|
72
|
+
|
73
|
+
def pagy_foundation_prev_html(pagy, link)
|
74
|
+
if (p_prev = pagy.prev)
|
75
|
+
%(<li class="prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
|
76
|
+
else
|
77
|
+
%(<li class="prev disabled">#{pagy_t 'pagy.nav.prev'}</li>)
|
78
|
+
end
|
54
79
|
end
|
55
80
|
|
81
|
+
def pagy_foundation_next_html(pagy, link)
|
82
|
+
if (p_next = pagy.next)
|
83
|
+
%(<li class="next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
|
84
|
+
else
|
85
|
+
%(<li class="next disabled">#{pagy_t 'pagy.nav.next'}</li>)
|
86
|
+
end
|
87
|
+
end
|
56
88
|
end
|
89
|
+
Frontend.prepend FoundationExtra
|
57
90
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
class Pagy # :nodoc:
|
6
|
+
DEFAULT[:steps] = false # default false will use {0 => @vars[:size]}
|
7
|
+
|
8
|
+
# Private module documented in the main classes
|
9
|
+
module FrontendHelpers
|
10
|
+
# Additions for the Pagy class
|
11
|
+
module Pagy
|
12
|
+
# `Pagy` instance method used by the `pagy*_nav_js` helpers.
|
13
|
+
# It returns the sequels of width/series generated from the :steps hash
|
14
|
+
# Example:
|
15
|
+
# >> pagy = Pagy.new(count:1000, page: 20, steps: {0 => [1,2,2,1], 350 => [2,3,3,2], 550 => [3,4,4,3]})
|
16
|
+
# >> pagy.sequels
|
17
|
+
# #=> { "0" => [1, :gap, 18, 19, "20", 21, 22, :gap, 50],
|
18
|
+
# "350" => [1, 2, :gap, 17, 18, 19, "20", 21, 22, 23, :gap, 49, 50],
|
19
|
+
# "550" => [1, 2, 3, :gap, 16, 17, 18, 19, "20", 21, 22, 23, 24, :gap, 48, 49, 50] }
|
20
|
+
# Notice: if :steps is false it will use the single {0 => @vars[:size]} size
|
21
|
+
def sequels(steps: @vars[:steps] || { 0 => @vars[:size] }, **_)
|
22
|
+
raise VariableError.new(self, :steps, 'to define the 0 width', steps) unless steps.key?(0)
|
23
|
+
|
24
|
+
{}.tap do |sequels|
|
25
|
+
steps.each { |width, step_size| sequels[width.to_s] = series(size: step_size) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Support for the Calendar API
|
30
|
+
def label_sequels(*); end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Additions for Calendar class
|
34
|
+
module Calendar
|
35
|
+
def label_sequels(sequels = self.sequels)
|
36
|
+
{}.tap do |label_sequels|
|
37
|
+
sequels.each do |width, series|
|
38
|
+
label_sequels[width] = series.map { |item| item == :gap ? :gap : label_for(item) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Additions for the Frontend
|
45
|
+
module Frontend
|
46
|
+
if defined?(Oj)
|
47
|
+
# Return a script tag with the JSON-serialized args generated with the faster oj gem
|
48
|
+
def pagy_json_attr(pagy, *args)
|
49
|
+
args << pagy.vars[:page_param] if pagy.vars[:trim_extra]
|
50
|
+
%(data-pagy-json="#{Oj.dump(args, mode: :strict).gsub('"', '"')}")
|
51
|
+
end
|
52
|
+
else
|
53
|
+
require 'json'
|
54
|
+
# Return a script tag with the JSON-serialized args generated with the slower to_json
|
55
|
+
def pagy_json_attr(pagy, *args)
|
56
|
+
args << pagy.vars[:page_param] if pagy.vars[:trim_extra]
|
57
|
+
%(data-pagy-json="#{args.to_json.gsub('"', '"')}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return the marked link to used by pagy.js
|
62
|
+
def pagy_marked_link(link)
|
63
|
+
link.call PAGE_PLACEHOLDER, '', 'style="display: none;"'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
prepend FrontendHelpers::Pagy
|
68
|
+
Calendar.prepend FrontendHelpers::Calendar if defined?(Calendar)
|
69
|
+
Frontend.prepend FrontendHelpers::Frontend
|
70
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/gearbox
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Pagy # :nodoc:
|
5
|
+
DEFAULT[:gearbox_extra] = true # extra enabled by default
|
6
|
+
DEFAULT[:gearbox_items] = [15, 30, 60, 100]
|
7
|
+
|
8
|
+
# Automatically change the number of items per page depending on the page number
|
9
|
+
# accepts an array as the :gearbox_items variable, that will determine the items for the first pages
|
10
|
+
module GearboxExtra
|
11
|
+
# Setup @items based on the :gearbox_items variable
|
12
|
+
def setup_items_var
|
13
|
+
return super if !@vars[:gearbox_extra] || @vars[:items_extra]
|
14
|
+
|
15
|
+
gearbox_items = @vars[:gearbox_items]
|
16
|
+
raise VariableError.new(self, :gearbox_items, 'to be an Array of positives', gearbox_items) \
|
17
|
+
unless gearbox_items.is_a?(Array) && gearbox_items.all? { |num| num.positive? rescue false } # rubocop:disable Style/RescueModifier
|
18
|
+
|
19
|
+
@items = gearbox_items[@page - 1] || gearbox_items.last
|
20
|
+
end
|
21
|
+
|
22
|
+
# Setup @pages and @last based on the :gearbox_items variable
|
23
|
+
def setup_pages_var
|
24
|
+
return super if !@vars[:gearbox_extra] || @vars[:items_extra]
|
25
|
+
|
26
|
+
gearbox_items = @vars[:gearbox_items]
|
27
|
+
# This algorithm is thousands of times faster than the one in the geared_pagination gem
|
28
|
+
@pages = @last = (if @count > (sum = gearbox_items.sum)
|
29
|
+
[((@count - sum).to_f / gearbox_items.last).ceil, 1].max + gearbox_items.count
|
30
|
+
else
|
31
|
+
pages = 0
|
32
|
+
remainder = @count
|
33
|
+
while remainder.positive?
|
34
|
+
pages += 1
|
35
|
+
remainder -= gearbox_items[pages - 1]
|
36
|
+
end
|
37
|
+
[pages, 1].max
|
38
|
+
end)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
prepend GearboxExtra
|
42
|
+
end
|
data/lib/pagy/extras/headers.rb
CHANGED
@@ -1,39 +1,53 @@
|
|
1
1
|
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/headers
|
2
|
-
# encoding: utf-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
|
-
|
6
|
-
# Add specialized backend methods to add pagination response headers
|
7
|
-
module Backend ; private
|
4
|
+
require 'pagy/url_helpers'
|
8
5
|
|
9
|
-
|
6
|
+
class Pagy # :nodoc:
|
7
|
+
DEFAULT[:headers] = { page: 'Current-Page',
|
8
|
+
items: 'Page-Items',
|
9
|
+
count: 'Total-Count',
|
10
|
+
pages: 'Total-Pages' }
|
11
|
+
# Add specialized backend methods to add pagination response headers
|
12
|
+
module HeadersExtra
|
13
|
+
include UrlHelpers
|
10
14
|
|
11
|
-
|
15
|
+
private
|
12
16
|
|
17
|
+
# Merge the pagy headers into the response.headers
|
13
18
|
def pagy_headers_merge(pagy)
|
14
19
|
response.headers.merge!(pagy_headers(pagy))
|
15
20
|
end
|
16
21
|
|
22
|
+
# Generate a hash of RFC-8288 compliant http headers
|
17
23
|
def pagy_headers(pagy)
|
18
|
-
|
19
|
-
|
20
|
-
|
24
|
+
pagy_headers_hash(pagy).tap do |hash|
|
25
|
+
hash['Link'] = hash['Link'].map { |rel, link| %(<#{link}>; rel="#{rel}") }.join(', ')
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
29
|
+
# Generates a hash structure of the headers
|
23
30
|
def pagy_headers_hash(pagy)
|
24
|
-
countless
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
countless = defined?(Countless) && pagy.is_a?(Countless)
|
32
|
+
rel = { 'first' => 1, 'prev' => pagy.prev, 'next' => pagy.next }
|
33
|
+
rel['last'] = pagy.last unless countless
|
34
|
+
url_str = pagy_url_for(pagy, PAGE_PLACEHOLDER, absolute: true)
|
35
|
+
link = rel.map do |r, num| # filter_map if ruby >=2.7
|
36
|
+
next unless num # rubocop:disable Layout/EmptyLineAfterGuardClause
|
37
|
+
[r, url_str.sub(PAGE_PLACEHOLDER, num.to_s)]
|
38
|
+
end.compact.to_h
|
39
|
+
hash = { 'Link' => link }
|
40
|
+
headers = pagy.vars[:headers]
|
41
|
+
hash[headers[:page]] = pagy.page.to_s if headers[:page]
|
42
|
+
if headers[:items] && !(defined?(Calendar) && pagy.is_a?(Calendar)) # items is not for Calendar
|
43
|
+
hash[headers[:items]] = pagy.vars[:items].to_s
|
44
|
+
end
|
31
45
|
unless countless
|
32
46
|
hash[headers[:pages]] = pagy.pages.to_s if headers[:pages]
|
33
|
-
hash[headers[:count]] = pagy.count.to_s if headers[:count]
|
47
|
+
hash[headers[:count]] = pagy.count.to_s if pagy.count && headers[:count] # count may be nil with Calendar
|
34
48
|
end
|
35
49
|
hash
|
36
50
|
end
|
37
|
-
|
38
51
|
end
|
52
|
+
Backend.prepend HeadersExtra
|
39
53
|
end
|
data/lib/pagy/extras/i18n.rb
CHANGED
@@ -1,25 +1,26 @@
|
|
1
1
|
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/i18n
|
2
|
-
# encoding: utf-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
|
-
class Pagy
|
4
|
+
class Pagy # :nodoc:
|
6
5
|
# Use ::I18n gem
|
7
|
-
module
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
alias_method :pagy_without_i18n, :pagy_t
|
14
|
-
if Gem::Version.new(::I18n::VERSION) < Gem::Version.new('1.6.0')
|
15
|
-
def pagy_t_with_i18n(*args) ::I18n.t(*args) end
|
16
|
-
else
|
17
|
-
# keep 1.9 compatibility by hiding 2.0+ syntax in string
|
18
|
-
module_eval <<-RUBY
|
19
|
-
def pagy_t_with_i18n(key, **opts) ::I18n.t(key, **opts) end
|
20
|
-
RUBY
|
6
|
+
module I18nExtra
|
7
|
+
# Frontend overriding for translation
|
8
|
+
module Frontend
|
9
|
+
def pagy_t(key, **opts)
|
10
|
+
::I18n.t(key, **opts)
|
11
|
+
end
|
21
12
|
end
|
22
|
-
alias_method :pagy_t, :pagy_t_with_i18n
|
23
13
|
|
14
|
+
# Calendar overriding for localization (see also the block in the calendar section of the config/pagy.rb initializer)
|
15
|
+
module Calendar
|
16
|
+
def localize(time, opts)
|
17
|
+
::I18n.l(time, **opts)
|
18
|
+
end
|
19
|
+
end
|
24
20
|
end
|
21
|
+
Frontend.prepend I18nExtra::Frontend
|
22
|
+
Calendar.prepend I18nExtra::Calendar if defined?(Calendar)
|
23
|
+
|
24
|
+
# Add the pagy locales to the I18n.load_path
|
25
|
+
::I18n.load_path += Dir[Pagy.root.join('locales', '*.yml')]
|
25
26
|
end
|