pagy 7.0.11 → 43.5.1
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/LICENSE.txt +1 -1
- data/apps/calendar.ru +741 -0
- data/apps/demo.ru +513 -0
- data/apps/enable_rails_page_segment.rb +54 -0
- data/apps/index.rb +9 -0
- data/apps/keynav+root_key.ru +316 -0
- data/apps/keynav.ru +255 -0
- data/apps/keyset.ru +219 -0
- data/apps/keyset_sequel.ru +212 -0
- data/apps/rails.ru +216 -0
- data/apps/repro.ru +185 -0
- data/bin/pagy +5 -0
- data/config/pagy.rb +46 -0
- data/javascripts/ai_widget.js +90 -0
- data/javascripts/pagy.js +168 -0
- data/javascripts/pagy.min.js +2 -0
- data/javascripts/pagy.mjs +161 -0
- data/javascripts/wand.js +1172 -0
- data/lib/pagy/classes/calendar/calendar.rb +101 -0
- data/lib/pagy/{calendar → classes/calendar}/day.rb +9 -12
- data/lib/pagy/{calendar → classes/calendar}/month.rb +7 -11
- data/lib/pagy/{calendar → classes/calendar}/quarter.rb +12 -16
- data/lib/pagy/classes/calendar/unit.rb +93 -0
- data/lib/pagy/{calendar → classes/calendar}/week.rb +7 -11
- data/lib/pagy/{calendar → classes/calendar}/year.rb +9 -9
- data/lib/pagy/classes/exceptions.rb +26 -0
- data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
- data/lib/pagy/classes/keyset/adapters/sequel.rb +62 -0
- data/lib/pagy/classes/keyset/keynav.rb +85 -0
- data/lib/pagy/classes/keyset/keyset.rb +150 -0
- data/lib/pagy/classes/offset/countish.rb +17 -0
- data/lib/pagy/classes/offset/countless.rb +63 -0
- data/lib/pagy/classes/offset/offset.rb +63 -0
- data/lib/pagy/classes/offset/search.rb +34 -0
- data/lib/pagy/classes/request.rb +48 -0
- data/lib/pagy/cli.rb +122 -0
- data/lib/pagy/console.rb +5 -20
- data/lib/pagy/deprecated.rb +84 -0
- data/lib/pagy/modules/abilities/configurable.rb +37 -0
- data/lib/pagy/modules/abilities/countable.rb +23 -0
- data/lib/pagy/modules/abilities/linkable.rb +72 -0
- data/lib/pagy/modules/abilities/rangeable.rb +14 -0
- data/lib/pagy/modules/abilities/shiftable.rb +12 -0
- data/lib/pagy/modules/b64.rb +35 -0
- data/lib/pagy/modules/console.rb +33 -0
- data/lib/pagy/modules/i18n/i18n.rb +72 -0
- data/lib/pagy/modules/i18n/p11n/arabic.rb +30 -0
- data/lib/pagy/modules/i18n/p11n/east_slavic.rb +27 -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 +27 -0
- data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
- data/lib/pagy/modules/i18n/p11n.rb +16 -0
- data/lib/pagy/modules/searcher.rb +20 -0
- data/lib/pagy/next.rb +25 -0
- data/lib/pagy/tasks/sync.rb +20 -0
- data/lib/pagy/toolbox/helpers/anchor_tags.rb +21 -0
- data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +28 -0
- data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +19 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +32 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +24 -0
- data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +25 -0
- data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +20 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +31 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +23 -0
- data/lib/pagy/toolbox/helpers/data_hash.rb +29 -0
- data/lib/pagy/toolbox/helpers/headers_hash.rb +30 -0
- data/lib/pagy/toolbox/helpers/info_tag.rb +30 -0
- data/lib/pagy/toolbox/helpers/input_nav_js.rb +22 -0
- data/lib/pagy/toolbox/helpers/limit_tag_js.rb +25 -0
- data/lib/pagy/toolbox/helpers/loaders.rb +55 -0
- data/lib/pagy/toolbox/helpers/page_url.rb +16 -0
- data/lib/pagy/toolbox/helpers/series_nav.rb +30 -0
- data/lib/pagy/toolbox/helpers/series_nav_js.rb +20 -0
- data/lib/pagy/toolbox/helpers/support/a_lambda.rb +36 -0
- data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +19 -0
- data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +10 -0
- data/lib/pagy/toolbox/helpers/support/series.rb +37 -0
- data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +19 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +17 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +42 -0
- data/lib/pagy/toolbox/helpers/urls_hash.rb +12 -0
- data/lib/pagy/toolbox/paginators/calendar.rb +34 -0
- data/lib/pagy/toolbox/paginators/countish.rb +38 -0
- data/lib/pagy/toolbox/paginators/countless.rb +22 -0
- data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +56 -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 +34 -0
- data/lib/pagy/toolbox/paginators/method.rb +38 -0
- data/lib/pagy/toolbox/paginators/offset.rb +24 -0
- data/lib/pagy/toolbox/paginators/searchkick.rb +34 -0
- data/lib/pagy/toolbox/paginators/typesense_rails.rb +34 -0
- data/lib/pagy.rb +67 -131
- data/locales/ar.yml +32 -0
- data/locales/be.yml +28 -0
- data/locales/bg.yml +24 -0
- data/locales/bs.yml +28 -0
- data/locales/ca.yml +24 -0
- data/locales/ckb.yml +20 -0
- data/locales/cs.yml +26 -0
- data/locales/da.yml +24 -0
- data/locales/de.yml +24 -0
- data/locales/dz.yml +20 -0
- data/locales/en.yml +24 -0
- data/{lib/locales → locales}/es.yml +9 -6
- data/locales/fr.yml +24 -0
- data/locales/hr.yml +28 -0
- data/locales/id.yml +20 -0
- data/locales/it.yml +24 -0
- data/locales/ja.yml +20 -0
- data/locales/km.yml +20 -0
- data/locales/ko.yml +20 -0
- data/locales/nb.yml +24 -0
- data/locales/nl.yml +24 -0
- data/locales/nn.yml +24 -0
- data/locales/pl.yml +28 -0
- data/{lib/locales → locales}/pt-BR.yml +10 -7
- data/{lib/locales → locales}/pt.yml +10 -7
- data/locales/ru.yml +28 -0
- data/locales/sk.yml +26 -0
- data/locales/sr.yml +28 -0
- data/locales/sv-SE.yml +24 -0
- data/locales/sv.yml +24 -0
- data/locales/sw.yml +28 -0
- data/locales/ta.yml +24 -0
- data/locales/tr.yml +24 -0
- data/locales/uk.yml +28 -0
- data/locales/vi.yml +20 -0
- data/locales/zh-CN.yml +20 -0
- data/locales/zh-HK.yml +20 -0
- data/locales/zh-TW.yml +20 -0
- data/stylesheets/pagy-tailwind.css +68 -0
- data/stylesheets/pagy.css +83 -0
- metadata +185 -94
- data/lib/config/pagy.rb +0 -258
- data/lib/javascripts/pagy-dev.js +0 -112
- data/lib/javascripts/pagy-module.js +0 -111
- data/lib/javascripts/pagy.js +0 -1
- data/lib/locales/ar.yml +0 -30
- data/lib/locales/be.yml +0 -25
- data/lib/locales/bg.yml +0 -21
- data/lib/locales/bs.yml +0 -25
- data/lib/locales/ca.yml +0 -23
- data/lib/locales/ckb.yml +0 -18
- data/lib/locales/cs.yml +0 -23
- data/lib/locales/da.yml +0 -23
- data/lib/locales/de.yml +0 -21
- data/lib/locales/en.yml +0 -21
- data/lib/locales/fr.yml +0 -21
- data/lib/locales/hr.yml +0 -25
- data/lib/locales/id.yml +0 -19
- data/lib/locales/it.yml +0 -21
- data/lib/locales/ja.yml +0 -19
- data/lib/locales/km.yml +0 -19
- data/lib/locales/ko.yml +0 -19
- data/lib/locales/nb.yml +0 -21
- data/lib/locales/nl.yml +0 -21
- data/lib/locales/nn.yml +0 -21
- data/lib/locales/pl.yml +0 -25
- data/lib/locales/ru.yml +0 -27
- data/lib/locales/sr.yml +0 -25
- data/lib/locales/sv-SE.yml +0 -21
- data/lib/locales/sv.yml +0 -21
- data/lib/locales/sw.yml +0 -23
- data/lib/locales/ta.yml +0 -23
- data/lib/locales/tr.yml +0 -19
- data/lib/locales/uk.yml +0 -25
- data/lib/locales/vi.yml +0 -17
- data/lib/locales/zh-CN.yml +0 -19
- data/lib/locales/zh-HK.yml +0 -19
- data/lib/locales/zh-TW.yml +0 -19
- data/lib/pagy/backend.rb +0 -39
- data/lib/pagy/calendar/helper.rb +0 -65
- data/lib/pagy/calendar.rb +0 -126
- data/lib/pagy/countless.rb +0 -37
- data/lib/pagy/exceptions.rb +0 -25
- data/lib/pagy/extras/arel.rb +0 -36
- data/lib/pagy/extras/array.rb +0 -24
- data/lib/pagy/extras/bootstrap.rb +0 -108
- data/lib/pagy/extras/bulma.rb +0 -105
- data/lib/pagy/extras/calendar.rb +0 -53
- data/lib/pagy/extras/countless.rb +0 -37
- data/lib/pagy/extras/elasticsearch_rails.rb +0 -80
- data/lib/pagy/extras/foundation.rb +0 -105
- data/lib/pagy/extras/frontend_helpers.rb +0 -67
- data/lib/pagy/extras/gearbox.rb +0 -54
- data/lib/pagy/extras/headers.rb +0 -53
- data/lib/pagy/extras/i18n.rb +0 -26
- data/lib/pagy/extras/items.rb +0 -61
- data/lib/pagy/extras/jsonapi.rb +0 -79
- data/lib/pagy/extras/materialize.rb +0 -96
- data/lib/pagy/extras/meilisearch.rb +0 -65
- data/lib/pagy/extras/metadata.rb +0 -38
- data/lib/pagy/extras/navs.rb +0 -51
- data/lib/pagy/extras/overflow.rb +0 -80
- data/lib/pagy/extras/searchkick.rb +0 -67
- data/lib/pagy/extras/semantic.rb +0 -95
- data/lib/pagy/extras/standalone.rb +0 -60
- data/lib/pagy/extras/support.rb +0 -40
- data/lib/pagy/extras/trim.rb +0 -29
- data/lib/pagy/extras/uikit.rb +0 -97
- data/lib/pagy/frontend.rb +0 -114
- data/lib/pagy/i18n.rb +0 -165
- data/lib/pagy/url_helpers.rb +0 -27
- data/lib/stylesheets/pagy.css +0 -61
- data/lib/stylesheets/pagy.scss +0 -50
- data/lib/stylesheets/pagy.tailwind.scss +0 -24
- /data/{lib/javascripts/pagy-module.d.ts → javascripts/pagy.d.ts} +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'previous_next_html'
|
|
4
|
+
require_relative '../support/wrap_series_nav'
|
|
5
|
+
|
|
6
|
+
class Pagy
|
|
7
|
+
module NumericHelpers
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def bulma_series_nav(classes: 'pagination', **)
|
|
11
|
+
a_lambda = a_lambda(**)
|
|
12
|
+
|
|
13
|
+
html = %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)})
|
|
14
|
+
series(**).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
|
15
|
+
html << case item
|
|
16
|
+
when Integer
|
|
17
|
+
%(<li>#{a_lambda.(item, page_label(item), classes: 'pagination-link')}</li>)
|
|
18
|
+
when String
|
|
19
|
+
%(<li><a role="link" class="pagination-link is-current" aria-current="page" aria-disabled="true">#{
|
|
20
|
+
page_label(item)}</a></li>)
|
|
21
|
+
when :gap
|
|
22
|
+
%(<li><span class="pagination-ellipsis">#{I18n.translate('pagy.gap')}</span></li>)
|
|
23
|
+
else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
html << %(#{bulma_html_for(:next, a_lambda)}</ul>)
|
|
27
|
+
|
|
28
|
+
wrap_series_nav(html, "pagy-bulma series-nav #{classes}", **)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'previous_next_html'
|
|
4
|
+
require_relative '../support/wrap_series_nav_js'
|
|
5
|
+
|
|
6
|
+
class Pagy
|
|
7
|
+
module NumericHelpers
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def bulma_series_nav_js(classes: 'pagination', **)
|
|
11
|
+
a_lambda = a_lambda(**)
|
|
12
|
+
|
|
13
|
+
tokens = { before: %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)}),
|
|
14
|
+
anchor: %(<li>#{a_lambda.(PAGE_TOKEN, LABEL_TOKEN, classes: 'pagination-link')}</li>),
|
|
15
|
+
current: %(<li><a role="link" class="pagination-link is-current" ) +
|
|
16
|
+
%(aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a></li>),
|
|
17
|
+
gap: %(<li><span class="pagination-ellipsis">#{I18n.translate('pagy.gap')}</span></li>),
|
|
18
|
+
after: %(#{bulma_html_for(:next, a_lambda)}</ul>) }
|
|
19
|
+
|
|
20
|
+
wrap_series_nav_js(tokens, "pagy-bulma series-nav-js #{classes}", **)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
DEFAULT_DATA_KEYS = %i[url_template first_url previous_url current_url page_url next_url last_url
|
|
5
|
+
count page limit last in from to previous next options].freeze
|
|
6
|
+
|
|
7
|
+
# Generate a hash of the wanted internal data
|
|
8
|
+
def data_hash(data_keys: @options[:data_keys] || DEFAULT_DATA_KEYS, **)
|
|
9
|
+
template = compose_page_url(PAGE_TOKEN, **)
|
|
10
|
+
to_url = ->(page) { template.sub(PAGE_TOKEN, page.to_s) if page }
|
|
11
|
+
|
|
12
|
+
data_keys -= %i[count limit] if calendar?
|
|
13
|
+
|
|
14
|
+
data_keys.each_with_object({}) do |key, data|
|
|
15
|
+
value = case key
|
|
16
|
+
when :url_template then template
|
|
17
|
+
when :first_url then compose_page_url(nil, **)
|
|
18
|
+
when :previous_url then to_url.(@previous)
|
|
19
|
+
when :current_url, :page_url then to_url.(@page)
|
|
20
|
+
when :next_url then to_url.(@next)
|
|
21
|
+
when :last_url then to_url.(@last)
|
|
22
|
+
else send(key)
|
|
23
|
+
end
|
|
24
|
+
data[key] = value if value
|
|
25
|
+
rescue NoMethodError
|
|
26
|
+
raise OptionError.new(self, :data_keys, 'to contain known keys/methods', key)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'urls_hash'
|
|
4
|
+
|
|
5
|
+
# Handle pagination response headers
|
|
6
|
+
class Pagy
|
|
7
|
+
DEFAULT_HEADERS_MAP = { page: 'current-page',
|
|
8
|
+
limit: 'page-limit',
|
|
9
|
+
count: 'total-count',
|
|
10
|
+
pages: 'total-pages' }.freeze
|
|
11
|
+
|
|
12
|
+
# Generate a hash of RFC-8288-compliant http headers
|
|
13
|
+
def headers_hash(headers_map: @options[:headers_map] || DEFAULT_HEADERS_MAP, **)
|
|
14
|
+
links = urls_hash(**, absolute: true).map { %(<#{_2}>; rel="#{_1}") }.join(', ')
|
|
15
|
+
|
|
16
|
+
headers_map.each_with_object('link' => links) do |(key, name), hash|
|
|
17
|
+
next unless name
|
|
18
|
+
|
|
19
|
+
value = case key
|
|
20
|
+
# :nocov:
|
|
21
|
+
when :page then @page
|
|
22
|
+
when :limit then @limit unless calendar?
|
|
23
|
+
when :pages then @last if @count
|
|
24
|
+
when :count then @count
|
|
25
|
+
# :nocov:
|
|
26
|
+
end
|
|
27
|
+
hash[name] = value.to_s if value
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
module NumericHelpers
|
|
5
|
+
# Instances with count return "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total".
|
|
6
|
+
# Instances with no count return only page info: "Page 3 of 100".
|
|
7
|
+
def info_tag(id: nil, item_name: nil)
|
|
8
|
+
i18n_key = if @count.nil?
|
|
9
|
+
'pagy.info_tag.no_count'
|
|
10
|
+
elsif @count.zero?
|
|
11
|
+
'pagy.info_tag.no_items'
|
|
12
|
+
elsif @in == @count
|
|
13
|
+
'pagy.info_tag.single_page'
|
|
14
|
+
else
|
|
15
|
+
'pagy.info_tag.multiple_pages'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
info_data = if @count.nil?
|
|
19
|
+
{ page: @page, pages: @last }
|
|
20
|
+
else
|
|
21
|
+
{ item_name: item_name || I18n.translate('pagy.item_name', count: @count),
|
|
22
|
+
count: @count,
|
|
23
|
+
from: @from,
|
|
24
|
+
to: @to }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
%(<span#{%( id="#{id}") if id} class="pagy info">#{I18n.translate(i18n_key, **info_data)}</span>)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'support/wrap_input_nav_js'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module NumericHelpers
|
|
7
|
+
def input_nav_js(style = nil, **)
|
|
8
|
+
return send(:"#{style}_input_nav_js", **) if style && style.to_s != 'pagy'
|
|
9
|
+
|
|
10
|
+
a_lambda = a_lambda(**)
|
|
11
|
+
|
|
12
|
+
input = %(<input name="page" type="number" min="1" max="#{@last}" value="#{@page}" aria-current="page" ) +
|
|
13
|
+
%(style="text-align: center; width: #{@page.to_s.length + 1}rem; padding: 0;">#{A_TAG})
|
|
14
|
+
|
|
15
|
+
html = %(#{previous_tag(a_lambda)}<label>#{
|
|
16
|
+
I18n.translate('pagy.input_nav_js', page_input: input, pages: @last)}</label>#{
|
|
17
|
+
next_tag(a_lambda)})
|
|
18
|
+
|
|
19
|
+
wrap_input_nav_js(html, 'pagy input-nav-js', **)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'support/data_pagy_attribute'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module NumericHelpers
|
|
7
|
+
def limit_tag_js(id: nil, item_name: nil, max_limit: @options[:max_limit], **)
|
|
8
|
+
raise OptionError.new(self, :max_limit, 'to be truthy', max_limit) unless max_limit
|
|
9
|
+
|
|
10
|
+
limit_input = %(<input name="limit" type="number" min="1" max="#{max_limit}" value="#{
|
|
11
|
+
@limit}" style="padding: 0; text-align: center; width: #{@limit.to_s.length + 1}rem;">#{A_TAG})
|
|
12
|
+
|
|
13
|
+
url_token = compose_page_url(PAGE_TOKEN, limit: LIMIT_TOKEN)
|
|
14
|
+
|
|
15
|
+
%(<span#{%( id="#{id}") if id} class="pagy limit-tag-js" #{
|
|
16
|
+
data_pagy_attribute(:ltj, @from, url_token, PAGE_TOKEN, LIMIT_TOKEN)
|
|
17
|
+
}><label>#{
|
|
18
|
+
I18n.translate('pagy.limit_tag_js',
|
|
19
|
+
item_name: item_name || I18n.translate('pagy.item_name', count: @limit),
|
|
20
|
+
limit_input:,
|
|
21
|
+
count: @limit)
|
|
22
|
+
}</label></span>)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
# The aliaser defines loader methods and aliases them.
|
|
5
|
+
# When a method is called for the first time, its loader will load the full implementation,
|
|
6
|
+
# which will overwrite the alias and will be executed.
|
|
7
|
+
# Subsequent calls will run the full implementation directly.
|
|
8
|
+
aliaser = lambda do |receiver, paths|
|
|
9
|
+
receiver.instance_eval do
|
|
10
|
+
paths.each do |visibility, methods|
|
|
11
|
+
methods.each do |method, path|
|
|
12
|
+
loader_name = :"_pagy_loader_for_#{method}"
|
|
13
|
+
define_method(loader_name) do |*args, **kwargs|
|
|
14
|
+
# Tests shadow the usage of these lines
|
|
15
|
+
# :nocov:
|
|
16
|
+
require_relative path
|
|
17
|
+
send(method, *args, **kwargs)
|
|
18
|
+
# :nocov:
|
|
19
|
+
end
|
|
20
|
+
alias_method method, loader_name
|
|
21
|
+
send(visibility, method)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module HelperLoader
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
paths = { public: { page_url: 'page_url',
|
|
31
|
+
data_hash: 'data_hash',
|
|
32
|
+
headers_hash: 'headers_hash',
|
|
33
|
+
urls_hash: 'urls_hash',
|
|
34
|
+
next_tag: 'anchor_tags' } }.freeze
|
|
35
|
+
|
|
36
|
+
aliaser.(HelperLoader, paths)
|
|
37
|
+
|
|
38
|
+
module NumericHelperLoader
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
paths = { public: { previous_tag: 'anchor_tags',
|
|
42
|
+
input_nav_js: 'input_nav_js',
|
|
43
|
+
info_tag: 'info_tag',
|
|
44
|
+
limit_tag_js: 'limit_tag_js',
|
|
45
|
+
series_nav: 'series_nav',
|
|
46
|
+
series_nav_js: 'series_nav_js' },
|
|
47
|
+
protected: { bootstrap_series_nav: 'bootstrap/series_nav',
|
|
48
|
+
bootstrap_series_nav_js: 'bootstrap/series_nav_js',
|
|
49
|
+
bootstrap_input_nav_js: 'bootstrap/input_nav_js',
|
|
50
|
+
bulma_series_nav: 'bulma/series_nav',
|
|
51
|
+
bulma_series_nav_js: 'bulma/series_nav_js',
|
|
52
|
+
bulma_input_nav_js: 'bulma/input_nav_js' } }.freeze
|
|
53
|
+
|
|
54
|
+
aliaser.(NumericHelperLoader, paths)
|
|
55
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
def page_url(page, **)
|
|
5
|
+
target = case page
|
|
6
|
+
when :first then nil
|
|
7
|
+
when :current, :page then @page
|
|
8
|
+
when :previous then @previous
|
|
9
|
+
when :next then @next
|
|
10
|
+
when :last then @last
|
|
11
|
+
else page
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
compose_page_url(target, **) if target || page == :first
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'support/wrap_series_nav'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module NumericHelpers
|
|
7
|
+
def series_nav(style = nil, **)
|
|
8
|
+
return send(:"#{style}_series_nav", **) if style && style.to_s != 'pagy'
|
|
9
|
+
|
|
10
|
+
a_lambda = a_lambda(**)
|
|
11
|
+
|
|
12
|
+
html = previous_tag(a_lambda)
|
|
13
|
+
series(**).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
|
14
|
+
html << case item
|
|
15
|
+
when Integer
|
|
16
|
+
a_lambda.(item)
|
|
17
|
+
when String
|
|
18
|
+
%(<a role="link" aria-disabled="true" aria-current="page">#{page_label(item)}</a>)
|
|
19
|
+
when :gap
|
|
20
|
+
%(<a role="separator" aria-disabled="true">#{I18n.translate('pagy.gap')}</a>)
|
|
21
|
+
else
|
|
22
|
+
raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
html << next_tag(a_lambda)
|
|
26
|
+
|
|
27
|
+
wrap_series_nav(html, 'pagy series-nav', **)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'support/wrap_series_nav_js'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module NumericHelpers
|
|
7
|
+
def series_nav_js(style = nil, **)
|
|
8
|
+
return send(:"#{style}_series_nav_js", **) if style && style.to_s != 'pagy'
|
|
9
|
+
|
|
10
|
+
a_lambda = a_lambda(**)
|
|
11
|
+
tokens = { before: previous_tag(a_lambda),
|
|
12
|
+
anchor: a_lambda.(PAGE_TOKEN, LABEL_TOKEN),
|
|
13
|
+
current: %(<a role="link" aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a>),
|
|
14
|
+
gap: %(<a role="separator" aria-disabled="true">#{I18n.translate('pagy.gap')}</a>),
|
|
15
|
+
after: next_tag(a_lambda) }
|
|
16
|
+
|
|
17
|
+
wrap_series_nav_js(tokens, 'pagy series-nav-js', **)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
protected
|
|
5
|
+
|
|
6
|
+
def page_label(page, **options)
|
|
7
|
+
return page.to_s unless calendar?
|
|
8
|
+
|
|
9
|
+
options[:format] ||= @options[:format]
|
|
10
|
+
localize(starting_time_for(page.to_i), **options) # page could be a string
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Return a performance optimized lambda to generate the anchor tag.
|
|
14
|
+
# Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to.
|
|
15
|
+
def a_lambda(anchor_string: @options[:anchor_string], **)
|
|
16
|
+
left, right = %(<a href="#{compose_page_url(PAGE_TOKEN, **)}"#{
|
|
17
|
+
%( #{anchor_string}) if anchor_string}).split(PAGE_TOKEN, 2)
|
|
18
|
+
|
|
19
|
+
lambda do |page, text = page_label(page), classes: nil, aria_label: nil|
|
|
20
|
+
title = if calendar? && (counts = @options[:counts])
|
|
21
|
+
count = counts[page - 1]
|
|
22
|
+
classes = classes ? "#{classes} empty-page" : 'empty-page' if count.zero?
|
|
23
|
+
info_key = count.zero? ? 'pagy.info_tag.no_items' : 'pagy.info_tag.single_page'
|
|
24
|
+
%( title="#{I18n.translate(info_key, item_name: I18n.translate('pagy.item_name', count:), count:)}")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
rel = case page
|
|
28
|
+
when @previous then %( rel="prev")
|
|
29
|
+
when @next then %( rel="next")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
%(#{left}#{page}#{right}#{title}#{
|
|
33
|
+
%( class="#{classes}") if classes}#{rel}#{%( aria-label="#{aria_label}") if aria_label}>#{text}</a>)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require_relative '../../../modules/b64'
|
|
5
|
+
|
|
6
|
+
class Pagy
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Compose the data-pagy attribute, with the base64 encoded JSON-serialized args. Use the faster oj gem if defined.
|
|
10
|
+
def data_pagy_attribute(*args)
|
|
11
|
+
data = if defined?(Oj)
|
|
12
|
+
Oj.dump(args, mode: :compat)
|
|
13
|
+
else
|
|
14
|
+
JSON.dump(args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
%(data-pagy="#{B64.encode(data)}")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
SERIES_SLOTS = 7
|
|
5
|
+
|
|
6
|
+
protected
|
|
7
|
+
|
|
8
|
+
# 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) unless slots.is_a?(Integer) && slots >= 0
|
|
11
|
+
return [] if slots.zero?
|
|
12
|
+
|
|
13
|
+
[].tap do |series|
|
|
14
|
+
if slots >= @last
|
|
15
|
+
series.push(*1..@last)
|
|
16
|
+
else
|
|
17
|
+
half = (slots - 1) / 2 # even slots makes the left half shorter by 1
|
|
18
|
+
start = if @page <= half # @page in the first half
|
|
19
|
+
1
|
|
20
|
+
elsif @page > (@last - slots + half) # @page in the last half
|
|
21
|
+
@last - slots + 1
|
|
22
|
+
else # @page in the middle
|
|
23
|
+
@page - half
|
|
24
|
+
end
|
|
25
|
+
series.push(*(start...(start + slots)))
|
|
26
|
+
unless compact || slots < SERIES_SLOTS # Set first, last and :gap when needed
|
|
27
|
+
series[0] = 1
|
|
28
|
+
series[1] = :gap unless series[1] == 2
|
|
29
|
+
series[-2] = :gap unless series[-2] == @last - 1
|
|
30
|
+
series[-1] = @last
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
current = series.index(@page)
|
|
34
|
+
series[current] = @page.to_s if current
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
class Pagy
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# Common input_nav_js logic
|
|
11
|
+
def wrap_input_nav_js(html, nav_classes, id: nil, aria_label: nil, **)
|
|
12
|
+
%(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{
|
|
13
|
+
nav_aria_label_attribute(aria_label:)} #{
|
|
14
|
+
data = [:inj, compose_page_url(PAGE_TOKEN, **), PAGE_TOKEN]
|
|
15
|
+
data.push(@update) if keynav?
|
|
16
|
+
data_pagy_attribute(*data)
|
|
17
|
+
}>#{html}</nav>)
|
|
18
|
+
end
|
|
19
|
+
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
|
+
class Pagy
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Common series_nav logic
|
|
12
|
+
def wrap_series_nav(html, nav_classes, id: nil, aria_label: nil, **)
|
|
13
|
+
data = %( #{data_pagy_attribute(:k, @update)}) if keynav?
|
|
14
|
+
|
|
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,42 @@
|
|
|
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
|
+
# 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 the Calendar API
|
|
22
|
+
def page_labels(series)
|
|
23
|
+
return unless calendar?
|
|
24
|
+
|
|
25
|
+
series.map do |s|
|
|
26
|
+
s.map { _1 == :gap ? :gap : page_label(_1) }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Common series_nav_js logic
|
|
31
|
+
def wrap_series_nav_js(tokens, nav_classes, id: nil, aria_label: nil, **)
|
|
32
|
+
sequels = sequels(**)
|
|
33
|
+
nav_classes = "pagy-rjs #{nav_classes}" if sequels[0].size > 1
|
|
34
|
+
|
|
35
|
+
%(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{
|
|
36
|
+
nav_aria_label_attribute(aria_label:)} #{
|
|
37
|
+
data = [:snj, tokens.values, PAGE_TOKEN, sequels]
|
|
38
|
+
data.push(@update) if keynav?
|
|
39
|
+
data_pagy_attribute(*data)
|
|
40
|
+
}></nav>)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
def urls_hash(**)
|
|
5
|
+
template = compose_page_url(PAGE_TOKEN, **)
|
|
6
|
+
|
|
7
|
+
{ first: compose_page_url(nil, **),
|
|
8
|
+
previous: @previous && template.sub(PAGE_TOKEN, @previous.to_s),
|
|
9
|
+
next: @next && template.sub(PAGE_TOKEN, @next.to_s),
|
|
10
|
+
last: @count && template.sub(PAGE_TOKEN, @last.to_s) }.compact
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
module CalendarPaginator
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Take a collection and a config Hash and return [calendar, pagy, results]
|
|
8
|
+
def paginate(context, collection, config)
|
|
9
|
+
context.instance_eval do
|
|
10
|
+
allowed_options = Calendar::UNITS + %i[offset disabled request]
|
|
11
|
+
|
|
12
|
+
unless config.is_a?(Hash) && (config.keys - allowed_options).empty?
|
|
13
|
+
raise ArgumentError, "keys must be in #{allowed_options.inspect}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
config[:offset] ||= {}
|
|
17
|
+
|
|
18
|
+
unless config[:disabled]
|
|
19
|
+
period = pagy_calendar_period(collection)
|
|
20
|
+
params = config[:request].params
|
|
21
|
+
|
|
22
|
+
calendar, from, to = Calendar.send(:init, config, period, params) do |unit, unit_period|
|
|
23
|
+
pagy_calendar_counts(collection, unit, *unit_period) if respond_to?(:pagy_calendar_counts)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
collection = pagy_calendar_filter(collection, from, to)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
pagy, records = pagy(:offset, collection, **config[:offset])
|
|
30
|
+
[calendar, pagy, records]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../modules/abilities/countable'
|
|
4
|
+
|
|
5
|
+
class Pagy
|
|
6
|
+
module CountishPaginator
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def paginate(collection, options)
|
|
10
|
+
options[:page] ||= options[:request].resolve_page(force_integer: false)
|
|
11
|
+
|
|
12
|
+
if options[:page].is_a?(String)
|
|
13
|
+
page, count, epoch = options[:page].split.map(&:to_i)
|
|
14
|
+
options[:page] = page
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
options[:limit] = options[:request].resolve_limit
|
|
18
|
+
setup_options(count, epoch, collection, options)
|
|
19
|
+
|
|
20
|
+
pagy = Offset::Countish.new(**options)
|
|
21
|
+
[pagy, pagy.records(collection)]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Get the count from the page and set epoch when ttl (Time To Live) requires it
|
|
25
|
+
def setup_options(count, epoch, collection, options)
|
|
26
|
+
now = Time.now.to_i
|
|
27
|
+
ongoing = !options[:ttl] || (epoch && epoch <= now && now < (epoch + options[:ttl]))
|
|
28
|
+
|
|
29
|
+
if !options[:count] && count && ongoing
|
|
30
|
+
options[:count] = count
|
|
31
|
+
options[:epoch] = epoch if options[:ttl]
|
|
32
|
+
else # recount
|
|
33
|
+
options[:count] ||= Countable.get_count(collection, options)
|
|
34
|
+
options[:epoch] = now if options[:ttl]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Pagy
|
|
4
|
+
module CountlessPaginator
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def paginate(collection, options)
|
|
8
|
+
options[:page] ||= options[:request].resolve_page(force_integer: false) # accept nil and strings
|
|
9
|
+
|
|
10
|
+
if options[:page].is_a?(String)
|
|
11
|
+
page, last = options[:page].split.map(&:to_i) # ' ' separator, (encoded as '+' by Countless#compose_page_param)
|
|
12
|
+
options[:page] = page
|
|
13
|
+
options[:last] = last if last&.positive?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
options[:limit] = options[:request].resolve_limit
|
|
17
|
+
|
|
18
|
+
pagy = Offset::Countless.new(**options)
|
|
19
|
+
[pagy, pagy.records(collection)]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|