pagy 8.4.0 → 9.0.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.
- checksums.yaml +4 -4
- data/apps/calendar.ru +682 -2137
- data/apps/demo.ru +17 -13
- data/apps/keyset_ar.ru +236 -0
- data/apps/keyset_s.ru +238 -0
- data/apps/rails.ru +19 -15
- data/apps/repro.ru +17 -14
- data/apps/tmp/calendar.sqlite3 +0 -0
- data/apps/tmp/calendar.sqlite3-shm +0 -0
- data/apps/tmp/calendar.sqlite3-wal +0 -0
- data/apps/tmp/local_secret.txt +1 -0
- data/apps/tmp/pagy-keyset-ar.sqlite3 +0 -0
- data/apps/tmp/pagy-keyset-ar.sqlite3-shm +0 -0
- data/apps/tmp/pagy-keyset-ar.sqlite3-wal +0 -0
- data/apps/tmp/pagy-keyset-s.sqlite3 +0 -0
- data/bin/pagy +17 -12
- data/config/pagy.rb +32 -33
- data/javascripts/pagy-module.js +94 -107
- data/javascripts/pagy.js +4 -1
- data/javascripts/pagy.min.js +4 -0
- data/javascripts/pagy.min.js.map +10 -0
- data/javascripts/pagy.mjs +100 -0
- data/lib/optimist.rb +1 -1
- data/lib/pagy/b64.rb +33 -0
- data/lib/pagy/backend.rb +21 -17
- data/lib/pagy/calendar/day.rb +4 -3
- data/lib/pagy/calendar/month.rb +4 -3
- data/lib/pagy/calendar/quarter.rb +4 -3
- data/lib/pagy/calendar/unit.rb +103 -0
- data/lib/pagy/calendar/week.rb +3 -3
- data/lib/pagy/calendar/year.rb +4 -3
- data/lib/pagy/calendar.rb +54 -97
- data/lib/pagy/countless.rb +15 -16
- data/lib/pagy/extras/arel.rb +8 -10
- data/lib/pagy/extras/array.rb +4 -6
- data/lib/pagy/extras/bootstrap.rb +5 -5
- data/lib/pagy/extras/bulma.rb +10 -7
- data/lib/pagy/extras/calendar.rb +34 -5
- data/lib/pagy/extras/countless.rb +6 -13
- data/lib/pagy/extras/elasticsearch_rails.rb +15 -15
- data/lib/pagy/extras/gearbox.rb +26 -26
- data/lib/pagy/extras/headers.rb +25 -24
- data/lib/pagy/extras/i18n.rb +1 -1
- data/lib/pagy/extras/js_tools.rb +9 -9
- data/lib/pagy/extras/jsonapi.rb +26 -16
- data/lib/pagy/extras/keyset.rb +26 -0
- data/lib/pagy/extras/limit.rb +63 -0
- data/lib/pagy/extras/meilisearch.rb +11 -11
- data/lib/pagy/extras/metadata.rb +6 -2
- data/lib/pagy/extras/overflow.rb +9 -8
- data/lib/pagy/extras/pagy.rb +16 -16
- data/lib/pagy/extras/searchkick.rb +11 -11
- data/lib/pagy/extras/size.rb +40 -0
- data/lib/pagy/extras/standalone.rb +6 -6
- data/lib/pagy/extras/trim.rb +3 -3
- data/lib/pagy/frontend.rb +38 -36
- data/lib/pagy/i18n.rb +1 -1
- data/lib/pagy/keyset/active_record.rb +38 -0
- data/lib/pagy/keyset/sequel.rb +51 -0
- data/lib/pagy/keyset.rb +99 -0
- data/lib/pagy/url_helpers.rb +5 -5
- data/lib/pagy.rb +92 -94
- data/locales/ar.yml +9 -10
- data/locales/be.yml +2 -2
- data/locales/bg.yml +2 -2
- data/locales/bs.yml +2 -2
- data/locales/ca.yml +5 -7
- data/locales/ckb.yml +2 -2
- data/locales/cs.yml +2 -2
- data/locales/da.yml +2 -2
- data/locales/de.yml +2 -2
- data/locales/en.yml +2 -2
- data/locales/es.yml +2 -2
- data/locales/fr.yml +2 -2
- data/locales/hr.yml +2 -2
- data/locales/id.yml +2 -2
- data/locales/it.yml +2 -2
- data/locales/ja.yml +2 -2
- data/locales/km.yml +2 -2
- data/locales/ko.yml +2 -2
- data/locales/nb.yml +2 -2
- data/locales/nl.yml +2 -2
- data/locales/nn.yml +2 -2
- data/locales/pl.yml +2 -2
- data/locales/pt-BR.yml +2 -2
- data/locales/pt.yml +2 -2
- data/locales/ru.yml +2 -2
- data/locales/sr.yml +2 -2
- data/locales/sv-SE.yml +2 -2
- data/locales/sv.yml +2 -2
- data/locales/sw.yml +2 -2
- data/locales/ta.yml +2 -2
- data/locales/tr.yml +2 -2
- data/locales/uk.yml +2 -2
- data/locales/vi.yml +2 -2
- data/locales/zh-CN.yml +2 -2
- data/locales/zh-HK.yml +2 -2
- data/locales/zh-TW.yml +2 -2
- data/pkg/pagy-9.0.0.gem +0 -0
- metadata +27 -17
- data/javascripts/pagy-dev.js +0 -114
- data/lib/pagy/calendar/helper.rb +0 -65
- data/lib/pagy/extras/foundation.rb +0 -95
- data/lib/pagy/extras/items.rb +0 -64
- data/lib/pagy/extras/materialize.rb +0 -100
- data/lib/pagy/extras/semantic.rb +0 -94
- data/lib/pagy/extras/uikit.rb +0 -98
- /data/javascripts/{pagy-module.d.ts → pagy.d.ts} +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/limit
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'js_tools'
|
|
5
|
+
|
|
6
|
+
class Pagy # :nodoc:
|
|
7
|
+
DEFAULT[:limit_param] = :limit
|
|
8
|
+
DEFAULT[:limit_max] = 100
|
|
9
|
+
DEFAULT[:limit_extra] = true # extra enabled by default
|
|
10
|
+
|
|
11
|
+
# Allow the client to request a custom limit per page with an optional selector UI
|
|
12
|
+
module LimitExtra
|
|
13
|
+
# Additions for the Backend module
|
|
14
|
+
module BackendAddOn
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Set the limit variable considering the params and other pagy variables
|
|
18
|
+
def pagy_get_limit(vars)
|
|
19
|
+
return unless vars.key?(:limit_extra) ? vars[:limit_extra] : DEFAULT[:limit_extra] # :limit_extra is false
|
|
20
|
+
return unless (limit_count = pagy_get_limit_param(vars)) # no limit from request params
|
|
21
|
+
|
|
22
|
+
vars[:limit] = [limit_count.to_i, vars.key?(:limit_max) ? vars[:limit_max] : DEFAULT[:limit_max]].compact.min
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get the limit count from the params
|
|
26
|
+
# Overridable by the jsonapi extra
|
|
27
|
+
def pagy_get_limit_param(vars)
|
|
28
|
+
params[vars[:limit_param] || DEFAULT[:limit_param]]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
Backend.prepend LimitExtra::BackendAddOn
|
|
32
|
+
|
|
33
|
+
# Additions for the Frontend module
|
|
34
|
+
module FrontendAddOn
|
|
35
|
+
LIMIT_TOKEN = '__pagy_limit__'
|
|
36
|
+
|
|
37
|
+
# Return the limit selector HTML. For example "Show [20] items per page"
|
|
38
|
+
def pagy_limit_selector_js(pagy, id: nil, item_name: nil)
|
|
39
|
+
return '' unless pagy.vars[:limit_extra]
|
|
40
|
+
|
|
41
|
+
id = %( id="#{id}") if id
|
|
42
|
+
vars = pagy.vars
|
|
43
|
+
limit = vars[:limit]
|
|
44
|
+
vars[:limit] = LIMIT_TOKEN
|
|
45
|
+
url_token = pagy_url_for(pagy, PAGE_TOKEN)
|
|
46
|
+
vars[:limit] = limit # restore the limit
|
|
47
|
+
|
|
48
|
+
limit_input = %(<input name="limit" type="number" min="1" max="#{vars[:limit_max]}" value="#{
|
|
49
|
+
limit}" style="padding: 0; text-align: center; width: #{limit.to_s.length + 1}rem;">#{JSTools::A_TAG})
|
|
50
|
+
|
|
51
|
+
%(<span#{id} class="pagy limit-selector-js" #{
|
|
52
|
+
pagy_data(pagy, :selector, pagy.from, url_token)
|
|
53
|
+
}><label>#{
|
|
54
|
+
pagy_t('pagy.limit_selector_js',
|
|
55
|
+
item_name: item_name || pagy_t('pagy.item_name', count: limit),
|
|
56
|
+
limit_input:,
|
|
57
|
+
count: limit)
|
|
58
|
+
}</label></span>)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
Frontend.prepend LimitExtra::FrontendAddOn
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -20,12 +20,12 @@ class Pagy # :nodoc:
|
|
|
20
20
|
# Extension for the Pagy class
|
|
21
21
|
module PagyExtension
|
|
22
22
|
# Create a Pagy object from a Meilisearch results
|
|
23
|
-
def new_from_meilisearch(results, vars
|
|
24
|
-
vars[:
|
|
23
|
+
def new_from_meilisearch(results, **vars)
|
|
24
|
+
vars[:limit] = results.raw_answer['hitsPerPage']
|
|
25
25
|
vars[:page] = results.raw_answer['page']
|
|
26
26
|
vars[:count] = results.raw_answer['totalHits']
|
|
27
27
|
|
|
28
|
-
new(vars)
|
|
28
|
+
new(**vars)
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
Pagy.extend PagyExtension
|
|
@@ -35,17 +35,17 @@ class Pagy # :nodoc:
|
|
|
35
35
|
private
|
|
36
36
|
|
|
37
37
|
# Return Pagy object and results
|
|
38
|
-
def pagy_meilisearch(pagy_search_args, vars
|
|
38
|
+
def pagy_meilisearch(pagy_search_args, **vars)
|
|
39
39
|
model, term, options = pagy_search_args
|
|
40
40
|
vars = pagy_meilisearch_get_vars(nil, vars)
|
|
41
|
-
options[:hits_per_page] = vars[:
|
|
41
|
+
options[:hits_per_page] = vars[:limit]
|
|
42
42
|
options[:page] = vars[:page]
|
|
43
43
|
results = model.send(:ms_search, term, options)
|
|
44
44
|
vars[:count] = results.raw_answer['totalHits']
|
|
45
45
|
|
|
46
|
-
pagy = ::Pagy.new(vars)
|
|
46
|
+
pagy = ::Pagy.new(**vars)
|
|
47
47
|
# with :last_page overflow we need to re-run the method in order to get the hits
|
|
48
|
-
return pagy_meilisearch(pagy_search_args, vars
|
|
48
|
+
return pagy_meilisearch(pagy_search_args, **vars, page: pagy.page) \
|
|
49
49
|
if defined?(::Pagy::OverflowExtra) && pagy.overflow? && pagy.vars[:overflow] == :last_page
|
|
50
50
|
|
|
51
51
|
[pagy, results]
|
|
@@ -54,10 +54,10 @@ class Pagy # :nodoc:
|
|
|
54
54
|
# Sub-method called only by #pagy_meilisearch: here for easy customization of variables by overriding.
|
|
55
55
|
# The _collection argument is not available when the method is called.
|
|
56
56
|
def pagy_meilisearch_get_vars(_collection, vars)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
vars.tap do |v|
|
|
58
|
+
v[:page] ||= pagy_get_page(v)
|
|
59
|
+
v[:limit] ||= pagy_get_limit(v) || DEFAULT[:limit]
|
|
60
|
+
end
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
Backend.prepend BackendAddOn
|
data/lib/pagy/extras/metadata.rb
CHANGED
|
@@ -5,7 +5,7 @@ require_relative '../url_helpers'
|
|
|
5
5
|
|
|
6
6
|
class Pagy # :nodoc:
|
|
7
7
|
DEFAULT[:metadata] = %i[ scaffold_url first_url prev_url page_url next_url last_url
|
|
8
|
-
count page
|
|
8
|
+
count page limit vars pages last in from to prev next series ]
|
|
9
9
|
|
|
10
10
|
# Add a specialized backend method for pagination metadata
|
|
11
11
|
module MetadataExtra
|
|
@@ -17,7 +17,11 @@ class Pagy # :nodoc:
|
|
|
17
17
|
def pagy_metadata(pagy, absolute: nil)
|
|
18
18
|
scaffold_url = pagy_url_for(pagy, PAGE_TOKEN, absolute:)
|
|
19
19
|
{}.tap do |metadata|
|
|
20
|
-
keys = defined?(Calendar) && pagy.is_a?(Calendar)
|
|
20
|
+
keys = if defined?(Calendar::Unit) && pagy.is_a?(Calendar::Unit)
|
|
21
|
+
pagy.vars[:metadata] - %i[count limit]
|
|
22
|
+
else
|
|
23
|
+
pagy.vars[:metadata]
|
|
24
|
+
end
|
|
21
25
|
keys.each do |key|
|
|
22
26
|
metadata[key] = case key
|
|
23
27
|
when :scaffold_url then scaffold_url
|
data/lib/pagy/extras/overflow.rb
CHANGED
|
@@ -14,7 +14,7 @@ class Pagy # :nodoc:
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# Add rescue clause for different behaviors
|
|
17
|
-
def initialize(vars)
|
|
17
|
+
def initialize(**vars)
|
|
18
18
|
@overflow ||= false # still true if :last_page re-run the method after an overflow
|
|
19
19
|
super
|
|
20
20
|
rescue OverflowError
|
|
@@ -24,11 +24,12 @@ class Pagy # :nodoc:
|
|
|
24
24
|
raise # same as without the extra
|
|
25
25
|
when :last_page
|
|
26
26
|
requested_page = @vars[:page] # save the requested page (even after re-run)
|
|
27
|
-
initialize
|
|
27
|
+
initialize(**vars, page: @last) # re-run with the last page
|
|
28
28
|
@vars[:page] = requested_page # restore the requested page
|
|
29
29
|
when :empty_page
|
|
30
|
-
@offset = @
|
|
31
|
-
if defined?(Calendar)
|
|
30
|
+
@offset = @limit = @in = @from = @to = 0 # vars relative to the actual page
|
|
31
|
+
if defined?(Calendar::Unit) \
|
|
32
|
+
&& is_a?(Calendar::Unit) # only for Calendar::Units instances
|
|
32
33
|
edge = @order == :asc ? @final : @initial # get the edge of the overflow side (neat, but any time would do)
|
|
33
34
|
@from = @to = edge # set both to the edge utc time (a >=&&< query will get no records)
|
|
34
35
|
end
|
|
@@ -51,12 +52,12 @@ class Pagy # :nodoc:
|
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
54
|
Pagy.prepend PagyOverride
|
|
54
|
-
Pagy::Calendar.prepend PagyOverride if defined?(Calendar)
|
|
55
|
+
Pagy::Calendar::Unit.prepend PagyOverride if defined?(Calendar::Unit)
|
|
55
56
|
|
|
56
57
|
# Support for Pagy::Countless class
|
|
57
58
|
module CountlessOverride
|
|
58
59
|
# Add rescue clause for different behaviors
|
|
59
|
-
def finalize(
|
|
60
|
+
def finalize(fetched_size)
|
|
60
61
|
@overflow = false
|
|
61
62
|
super
|
|
62
63
|
rescue OverflowError
|
|
@@ -65,8 +66,8 @@ class Pagy # :nodoc:
|
|
|
65
66
|
when :exception
|
|
66
67
|
raise # same as without the extra
|
|
67
68
|
when :empty_page
|
|
68
|
-
@offset = @
|
|
69
|
-
@vars[:size] =
|
|
69
|
+
@offset = @limit = @from = @to = 0 # vars relative to the actual page
|
|
70
|
+
@vars[:size] = 0 # no page in the series
|
|
70
71
|
self
|
|
71
72
|
else
|
|
72
73
|
raise VariableError.new(self, :overflow, 'to be in [:empty_page, :exception]', @vars[:overflow])
|
data/lib/pagy/extras/pagy.rb
CHANGED
|
@@ -12,7 +12,7 @@ class Pagy # :nodoc:
|
|
|
12
12
|
def pagy_nav_js(pagy, id: nil, aria_label: nil, **vars)
|
|
13
13
|
sequels = pagy.sequels(**vars)
|
|
14
14
|
id = %( id="#{id}") if id
|
|
15
|
-
a = pagy_anchor(pagy)
|
|
15
|
+
a = pagy_anchor(pagy, **vars)
|
|
16
16
|
tokens = { 'before' => prev_a(pagy, a),
|
|
17
17
|
'a' => a.(PAGE_TOKEN, LABEL_TOKEN),
|
|
18
18
|
'current' => %(<a class="current" role="link" aria-current="page" aria-disabled="true">#{
|
|
@@ -27,9 +27,9 @@ class Pagy # :nodoc:
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Javascript combo pagination: it returns a nav with a data-pagy attribute used by the pagy.js file
|
|
30
|
-
def pagy_combo_nav_js(pagy, id: nil, aria_label: nil)
|
|
30
|
+
def pagy_combo_nav_js(pagy, id: nil, aria_label: nil, **vars)
|
|
31
31
|
id = %( id="#{id}") if id
|
|
32
|
-
a = pagy_anchor(pagy)
|
|
32
|
+
a = pagy_anchor(pagy, **vars)
|
|
33
33
|
pages = pagy.pages
|
|
34
34
|
|
|
35
35
|
page_input = %(<input name="page" type="number" min="1" max="#{pages}" value="#{pagy.page}" aria-current="page" ) <<
|
|
@@ -37,7 +37,7 @@ class Pagy # :nodoc:
|
|
|
37
37
|
|
|
38
38
|
%(<nav#{id} class="pagy combo-nav-js" #{
|
|
39
39
|
nav_aria_label(pagy, aria_label:)} #{
|
|
40
|
-
pagy_data(pagy, :combo, pagy_url_for(pagy, PAGE_TOKEN))}>#{
|
|
40
|
+
pagy_data(pagy, :combo, pagy_url_for(pagy, PAGE_TOKEN, **vars))}>#{
|
|
41
41
|
prev_a(pagy, a)
|
|
42
42
|
}<label>#{
|
|
43
43
|
pagy_t('pagy.combo_nav_js', page_input:, pages:)
|
|
@@ -47,35 +47,35 @@ class Pagy # :nodoc:
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# Return the previous page URL string or nil
|
|
50
|
-
def pagy_prev_url(pagy,
|
|
51
|
-
pagy_url_for(pagy, pagy.prev,
|
|
50
|
+
def pagy_prev_url(pagy, **vars)
|
|
51
|
+
pagy_url_for(pagy, pagy.prev, **vars) if pagy.prev
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Return the next page URL string or nil
|
|
55
|
-
def pagy_next_url(pagy,
|
|
56
|
-
pagy_url_for(pagy, pagy.next,
|
|
55
|
+
def pagy_next_url(pagy, **vars)
|
|
56
|
+
pagy_url_for(pagy, pagy.next, **vars) if pagy.next
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# Return the enabled/disabled previous page anchor tag
|
|
60
|
-
def pagy_prev_a(pagy, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'))
|
|
61
|
-
a = pagy_anchor(pagy)
|
|
60
|
+
def pagy_prev_a(pagy, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'), **vars)
|
|
61
|
+
a = pagy_anchor(pagy, **vars)
|
|
62
62
|
prev_a(pagy, a, text:, aria_label:)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# Return the enabled/disabled next page anchor tag
|
|
66
|
-
def pagy_next_a(pagy, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.
|
|
67
|
-
a = pagy_anchor(pagy)
|
|
66
|
+
def pagy_next_a(pagy, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.next'), **vars)
|
|
67
|
+
a = pagy_anchor(pagy, **vars)
|
|
68
68
|
next_a(pagy, a, text:, aria_label:)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
# Conditionally return the previous page link tag
|
|
72
|
-
def pagy_prev_link(pagy,
|
|
73
|
-
%(<link href="#{pagy_url_for(pagy, pagy.prev,
|
|
72
|
+
def pagy_prev_link(pagy, **vars)
|
|
73
|
+
%(<link href="#{pagy_url_for(pagy, pagy.prev, **vars)}"/>) if pagy.prev
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Conditionally return the next page link tag
|
|
77
|
-
def pagy_next_link(pagy,
|
|
78
|
-
%(<link href="#{pagy_url_for(pagy, pagy.next,
|
|
77
|
+
def pagy_next_link(pagy, **vars)
|
|
78
|
+
%(<link href="#{pagy_url_for(pagy, pagy.next, **vars)}"/>) if pagy.next
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
Frontend.prepend PagyExtra
|
|
@@ -23,11 +23,11 @@ class Pagy # :nodoc:
|
|
|
23
23
|
# Additions for the Pagy class
|
|
24
24
|
module PagyExtension
|
|
25
25
|
# Create a Pagy object from a Searchkick::Results object
|
|
26
|
-
def new_from_searchkick(results, vars
|
|
27
|
-
vars[:
|
|
26
|
+
def new_from_searchkick(results, **vars)
|
|
27
|
+
vars[:limit] = results.options[:per_page]
|
|
28
28
|
vars[:page] = results.options[:page]
|
|
29
29
|
vars[:count] = results.total_count
|
|
30
|
-
new(vars)
|
|
30
|
+
new(**vars)
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
Pagy.extend PagyExtension
|
|
@@ -37,17 +37,17 @@ class Pagy # :nodoc:
|
|
|
37
37
|
private
|
|
38
38
|
|
|
39
39
|
# Return Pagy object and results
|
|
40
|
-
def pagy_searchkick(pagy_search_args, vars
|
|
40
|
+
def pagy_searchkick(pagy_search_args, **vars)
|
|
41
41
|
model, term, options, block, *called = pagy_search_args
|
|
42
42
|
vars = pagy_searchkick_get_vars(nil, vars)
|
|
43
|
-
options[:per_page] = vars[:
|
|
43
|
+
options[:per_page] = vars[:limit]
|
|
44
44
|
options[:page] = vars[:page]
|
|
45
45
|
results = model.send(DEFAULT[:searchkick_search], term, **options, &block)
|
|
46
46
|
vars[:count] = results.total_count
|
|
47
47
|
|
|
48
|
-
pagy = ::Pagy.new(vars)
|
|
48
|
+
pagy = ::Pagy.new(**vars)
|
|
49
49
|
# with :last_page overflow we need to re-run the method in order to get the hits
|
|
50
|
-
return pagy_searchkick(pagy_search_args, vars
|
|
50
|
+
return pagy_searchkick(pagy_search_args, **vars, page: pagy.page) \
|
|
51
51
|
if defined?(::Pagy::OverflowExtra) && pagy.overflow? && pagy.vars[:overflow] == :last_page
|
|
52
52
|
|
|
53
53
|
[pagy, called.empty? ? results : results.send(*called)]
|
|
@@ -56,10 +56,10 @@ class Pagy # :nodoc:
|
|
|
56
56
|
# Sub-method called only by #pagy_searchkick: here for easy customization of variables by overriding
|
|
57
57
|
# the _collection argument is not available when the method is called
|
|
58
58
|
def pagy_searchkick_get_vars(_collection, vars)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
vars.tap do |v|
|
|
60
|
+
v[:page] ||= pagy_get_page(v)
|
|
61
|
+
v[:limit] ||= pagy_get_limit(v) || DEFAULT[:limit]
|
|
62
|
+
end
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
Backend.prepend BackendAddOn
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/size
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
class Pagy # :nodoc:
|
|
5
|
+
# Implement the legacy bar using the array size.
|
|
6
|
+
# Unless you have very specific requirements, use the faster and better looking default bar.
|
|
7
|
+
module SizeExtra
|
|
8
|
+
# Implements the old series algorithm
|
|
9
|
+
def series(size: @vars[:size], **_)
|
|
10
|
+
return super unless size.is_a?(Array)
|
|
11
|
+
return [] if size == []
|
|
12
|
+
raise VariableError.new(self, :size, 'to be an Array of 4 Integers or []', size) \
|
|
13
|
+
unless size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier
|
|
14
|
+
|
|
15
|
+
[].tap do |series|
|
|
16
|
+
# This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3)
|
|
17
|
+
# However the behavior of the legacy nav bar was taken straight from WillPaginate and Kaminari:
|
|
18
|
+
# it's ill-concieved and complicates the experience of devs and users.
|
|
19
|
+
left_gap_start = 1 + size[0]
|
|
20
|
+
left_gap_end = @page - size[1] - 1
|
|
21
|
+
right_gap_start = @page + size[2] + 1
|
|
22
|
+
right_gap_end = @last - size[3]
|
|
23
|
+
left_gap_end = right_gap_end if left_gap_end > right_gap_end
|
|
24
|
+
right_gap_start = left_gap_start if left_gap_start > right_gap_start
|
|
25
|
+
start = 1
|
|
26
|
+
if (left_gap_end - left_gap_start).positive?
|
|
27
|
+
series.push(*start...left_gap_start, :gap)
|
|
28
|
+
start = left_gap_end + 1
|
|
29
|
+
end
|
|
30
|
+
if (right_gap_end - right_gap_start).positive?
|
|
31
|
+
series.push(*start...right_gap_start, :gap)
|
|
32
|
+
start = right_gap_end + 1
|
|
33
|
+
end
|
|
34
|
+
series.push(*start..@last)
|
|
35
|
+
series[series.index(@page)] = @page.to_s
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
prepend SizeExtra
|
|
40
|
+
end
|
|
@@ -12,10 +12,6 @@ class Pagy # :nodoc:
|
|
|
12
12
|
module QueryUtils
|
|
13
13
|
module_function
|
|
14
14
|
|
|
15
|
-
def escape(str)
|
|
16
|
-
URI.encode_www_form_component(str)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
15
|
def build_nested_query(value, prefix = nil)
|
|
20
16
|
case value
|
|
21
17
|
when Array
|
|
@@ -32,13 +28,17 @@ class Pagy # :nodoc:
|
|
|
32
28
|
"#{escape(prefix)}=#{escape(value)}"
|
|
33
29
|
end
|
|
34
30
|
end
|
|
31
|
+
|
|
32
|
+
def escape(str)
|
|
33
|
+
URI.encode_www_form_component(str)
|
|
34
|
+
end
|
|
35
35
|
end
|
|
36
36
|
# :nocov:
|
|
37
37
|
|
|
38
38
|
# Return the URL for the page. If there is no pagy.vars[:url]
|
|
39
39
|
# it works exactly as the regular #pagy_url_for, relying on the params method and Rack.
|
|
40
40
|
# If there is a defined pagy.vars[:url] variable it does not need the params method nor Rack.
|
|
41
|
-
def pagy_url_for(pagy, page,
|
|
41
|
+
def pagy_url_for(pagy, page, fragment: nil, **_)
|
|
42
42
|
return super unless pagy.vars[:url]
|
|
43
43
|
|
|
44
44
|
vars = pagy.vars
|
|
@@ -46,7 +46,7 @@ class Pagy # :nodoc:
|
|
|
46
46
|
pagy_set_query_params(page, vars, params)
|
|
47
47
|
params = vars[:params].(params) if vars[:params].is_a?(Proc)
|
|
48
48
|
query_string = "?#{QueryUtils.build_nested_query(params)}"
|
|
49
|
-
"#{vars[:url]}#{query_string}#{
|
|
49
|
+
"#{vars[:url]}#{query_string}#{fragment}"
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
UrlHelpers.prepend StandaloneExtra
|
data/lib/pagy/extras/trim.rb
CHANGED
|
@@ -6,10 +6,10 @@ class Pagy # :nodoc:
|
|
|
6
6
|
|
|
7
7
|
# Remove the page=1 param from the first page link
|
|
8
8
|
module TrimExtra
|
|
9
|
-
# Override the original
|
|
9
|
+
# Override the original pagy_anchor.
|
|
10
10
|
# Call the pagy_trim method for page 1 if the trim_extra is enabled
|
|
11
|
-
def pagy_anchor(pagy)
|
|
12
|
-
a_proc = super
|
|
11
|
+
def pagy_anchor(pagy, **_)
|
|
12
|
+
a_proc = super
|
|
13
13
|
return a_proc unless pagy.vars[:trim_extra]
|
|
14
14
|
|
|
15
15
|
lambda do |page, text = pagy.label_for(page), **opts|
|
data/lib/pagy/frontend.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/docs/api/frontend
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require_relative 'url_helpers'
|
|
5
4
|
require_relative 'i18n'
|
|
5
|
+
require_relative 'url_helpers'
|
|
6
6
|
|
|
7
7
|
class Pagy
|
|
8
8
|
# Used for search and replace, hardcoded also in the pagy.js file
|
|
@@ -14,13 +14,44 @@ class Pagy
|
|
|
14
14
|
module Frontend
|
|
15
15
|
include UrlHelpers
|
|
16
16
|
|
|
17
|
+
# Return a performance optimized lambda to generate the HTML anchor element (a tag)
|
|
18
|
+
# Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
|
|
19
|
+
def pagy_anchor(pagy, anchor_string: nil, **vars)
|
|
20
|
+
anchor_string &&= %( #{anchor_string})
|
|
21
|
+
left, right = %(<a#{anchor_string} href="#{pagy_url_for(pagy, PAGE_TOKEN, **vars)}").split(PAGE_TOKEN, 2)
|
|
22
|
+
# lambda used by all the helpers
|
|
23
|
+
lambda do |page, text = pagy.label_for(page), classes: nil, aria_label: nil|
|
|
24
|
+
classes = %( class="#{classes}") if classes
|
|
25
|
+
aria_label = %( aria-label="#{aria_label}") if aria_label
|
|
26
|
+
%(#{left}#{page}#{right}#{classes}#{aria_label}>#{text}</a>)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
|
|
31
|
+
def pagy_info(pagy, id: nil, item_name: nil)
|
|
32
|
+
id = %( id="#{id}") if id
|
|
33
|
+
p_count = pagy.count
|
|
34
|
+
key = if p_count.zero?
|
|
35
|
+
'pagy.info.no_items'
|
|
36
|
+
elsif pagy.pages == 1
|
|
37
|
+
'pagy.info.single_page'
|
|
38
|
+
else
|
|
39
|
+
'pagy.info.multiple_pages'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
%(<span#{id} class="pagy info">#{
|
|
43
|
+
pagy_t key, item_name: item_name || pagy_t('pagy.item_name', count: p_count),
|
|
44
|
+
count: p_count, from: pagy.from, to: pagy.to
|
|
45
|
+
}</span>)
|
|
46
|
+
end
|
|
47
|
+
|
|
17
48
|
# Generic pagination: it returns the html with the series of links to the pages
|
|
18
|
-
def pagy_nav(pagy, id: nil, aria_label: nil, **vars)
|
|
49
|
+
def pagy_nav(pagy, id: nil, aria_label: nil, anchor_string: nil, **vars)
|
|
19
50
|
id = %( id="#{id}") if id
|
|
20
|
-
a = pagy_anchor(pagy)
|
|
51
|
+
a = pagy_anchor(pagy, anchor_string:)
|
|
21
52
|
|
|
22
53
|
html = %(<nav#{id} class="pagy nav" #{nav_aria_label(pagy, aria_label:)}>#{
|
|
23
|
-
|
|
54
|
+
prev_a(pagy, a)})
|
|
24
55
|
pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
|
25
56
|
html << case item
|
|
26
57
|
when Integer
|
|
@@ -36,39 +67,10 @@ class Pagy
|
|
|
36
67
|
html << %(#{next_a(pagy, a)}</nav>)
|
|
37
68
|
end
|
|
38
69
|
|
|
39
|
-
# Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
|
|
40
|
-
def pagy_info(pagy, id: nil, item_name: nil)
|
|
41
|
-
id = %( id="#{id}") if id
|
|
42
|
-
p_count = pagy.count
|
|
43
|
-
key = if p_count.zero? then 'pagy.info.no_items'
|
|
44
|
-
elsif pagy.pages == 1 then 'pagy.info.single_page'
|
|
45
|
-
else 'pagy.info.multiple_pages' # rubocop:disable Lint/ElseLayout
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
%(<span#{id} class="pagy info">#{
|
|
49
|
-
pagy_t key, item_name: item_name || pagy_t('pagy.item_name', count: p_count),
|
|
50
|
-
count: p_count, from: pagy.from, to: pagy.to
|
|
51
|
-
}</span>)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Return a performance optimized lambda to generate the HtML anchor element (a tag)
|
|
55
|
-
# Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
|
|
56
|
-
def pagy_anchor(pagy)
|
|
57
|
-
a_string = pagy.vars[:anchor_string]
|
|
58
|
-
a_string = %( #{a_string}) if a_string
|
|
59
|
-
left, right = %(<a#{a_string} href="#{pagy_url_for(pagy, PAGE_TOKEN)}").split(PAGE_TOKEN, 2)
|
|
60
|
-
# lambda used by all the helpers
|
|
61
|
-
lambda do |page, text = pagy.label_for(page), classes: nil, aria_label: nil|
|
|
62
|
-
classes = %( class="#{classes}") if classes
|
|
63
|
-
aria_label = %( aria-label="#{aria_label}") if aria_label
|
|
64
|
-
%(#{left}#{page}#{right}#{classes}#{aria_label}>#{text}</a>)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
70
|
# Similar to I18n.t: just ~18x faster using ~10x less memory
|
|
69
71
|
# (@pagy_locale explicitly initialized in order to avoid warning)
|
|
70
|
-
def pagy_t(key, opts
|
|
71
|
-
Pagy::I18n.translate(@pagy_locale ||= nil, key, opts)
|
|
72
|
+
def pagy_t(key, **opts)
|
|
73
|
+
Pagy::I18n.translate(@pagy_locale ||= nil, key, **opts)
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
private
|
|
@@ -90,7 +92,7 @@ class Pagy
|
|
|
90
92
|
if (p_next = pagy.next)
|
|
91
93
|
a.(p_next, text, aria_label:)
|
|
92
94
|
else
|
|
93
|
-
%(<a role="link" aria-disabled="true" aria-label
|
|
95
|
+
%(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
|
|
94
96
|
end
|
|
95
97
|
end
|
|
96
98
|
end
|
data/lib/pagy/i18n.rb
CHANGED
|
@@ -154,7 +154,7 @@ class Pagy
|
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
# Translate and pluralize the key with the locale DATA
|
|
157
|
-
def translate(locale, key, opts
|
|
157
|
+
def translate(locale, key, **opts)
|
|
158
158
|
data, pluralize = DATA[locale]
|
|
159
159
|
translation = data[key] || (opts[:count] && data[key += ".#{pluralize.call(opts[:count])}"]) \
|
|
160
160
|
or return %([translation missing: "#{key}"])
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/keyset
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
class Pagy
|
|
5
|
+
class Keyset
|
|
6
|
+
# Keyset adapter for ActiveRecord
|
|
7
|
+
class ActiveRecord < Keyset
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
# Get the keyset attributes of the record
|
|
11
|
+
def latest_from(latest_record) = latest_record.slice(*@keyset.keys)
|
|
12
|
+
|
|
13
|
+
# Extract the keyset from the set
|
|
14
|
+
def extract_keyset
|
|
15
|
+
@set.order_values.each_with_object({}) do |node, keyset|
|
|
16
|
+
keyset[node.value.name.to_sym] = node.direction
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Filter out the already retrieved records
|
|
21
|
+
def after_latest = @set.where(after_latest_query, **@latest)
|
|
22
|
+
|
|
23
|
+
# Append the missing keyset keys if the set is restricted by select
|
|
24
|
+
def apply_select
|
|
25
|
+
@set.select(*@keyset.keys)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Set with selected columns?
|
|
29
|
+
def select? = !@set.select_values.empty?
|
|
30
|
+
|
|
31
|
+
# Typecast the latest attributes
|
|
32
|
+
def typecast_latest(latest)
|
|
33
|
+
@set.model.new(latest).slice(latest.keys)
|
|
34
|
+
.to_hash.transform_keys(&:to_sym)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/keyset
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
class Pagy
|
|
5
|
+
class Keyset
|
|
6
|
+
# Keyset adapter for sequel
|
|
7
|
+
class Sequel < Keyset
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
# Get the keyset attributes of the latest record
|
|
11
|
+
def latest_from(latest_record) = latest_record.to_hash.slice(*@keyset.keys)
|
|
12
|
+
|
|
13
|
+
# Extract the keyset from the set
|
|
14
|
+
def extract_keyset
|
|
15
|
+
return {} unless @set.opts[:order]
|
|
16
|
+
|
|
17
|
+
@set.opts[:order].each_with_object({}) do |item, keyset|
|
|
18
|
+
case item
|
|
19
|
+
when Symbol
|
|
20
|
+
keyset[item] = :asc
|
|
21
|
+
when ::Sequel::SQL::OrderedExpression
|
|
22
|
+
keyset[item.expression] = item.descending ? :desc : :asc
|
|
23
|
+
else
|
|
24
|
+
raise TypeError, "#{item.class.inspect} is not a supported Sequel::SQL::OrderedExpression"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Filter out the already retrieved records
|
|
30
|
+
def after_latest = @set.where(::Sequel.lit(after_latest_query, **@latest))
|
|
31
|
+
|
|
32
|
+
# Append the missing keyset keys if the set is restricted by select
|
|
33
|
+
def apply_select
|
|
34
|
+
selected = @set.opts[:select]
|
|
35
|
+
@set.select_append(*@keyset.keys.reject { |c| selected.include?(c) })
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Set with selected columns?
|
|
39
|
+
def select? = !@set.opts[:select].nil?
|
|
40
|
+
|
|
41
|
+
# Typecast the latest attributes
|
|
42
|
+
def typecast_latest(latest)
|
|
43
|
+
model = @set.opts[:model]
|
|
44
|
+
model.unrestrict_primary_key if (restricted_pk = model.restrict_primary_key?)
|
|
45
|
+
latest = model.new(latest).to_hash.slice(*latest.keys.map(&:to_sym))
|
|
46
|
+
model.restrict_primary_key if restricted_pk
|
|
47
|
+
latest
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|