pagy 9.4.0 → 43.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/apps/calendar.ru +548 -552
- data/apps/demo.ru +224 -181
- data/apps/index.rb +3 -1
- data/apps/keynav+root_key.ru +321 -0
- data/apps/keynav.ru +260 -0
- data/apps/{keyset_ar.ru → keyset.ru} +26 -32
- data/apps/{keyset_s.ru → keyset_sequel.ru} +23 -29
- data/apps/rails.ru +51 -48
- data/apps/repro.ru +51 -47
- data/bin/pagy +20 -21
- data/config/pagy.rb +35 -207
- data/javascripts/ai_widget.js +90 -0
- data/javascripts/pagy.js +153 -0
- data/javascripts/pagy.js.map +10 -0
- data/javascripts/pagy.min.js +2 -4
- data/javascripts/pagy.mjs +113 -63
- data/javascripts/wand.js +1172 -0
- data/lib/pagy/classes/calendar/calendar.rb +96 -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 +93 -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 +25 -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 +79 -0
- data/lib/pagy/classes/keyset/keyset.rb +126 -0
- data/lib/pagy/classes/keyset/sequel.rb +11 -0
- data/lib/pagy/classes/offset/countish.rb +17 -0
- data/lib/pagy/classes/offset/countless.rb +61 -0
- data/lib/pagy/classes/offset/offset.rb +56 -0
- data/lib/pagy/classes/offset/search.rb +32 -0
- data/lib/pagy/classes/request.rb +36 -0
- data/lib/pagy/console.rb +3 -20
- data/lib/pagy/modules/abilities/configurable.rb +36 -0
- data/lib/pagy/modules/abilities/countable.rb +23 -0
- data/lib/pagy/modules/abilities/linkable.rb +60 -0
- data/lib/pagy/modules/abilities/rangeable.rb +16 -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 +24 -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 +17 -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 +27 -0
- data/lib/pagy/toolbox/helpers/headers_hash.rb +29 -0
- data/lib/pagy/toolbox/helpers/info_tag.rb +26 -0
- data/lib/pagy/toolbox/helpers/input_nav_js.rb +18 -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 +18 -0
- data/lib/pagy/toolbox/helpers/series_nav.rb +28 -0
- data/lib/pagy/toolbox/helpers/series_nav_js.rb +18 -0
- data/lib/pagy/toolbox/helpers/support/a_lambda.rb +35 -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 +37 -0
- data/lib/pagy/toolbox/helpers/urls_hash.rb +13 -0
- data/lib/pagy/toolbox/paginators/calendar.rb +29 -0
- data/lib/pagy/toolbox/paginators/countish.rb +37 -0
- data/lib/pagy/toolbox/paginators/countless.rb +20 -0
- data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +50 -0
- data/lib/pagy/toolbox/paginators/keynav_js.rb +26 -0
- data/lib/pagy/toolbox/paginators/keyset.rb +15 -0
- data/lib/pagy/toolbox/paginators/meilisearch.rb +31 -0
- data/lib/pagy/toolbox/paginators/method.rb +35 -0
- data/lib/pagy/toolbox/paginators/offset.rb +18 -0
- data/lib/pagy/toolbox/paginators/searchkick.rb +31 -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 +10 -9
- data/locales/it.yml +9 -6
- data/locales/ja.yml +10 -9
- data/locales/km.yml +10 -9
- 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/sk.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 +9 -6
- data/locales/tr.yml +17 -12
- 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 +118 -52
- 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/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 -167
- 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,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
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'nav_aria_label_attribute'
|
|
4
|
+
require_relative 'data_pagy_attribute'
|
|
5
|
+
require_relative 'a_lambda' # inherited use
|
|
6
|
+
|
|
7
|
+
# Relegate internal functions. Make overriding navs easier.
|
|
8
|
+
class Pagy
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Build the input_nav_js tag, with the specific inner html for the style
|
|
12
|
+
def wrap_input_nav_js(html, nav_classes, id: nil, aria_label: nil, **)
|
|
13
|
+
%(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{
|
|
14
|
+
nav_aria_label_attribute(aria_label:)} #{
|
|
15
|
+
data = [:inj, compose_page_url(PAGE_TOKEN, **)]
|
|
16
|
+
data.push(@update) if keynav?
|
|
17
|
+
data_pagy_attribute(*data)
|
|
18
|
+
}>#{html}</nav>)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'series'
|
|
4
|
+
require_relative 'nav_aria_label_attribute'
|
|
5
|
+
require_relative 'data_pagy_attribute'
|
|
6
|
+
require_relative 'a_lambda' # inherited use
|
|
7
|
+
|
|
8
|
+
# Relegate internal functions. Make overriding navs easier.
|
|
9
|
+
class Pagy
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# Build the nav tag, with the specific inner html for the style
|
|
13
|
+
def wrap_series_nav(html, nav_classes, id: nil, aria_label: nil, **)
|
|
14
|
+
data = %( #{data_pagy_attribute(:k, @update)}) if keynav?
|
|
15
|
+
%(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{nav_aria_label_attribute(aria_label:)}#{data}>#{html}</nav>)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'series'
|
|
4
|
+
require_relative 'nav_aria_label_attribute'
|
|
5
|
+
require_relative 'data_pagy_attribute'
|
|
6
|
+
require_relative 'a_lambda' # inherited use
|
|
7
|
+
|
|
8
|
+
# Relegate internal functions. Make overriding navs easier.
|
|
9
|
+
class Pagy
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# Return the reverse sorted array of widths, series, and labels generated from the :steps hash
|
|
13
|
+
# If :steps is false it will use the single {0 => @options[:slots]} length
|
|
14
|
+
def sequels(steps: @options[:steps] || { 0 => @options[:slots] || SERIES_SLOTS }, **)
|
|
15
|
+
raise OptionError.new(self, :steps, 'to define the 0 width', steps) unless steps.key?(0)
|
|
16
|
+
|
|
17
|
+
widths, series = steps.sort.reverse.map { |width, slots| [width, series(slots:)] }.transpose
|
|
18
|
+
[widths, series, page_labels(series)]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Support for the Calendar API
|
|
22
|
+
def page_labels(series)
|
|
23
|
+
series.map { |s| s.map { |item| item == :gap ? :gap : page_label(item) } } if calendar?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Build the nav_js tag, with the specific tokens for the style
|
|
27
|
+
def wrap_series_nav_js(tokens, nav_classes, id: nil, aria_label: nil, **)
|
|
28
|
+
sequels = sequels(**)
|
|
29
|
+
nav_classes = "pagy-rjs #{nav_classes}" if sequels[0].size > 1
|
|
30
|
+
%(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{
|
|
31
|
+
nav_aria_label_attribute(aria_label:)} #{
|
|
32
|
+
data = [:snj, tokens.values, sequels]
|
|
33
|
+
data.push(@update) if keynav?
|
|
34
|
+
data_pagy_attribute(*data)
|
|
35
|
+
}></nav>)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
# Generate the hash of the pagination links
|
|
5
|
+
def urls_hash(**)
|
|
6
|
+
template = compose_page_url(PAGE_TOKEN, **)
|
|
7
|
+
|
|
8
|
+
{ first: compose_page_url(nil, **),
|
|
9
|
+
previous: @previous && template.sub(PAGE_TOKEN, @previous.to_s),
|
|
10
|
+
next: @next && template.sub(PAGE_TOKEN, @next.to_s),
|
|
11
|
+
last: @count && template.sub(PAGE_TOKEN, @last.to_s) }.compact
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
module CalendarPaginator
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Take a collection and a configuration Hash and return an array with 3 items: [calendar, pagy, results]
|
|
8
|
+
def paginate(context, collection, config)
|
|
9
|
+
context.instance_eval do
|
|
10
|
+
allowed_options = Calendar::UNITS + %i[offset disabled request]
|
|
11
|
+
raise ArgumentError, "keys must be in #{allowed_options.inspect}" \
|
|
12
|
+
unless config.is_a?(Hash) && (config.keys - allowed_options).empty?
|
|
13
|
+
|
|
14
|
+
config[:offset] ||= {}
|
|
15
|
+
unless config[:disabled]
|
|
16
|
+
calendar, from, to =
|
|
17
|
+
Calendar.send(:init, config,
|
|
18
|
+
pagy_calendar_period(collection),
|
|
19
|
+
config[:request].params) do |unit, period|
|
|
20
|
+
pagy_calendar_counts(collection, unit, *period) if respond_to?(:pagy_calendar_counts)
|
|
21
|
+
end
|
|
22
|
+
collection = pagy_calendar_filter(collection, from, to)
|
|
23
|
+
end
|
|
24
|
+
pagy, records = pagy(:offset, collection, **config[:offset])
|
|
25
|
+
[calendar, pagy, records]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../modules/abilities/countable'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module CountishPaginator
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Return the Offset::Countish instance and records
|
|
10
|
+
def paginate(collection, options)
|
|
11
|
+
options[:page] ||= options[:request].resolve_page(force_integer: false)
|
|
12
|
+
if options[:page].is_a?(String)
|
|
13
|
+
page, count, epoch = options[:page].split.map(&:to_i)
|
|
14
|
+
options[:page] = page
|
|
15
|
+
end
|
|
16
|
+
setup_options(count, epoch, collection, options)
|
|
17
|
+
options[:limit] = options[:request].resolve_limit
|
|
18
|
+
pagy = Offset::Countish.new(**options)
|
|
19
|
+
[pagy, pagy.records(collection)]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Get the count from the page and set epoch when ttl (Time To Live) requires it
|
|
23
|
+
def setup_options(count, epoch, collection, options)
|
|
24
|
+
now = Time.now.to_i
|
|
25
|
+
if !options[:count] && count && (!options[:ttl] ||
|
|
26
|
+
(epoch && epoch <= now && now < (epoch + options[:ttl]))) # ongoing
|
|
27
|
+
# puts 'ongoing'
|
|
28
|
+
options[:count] = count
|
|
29
|
+
options[:epoch] = epoch if options[:ttl]
|
|
30
|
+
else # recount
|
|
31
|
+
# puts 'recount'
|
|
32
|
+
options[:count] ||= Countable.get_count(collection, options)
|
|
33
|
+
options[:epoch] = now if options[:ttl]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
module CountlessPaginator
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Return the Offset::Countless instance and records
|
|
8
|
+
def paginate(collection, options)
|
|
9
|
+
options[:page] ||= options[:request].resolve_page(force_integer: false) # accept nil and strings
|
|
10
|
+
if options[:page].is_a?(String)
|
|
11
|
+
page, last = options[:page].split.map(&:to_i) # decoded '+' added by the compose_page_url
|
|
12
|
+
options[:page] = page
|
|
13
|
+
options[:last] = last if last&.positive?
|
|
14
|
+
end
|
|
15
|
+
options[:limit] = options[:request].resolve_limit
|
|
16
|
+
pagy = Offset::Countless.new(**options)
|
|
17
|
+
[pagy, pagy.records(collection)]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../modules/searcher'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module ElasticsearchRailsPaginator
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Paginate from the search object
|
|
10
|
+
def paginate(search, options)
|
|
11
|
+
if search.is_a?(Search::Arguments)
|
|
12
|
+
# The search is the array of pagy_search arguments
|
|
13
|
+
Searcher.wrap(search, options) do
|
|
14
|
+
model, query_or_payload, search_options = search
|
|
15
|
+
search_options[:size] = options[:limit]
|
|
16
|
+
search_options[:from] = options[:limit] * ((options[:page] || 1) - 1)
|
|
17
|
+
response_object = model.send(options[:search_method] || ElasticsearchRails::DEFAULT[:search_method],
|
|
18
|
+
query_or_payload, **search_options)
|
|
19
|
+
options[:count] = total_count_from(response_object)
|
|
20
|
+
[ElasticsearchRails.new(**options), response_object]
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
from, size = pagination_params_from(search)
|
|
24
|
+
options[:limit] = size
|
|
25
|
+
options[:page] = ((from || 0) / options[:limit]) + 1
|
|
26
|
+
options[:count] = total_count_from(search)
|
|
27
|
+
ElasticsearchRails.new(**options)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get from and size params from the response object, supporting different versions of ElasticsearchRails
|
|
32
|
+
def pagination_params_from(response_object)
|
|
33
|
+
definition = response_object.search.definition
|
|
34
|
+
definition = definition.to_hash if definition.respond_to?(:to_hash)
|
|
35
|
+
container = (definition.is_a?(Hash) && (definition[:body] || definition)) || response_object.search.options
|
|
36
|
+
from = (container[:from] || container['from']).to_i
|
|
37
|
+
size = (container[:size] || container['size']).to_i
|
|
38
|
+
size = 10 if size.zero?
|
|
39
|
+
[from, size]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Get the count from the response object, supporting different versions of ElasticsearchRails
|
|
43
|
+
def total_count_from(response_object)
|
|
44
|
+
total = response_object.instance_eval do
|
|
45
|
+
respond_to?(:response) ? response['hits']['total'] : raw_response['hits']['total']
|
|
46
|
+
end
|
|
47
|
+
total.is_a?(Hash) ? total['value'] : total
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../../pagy/modules/b64'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module KeynavJsPaginator
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Return the Pagy::Keyset::Keynav instance and paginated records.
|
|
10
|
+
# Fall back to :countless if the :page has no client data.
|
|
11
|
+
def paginate(set, options)
|
|
12
|
+
page = options[:request].resolve_page(force_integer: false) # allow nil
|
|
13
|
+
if page&.match(' ') # countless page -> no augmentation -> fallback
|
|
14
|
+
return CountlessPaginator.paginate(set, page:, **options)
|
|
15
|
+
elsif page.is_a?(String) # keynav page param
|
|
16
|
+
page_arguments = JSON.parse(B64.urlsafe_decode(page))
|
|
17
|
+
# Restart the pagination from page 1/nil if the url has been requested from another browser
|
|
18
|
+
options[:page] = page_arguments if options[:request].cookie == page_arguments.shift
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
options[:limit] = options[:request].resolve_limit
|
|
22
|
+
pagy = Keyset::Keynav.new(set, **options)
|
|
23
|
+
[pagy, pagy.records]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
module KeysetPaginator
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Return Pagy::Keyset instance and paginated records
|
|
8
|
+
def paginate(set, options)
|
|
9
|
+
options[:page] ||= options[:request].resolve_page(force_integer: false) # allow nil
|
|
10
|
+
options[:limit] = options[:request].resolve_limit
|
|
11
|
+
pagy = Keyset.new(set, **options)
|
|
12
|
+
[pagy, pagy.records]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../modules/searcher'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module MeilisearchPaginator
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Paginate from the search object
|
|
10
|
+
def paginate(search, options)
|
|
11
|
+
if search.is_a?(Search::Arguments)
|
|
12
|
+
# The search is the array of pagy_search arguments
|
|
13
|
+
Searcher.wrap(search, options) do
|
|
14
|
+
model, term, search_options = search
|
|
15
|
+
search_options[:hits_per_page] = options[:limit]
|
|
16
|
+
search_options[:page] = options[:page]
|
|
17
|
+
results = model.send(options[:search_method] || Meilisearch::DEFAULT[:search_method],
|
|
18
|
+
term, search_options)
|
|
19
|
+
options[:count] = results.raw_answer['totalHits']
|
|
20
|
+
[Meilisearch.new(**options), results]
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
# The search is a meilisearch results object
|
|
24
|
+
options[:limit] = search.raw_answer['hitsPerPage']
|
|
25
|
+
options[:page] = search.raw_answer['page']
|
|
26
|
+
options[:count] = search.raw_answer['totalHits']
|
|
27
|
+
Meilisearch.new(**options)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../classes/request'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
paginators = { offset: :OffsetPaginator,
|
|
7
|
+
countless: :CountlessPaginator,
|
|
8
|
+
countish: :CountishPaginator,
|
|
9
|
+
keyset: :KeysetPaginator,
|
|
10
|
+
keynav_js: :KeynavJsPaginator,
|
|
11
|
+
calendar: :CalendarPaginator,
|
|
12
|
+
elasticsearch_rails: :ElasticsearchRailsPaginator,
|
|
13
|
+
meilisearch: :MeilisearchPaginator,
|
|
14
|
+
searchkick: :SearchkickPaginator }.freeze
|
|
15
|
+
|
|
16
|
+
path = Pathname.new(__dir__)
|
|
17
|
+
paginators.each { |symbol, name| autoload name, path.join(symbol.to_s) }
|
|
18
|
+
|
|
19
|
+
# Pagy::Method defines the #pagy method to be included in the app controller/view.
|
|
20
|
+
Method = Module.new do
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
define_method :pagy do |paginator = :offset, collection, **options|
|
|
24
|
+
arguments = if paginator == :calendar
|
|
25
|
+
[self, collection, options]
|
|
26
|
+
else
|
|
27
|
+
[collection, options = Pagy.options.merge(options)]
|
|
28
|
+
end
|
|
29
|
+
options[:root_key] = 'page' if options[:jsonapi] # enforce 'page' root_key for JSON:API
|
|
30
|
+
options[:request] ||= request # user set request or self.request
|
|
31
|
+
options[:request] = Request.new(options) # Pagy::Request
|
|
32
|
+
Pagy.const_get(paginators[paginator]).paginate(*arguments)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../modules/abilities/countable'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module OffsetPaginator
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Return the Pagy::Offset instance and results
|
|
10
|
+
def paginate(collection, options)
|
|
11
|
+
options[:page] ||= options[:request].resolve_page
|
|
12
|
+
options[:limit] = options[:request].resolve_limit
|
|
13
|
+
options[:count] ||= Countable.get_count(collection, options)
|
|
14
|
+
pagy = Offset.new(**options)
|
|
15
|
+
[pagy, collection.instance_of?(Array) ? collection[pagy.offset, pagy.limit] : pagy.records(collection)]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../modules/searcher'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module SearchkickPaginator
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Paginate from the search object
|
|
10
|
+
def paginate(search, options)
|
|
11
|
+
if search.is_a?(Search::Arguments)
|
|
12
|
+
# The search is the array of pagy_search arguments
|
|
13
|
+
Searcher.wrap(search, options) do
|
|
14
|
+
model, term, search_options, block = search
|
|
15
|
+
search_options[:per_page] = options[:limit]
|
|
16
|
+
search_options[:page] = options[:page]
|
|
17
|
+
results = model.send(options[:search_method] || Searchkick::DEFAULT[:search_method],
|
|
18
|
+
term || '*', **search_options, &block)
|
|
19
|
+
options[:count] = results.total_count
|
|
20
|
+
[Searchkick.new(**options), results]
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
# The search is a searchkick results object
|
|
24
|
+
options[:limit] = search.respond_to?(:options) ? search.options[:per_page] : search.per_page
|
|
25
|
+
options[:page] = search.respond_to?(:options) ? search.options[:page] : search.current_page
|
|
26
|
+
options[:count] = search.total_count
|
|
27
|
+
Searchkick.new(**options)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/pagy.rb
CHANGED
|
@@ -1,106 +1,68 @@
|
|
|
1
|
-
# See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/pagy
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require 'pathname'
|
|
5
|
-
require_relative 'pagy/
|
|
4
|
+
require_relative 'pagy/classes/exceptions'
|
|
5
|
+
require_relative 'pagy/modules/abilities/linkable'
|
|
6
|
+
require_relative 'pagy/modules/abilities/configurable'
|
|
7
|
+
require_relative 'pagy/toolbox/helpers/loader'
|
|
6
8
|
|
|
7
|
-
# Top superclass: it
|
|
9
|
+
# Top superclass: it defines only what's common to all the subclasses
|
|
8
10
|
class Pagy
|
|
9
|
-
VERSION
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Assign @prev and @next
|
|
56
|
-
def assign_prev_and_next
|
|
57
|
-
@prev = (@page - 1 unless @page == 1)
|
|
58
|
-
@next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Checks the @page <= @last
|
|
62
|
-
def check_overflow
|
|
63
|
-
raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
|
|
11
|
+
VERSION = '43.2.0'
|
|
12
|
+
ROOT = Pathname.new(__dir__).parent.freeze
|
|
13
|
+
DEFAULT = { limit: 20, limit_key: 'limit', page_key: 'page' }.freeze
|
|
14
|
+
PAGE_TOKEN = EscapedValue.new('P ')
|
|
15
|
+
LIMIT_TOKEN = EscapedValue.new('L ')
|
|
16
|
+
LABEL_TOKEN = 'L'
|
|
17
|
+
A_TAG = '<a style="display: none;">#</a>'
|
|
18
|
+
|
|
19
|
+
path = Pathname.new(__FILE__).sub_ext('')
|
|
20
|
+
autoload :Method, path.join('toolbox/paginators/method')
|
|
21
|
+
autoload :I18n, path.join('modules/i18n/i18n')
|
|
22
|
+
autoload :Console, path.join('modules/console')
|
|
23
|
+
autoload :Calendar, path.join('classes/calendar/calendar')
|
|
24
|
+
autoload :Offset, path.join('classes/offset/offset')
|
|
25
|
+
autoload :Search, path.join('classes/offset/search')
|
|
26
|
+
autoload :ElasticsearchRails, path.join('classes/offset/search')
|
|
27
|
+
autoload :Meilisearch, path.join('classes/offset/search')
|
|
28
|
+
autoload :Searchkick, path.join('classes/offset/search')
|
|
29
|
+
autoload :Keyset, path.join('classes/keyset/keyset')
|
|
30
|
+
|
|
31
|
+
def self.options = @options ||= {}
|
|
32
|
+
|
|
33
|
+
extend Configurable
|
|
34
|
+
include Linkable
|
|
35
|
+
include Loader
|
|
36
|
+
|
|
37
|
+
attr_reader :page, :next, :in, :limit, :options
|
|
38
|
+
|
|
39
|
+
protected
|
|
40
|
+
|
|
41
|
+
# Define the hierarchical identity methods, overridden by the respective classes
|
|
42
|
+
def offset? = false
|
|
43
|
+
def countless? = false
|
|
44
|
+
def calendar? = false
|
|
45
|
+
def search? = false
|
|
46
|
+
def keyset? = false
|
|
47
|
+
def keynav? = false
|
|
48
|
+
|
|
49
|
+
# Validates and assign the passed options: they must be present and value.to_i must be >= min
|
|
50
|
+
def assign_and_check(name_min)
|
|
51
|
+
name_min.each do |name, min|
|
|
52
|
+
raise OptionError.new(self, name, ">= #{min}", @options[name]) \
|
|
53
|
+
unless @options[name].respond_to?(:to_i) && instance_variable_set(:"@#{name}", @options[name].to_i) >= min
|
|
54
|
+
end
|
|
64
55
|
end
|
|
65
56
|
|
|
66
|
-
#
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return [] if size.zero?
|
|
77
|
-
|
|
78
|
-
[].tap do |series|
|
|
79
|
-
if size >= @last
|
|
80
|
-
series.push(*1..@last)
|
|
81
|
-
else
|
|
82
|
-
left = ((size - 1) / 2.0).floor # left half might be 1 page shorter for even size
|
|
83
|
-
start = if @page <= left # beginning pages
|
|
84
|
-
1
|
|
85
|
-
elsif @page > (@last - size + left) # end pages
|
|
86
|
-
@last - size + 1
|
|
87
|
-
else # intermediate pages
|
|
88
|
-
@page - left
|
|
89
|
-
end
|
|
90
|
-
series.push(*start...(start + size))
|
|
91
|
-
# Set first and last pages plus gaps when needed, respecting the size
|
|
92
|
-
if vars[:ends] && size >= 7
|
|
93
|
-
series[0] = 1
|
|
94
|
-
series[1] = :gap unless series[1] == 2
|
|
95
|
-
series[-2] = :gap unless series[-2] == @last - 1
|
|
96
|
-
series[-1] = @last
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
series[series.index(@page)] = @page.to_s
|
|
100
|
-
end
|
|
57
|
+
# Merge all the DEFAULT constants of the class hierarchy with the options
|
|
58
|
+
def assign_options(**options)
|
|
59
|
+
@request = options.delete(:request) # internal object
|
|
60
|
+
default = {}
|
|
61
|
+
current = self.class
|
|
62
|
+
begin
|
|
63
|
+
default = current::DEFAULT.merge(default)
|
|
64
|
+
current = current.superclass
|
|
65
|
+
end until current == Object # rubocop:disable Lint/Loop -- see https://github.com/rubocop/rubocop-performance/issues/362
|
|
66
|
+
@options = default.merge!(options.delete_if { |k, v| default.key?(k) && (v.nil? || v == '') }).freeze
|
|
101
67
|
end
|
|
102
68
|
end
|
|
103
|
-
|
|
104
|
-
require_relative 'pagy/backend'
|
|
105
|
-
require_relative 'pagy/frontend'
|
|
106
|
-
require_relative 'pagy/exceptions'
|
data/locales/ar.yml
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# See https://ddnexus.github.io/pagy/resources/i18n
|
|
2
|
+
|
|
2
3
|
ar:
|
|
3
4
|
pagy:
|
|
5
|
+
p11n: 'Arabic'
|
|
4
6
|
aria_label:
|
|
5
7
|
nav:
|
|
6
8
|
zero: "لا يوجد صفحات"
|
|
@@ -9,9 +11,9 @@ ar:
|
|
|
9
11
|
few: "صفحات"
|
|
10
12
|
many: "صفحات"
|
|
11
13
|
other: "صفحات"
|
|
12
|
-
|
|
14
|
+
previous: "السابق"
|
|
13
15
|
next: "التالي"
|
|
14
|
-
|
|
16
|
+
previous: "<"
|
|
15
17
|
next: ">"
|
|
16
18
|
gap: "…"
|
|
17
19
|
item_name:
|
|
@@ -21,9 +23,10 @@ ar:
|
|
|
21
23
|
few: "قليل"
|
|
22
24
|
many: "كثير"
|
|
23
25
|
other: "عناصر"
|
|
24
|
-
|
|
26
|
+
info_tag:
|
|
27
|
+
no_count: "الصفحة %{page} من %{pages}"
|
|
25
28
|
no_items: "لا يوجد %{item_name}"
|
|
26
29
|
single_page: "عرض %{count} %{item_name}"
|
|
27
30
|
multiple_pages: "عرض %{item_name} %{from}-%{to} من اجمالي %{count}"
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
input_nav_js: "الصفحة %{page_input} من %{pages}"
|
|
32
|
+
limit_tag_js: "عرض %{limit_input} %{item_name} لكل صفحة"
|