pagy 9.3.5 → 43.0.0.rc1

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.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/apps/calendar.ru +547 -551
  3. data/apps/demo.ru +221 -178
  4. data/apps/index.rb +3 -1
  5. data/apps/keynav.ru +258 -0
  6. data/apps/{keyset_ar.ru → keyset.ru} +27 -32
  7. data/apps/{keyset_s.ru → keyset_sequel.ru} +24 -29
  8. data/apps/rails.ru +51 -48
  9. data/apps/repro.ru +46 -43
  10. data/bin/pagy +20 -21
  11. data/config/pagy.rb +35 -207
  12. data/javascripts/ai_widget.js +76 -0
  13. data/javascripts/pagy.js +151 -0
  14. data/javascripts/pagy.js.map +10 -0
  15. data/javascripts/pagy.min.js +1 -4
  16. data/javascripts/pagy.mjs +111 -63
  17. data/javascripts/wand.js +1166 -0
  18. data/lib/pagy/classes/calendar/calendar.rb +98 -0
  19. data/lib/pagy/{calendar → classes/calendar}/day.rb +7 -11
  20. data/lib/pagy/{calendar → classes/calendar}/month.rb +5 -10
  21. data/lib/pagy/{calendar → classes/calendar}/quarter.rb +10 -15
  22. data/lib/pagy/classes/calendar/unit.rb +92 -0
  23. data/lib/pagy/{calendar → classes/calendar}/week.rb +5 -9
  24. data/lib/pagy/{calendar → classes/calendar}/year.rb +5 -6
  25. data/lib/pagy/classes/exceptions.rb +33 -0
  26. data/lib/pagy/classes/keyset/active_record.rb +11 -0
  27. data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
  28. data/lib/pagy/classes/keyset/adapters/sequel.rb +63 -0
  29. data/lib/pagy/classes/keyset/keynav/active_record.rb +13 -0
  30. data/lib/pagy/classes/keyset/keynav/sequel.rb +13 -0
  31. data/lib/pagy/classes/keyset/keynav.rb +77 -0
  32. data/lib/pagy/classes/keyset/keyset.rb +126 -0
  33. data/lib/pagy/classes/keyset/sequel.rb +11 -0
  34. data/lib/pagy/classes/offset/countless.rb +56 -0
  35. data/lib/pagy/classes/offset/offset.rb +54 -0
  36. data/lib/pagy/classes/offset/search.rb +38 -0
  37. data/lib/pagy/classes/request.rb +36 -0
  38. data/lib/pagy/modules/abilities/configurable.rb +36 -0
  39. data/lib/pagy/modules/abilities/linkable.rb +59 -0
  40. data/lib/pagy/modules/abilities/rangeable.rb +15 -0
  41. data/lib/pagy/modules/abilities/shiftable.rb +12 -0
  42. data/lib/pagy/{b64.rb → modules/b64.rb} +3 -7
  43. data/lib/pagy/modules/console.rb +38 -0
  44. data/lib/pagy/modules/i18n/i18n.rb +48 -0
  45. data/lib/pagy/modules/i18n/p11n/arabic.rb +29 -0
  46. data/lib/pagy/modules/i18n/p11n/east_slavic.rb +26 -0
  47. data/lib/pagy/modules/i18n/p11n/one_other.rb +15 -0
  48. data/lib/pagy/modules/i18n/p11n/one_upto_two_other.rb +15 -0
  49. data/lib/pagy/modules/i18n/p11n/other.rb +13 -0
  50. data/lib/pagy/modules/i18n/p11n/polish.rb +26 -0
  51. data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
  52. data/lib/pagy/modules/i18n/p11n.rb +16 -0
  53. data/lib/pagy/modules/searcher.rb +20 -0
  54. data/lib/pagy/toolbox/helpers/anchor_tags.rb +25 -0
  55. data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +24 -0
  56. data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +18 -0
  57. data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +29 -0
  58. data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +21 -0
  59. data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +21 -0
  60. data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +19 -0
  61. data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +28 -0
  62. data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +20 -0
  63. data/lib/pagy/toolbox/helpers/data_hash.rb +26 -0
  64. data/lib/pagy/toolbox/helpers/headers_hash.rb +20 -0
  65. data/lib/pagy/toolbox/helpers/info_tag.rb +26 -0
  66. data/lib/pagy/toolbox/helpers/input_nav_js.rb +19 -0
  67. data/lib/pagy/toolbox/helpers/limit_tag_js.rb +23 -0
  68. data/lib/pagy/toolbox/helpers/loader.rb +33 -0
  69. data/lib/pagy/toolbox/helpers/page_url.rb +23 -0
  70. data/lib/pagy/toolbox/helpers/series_nav.rb +29 -0
  71. data/lib/pagy/toolbox/helpers/series_nav_js.rb +19 -0
  72. data/lib/pagy/toolbox/helpers/support/a_lambda.rb +34 -0
  73. data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +15 -0
  74. data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +12 -0
  75. data/lib/pagy/toolbox/helpers/support/series.rb +38 -0
  76. data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +20 -0
  77. data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +17 -0
  78. data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +36 -0
  79. data/lib/pagy/toolbox/helpers/urls_hash.rb +13 -0
  80. data/lib/pagy/toolbox/paginators/calendar.rb +30 -0
  81. data/lib/pagy/toolbox/paginators/countless.rb +25 -0
  82. data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +32 -0
  83. data/lib/pagy/toolbox/paginators/keynav_js.rb +29 -0
  84. data/lib/pagy/toolbox/paginators/keyset.rb +18 -0
  85. data/lib/pagy/toolbox/paginators/meilisearch.rb +32 -0
  86. data/lib/pagy/toolbox/paginators/method.rb +26 -0
  87. data/lib/pagy/toolbox/paginators/offset.rb +33 -0
  88. data/lib/pagy/toolbox/paginators/searchkick.rb +32 -0
  89. data/lib/pagy.rb +59 -97
  90. data/locales/ar.yml +9 -6
  91. data/locales/be.yml +9 -6
  92. data/locales/bg.yml +9 -6
  93. data/locales/bs.yml +9 -6
  94. data/locales/ca.yml +9 -6
  95. data/locales/ckb.yml +8 -6
  96. data/locales/cs.yml +9 -6
  97. data/locales/da.yml +9 -6
  98. data/locales/de.yml +9 -6
  99. data/locales/dz.yml +9 -6
  100. data/locales/en.yml +9 -6
  101. data/locales/es.yml +9 -6
  102. data/locales/fr.yml +9 -6
  103. data/locales/hr.yml +9 -6
  104. data/locales/id.yml +9 -6
  105. data/locales/it.yml +9 -6
  106. data/locales/ja.yml +9 -6
  107. data/locales/km.yml +9 -6
  108. data/locales/ko.yml +9 -6
  109. data/locales/nb.yml +9 -6
  110. data/locales/nl.yml +9 -6
  111. data/locales/nn.yml +9 -6
  112. data/locales/pl.yml +9 -6
  113. data/locales/pt-BR.yml +10 -7
  114. data/locales/pt.yml +10 -7
  115. data/locales/ru.yml +9 -6
  116. data/locales/sr.yml +9 -6
  117. data/locales/sv-SE.yml +9 -6
  118. data/locales/sv.yml +9 -6
  119. data/locales/sw.yml +12 -9
  120. data/locales/ta.yml +12 -9
  121. data/locales/tr.yml +9 -6
  122. data/locales/uk.yml +9 -6
  123. data/locales/vi.yml +9 -6
  124. data/locales/zh-CN.yml +9 -6
  125. data/locales/zh-HK.yml +9 -6
  126. data/locales/zh-TW.yml +9 -6
  127. data/stylesheets/pagy-tailwind.css +64 -0
  128. data/stylesheets/pagy.css +63 -27
  129. metadata +112 -51
  130. data/javascripts/pagy.min.js.map +0 -10
  131. data/lib/pagy/backend.rb +0 -44
  132. data/lib/pagy/calendar/unit.rb +0 -103
  133. data/lib/pagy/calendar.rb +0 -84
  134. data/lib/pagy/console.rb +0 -23
  135. data/lib/pagy/countless.rb +0 -38
  136. data/lib/pagy/exceptions.rb +0 -25
  137. data/lib/pagy/extras/arel.rb +0 -28
  138. data/lib/pagy/extras/array.rb +0 -19
  139. data/lib/pagy/extras/bootstrap.rb +0 -97
  140. data/lib/pagy/extras/bulma.rb +0 -93
  141. data/lib/pagy/extras/calendar.rb +0 -79
  142. data/lib/pagy/extras/countless.rb +0 -32
  143. data/lib/pagy/extras/elasticsearch_rails.rb +0 -71
  144. data/lib/pagy/extras/gearbox.rb +0 -55
  145. data/lib/pagy/extras/headers.rb +0 -54
  146. data/lib/pagy/extras/i18n.rb +0 -26
  147. data/lib/pagy/extras/js_tools.rb +0 -70
  148. data/lib/pagy/extras/jsonapi.rb +0 -88
  149. data/lib/pagy/extras/keyset.rb +0 -30
  150. data/lib/pagy/extras/limit.rb +0 -63
  151. data/lib/pagy/extras/meilisearch.rb +0 -57
  152. data/lib/pagy/extras/metadata.rb +0 -42
  153. data/lib/pagy/extras/overflow.rb +0 -81
  154. data/lib/pagy/extras/pagy.rb +0 -82
  155. data/lib/pagy/extras/searchkick.rb +0 -59
  156. data/lib/pagy/extras/size.rb +0 -40
  157. data/lib/pagy/extras/standalone.rb +0 -60
  158. data/lib/pagy/extras/trim.rb +0 -29
  159. data/lib/pagy/frontend.rb +0 -99
  160. data/lib/pagy/i18n.rb +0 -166
  161. data/lib/pagy/keyset/active_record.rb +0 -44
  162. data/lib/pagy/keyset/sequel.rb +0 -57
  163. data/lib/pagy/keyset.rb +0 -118
  164. data/lib/pagy/shared_methods.rb +0 -26
  165. data/lib/pagy/url_helpers.rb +0 -26
  166. data/stylesheets/pagy.scss +0 -48
  167. data/stylesheets/pagy.tailwind.css +0 -21
