pagy 4.11.0 → 6.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/LICENSE.txt +1 -1
- data/lib/config/pagy.rb +119 -58
- data/lib/javascripts/pagy-dev.js +114 -0
- data/lib/javascripts/pagy-module.d.ts +5 -0
- data/lib/javascripts/pagy-module.js +113 -0
- data/lib/javascripts/pagy.js +1 -121
- data/lib/locales/de.yml +1 -1
- data/lib/locales/ko.yml +1 -1
- data/lib/locales/nn.yml +22 -0
- data/lib/locales/ta.yml +22 -0
- data/lib/pagy/backend.rb +10 -13
- data/lib/pagy/calendar/day.rb +39 -0
- data/lib/pagy/calendar/helper.rb +61 -0
- data/lib/pagy/calendar/month.rb +40 -0
- data/lib/pagy/calendar/quarter.rb +47 -0
- data/lib/pagy/calendar/week.rb +39 -0
- data/lib/pagy/calendar/year.rb +33 -0
- data/lib/pagy/calendar.rb +100 -0
- data/lib/pagy/console.rb +6 -4
- data/lib/pagy/countless.rb +22 -23
- data/lib/pagy/exceptions.rb +14 -16
- data/lib/pagy/extras/arel.rb +11 -7
- data/lib/pagy/extras/array.rb +9 -9
- data/lib/pagy/extras/bootstrap.rb +45 -38
- data/lib/pagy/extras/bulma.rb +50 -38
- data/lib/pagy/extras/calendar.rb +49 -0
- data/lib/pagy/extras/countless.rb +15 -18
- data/lib/pagy/extras/elasticsearch_rails.rb +67 -48
- data/lib/pagy/extras/foundation.rb +39 -35
- data/lib/pagy/extras/frontend_helpers.rb +72 -0
- data/lib/pagy/extras/gearbox.rb +54 -0
- data/lib/pagy/extras/headers.rb +30 -20
- data/lib/pagy/extras/i18n.rb +15 -13
- data/lib/pagy/extras/items.rb +42 -40
- data/lib/pagy/extras/materialize.rb +40 -38
- data/lib/pagy/extras/meilisearch.rb +53 -44
- data/lib/pagy/extras/metadata.rb +15 -20
- data/lib/pagy/extras/navs.rb +35 -34
- data/lib/pagy/extras/overflow.rb +62 -61
- data/lib/pagy/extras/searchkick.rb +54 -46
- data/lib/pagy/extras/semantic.rb +42 -40
- data/lib/pagy/extras/standalone.rb +50 -46
- data/lib/pagy/extras/support.rb +24 -16
- data/lib/pagy/extras/trim.rb +15 -14
- data/lib/pagy/extras/uikit.rb +41 -38
- data/lib/pagy/frontend.rb +36 -59
- data/lib/pagy/i18n.rb +164 -0
- data/lib/pagy/url_helpers.rb +24 -0
- data/lib/pagy.rb +90 -31
- data/lib/templates/bootstrap_nav.html.erb +2 -2
- data/lib/templates/bootstrap_nav.html.haml +2 -2
- data/lib/templates/bootstrap_nav.html.slim +2 -2
- data/lib/templates/foundation_nav.html.erb +1 -1
- data/lib/templates/foundation_nav.html.haml +1 -1
- data/lib/templates/foundation_nav.html.slim +1 -1
- data/lib/templates/nav.html.erb +1 -1
- data/lib/templates/nav.html.haml +1 -1
- data/lib/templates/nav.html.slim +1 -1
- data/lib/templates/uikit_nav.html.erb +2 -2
- data/lib/templates/uikit_nav.html.haml +1 -1
- data/lib/templates/uikit_nav.html.slim +2 -2
- metadata +29 -13
- data/lib/locales/utils/i18n.rb +0 -17
- data/lib/locales/utils/loader.rb +0 -31
- data/lib/locales/utils/p11n.rb +0 -112
- data/lib/pagy/deprecation.rb +0 -27
- data/lib/pagy/extras/shared.rb +0 -52
data/lib/javascripts/pagy.js
CHANGED
@@ -1,121 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
function Pagy(){}
|
4
|
-
|
5
|
-
Pagy.version = '4.11.0'
|
6
|
-
|
7
|
-
Pagy.delay = 100
|
8
|
-
|
9
|
-
Pagy.init =
|
10
|
-
function(arg) {
|
11
|
-
var target = arg instanceof Event || arg === undefined ? document : arg,
|
12
|
-
elements = target.querySelectorAll('[data-pagy-json]')
|
13
|
-
for (var i = 0, len = elements.length; i < len; i++) {
|
14
|
-
var args = JSON.parse(elements[i].getAttribute('data-pagy-json')),
|
15
|
-
fname = args.shift()
|
16
|
-
args.unshift(elements[i])
|
17
|
-
Pagy[fname].apply(null, args)
|
18
|
-
}
|
19
|
-
}
|
20
|
-
|
21
|
-
Pagy.nav =
|
22
|
-
function(pagyEl, tags, sequels, trimParam) {
|
23
|
-
var lastWidth,
|
24
|
-
pageREg = new RegExp(/__pagy_page__/g),
|
25
|
-
widths = []
|
26
|
-
for (var width in sequels) {
|
27
|
-
if (sequels.hasOwnProperty(width)) { widths.push(parseInt(width, 10)) }
|
28
|
-
}
|
29
|
-
widths.sort(function(a, b) { return b - a })
|
30
|
-
|
31
|
-
pagyEl.render =
|
32
|
-
function() {
|
33
|
-
var width, i, len
|
34
|
-
for (i = 0, len = widths.length; i < len; i++) {
|
35
|
-
if (this.parentElement.clientWidth > widths[i]) { width = widths[i]; break }
|
36
|
-
}
|
37
|
-
if (width !== lastWidth) {
|
38
|
-
var html = tags.before,
|
39
|
-
series = sequels[width]
|
40
|
-
for (i = 0, len = series.length; i < len; i++) {
|
41
|
-
var item = series[i]
|
42
|
-
if (typeof(trimParam) === 'string' && item === 1) { html += Pagy.trim(tags.link.replace(pageREg, item), trimParam) }
|
43
|
-
else if (typeof(item) === 'number') { html += tags.link.replace(pageREg, item) }
|
44
|
-
else if (item === 'gap') { html += tags.gap }
|
45
|
-
else if (typeof(item) === 'string') { html += tags.active.replace(pageREg, item) }
|
46
|
-
}
|
47
|
-
html += tags.after
|
48
|
-
this.innerHTML = ''
|
49
|
-
this.insertAdjacentHTML('afterbegin', html)
|
50
|
-
lastWidth = width
|
51
|
-
}
|
52
|
-
}.bind(pagyEl)
|
53
|
-
pagyEl.render()
|
54
|
-
}
|
55
|
-
|
56
|
-
Pagy.combo_nav =
|
57
|
-
function(pagyEl, page, link, trimParam) {
|
58
|
-
var input = pagyEl.getElementsByTagName('input')[0],
|
59
|
-
toPage =
|
60
|
-
function() {
|
61
|
-
if (page !== input.value) {
|
62
|
-
var html = link.replace(/__pagy_page__/, input.value)
|
63
|
-
if (typeof (trimParam) === 'string' && input.value === '1') { html = Pagy.trim(html, trimParam) }
|
64
|
-
pagyEl.insertAdjacentHTML('afterbegin', html)
|
65
|
-
pagyEl.getElementsByTagName('a')[0].click()
|
66
|
-
}
|
67
|
-
}
|
68
|
-
Pagy.addInputEventListeners(input, toPage)
|
69
|
-
}
|
70
|
-
|
71
|
-
Pagy.items_selector =
|
72
|
-
function(pagyEl, from, link, param) {
|
73
|
-
var input = pagyEl.getElementsByTagName('input')[0],
|
74
|
-
current = input.value,
|
75
|
-
toPage =
|
76
|
-
function() {
|
77
|
-
var items = input.value
|
78
|
-
if (current !== items) {
|
79
|
-
var page = Math.max(Math.ceil(from / items), 1),
|
80
|
-
html = link.replace(/__pagy_page__/, page).replace(/__pagy_items__/, items)
|
81
|
-
if (typeof (param) === 'string' && page === 1) { html = Pagy.trim(html, param) }
|
82
|
-
pagyEl.insertAdjacentHTML('afterbegin', html)
|
83
|
-
pagyEl.getElementsByTagName('a')[0].click()
|
84
|
-
}
|
85
|
-
}
|
86
|
-
Pagy.addInputEventListeners(input, toPage)
|
87
|
-
}
|
88
|
-
|
89
|
-
Pagy.addInputEventListeners =
|
90
|
-
function(input, toPage) {
|
91
|
-
// select the content on click: easier for typing a number
|
92
|
-
input.addEventListener('click', function() { this.select() })
|
93
|
-
// toPage when the input looses focus
|
94
|
-
input.addEventListener('focusout', toPage)
|
95
|
-
// … and when pressing enter inside the input
|
96
|
-
input.addEventListener('keyup', function(e) { if (e.which === 13) {toPage()} }.bind(this))
|
97
|
-
}
|
98
|
-
|
99
|
-
Pagy.trim =
|
100
|
-
function(html, param) {
|
101
|
-
var re = new RegExp('[?&]' + param + '=1\\b(?!&)|\\b' + param + '=1&')
|
102
|
-
return html.replace(re, '')
|
103
|
-
}
|
104
|
-
|
105
|
-
Pagy.renderNavs =
|
106
|
-
function() {
|
107
|
-
var navs = document.getElementsByClassName('pagy-njs') // 'pagy-njs' is common to all *nav_js helpers
|
108
|
-
for (var i = 0, len = navs.length; i < len; i++) { navs[i].render() }
|
109
|
-
}
|
110
|
-
|
111
|
-
Pagy.waitForMe =
|
112
|
-
function() {
|
113
|
-
if (typeof(Pagy.tid) === 'number') { clearTimeout(Pagy.tid) }
|
114
|
-
Pagy.tid = setTimeout(Pagy.renderNavs, Pagy.delay)
|
115
|
-
}
|
116
|
-
|
117
|
-
|
118
|
-
if (typeof window !== "undefined") {
|
119
|
-
window.addEventListener('resize', Pagy.waitForMe, true)
|
120
|
-
}
|
121
|
-
|
1
|
+
!function(){var e=(()=>{const e=new ResizeObserver((e=>e.forEach((e=>e.target.querySelectorAll(".pagy-rjs").forEach((e=>e.pagyRender())))))),t=(t,[n,r,a,c])=>{const i=t.parentElement??t,s=Object.keys(r).map((e=>parseInt(e))).sort(((e,t)=>t-e));let p=-1;const l=(e,t,n)=>e.replace(/__pagy_page__/g,t).replace(/__pagy_label__/g,n);(t.pagyRender=function(){const e=s.find((e=>e<i.clientWidth))||0;if(e===p)return;let g=n.before;const y=r[e.toString()],f=a?.[e.toString()]??y.map((e=>e.toString()));for(const e in y){const t=y[e],r=f[e];g+="string"==typeof c&&1===t?o(l(n.link,t.toString(),r),c):"number"==typeof t?l(n.link,t.toString(),r):"gap"===t?n.gap:l(n.active,t,r)}g+=n.after,t.innerHTML="",t.insertAdjacentHTML("afterbegin",g),p=e})(),t.classList.contains("pagy-rjs")&&e.observe(i)},n=(e,[t,n])=>a(e,(e=>[e,t.replace(/__pagy_page__/,e)]),n),r=(e,[t,n,r])=>{a(e,(e=>{const r=Math.max(Math.ceil(t/parseInt(e)),1).toString();return[r,n.replace(/__pagy_page__/,r).replace(/__pagy_items__/,e)]}),r)},a=(e,t,n)=>{const r=e.querySelector("input"),a=r.value,c=function(){if(r.value===a)return;const[c,i,s]=[r.min,r.value,r.max].map((e=>parseInt(e)||0));if(i<c||i>s)return r.value=a,void r.select();let[p,l]=t(r.value);"string"==typeof n&&"1"===p&&(l=o(l,n)),e.insertAdjacentHTML("afterbegin",l),e.querySelector("a").click()};["change","focus"].forEach((e=>r.addEventListener(e,r.select))),r.addEventListener("focusout",c),r.addEventListener("keypress",(e=>{"Enter"===e.key&&c()}))},o=(e,t)=>e.replace(new RegExp(`(\\?|&)${t}=1\\b(?!&)|\\b${t}=1&`),"");return{version:"6.0.0",init(e){const a=(e instanceof Element?e:document).querySelectorAll("[data-pagy]");for(const e of a)try{const a=Uint8Array.from(atob(e.getAttribute("data-pagy")),(e=>e.charCodeAt(0))),[o,...c]=JSON.parse((new TextDecoder).decode(a));"nav"===o?t(e,c):"combo"===o?n(e,c):"selector"===o?r(e,c):console.warn("Skipped Pagy.init() for: %o\nUnknown keyword '%s'",e,o)}catch(t){console.warn("Skipped Pagy.init() for: %o\n%s",e,t)}}}})();window.Pagy=e}();
|
data/lib/locales/de.yml
CHANGED
@@ -13,7 +13,7 @@ de:
|
|
13
13
|
gap: "…"
|
14
14
|
|
15
15
|
info:
|
16
|
-
no_items: "
|
16
|
+
no_items: "Keine %{item_name} gefunden"
|
17
17
|
single_page: "Zeige <b>%{count}</b> %{item_name}"
|
18
18
|
multiple_pages: "Zeige %{item_name} <b>%{from}-%{to}</b> von <b>%{count}</b> gesamt"
|
19
19
|
|
data/lib/locales/ko.yml
CHANGED
data/lib/locales/nn.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# :one_other pluralization (see https://github.com/ddnexus/pagy/blob/master/lib/locales/utils/p11n.rb)
|
2
|
+
|
3
|
+
nn:
|
4
|
+
pagy:
|
5
|
+
|
6
|
+
item_name:
|
7
|
+
one: "resultat"
|
8
|
+
other: "resultat"
|
9
|
+
|
10
|
+
nav:
|
11
|
+
prev: "‹ Førre"
|
12
|
+
next: "Neste ›"
|
13
|
+
gap: "…"
|
14
|
+
|
15
|
+
info:
|
16
|
+
no_items: "Ingen %{item_name} funne"
|
17
|
+
single_page: "Viser <b>%{count}</b> %{item_name}"
|
18
|
+
multiple_pages: "Viser %{item_name} <b>%{from}-%{to}</b> av totalt <b>%{count}</b>"
|
19
|
+
|
20
|
+
combo_nav_js: "<label>Side %{page_input} av %{pages}</label>"
|
21
|
+
|
22
|
+
items_selector_js: "<label>Vis %{items_input} %{item_name} per side</label>"
|
data/lib/locales/ta.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# :one_other pluralization (see https://github.com/ddnexus/pagy/blob/master/lib/locales/utils/p11n.rb)
|
2
|
+
|
3
|
+
ta:
|
4
|
+
pagy:
|
5
|
+
|
6
|
+
item_name:
|
7
|
+
one: "பதிவு"
|
8
|
+
other: "பதிவுகள்"
|
9
|
+
|
10
|
+
nav:
|
11
|
+
prev: "‹ முந்தையது"
|
12
|
+
next: "அடுத்தது ›"
|
13
|
+
gap: "…"
|
14
|
+
|
15
|
+
info:
|
16
|
+
no_items: "%{item_name} கிடைக்கவில்லை"
|
17
|
+
single_page: "<b>%{count}</b> %{item_name} காட்டப்படுகின்றது"
|
18
|
+
multiple_pages: "மொத்தம் <b>%{count}</b> %{item_name}, காட்டப்படுபவை <b>%{from}-%{to}</b>"
|
19
|
+
|
20
|
+
combo_nav_js: "<label>%{pages}-இல் %{page_input}-வது பக்கம்</label>"
|
21
|
+
|
22
|
+
items_selector_js: "<label>ஒரு பக்கத்திற்கு %{items_input} %{item_name} காட்டு</label>"
|
data/lib/pagy/backend.rb
CHANGED
@@ -2,34 +2,31 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
class Pagy
|
5
|
-
#
|
5
|
+
# Define a few generic methods to paginate an ORM collection out of the box,
|
6
6
|
# or any collection by overriding pagy_get_items and/or pagy_get_vars in your controller
|
7
|
-
|
8
7
|
# See also the extras if you need specialized methods to paginate Arrays or other collections
|
9
|
-
|
10
|
-
|
11
8
|
module Backend
|
12
|
-
private
|
9
|
+
private
|
13
10
|
|
14
|
-
# Return Pagy object and items
|
15
|
-
def pagy(collection, vars={})
|
11
|
+
# Return Pagy object and paginated items/results
|
12
|
+
def pagy(collection, vars = {})
|
16
13
|
pagy = Pagy.new(pagy_get_vars(collection, vars))
|
17
|
-
[
|
14
|
+
[pagy, pagy_get_items(collection, pagy)]
|
18
15
|
end
|
19
16
|
|
20
17
|
# Sub-method called only by #pagy: here for easy customization of variables by overriding
|
18
|
+
# You may need to override the count call for non AR collections
|
21
19
|
def pagy_get_vars(collection, vars)
|
22
|
-
pagy_set_items_from_params(vars) if defined?(
|
23
|
-
vars[:count] ||= (
|
24
|
-
vars[:page] ||= params[
|
20
|
+
pagy_set_items_from_params(vars) if defined?(ItemsExtra)
|
21
|
+
vars[:count] ||= (count = collection.count(:all)).is_a?(Hash) ? count.size : count
|
22
|
+
vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
|
25
23
|
vars
|
26
24
|
end
|
27
25
|
|
28
26
|
# Sub-method called only by #pagy: here for easy customization of record-extraction by overriding
|
27
|
+
# You may need to override this method for collections without offset|limit
|
29
28
|
def pagy_get_items(collection, pagy)
|
30
|
-
# This should work with ActiveRecord, Sequel, Mongoid...
|
31
29
|
collection.offset(pagy.offset).limit(pagy.items)
|
32
30
|
end
|
33
|
-
|
34
31
|
end
|
35
32
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Pagy # :nodoc:
|
5
|
+
class Calendar # :nodoc:
|
6
|
+
# Calendar day subclass
|
7
|
+
class Day < Calendar
|
8
|
+
DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
|
9
|
+
format: '%Y-%m-%d' }
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# Setup the calendar variables
|
14
|
+
def setup_unit_vars
|
15
|
+
super
|
16
|
+
@initial = @starting.beginning_of_day
|
17
|
+
@final = @ending.tomorrow.beginning_of_day
|
18
|
+
@pages = @last = page_offset(@initial, @final)
|
19
|
+
@from = starting_time_for(@page)
|
20
|
+
@to = @from.tomorrow
|
21
|
+
end
|
22
|
+
|
23
|
+
# Starting time for the page
|
24
|
+
def starting_time_for(page)
|
25
|
+
@initial.days_since(time_offset_for(page))
|
26
|
+
end
|
27
|
+
|
28
|
+
def page_offset_at(time)
|
29
|
+
page_offset(@initial, time.beginning_of_day)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def page_offset(time_a, time_b) # remove in 6.0
|
35
|
+
(time_b.time - time_a.time).to_i / 1.day
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Pagy # :nodoc:
|
4
|
+
class Calendar # :nodoc:
|
5
|
+
# Initializes the calendar objects, reducing complexity in the extra
|
6
|
+
# The returned calendar is a simple hash of units/objects with an added helper
|
7
|
+
# returning the last_object_at(time) used in the extra
|
8
|
+
class Helper < Hash
|
9
|
+
class << self
|
10
|
+
private
|
11
|
+
|
12
|
+
def init(conf, period, params)
|
13
|
+
new.send(:init, conf, period, params)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def init(conf, period, params)
|
20
|
+
@units = Calendar::UNITS & conf.keys # get the units in time length desc order
|
21
|
+
raise ArgumentError, 'no calendar unit found in pagy_calendar @configuration' if @units.empty?
|
22
|
+
|
23
|
+
@period = period
|
24
|
+
@params = params
|
25
|
+
@page_param = conf[:pagy][:page_param] || DEFAULT[:page_param]
|
26
|
+
@conf = Marshal.load(Marshal.dump(conf)) # store a copy
|
27
|
+
@units.each do |unit| # set all the :page_param vars for later deletion
|
28
|
+
unit_page_param = :"#{unit}_#{@page_param}"
|
29
|
+
conf[unit][:page_param] = unit_page_param
|
30
|
+
conf[unit][:page] = @params[unit_page_param]
|
31
|
+
end
|
32
|
+
calendar = {}
|
33
|
+
object = nil
|
34
|
+
@units.each_with_index do |unit, index|
|
35
|
+
params_to_delete = @units[(index + 1), @units.size].map { |sub| conf[sub][:page_param] } + [@page_param]
|
36
|
+
conf[unit][:params] = lambda do |unit_params| # delete page_param from the sub-units
|
37
|
+
# Hash#except missing from ruby 2.5 baseline
|
38
|
+
params_to_delete.each { |p| unit_params.delete(p.to_s) }
|
39
|
+
unit_params
|
40
|
+
end
|
41
|
+
conf[unit][:period] = object&.send(:active_period) || @period
|
42
|
+
calendar[unit] = object = Calendar.send(:create, unit, conf[unit])
|
43
|
+
end
|
44
|
+
[replace(calendar), object.from, object.to]
|
45
|
+
end
|
46
|
+
|
47
|
+
def last_object_at(time)
|
48
|
+
conf = Marshal.load(Marshal.dump(@conf))
|
49
|
+
page_params = {}
|
50
|
+
@units.inject(nil) do |object, unit|
|
51
|
+
conf[unit][:period] = object&.send(:active_period) || @period
|
52
|
+
conf[unit][:page] = page_params[:"#{unit}_#{@page_param}"] \
|
53
|
+
= Calendar.send(:create, unit, conf[unit]).send(:page_at, time)
|
54
|
+
conf[unit][:params] ||= {}
|
55
|
+
conf[unit][:params].merge!(page_params)
|
56
|
+
Calendar.send(:create, unit, conf[unit])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Pagy # :nodoc:
|
5
|
+
class Calendar # :nodoc:
|
6
|
+
# Calendar month subclass
|
7
|
+
class Month < Calendar
|
8
|
+
DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
|
9
|
+
format: '%Y-%m' }
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# Setup the calendar variables
|
14
|
+
def setup_unit_vars
|
15
|
+
super
|
16
|
+
@initial = @starting.beginning_of_month
|
17
|
+
@final = @ending.next_month.beginning_of_month
|
18
|
+
@pages = @last = (months_in(@final) - months_in(@initial))
|
19
|
+
@from = starting_time_for(@page)
|
20
|
+
@to = @from.next_month
|
21
|
+
end
|
22
|
+
|
23
|
+
# Starting time for the page
|
24
|
+
def starting_time_for(page)
|
25
|
+
@initial.months_since(time_offset_for(page))
|
26
|
+
end
|
27
|
+
|
28
|
+
def page_offset_at(time)
|
29
|
+
months_in(time.beginning_of_month) - months_in(@initial)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Number of months in time
|
35
|
+
def months_in(time)
|
36
|
+
(time.year * 12) + time.month
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Pagy # :nodoc:
|
5
|
+
class Calendar # :nodoc:
|
6
|
+
# Calendar quarter subclass
|
7
|
+
class Quarter < Calendar
|
8
|
+
DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
|
9
|
+
format: '%Y-Q%q' } # '%q' token
|
10
|
+
|
11
|
+
# The label for any page, with the substitution of the '%q' token
|
12
|
+
def label_for(page, opts = {})
|
13
|
+
starting_time = starting_time_for(page.to_i) # page could be a string
|
14
|
+
opts[:format] = (opts[:format] || @vars[:format]).gsub('%q') { (starting_time.month / 3.0).ceil }
|
15
|
+
localize(starting_time, opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
# Setup the calendar variables
|
21
|
+
def setup_unit_vars
|
22
|
+
super
|
23
|
+
@initial = @starting.beginning_of_quarter
|
24
|
+
@final = @ending.next_quarter.beginning_of_quarter
|
25
|
+
@pages = @last = (months_in(@final) - months_in(@initial)) / 3
|
26
|
+
@from = starting_time_for(@page)
|
27
|
+
@to = @from.next_quarter
|
28
|
+
end
|
29
|
+
|
30
|
+
# Starting time for the page
|
31
|
+
def starting_time_for(page)
|
32
|
+
@initial.months_since(time_offset_for(page) * 3)
|
33
|
+
end
|
34
|
+
|
35
|
+
def page_offset_at(time)
|
36
|
+
(months_in(time.beginning_of_quarter) - months_in(@initial)) / 3
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Number of months in time
|
42
|
+
def months_in(time)
|
43
|
+
(time.year * 12) + time.month
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Pagy # :nodoc:
|
5
|
+
class Calendar # :nodoc:
|
6
|
+
# Calendar week subclass
|
7
|
+
class Week < Calendar
|
8
|
+
DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
|
9
|
+
format: '%Y-%W' }
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# Setup the calendar variables
|
14
|
+
def setup_unit_vars
|
15
|
+
super
|
16
|
+
@initial = @starting.beginning_of_week
|
17
|
+
@final = @ending.next_week.beginning_of_week
|
18
|
+
@pages = @last = page_offset(@initial, @final)
|
19
|
+
@from = starting_time_for(@page)
|
20
|
+
@to = @from.next_week
|
21
|
+
end
|
22
|
+
|
23
|
+
# Starting time for the page
|
24
|
+
def starting_time_for(page)
|
25
|
+
@initial.weeks_since(time_offset_for(page))
|
26
|
+
end
|
27
|
+
|
28
|
+
def page_offset_at(time)
|
29
|
+
page_offset(@initial, time.beginning_of_week)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def page_offset(time_a, time_b) # remove in 6.0
|
35
|
+
(time_b.time - time_a.time).to_i / 1.week
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Pagy # :nodoc:
|
5
|
+
class Calendar # :nodoc:
|
6
|
+
# Calendar year subclass
|
7
|
+
class Year < Calendar
|
8
|
+
DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
|
9
|
+
format: '%Y' }
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# Setup the calendar variables
|
14
|
+
def setup_unit_vars
|
15
|
+
super
|
16
|
+
@initial = @starting.beginning_of_year
|
17
|
+
@final = @ending.next_year.beginning_of_year
|
18
|
+
@pages = @last = @final.year - @initial.year
|
19
|
+
@from = starting_time_for(@page)
|
20
|
+
@to = @from.next_year
|
21
|
+
end
|
22
|
+
|
23
|
+
# Starting time for the page
|
24
|
+
def starting_time_for(page)
|
25
|
+
@initial.years_since(time_offset_for(page))
|
26
|
+
end
|
27
|
+
|
28
|
+
def page_offset_at(time)
|
29
|
+
time.beginning_of_year.year - @initial.year
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/core_ext/time'
|
6
|
+
require 'active_support/core_ext/date_and_time/calculations'
|
7
|
+
require 'active_support/core_ext/numeric/time'
|
8
|
+
require 'active_support/core_ext/integer/time'
|
9
|
+
|
10
|
+
require 'pagy'
|
11
|
+
|
12
|
+
class Pagy # :nodoc:
|
13
|
+
# Base class for time units subclasses (Year, Quarter, Month, Week, Day)
|
14
|
+
class Calendar < Pagy
|
15
|
+
# Specific out of range error
|
16
|
+
class OutOfRangeError < StandardError; end
|
17
|
+
|
18
|
+
# List of units in desc order of duration. It can be used for custom units.
|
19
|
+
UNITS = %i[year quarter month week day] # rubocop:disable Style/MutableConstant
|
20
|
+
|
21
|
+
attr_reader :order
|
22
|
+
|
23
|
+
# Merge and validate the options, do some simple arithmetic and set a few instance variables
|
24
|
+
def initialize(vars) # rubocop:disable Lint/MissingSuper
|
25
|
+
raise InternalError, 'Pagy::Calendar is a base class; use one of its subclasses' if instance_of?(Pagy::Calendar)
|
26
|
+
|
27
|
+
vars = self.class::DEFAULT.merge(vars) # subclass specific default
|
28
|
+
normalize_vars(vars) # general default
|
29
|
+
setup_vars(page: 1)
|
30
|
+
setup_unit_vars
|
31
|
+
setup_params_var
|
32
|
+
raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
|
33
|
+
|
34
|
+
@prev = (@page - 1 unless @page == 1)
|
35
|
+
@next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
|
36
|
+
end
|
37
|
+
|
38
|
+
# The label for the current page (it can pass along the I18n gem opts when it's used with the i18n extra)
|
39
|
+
def label(opts = {})
|
40
|
+
label_for(@page, opts)
|
41
|
+
end
|
42
|
+
|
43
|
+
# The label for any page (it can pass along the I18n gem opts when it's used with the i18n extra)
|
44
|
+
def label_for(page, opts = {})
|
45
|
+
opts[:format] ||= @vars[:format]
|
46
|
+
localize(starting_time_for(page.to_i), opts) # page could be a string
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
# The page that includes time
|
52
|
+
def page_at(time)
|
53
|
+
raise OutOfRangeError unless time.between?(@initial, @final)
|
54
|
+
|
55
|
+
offset = page_offset_at(time) # offset starts from 0
|
56
|
+
@order == :asc ? offset + 1 : @pages - offset
|
57
|
+
end
|
58
|
+
|
59
|
+
# Base class method for the setup of the unit variables (subclasses must implement it and call super)
|
60
|
+
def setup_unit_vars
|
61
|
+
raise VariableError.new(self, :format, 'to be a strftime format', @vars[:format]) unless @vars[:format].is_a?(String)
|
62
|
+
raise VariableError.new(self, :order, 'to be in [:asc, :desc]', @order) \
|
63
|
+
unless %i[asc desc].include?(@order = @vars[:order])
|
64
|
+
|
65
|
+
@starting, @ending = @vars[:period]
|
66
|
+
raise VariableError.new(self, :period, 'to be a an Array of min and max TimeWithZone instances', @vars[:period]) \
|
67
|
+
unless @starting.is_a?(ActiveSupport::TimeWithZone) \
|
68
|
+
&& @ending.is_a?(ActiveSupport::TimeWithZone) && @starting <= @ending
|
69
|
+
end
|
70
|
+
|
71
|
+
# Apply the strftime format to the time (overridden by the i18n extra when localization is required)
|
72
|
+
def localize(time, opts)
|
73
|
+
time.strftime(opts[:format])
|
74
|
+
end
|
75
|
+
|
76
|
+
# Number of time units to offset from the @initial time, in order to get the ordered starting time for the page.
|
77
|
+
# Used in starting_time_for(page) where page starts from 1 (e.g. page to starting_time means subtracting 1)
|
78
|
+
def time_offset_for(page)
|
79
|
+
@order == :asc ? page - 1 : @pages - page
|
80
|
+
end
|
81
|
+
|
82
|
+
# Period of the active page (used internally for nested units)
|
83
|
+
def active_period
|
84
|
+
[[@starting, @from].max, [@to - 1, @ending].min] # -1 sec: include only last unit day
|
85
|
+
end
|
86
|
+
|
87
|
+
class << self
|
88
|
+
# Create a subclass instance by unit name (internal use)
|
89
|
+
def create(unit, vars)
|
90
|
+
raise InternalError, "unit must be in #{UNITS.inspect}; got #{unit}" unless UNITS.include?(unit)
|
91
|
+
|
92
|
+
name = unit.to_s
|
93
|
+
name[0] = name[0].capitalize
|
94
|
+
Object.const_get("Pagy::Calendar::#{name}").new(vars)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
# Require the subclass files in UNITS (no custom unit at this point yet)
|
99
|
+
Calendar::UNITS.each { |unit| require "pagy/calendar/#{unit}" }
|
100
|
+
end
|
data/lib/pagy/console.rb
CHANGED
@@ -1,21 +1,23 @@
|
|
1
|
+
# See Pagy::Console API documentation: https://ddnexus.github.io/pagy/api/console
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pagy' # so you can require just the extra in the console
|
4
5
|
require 'pagy/extras/standalone'
|
5
6
|
|
6
7
|
class Pagy
|
7
|
-
#
|
8
|
+
# Provide a ready to use pagy environment when included in irb/rails console
|
8
9
|
module Console
|
10
|
+
# Include Backend, Frontend and set the default URL
|
9
11
|
def self.included(main)
|
10
12
|
main.include(Backend)
|
11
13
|
main.include(Frontend)
|
12
|
-
|
14
|
+
DEFAULT[:url] = 'http://www.example.com/subdir'
|
13
15
|
end
|
14
16
|
|
17
|
+
# Require the extras passed as arguments
|
15
18
|
def pagy_extras(*extras)
|
16
|
-
extras.each {|extra| require "pagy/extras/#{extra}"}
|
19
|
+
extras.each { |extra| require "pagy/extras/#{extra}" }
|
17
20
|
puts "Required extras: #{extras.map(&:inspect).join(', ')}"
|
18
21
|
end
|
19
22
|
end
|
20
|
-
|
21
23
|
end
|