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
data/lib/pagy/frontend.rb
CHANGED
@@ -1,75 +1,83 @@
|
|
1
1
|
# See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/api/frontend
|
2
|
-
# encoding: utf-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
|
-
require '
|
4
|
+
require 'pagy/url_helpers'
|
5
|
+
require 'pagy/i18n'
|
6
6
|
|
7
7
|
class Pagy
|
8
|
+
# Used for search and replace, hardcoded also in the pagy.js file
|
9
|
+
PAGE_PLACEHOLDER = '__pagy_page__'
|
10
|
+
LABEL_PLACEHOLDER = '__pagy_label__'
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
# I18n static hash loaded at startup, used as default alternative to the i18n gem.
|
12
|
-
# see https://ddnexus.github.io/pagy/api/frontend#i18n
|
13
|
-
I18n = eval(Pagy.root.join('locales', 'utils', 'i18n.rb').read) #rubocop:disable Security/Eval
|
14
|
-
|
15
|
-
module Helpers
|
16
|
-
# This works with all Rack-based frameworks (Sinatra, Padrino, Rails, ...)
|
17
|
-
def pagy_url_for(page, pagy, url=false)
|
18
|
-
p_vars = pagy.vars; params = request.GET.merge(p_vars[:params]); params[p_vars[:page_param].to_s] = page
|
19
|
-
"#{request.base_url if url}#{request.path}?#{Rack::Utils.build_nested_query(pagy_get_params(params))}#{p_vars[:anchor]}"
|
20
|
-
end
|
21
|
-
|
22
|
-
# Sub-method called only by #pagy_url_for: here for easy customization of params by overriding
|
23
|
-
def pagy_get_params(params) params end
|
24
|
-
end
|
25
|
-
|
26
|
-
# All the code here has been optimized for performance: it may not look very pretty
|
27
|
-
# (as most code dealing with many long strings), but its performance makes it very sexy! ;)
|
12
|
+
# Frontend modules are specially optimized for performance.
|
13
|
+
# The resulting code may not look very elegant, but produces the best benchmarks
|
28
14
|
module Frontend
|
29
|
-
|
30
|
-
include Helpers
|
31
|
-
|
32
|
-
EMPTY = '' # EMPTY + 'string' is almost as fast as +'string' but is also 1.9 compatible
|
33
|
-
MARK = PAGE_PLACEHOLDER # backward compatibility in case of helper-overriding in legacy apps
|
15
|
+
include UrlHelpers
|
34
16
|
|
35
17
|
# Generic pagination: it returns the html with the series of links to the pages
|
36
|
-
def pagy_nav(pagy)
|
37
|
-
|
18
|
+
def pagy_nav(pagy, pagy_id: nil, link_extra: '', **vars)
|
19
|
+
p_id = %( id="#{pagy_id}") if pagy_id
|
20
|
+
link = pagy_link_proc(pagy, link_extra: link_extra)
|
21
|
+
p_prev = pagy.prev
|
22
|
+
p_next = pagy.next
|
38
23
|
|
39
|
-
html =
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
24
|
+
html = +%(<nav#{p_id} class="pagy-nav pagination" aria-label="pager">)
|
25
|
+
html << if p_prev
|
26
|
+
%(<span class="page prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</span> )
|
27
|
+
else
|
28
|
+
%(<span class="page prev disabled">#{pagy_t('pagy.nav.prev')}</span> )
|
29
|
+
end
|
30
|
+
pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
31
|
+
html << case item
|
32
|
+
when Integer then %(<span class="page">#{link.call item}</span> )
|
33
|
+
when String then %(<span class="page active">#{pagy.label_for(item)}</span> )
|
34
|
+
when :gap then %(<span class="page gap">#{pagy_t('pagy.nav.gap')}</span> )
|
35
|
+
else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
|
45
36
|
end
|
46
37
|
end
|
47
|
-
html <<
|
48
|
-
|
49
|
-
|
38
|
+
html << if p_next
|
39
|
+
%(<span class="page next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</span>)
|
40
|
+
else
|
41
|
+
%(<span class="page next disabled">#{pagy_t('pagy.nav.next')}</span>)
|
42
|
+
end
|
43
|
+
html << %(</nav>)
|
50
44
|
end
|
51
45
|
|
52
|
-
# Return examples: "Displaying items 41-60 of 324 in total"
|
53
|
-
def pagy_info(pagy, item_name
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
46
|
+
# Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
|
47
|
+
def pagy_info(pagy, pagy_id: nil, item_name: nil, i18n_key: nil)
|
48
|
+
p_id = %( id="#{pagy_id}") if pagy_id
|
49
|
+
p_count = pagy.count
|
50
|
+
key = if p_count.zero? then 'pagy.info.no_items'
|
51
|
+
elsif pagy.pages == 1 then 'pagy.info.single_page'
|
52
|
+
else 'pagy.info.multiple_pages' # rubocop:disable Lint/ElseLayout
|
53
|
+
end
|
54
|
+
|
55
|
+
%(<span#{p_id} class="pagy-info">#{
|
56
|
+
pagy_t key, item_name: item_name || pagy_t(i18n_key || pagy.vars[:i18n_key], count: p_count),
|
57
|
+
count: p_count, from: pagy.from, to: pagy.to
|
58
|
+
}</span>)
|
58
59
|
end
|
59
60
|
|
60
|
-
#
|
61
|
+
# Return a performance optimized proc to generate the HTML links
|
61
62
|
# Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
|
62
|
-
def pagy_link_proc(pagy, link_extra
|
63
|
-
p_prev
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
def pagy_link_proc(pagy, link_extra: '')
|
64
|
+
p_prev = pagy.prev
|
65
|
+
p_next = pagy.next
|
66
|
+
left, right = %(<a href="#{pagy_url_for pagy, PAGE_PLACEHOLDER}" #{
|
67
|
+
pagy.vars[:link_extra]} #{link_extra}).split(PAGE_PLACEHOLDER, 2)
|
68
|
+
lambda do |page, text = pagy.label_for(page), extra_attrs = ''|
|
69
|
+
%(#{left}#{page}#{right}#{ case page
|
70
|
+
when p_prev then ' rel="prev"'
|
71
|
+
when p_next then ' rel="next"'
|
72
|
+
else ''
|
73
|
+
end } #{extra_attrs}>#{text}</a>)
|
74
|
+
end
|
68
75
|
end
|
69
76
|
|
70
77
|
# Similar to I18n.t: just ~18x faster using ~10x less memory
|
71
|
-
# (@pagy_locale explicitly
|
72
|
-
def pagy_t(
|
73
|
-
|
78
|
+
# (@pagy_locale explicitly initialized in order to avoid warning)
|
79
|
+
def pagy_t(key, opts = {})
|
80
|
+
Pagy::I18n.translate(@pagy_locale ||= nil, key, opts)
|
81
|
+
end
|
74
82
|
end
|
75
83
|
end
|
data/lib/pagy/i18n.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# See Pagy::I18n API documentation https://ddnexus.github.io/pagy/api/i18n
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
class Pagy
|
7
|
+
# Pagy i18n implementation, compatible with the I18n gem, just a lot faster and lighter
|
8
|
+
module I18n
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Pluralization rules
|
12
|
+
module P11n
|
13
|
+
# Pluralization variables
|
14
|
+
from0to1 = (0..1).to_a.freeze
|
15
|
+
from2to4 = (2..4).to_a.freeze
|
16
|
+
from3to10 = (3..10).to_a.freeze
|
17
|
+
from5to9 = (5..9).to_a.freeze
|
18
|
+
from11to14 = (11..14).to_a.freeze
|
19
|
+
from11to99 = (11..99).to_a.freeze
|
20
|
+
from12to14 = (12..14).to_a.freeze
|
21
|
+
|
22
|
+
from0to1_from5to9 = from0to1 + from5to9
|
23
|
+
|
24
|
+
# Store the proc defining each pluralization RULE
|
25
|
+
# Logic adapted from https://github.com/svenfuchs/rails-i18n
|
26
|
+
RULE = {
|
27
|
+
arabic:
|
28
|
+
lambda do |n = 0|
|
29
|
+
mod100 = n % 100
|
30
|
+
case
|
31
|
+
when n == 0 then 'zero' # rubocop:disable Style/NumericPredicate
|
32
|
+
when n == 1 then 'one'
|
33
|
+
when n == 2 then 'two'
|
34
|
+
when from3to10.include?(mod100) then 'few'
|
35
|
+
when from11to99.include?(mod100) then 'many'
|
36
|
+
else 'other'
|
37
|
+
end
|
38
|
+
end,
|
39
|
+
|
40
|
+
east_slavic:
|
41
|
+
lambda do |n = 0|
|
42
|
+
mod10 = n % 10
|
43
|
+
mod100 = n % 100
|
44
|
+
case
|
45
|
+
when mod10 == 1 && mod100 != 11 then 'one'
|
46
|
+
when from2to4.include?(mod10) && !from12to14.include?(mod100) then 'few'
|
47
|
+
when mod10 == 0 || from5to9.include?(mod10) || from11to14.include?(mod100) then 'many' # rubocop:disable Style/NumericPredicate
|
48
|
+
else 'other'
|
49
|
+
end
|
50
|
+
end,
|
51
|
+
|
52
|
+
one_other:
|
53
|
+
->(n) { n == 1 ? 'one' : 'other' }, # default RULE
|
54
|
+
|
55
|
+
one_two_other:
|
56
|
+
lambda do |n|
|
57
|
+
case n
|
58
|
+
when 1 then 'one'
|
59
|
+
when 2 then 'two'
|
60
|
+
else 'other'
|
61
|
+
end
|
62
|
+
end,
|
63
|
+
|
64
|
+
one_upto_two_other:
|
65
|
+
->(n) { n && n >= 0 && n < 2 ? 'one' : 'other' },
|
66
|
+
|
67
|
+
other:
|
68
|
+
->(*) { 'other' },
|
69
|
+
|
70
|
+
polish:
|
71
|
+
lambda do |n = 0|
|
72
|
+
mod10 = n % 10
|
73
|
+
mod100 = n % 100
|
74
|
+
case
|
75
|
+
when n == 1 then 'one'
|
76
|
+
when from2to4.include?(mod10) && !from12to14.include?(mod100) then 'few'
|
77
|
+
when from0to1_from5to9.include?(mod10) || from12to14.include?(mod100) then 'many'
|
78
|
+
else 'other'
|
79
|
+
end
|
80
|
+
end,
|
81
|
+
|
82
|
+
west_slavic:
|
83
|
+
lambda do |n|
|
84
|
+
case n
|
85
|
+
when 1 then 'one'
|
86
|
+
when *from2to4 then 'few'
|
87
|
+
else 'other'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
}.freeze
|
92
|
+
|
93
|
+
# Store the RULE to apply to each LOCALE
|
94
|
+
# the :one_other RULE is the default for locales missing from this list
|
95
|
+
LOCALE = Hash.new(RULE[:one_other]).tap do |hash|
|
96
|
+
hash['ar'] = RULE[:arabic]
|
97
|
+
hash['bs'] = RULE[:east_slavic]
|
98
|
+
hash['cs'] = RULE[:west_slavic]
|
99
|
+
hash['id'] = RULE[:other]
|
100
|
+
hash['fr'] = RULE[:one_upto_two_other]
|
101
|
+
hash['hr'] = RULE[:east_slavic]
|
102
|
+
hash['ja'] = RULE[:other]
|
103
|
+
hash['km'] = RULE[:other]
|
104
|
+
hash['ko'] = RULE[:other]
|
105
|
+
hash['pl'] = RULE[:polish]
|
106
|
+
hash['ru'] = RULE[:east_slavic]
|
107
|
+
hash['sr'] = RULE[:east_slavic]
|
108
|
+
hash['sv'] = RULE[:one_two_other]
|
109
|
+
hash['sv-SE'] = RULE[:one_two_other]
|
110
|
+
hash['tr'] = RULE[:other]
|
111
|
+
hash['uk'] = RULE[:east_slavic]
|
112
|
+
hash['zh-CN'] = RULE[:other]
|
113
|
+
hash['zh-HK'] = RULE[:other]
|
114
|
+
hash['zh-TW'] = RULE[:other]
|
115
|
+
end.freeze
|
116
|
+
end
|
117
|
+
|
118
|
+
# Stores the i18n DATA structure for each loaded locale
|
119
|
+
# default on the first locale DATA
|
120
|
+
DATA = Hash.new { |hash, _| hash.first[1] }
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# Create a flat hash with dotted notation keys
|
125
|
+
def flatten(initial, prefix = '')
|
126
|
+
initial.each.reduce({}) do |hash, (key, value)|
|
127
|
+
hash.merge!(value.is_a?(Hash) ? flatten(value, "#{prefix}#{key}.") : { "#{prefix}#{key}" => value })
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Build the DATA hash out of the passed locales
|
132
|
+
def build(*locales)
|
133
|
+
locales.each do |locale|
|
134
|
+
locale[:filepath] ||= Pagy.root.join('locales', "#{locale[:locale]}.yml")
|
135
|
+
locale[:pluralize] ||= P11n::LOCALE[locale[:locale]]
|
136
|
+
dictionary = YAML.safe_load(File.read(locale[:filepath], encoding: 'UTF-8'))
|
137
|
+
raise I18nError, %(expected :locale "#{locale[:locale]}" not found in :filepath "#{locale[:filepath].inspect}") \
|
138
|
+
unless dictionary.key?(locale[:locale])
|
139
|
+
|
140
|
+
DATA[locale[:locale]] = [flatten(dictionary[locale[:locale]]), locale[:pluralize]]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
# Build the default at require time
|
144
|
+
build(locale: 'en')
|
145
|
+
|
146
|
+
public
|
147
|
+
|
148
|
+
# Public method to configure the locales: overrides the default, build the DATA and freezes it
|
149
|
+
def load(*locales)
|
150
|
+
DATA.clear
|
151
|
+
build(*locales)
|
152
|
+
DATA.freeze
|
153
|
+
end
|
154
|
+
|
155
|
+
# Translate and pluralize the key with the locale DATA
|
156
|
+
def translate(locale, key, opts = {})
|
157
|
+
data, pluralize = DATA[locale]
|
158
|
+
translation = data[key] || (opts[:count] && data[key += ".#{pluralize.call(opts[:count])}"]) \
|
159
|
+
or return %([translation missing: "#{key}"])
|
160
|
+
translation.gsub(/%{[^}]+?}/) { |match| opts[:"#{match[2..-2]}"] || match }
|
161
|
+
end
|
162
|
+
alias t translate
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Pagy
|
4
|
+
# Provide the helpers to handle the url in frontend and backend
|
5
|
+
module UrlHelpers
|
6
|
+
# Return the URL for the page, relying on the params method and Rack by default.
|
7
|
+
# It supports all Rack-based frameworks (Sinatra, Padrino, Rails, ...).
|
8
|
+
# For non-rack environments you can use the standalone extra
|
9
|
+
def pagy_url_for(pagy, page, absolute: nil)
|
10
|
+
vars = pagy.vars
|
11
|
+
page_param = vars[:page_param].to_s
|
12
|
+
items_param = vars[:items_param].to_s
|
13
|
+
params = pagy.params.is_a?(Hash) ? pagy.params.transform_keys(&:to_s) : {}
|
14
|
+
params = request.GET.merge(params)
|
15
|
+
params[page_param] = page
|
16
|
+
params[items_param] = vars[:items] if vars[:items_extra]
|
17
|
+
query_string = "?#{Rack::Utils.build_nested_query(pagy_deprecated_params(pagy, params))}" # remove in 6.0
|
18
|
+
# params = pagy.params.call(params) if pagy.params.is_a?(Proc) # add in 6.0
|
19
|
+
# query_string = "?#{Rack::Utils.build_nested_query(params)}" # add in 6.0
|
20
|
+
"#{request.base_url if absolute}#{request.path}#{query_string}#{vars[:fragment]}"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Transitional code to handle params deprecations. It will be removed in version 6.0
|
26
|
+
def pagy_deprecated_params(pagy, params) # remove in 6.0
|
27
|
+
if pagy.params.is_a?(Proc) # new code
|
28
|
+
pagy.params.call(params)
|
29
|
+
elsif respond_to?(:pagy_massage_params) # deprecated code
|
30
|
+
Warning.warn '[PAGY WARNING] The pagy_massage_params method has been deprecated and it will be ignored from version 6. ' \
|
31
|
+
'Set the :params variable to a Proc with the same code as the pagy_massage_params method.'
|
32
|
+
pagy_massage_params(params)
|
33
|
+
else
|
34
|
+
params # no massage params
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/pagy.rb
CHANGED
@@ -1,50 +1,116 @@
|
|
1
1
|
# See Pagy API documentation: https://ddnexus.github.io/pagy/api/pagy
|
2
|
-
# encoding: utf-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
4
|
require 'pathname'
|
6
5
|
|
7
|
-
|
6
|
+
# Core class
|
7
|
+
class Pagy
|
8
|
+
VERSION = '5.7.3'
|
8
9
|
|
9
10
|
# Root pathname to get the path of Pagy files like templates or dictionaries
|
10
|
-
def self.root
|
11
|
+
def self.root
|
12
|
+
@root ||= Pathname.new(__dir__).freeze
|
13
|
+
end
|
11
14
|
|
12
|
-
#
|
13
|
-
|
15
|
+
# Default core vars: constant for easy access, but mutable for customizable defaults
|
16
|
+
DEFAULT = { page: 1, # rubocop:disable Style/MutableConstant
|
17
|
+
items: 20,
|
18
|
+
outset: 0,
|
19
|
+
size: [1, 4, 4, 1],
|
20
|
+
page_param: :page,
|
21
|
+
params: {},
|
22
|
+
fragment: '',
|
23
|
+
link_extra: '',
|
24
|
+
i18n_key: 'pagy.item_name',
|
25
|
+
cycle: false }
|
14
26
|
|
15
|
-
attr_reader :count, :page, :items, :vars, :pages, :last, :offset, :from, :to, :prev, :next
|
27
|
+
attr_reader :count, :page, :items, :vars, :pages, :last, :offset, :in, :from, :to, :prev, :next, :params
|
16
28
|
|
17
29
|
# Merge and validate the options, do some simple arithmetic and set the instance variables
|
18
30
|
def initialize(vars)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@offset = @items * (@page - 1) + @outset
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@prev = (@page-1 unless @page == 1)
|
31
|
-
@next = @page == @last ? (1 if @vars[:cycle]) : @page+
|
31
|
+
normalize_vars(vars)
|
32
|
+
setup_vars(count: 0, page: 1, outset: 0)
|
33
|
+
setup_items_var
|
34
|
+
setup_pages_var
|
35
|
+
setup_params_var
|
36
|
+
raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
|
37
|
+
|
38
|
+
@offset = (@items * (@page - 1)) + @outset
|
39
|
+
@from = [@offset - @outset + 1, @count].min
|
40
|
+
@to = [@offset - @outset + @items, @count].min
|
41
|
+
@in = [@to - @from + 1, @count].min
|
42
|
+
@prev = (@page - 1 unless @page == 1)
|
43
|
+
@next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
|
32
44
|
end
|
33
45
|
|
34
46
|
# Return the array of page numbers and :gap items e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
35
|
-
def series(size
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
47
|
+
def series(size: @vars[:size], **_)
|
48
|
+
return [] if size.empty?
|
49
|
+
raise VariableError.new(self, :size, 'to contain 4 items >= 0', size) \
|
50
|
+
unless size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier
|
51
|
+
|
52
|
+
# This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3)
|
53
|
+
left_gap_start = 1 + size[0] # rubocop:disable Layout/ExtraSpacing, Layout/SpaceAroundOperators
|
54
|
+
left_gap_end = @page - size[1] - 1
|
55
|
+
right_gap_start = @page + size[2] + 1
|
56
|
+
right_gap_end = @last - size[3]
|
57
|
+
left_gap_end = right_gap_end if left_gap_end > right_gap_end
|
58
|
+
right_gap_start = left_gap_start if left_gap_start > right_gap_start
|
59
|
+
series = []
|
60
|
+
start = 1
|
61
|
+
if (left_gap_end - left_gap_start).positive?
|
62
|
+
series.push(*start...left_gap_start, :gap)
|
63
|
+
start = left_gap_end + 1
|
64
|
+
end
|
65
|
+
if (right_gap_end - right_gap_start).positive?
|
66
|
+
series.push(*start...right_gap_start, :gap)
|
67
|
+
start = right_gap_end + 1
|
68
|
+
end
|
69
|
+
series.push(*start..@last)
|
70
|
+
series[series.index(@page)] = @page.to_s
|
71
|
+
series
|
46
72
|
end
|
47
73
|
|
74
|
+
# Allow the customization of the output (overridden by the calendar extra)
|
75
|
+
def label_for(page)
|
76
|
+
page.to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
# Allow the customization of the output (overridden by the calendar extra)
|
80
|
+
def label
|
81
|
+
@page.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
# Apply defaults, cleanup blanks and set @vars
|
87
|
+
def normalize_vars(vars)
|
88
|
+
@vars = DEFAULT.merge(vars.delete_if { |k, v| DEFAULT.key?(k) && (v.nil? || v == '') })
|
89
|
+
end
|
90
|
+
|
91
|
+
# Setup and validates the passed vars: var must be present and value.to_i must be >= to min
|
92
|
+
def setup_vars(name_min)
|
93
|
+
name_min.each do |name, min|
|
94
|
+
raise VariableError.new(self, name, ">= #{min}", @vars[name]) \
|
95
|
+
unless @vars[name] && instance_variable_set(:"@#{name}", @vars[name].to_i) >= min
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Setup and validate the items (overridden by the gearbox extra)
|
100
|
+
def setup_items_var
|
101
|
+
setup_vars(items: 1)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Setup and validates the pages (overridden by the gearbox extra)
|
105
|
+
def setup_pages_var
|
106
|
+
@pages = @last = [(@count.to_f / @items).ceil, 1].max
|
107
|
+
end
|
108
|
+
|
109
|
+
# Setup and validates the params
|
110
|
+
def setup_params_var
|
111
|
+
raise VariableError.new(self, :params, 'must be a Hash or a Proc', @params) \
|
112
|
+
unless (@params = @vars[:params]).is_a?(Hash) || @params.is_a?(Proc)
|
113
|
+
end
|
48
114
|
end
|
49
115
|
|
50
116
|
require 'pagy/backend'
|
@@ -5,7 +5,7 @@
|
|
5
5
|
The link variable is set to a proc that returns the link tag.
|
6
6
|
Usage: link.call( page_number [, text [, extra_attributes_string ]])
|
7
7
|
-%>
|
8
|
-
<% link = pagy_link_proc(pagy, 'class="page-link"') -%>
|
8
|
+
<% link = pagy_link_proc(pagy, link_extra: 'class="page-link"') -%>
|
9
9
|
<%# -%><nav aria-label="pager" class="pagy-bootstrap-nav" role="navigation">
|
10
10
|
<%# -%> <ul class="pagination">
|
11
11
|
<% if pagy.prev -%> <li class="page-item prev"><%== link.call(pagy.prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"') %></li>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
-# The link variable is set to a proc that returns the link tag.
|
5
5
|
-# Usage: link.call( page_number [, text [, extra_attributes_string ]])
|
6
6
|
|
7
|
-
- link = pagy_link_proc(pagy, 'class="page-link"')
|
7
|
+
- link = pagy_link_proc(pagy, link_extra: 'class="page-link"')
|
8
8
|
|
9
9
|
%nav.pagy-bootstrap-nav{"aria-label" => "pager", :role => "navigation"}
|
10
10
|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
/ The link variable is set to a proc that returns the link tag.
|
5
5
|
/ Usage: link.call( page_number [, text [, extra_attributes_string ]])
|
6
6
|
|
7
|
-
- link = pagy_link_proc(pagy, 'class="page-link"')
|
7
|
+
- link = pagy_link_proc(pagy, link_extra: 'class="page-link"')
|
8
8
|
|
9
9
|
nav.pagy-bootstrap-nav role="navigation" aria-label="pager"
|
10
10
|
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<% end -%>
|
14
14
|
<% pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36] -%>
|
15
15
|
<% if item.is_a?(Integer) -%> <li><%== link.call(item) %></li>
|
16
|
-
<% elsif item.is_a?(String) -%> <li class="current"><%= item %></li>
|
16
|
+
<% elsif item.is_a?(String) -%> <li class="current"><%= pagy.label_for(item) %></li>
|
17
17
|
<% elsif item == :gap -%> <li class="ellipsis gap" aria-hidden="true"></li>
|
18
18
|
<% end -%>
|
19
19
|
<% end -%>
|
@@ -5,11 +5,11 @@
|
|
5
5
|
<% end -%>
|
6
6
|
<% pagy.series.each do |item| -%>
|
7
7
|
<% if item.is_a?(Integer) -%> <li><%== link.call(item) %></li>
|
8
|
-
<% elsif item.is_a?(String) -%> <li class="uk-active"><span><%== item %></span></li>
|
8
|
+
<% elsif item.is_a?(String) -%> <li class="uk-active"><span><%== pagy.label_for(item) %></span></li>
|
9
9
|
<% elsif item == :gap -%> <li class="uk-disabled"><span><%== pagy_t('pagy.nav.gap') %></span></li>
|
10
10
|
<% end -%>
|
11
11
|
<% end -%>
|
12
|
-
<% if pagy.next -%> <li><%== link.call(
|
12
|
+
<% if pagy.next -%> <li><%== link.call(pagy.next, "<span uk-pagination-next>#{pagy_t('pagy.nav.next')}</span>") %></li>
|
13
13
|
<% else -%> <li class="uk-disabled"><a href="#"><span uk-pagination-next><%== pagy_t('pagy.nav.next') %></span></a></li>
|
14
14
|
<% end -%>
|
15
15
|
<%# -%> </ul>
|
@@ -14,14 +14,14 @@ ul.uk-pagination.uk-flex-center
|
|
14
14
|
|
15
15
|
- elsif item.is_a?(String)
|
16
16
|
li.uk-active
|
17
|
-
span== item
|
17
|
+
span== pagy.label_for(item)
|
18
18
|
|
19
19
|
- elsif item == :gap
|
20
20
|
li.uk-disabled
|
21
21
|
span== pagy_t('pagy.nav.gap')
|
22
22
|
|
23
23
|
- if pagy.next
|
24
|
-
li== link.call(
|
24
|
+
li== link.call(pagy.next, "<span uk-pagination-next>#{pagy_t('pagy.nav.next')}</span>")
|
25
25
|
- else
|
26
26
|
li.uk-disabled
|
27
27
|
a href="#"
|