data/javascripts/pagy.mjs CHANGED
@@ -1,52 +1,107 @@
1
1
  const Pagy = (() => {
2
- const rjsObserver = new ResizeObserver((entries) => entries.forEach((e) => e.target.querySelectorAll(".pagy-rjs").forEach((el) => el.pagyRender())));
3
- const initNav = (el, [tokens, sequels, labelSequels, trimParam]) => {
4
- const container = el.parentElement ?? el;
5
- const widths = Object.keys(sequels).map((w) => parseInt(w)).sort((a, b) => b - a);
2
+ const storageSupport = "sessionStorage" in window && "BroadcastChannel" in window, pageRe = "P ";
3
+ let pagy = "pagy", storage, sync, tabId;
4
+ if (storageSupport) {
5
+ storage = sessionStorage;
6
+ sync = new BroadcastChannel(pagy);
7
+ tabId = Date.now();
8
+ sync.addEventListener("message", (e) => {
9
+ if (e.data.from) {
10
+ const cutoffs = storage.getItem(e.data.key);
11
+ if (cutoffs) {
12
+ sync.postMessage({ to: e.data.from, key: e.data.key, str: cutoffs });
13
+ }
14
+ } else if (e.data.to) {
15
+ if (e.data.to == tabId) {
16
+ storage.setItem(e.data.key, e.data.str);
17
+ }
18
+ }
19
+ });
20
+ }
21
+ const rjsObserver = new ResizeObserver((entries) => entries.forEach((e) => {
22
+ e.target.querySelectorAll(".pagy-rjs").forEach((el) => el.render());
23
+ }));
24
+ const B64SafeEncode = (unicode) => btoa(String.fromCharCode(...new TextEncoder().encode(unicode))).replace(/[+/=]/g, (m) => m == "+" ? "-" : m == "/" ? "_" : ""), B64Decode = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
25
+ const randKey = () => Math.floor(Math.random() * 36 ** 3).toString(36);
26
+ const augmentKeynav = async (nav, [storageKey, pageKey, last, spliceArgs]) => {
27
+ let augment;
28
+ const browserKey = document.cookie.split(/;\s+/).find((row) => row.startsWith(pagy + "="))?.split("=")[1] ?? randKey();
29
+ document.cookie = pagy + "=" + browserKey;
30
+ if (storageKey && !(storageKey in storage)) {
31
+ sync.postMessage({ from: tabId, key: storageKey });
32
+ await new Promise((resolve) => setTimeout(() => resolve(""), 100));
33
+ if (!(storageKey in storage)) {
34
+ augment = (page) => page + "+" + last;
35
+ }
36
+ }
37
+ if (!augment) {
38
+ if (!storageKey) {
39
+ do {
40
+ storageKey = randKey();
41
+ } while (storageKey in storage);
42
+ }
43
+ const data = storage.getItem(storageKey), cutoffs = data ? JSON.parse(data) : [undefined];
44
+ if (spliceArgs) {
45
+ cutoffs.splice(...spliceArgs);
46
+ storage.setItem(storageKey, JSON.stringify(cutoffs));
47
+ }
48
+ augment = (page) => {
49
+ const pageNum = parseInt(page);
50
+ return B64SafeEncode(JSON.stringify([
51
+ browserKey,
52
+ storageKey,
53
+ pageNum,
54
+ cutoffs.length,
55
+ cutoffs[pageNum - 1],
56
+ cutoffs[pageNum]
57
+ ]));
58
+ };
59
+ }
60
+ for (const a of nav.querySelectorAll("a[href]")) {
61
+ const url = a.href, re = new RegExp(`(?<=\\?.*)\\b${pageKey}=(\\d+)`);
62
+ a.href = url.replace(re, pageKey + "=" + augment(url.match(re)[1]));
63
+ }
64
+ return augment;
65
+ };
66
+ const buildNavJs = (nav, [
67
+ [before, anchor, current, gap, after],
68
+ [widths, series, labels],
69
+ keynavArgs
70
+ ]) => {
71
+ const parent = nav.parentElement;
6
72
  let lastWidth = -1;
7
- const fillIn = (a, page, label) => a.replace(/__pagy_page__/g, page).replace(/__pagy_label__/g, label);
8
- (el.pagyRender = function() {
9
- const width = widths.find((w) => w < container.clientWidth) || 0;
10
- if (width === lastWidth) {
73
+ (nav.render = () => {
74
+ const index = widths.findIndex((w) => w < parent.clientWidth);
75
+ if (widths[index] === lastWidth) {
11
76
  return;
12
77
  }
13
- let html = tokens.before;
14
- const series = sequels[width.toString()];
15
- const labels = labelSequels?.[width.toString()] ?? series.map((l) => l.toString());
16
- series.forEach((item, i) => {
17
- const label = labels[i];
18
- let filled;
19
- if (typeof item === "number") {
20
- filled = fillIn(tokens.a, item.toString(), label);
21
- } else if (item === "gap") {
22
- filled = tokens.gap;
23
- } else {
24
- filled = fillIn(tokens.current, item, label);
25
- }
26
- html += typeof trimParam === "string" && item == 1 ? trim(filled, trimParam) : filled;
78
+ let html = before;
79
+ series[index].forEach((item, i) => {
80
+ html += item == "gap" ? gap : (typeof item == "number" ? anchor.replace(pageRe, item) : current).replace("L<", labels?.[index][i] ?? item + "<");
27
81
  });
28
- html += tokens.after;
29
- el.innerHTML = "";
30
- el.insertAdjacentHTML("afterbegin", html);
31
- lastWidth = width;
82
+ html += after;
83
+ nav.innerHTML = "";
84
+ nav.insertAdjacentHTML("afterbegin", html);
85
+ lastWidth = widths[index];
86
+ if (keynavArgs && storageSupport) {
87
+ augmentKeynav(nav, keynavArgs);
88
+ }
32
89
  })();
33
- if (el.classList.contains("pagy-rjs")) {
34
- rjsObserver.observe(container);
90
+ if (nav.classList.contains(pagy + "-rjs")) {
91
+ rjsObserver.observe(parent);
35
92
  }
36
93
  };
37
- const initCombo = (el, [url_token, trimParam]) => initInput(el, (inputValue) => [inputValue, url_token.replace(/__pagy_page__/, inputValue)], trimParam);
38
- const initSelector = (el, [from, url_token, trimParam]) => {
39
- initInput(el, (inputValue) => {
40
- const page = Math.max(Math.ceil(from / parseInt(inputValue)), 1).toString();
41
- const url = url_token.replace(/__pagy_page__/, page).replace(/__pagy_limit__/, inputValue);
42
- return [page, url];
43
- }, trimParam);
94
+ const initInputNavJs = async (nav, [url_token, keynavArgs]) => {
95
+ const augment = keynavArgs && storageSupport ? await augmentKeynav(nav, keynavArgs) : (page) => page;
96
+ initInput(nav, (inputValue) => url_token.replace(pageRe, augment(inputValue)));
97
+ };
98
+ const initLimitTagJs = (span, [from, url_token]) => {
99
+ initInput(span, (inputValue) => {
100
+ return url_token.replace(pageRe, Math.max(Math.ceil(from / parseInt(inputValue)), 1)).replace("L ", inputValue);
101
+ });
44
102
  };
45
- const initInput = (el, getVars, trimParam) => {
46
- const input = el.querySelector("input");
47
- const link = el.querySelector("a");
48
- const initial = input.value;
49
- const action = function() {
103
+ const initInput = (element, getUrl) => {
104
+ const input = element.querySelector("input"), link = element.querySelector("a"), initial = input.value, action = () => {
50
105
  if (input.value === initial) {
51
106
  return;
52
107
  }
@@ -56,42 +111,35 @@ const Pagy = (() => {
56
111
  input.select();
57
112
  return;
58
113
  }
59
- let [page, url] = getVars(input.value);
60
- if (typeof trimParam === "string" && page === "1") {
61
- url = trim(url, trimParam);
62
- }
63
- link.href = url;
114
+ link.href = getUrl(input.value);
64
115
  link.click();
65
116
  };
66
- ["change", "focus"].forEach((e) => input.addEventListener(e, () => input.select()));
117
+ input.addEventListener("focus", () => input.select());
67
118
  input.addEventListener("focusout", action);
68
119
  input.addEventListener("keypress", (e) => {
69
- if (e.key === "Enter") {
120
+ if (e.key == "Enter") {
70
121
  action();
71
122
  }
72
123
  });
73
124
  };
74
- const trim = (a, param) => a.replace(new RegExp(`[?&]${param}=1\\b(?!&)|\\b${param}=1&`), "");
75
125
  return {
76
- version: "9.3.5",
126
+ version: "43.0.0.rc1",
77
127
  init(arg) {
78
- const target = arg instanceof Element ? arg : document;
79
- const elements = target.querySelectorAll("[data-pagy]");
80
- for (const el of elements) {
128
+ const target = arg instanceof HTMLElement ? arg : document, elements = target.querySelectorAll("[data-pagy]");
129
+ for (const element of elements) {
81
130
  try {
82
- const uint8array = Uint8Array.from(atob(el.getAttribute("data-pagy")), (c) => c.charCodeAt(0));
83
- const [keyword, ...args] = JSON.parse(new TextDecoder().decode(uint8array));
84
- if (keyword === "nav") {
85
- initNav(el, args);
86
- } else if (keyword === "combo") {
87
- initCombo(el, args);
88
- } else if (keyword === "selector") {
89
- initSelector(el, args);
90
- } else {
91
- console.warn("Skipped Pagy.init() for: %o\nUnknown keyword '%s'", el, keyword);
131
+ const [helperId, ...args] = JSON.parse(B64Decode(element.getAttribute("data-pagy")));
132
+ if (helperId == "k") {
133
+ augmentKeynav(element, ...args);
134
+ } else if (helperId == "snj") {
135
+ buildNavJs(element, args);
136
+ } else if (helperId == "inj") {
137
+ initInputNavJs(element, args);
138
+ } else if (helperId == "ltj") {
139
+ initLimitTagJs(element, args);
92
140
  }
93
141
  } catch (err) {
94
- console.warn("Skipped Pagy.init() for: %o\n%s", el, err);
142
+ console.warn("Pagy.init: %o\n%s", element, err);
95
143
  }
96
144
  }
97
145
  }