pagy 43.2.6 → 43.2.7

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/apps/calendar.ru +1 -1
  3. data/apps/demo.ru +1 -1
  4. data/apps/keynav+root_key.ru +1 -1
  5. data/apps/keynav.ru +1 -1
  6. data/apps/keyset.ru +1 -1
  7. data/apps/keyset_sequel.ru +1 -1
  8. data/apps/rails.ru +1 -1
  9. data/apps/repro.ru +1 -1
  10. data/config/pagy.rb +1 -1
  11. data/javascripts/pagy.js +2 -2
  12. data/javascripts/pagy.js.map +2 -2
  13. data/javascripts/pagy.min.js +1 -1
  14. data/javascripts/pagy.mjs +1 -1
  15. data/lib/pagy/classes/keyset/keyset.rb +17 -9
  16. data/lib/pagy/modules/b64.rb +1 -1
  17. data/lib/pagy/modules/i18n/p11n/arabic.rb +1 -0
  18. data/lib/pagy/modules/i18n/p11n/east_slavic.rb +1 -0
  19. data/lib/pagy/modules/i18n/p11n/polish.rb +1 -0
  20. data/lib/pagy/toolbox/helpers/anchor_tags.rb +11 -15
  21. data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +3 -0
  22. data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +3 -1
  23. data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +2 -0
  24. data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +3 -0
  25. data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +3 -1
  26. data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +2 -0
  27. data/lib/pagy/toolbox/helpers/data_hash.rb +3 -2
  28. data/lib/pagy/toolbox/helpers/input_nav_js.rb +7 -5
  29. data/lib/pagy/toolbox/helpers/limit_tag_js.rb +2 -1
  30. data/lib/pagy/toolbox/helpers/series_nav.rb +3 -3
  31. data/lib/pagy/toolbox/helpers/support/a_lambda.rb +6 -4
  32. data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +6 -1
  33. data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +1 -2
  34. data/lib/pagy/toolbox/helpers/support/series.rb +1 -2
  35. data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +1 -0
  36. data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +7 -2
  37. data/lib/pagy/toolbox/paginators/calendar.rb +4 -2
  38. data/lib/pagy/toolbox/paginators/countish.rb +4 -5
  39. data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +1 -0
  40. data/lib/pagy/toolbox/paginators/keyset.rb +1 -1
  41. data/lib/pagy/toolbox/paginators/meilisearch.rb +1 -0
  42. data/lib/pagy/toolbox/paginators/searchkick.rb +3 -2
  43. data/lib/pagy.rb +12 -4
  44. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b2cea241fa37b5ad6a230e342a2d317dee750bd834fbfc7bc80c2bbea4df581a
4
- data.tar.gz: 5073e531947cea02f78748b662643c6ade3f7cf412ed96431da5f283ac18e33e
3
+ metadata.gz: 17c8ff1175a4401c1b0491a11369da541e0d55a6cb5590af9840a815c5453358
4
+ data.tar.gz: c04ad1216d0b0672100c7b7b04e839b74aca129c3cc7044a6d10a781e3d31d00
5
5
  SHA512:
6
- metadata.gz: 436d39d4742c8360267346b3e277679a6d25243f4a5a7cf6b44cde048aac33d011a51d90f42f7c3f620c25ebc30952ffcaf159907fb296c5bf22622a53afd096
7
- data.tar.gz: af0603b45ba5d37bd505582af5a215e8695ed39bf7dd860a63e643f151ab4cb7073a1830bc1069abe925526c1d40863cac2649b92174dd909bf24bea664c138d
6
+ metadata.gz: 8f79a780b5f763eef0829c60b584f91a22757cc714a83f71f69215d654b2de9f6beaa76486ce58b1fa75f8a542fdebea51fcaac3ddac970e9f791a7fca5a6511
7
+ data.tar.gz: 136b87f07fd0ad161e03ae8cd689eee102a02c2296426bf7fc28c34421c27ca99927fdacab69f419f45a39e99fcc71201fb448a35336c14b652240a0fa0051c8
data/apps/calendar.ru CHANGED
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.6'
19
+ VERSION = '43.2.7'
20
20
 
21
21
  if VERSION != Pagy::VERSION
22
22
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
data/apps/demo.ru CHANGED
@@ -19,7 +19,7 @@
19
19
  # URL
20
20
  # http://127.0.0.1:8000
21
21
 
22
- VERSION = '43.2.6'
22
+ VERSION = '43.2.7'
23
23
 
24
24
  if VERSION != Pagy::VERSION
25
25
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.6'
19
+ VERSION = '43.2.7'
20
20
 
21
21
  if VERSION != Pagy::VERSION
22
22
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
data/apps/keynav.ru CHANGED
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.6'
19
+ VERSION = '43.2.7'
20
20
 
21
21
  if VERSION != Pagy::VERSION
22
22
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
data/apps/keyset.ru CHANGED
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.6'
19
+ VERSION = '43.2.7'
20
20
 
21
21
  if VERSION != Pagy::VERSION
22
22
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.6'
19
+ VERSION = '43.2.7'
20
20
 
21
21
  if VERSION != Pagy::VERSION
22
22
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
data/apps/rails.ru CHANGED
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.6'
19
+ VERSION = '43.2.7'
20
20
 
21
21
  if VERSION != Pagy::VERSION
22
22
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
data/apps/repro.ru CHANGED
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.6'
19
+ VERSION = '43.2.7'
20
20
 
21
21
  if VERSION != Pagy::VERSION
22
22
  Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
data/config/pagy.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Pagy initializer file (43.2.6)
3
+ # Pagy initializer file (43.2.7)
4
4
  # See https://ddnexus.github.io/pagy/resources/initializer/
5
5
 
6
6
  ############ Global Options ################################################################
data/javascripts/pagy.js CHANGED
@@ -126,7 +126,7 @@ window.Pagy = (() => {
126
126
  });
127
127
  };
128
128
  return {
129
- version: "43.2.6",
129
+ version: "43.2.7",
130
130
  init(arg) {
131
131
  const target = arg instanceof HTMLElement ? arg : document, elements = target.querySelectorAll("[data-pagy]");
132
132
  for (const element of elements) {
@@ -150,5 +150,5 @@ window.Pagy = (() => {
150
150
  };
151
151
  })();
152
152
 
153
- //# debugId=A3F308FA5DB8330B64756E2164756E21
153
+ //# debugId=2F5F1987603BEC1464756E2164756E21
154
154
  //# sourceMappingURL=pagy.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/pagy.ts"],
4
4
  "sourcesContent": [
5
- "interface SyncData {\n from?: number\n to?: number\n key: string\n str?: string\n}\ntype InitArgs = [\"k\", KeynavArgs] | // series_nav[_js] with keynav instance\n [\"snj\", SeriesNavJsArgs] | // series_nav_js\n [\"inj\", InputNavJsArgs] | // input_nav_js\n [\"ltj\", LimitTagJsArgs] // limit_tag_js\ntype AugmentKeynav = (nav:HTMLElement, keynavArgs:KeynavArgs) => Promise<((page: string) => string)>\ntype KeynavArgs = readonly [storageKey: string | null,\n rootKey: string | null,\n pageKey: string,\n last: number,\n spliceArgs?: SpliceArgs]\ntype SpliceArgs = readonly [start: number,\n deleteCount: number, // it would be optional, but ts complains\n ...items: Cutoff[]]\ntype Cutoff = readonly (string | number | boolean)[]\ntype AugmentedPage = [browserId: string,\n storageKey: string,\n pageNumber: number,\n pages: number,\n priorCutoff: Cutoff | null,\n pageCutoff: Cutoff | null]\ntype SeriesNavJsArgs = readonly [NavJsTokens, pageToken: string, NavJsSeries, KeynavArgs?]\ntype NavJsSeries = readonly [widths: number[],\n series: (string | number)[][],\n labels: string[][] | null]\ntype InputNavJsArgs = readonly [urlToken: string,\n pageToken: string,\n KeynavArgs?]\ntype LimitTagJsArgs = readonly [from: number,\n urlToken: string,\n pageToken: string,\n limitToken: string]\ntype NavJsTokens = readonly [before: string,\n anchor: string,\n current: string,\n gap: string,\n after: string]\ninterface NavJsElement extends HTMLElement {\n render(): void\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst Pagy = (() => {\n const storageSupport = 'sessionStorage' in window && 'BroadcastChannel' in window;\n // eslint-disable-next-line prefer-const\n let pagy = \"pagy\", storage: Storage, sync: BroadcastChannel, tabId: number;\n if (storageSupport) {\n storage = sessionStorage; // shorten the compiled size\n sync = new BroadcastChannel(pagy);\n tabId = Date.now();\n // Sync the sessionStorage keys for the cutoffs opened in a new tab/window\n sync.addEventListener(\"message\", (e:MessageEvent<SyncData>) => {\n if (e.data.from) { // request cutoffs\n const cutoffs = storage.getItem(e.data.key);\n if (cutoffs) {\n sync.postMessage(<SyncData>{to: e.data.from, key: e.data.key, str: cutoffs});\n } // send response\n } else if (e.data.to) { // receive cutoffs\n if (e.data.to == tabId) {\n storage.setItem(e.data.key, <string>e.data.str);\n }\n }\n });\n }\n // The observer instance for responsive navs\n const rjsObserver = new ResizeObserver(\n entries => entries.forEach(e => {\n e.target.querySelectorAll<NavJsElement>(\".pagy-rjs\").forEach(el => el.render());\n }));\n\n /* Full set of B64 functions\n const B64Encode = (unicode:string) => btoa(String.fromCharCode(...(new TextEncoder).encode(unicode))),\n B64Safe = (unsafe:string) => unsafe.replace(/[+/=]/g, (m) => m == \"+\" ? \"-\" : m == \"/\" ? \"_\" : \"\"),\n B64SafeEncode = (unicode:string) => B64Safe(B64Encode(unicode)),\n B64Decode = (base64:string) => (new TextDecoder()).decode(Uint8Array.from(atob(base64), c => c.charCodeAt(0))),\n B64Unsafe = (safe:string) => safe.replace(/[-_]/g, (match) => match == \"-\" ? \"+\" : \"/\"),\n B64SafeDecode = (base64:string) => B64Decode(B64Unsafe(base64))\n */\n const B64SafeEncode = (unicode:string) => btoa(String.fromCharCode(...(new TextEncoder).encode(unicode)))\n .replace(/[+/=]/g, (m) => m == \"+\" ? \"-\" : m == \"/\" ? \"_\" : \"\"),\n B64Decode = (base64:string) => (new TextDecoder()).decode(Uint8Array.from(atob(base64), c => c.charCodeAt(0)));\n\n // Return a random key: 3 chars max, base-36 number < 36**3\n const randKey = () => Math.floor(Math.random() * 36 ** 3).toString(36);\n\n // Manage the page augmentation for Keynav, called only if storageSupport\n const augmentKeynav: AugmentKeynav = async (nav, [storageKey, rootKey, pageKey, last, spliceArgs]) => {\n let augmentPage:(page: string) => string;\n const browserKey = document.cookie.split(/;\\s+/) // it works even if malformed\n .find((row) => row.startsWith(pagy + \"=\"))\n ?.split(\"=\")[1] ?? randKey();\n document.cookie = pagy + \"=\" + browserKey; // Smaller .min size: set the cookie without checking\n if (storageKey && !(storageKey in storage)) {\n // Sync the sessiongStorage from other tabs/windows (e.g., open page in the new tab/window)\n sync.postMessage(<SyncData>{ from: tabId, key: storageKey });\n // Wait for the listener to copy the cutoffs in the current sessionStorage\n await new Promise<string|null>((resolve) => setTimeout(() => resolve(\"\"), 100));\n if (!(storageKey in storage)) { // the storageKey didn't get copied: fallback to countless pagination\n augmentPage = (page: string) => page + '+' + last;\n }\n }\n // @ts-expect-error If it is not assigned it means it supports keynav\n if (!augmentPage) { // regular keynav pagination\n if (!storageKey) { do { storageKey = randKey() } while (storageKey in storage) } // no dup keys\n const data = storage.getItem(storageKey),\n cutoffs = <Cutoff[]>(data ? JSON.parse(data) : [undefined]);\n if (spliceArgs) {\n cutoffs.splice(...spliceArgs);\n storage.setItem(storageKey, JSON.stringify(cutoffs));\n }\n // Augment function\n augmentPage = (page:string) => {\n const pageNum = parseInt(page);\n return B64SafeEncode(JSON.stringify(\n <AugmentedPage>[browserKey,\n storageKey,\n pageNum,\n cutoffs.length, // pages/last\n cutoffs[pageNum - 1], // priorCutoff\n cutoffs[pageNum]])); // pageCutoff\n };\n }\n const search = (rootKey) ? `${rootKey}%5B${pageKey}%5D` : pageKey;\n const re = new RegExp(`(?<=\\\\?.*)(\\\\b${search}=)(\\\\d+)`);\n // Augment the page param of each href\n for (const a of <NodeListOf<HTMLAnchorElement>><unknown>nav.querySelectorAll('a[href]')) {\n a.href = a.href.replace(re, (_match, prefix, digit): string => `${prefix}${augmentPage(<string>digit)}`);\n }\n // Return the augment function for further augmentation (i.e., url token in input_nav_js)\n return augmentPage;\n };\n\n // Build the series_nav_js helper\n const buildNavJs = (nav:NavJsElement, [[before, anchor, current, gap, after], pageToken,\n [widths, series, labels], keynavArgs]:SeriesNavJsArgs) => {\n const parent = <HTMLElement>nav.parentElement;\n let lastWidth = -1;\n (nav.render = () => {\n const index = widths.findIndex(w => w < parent.clientWidth);\n if (widths[index] === lastWidth) { return } // no change: abort\n\n let html = before;\n series[index].forEach((item, i) => {\n // Avoid the if blocks and chain the results (shorter pagy.min.js and easier reading)\n html += item == \"gap\" ? gap :\n // @ts-expect-error the item may be a number, but the 'replace' converts it to string (shorter pagy.min.js)\n (typeof item == \"number\" ? anchor.replace(pageToken, item) : current)\n .replace(\"L<\", labels?.[index][i] ?? item + \"<\");\n });\n html += after;\n nav.innerHTML = \"\";\n nav.insertAdjacentHTML(\"afterbegin\", html);\n lastWidth = widths[index];\n if (keynavArgs && storageSupport) { void augmentKeynav(nav, keynavArgs) }\n })();\n if (nav.classList.contains(pagy + \"-rjs\")) { rjsObserver.observe(parent) }\n };\n\n // Init the input_nav_js helpers\n const initInputNavJs = async (nav:HTMLElement, [url_token, pageToken, keynavArgs]:InputNavJsArgs) => {\n const augment = keynavArgs && storageSupport\n ? await augmentKeynav(nav, keynavArgs)\n : (page: string) => page;\n initInput(nav, inputValue => url_token.replace(pageToken, augment(inputValue)));\n };\n\n // Init the limit_tag_js helper\n const initLimitTagJs = (span:HTMLSpanElement, [from, url_token, page_token, limitToken]:LimitTagJsArgs) => {\n initInput(span, inputValue => {\n // @ts-expect-error the page is a number, but the 'replace' converts it to string (shorter pagy.min.js)\n return url_token.replace(page_token, Math.max(Math.ceil(from / parseInt(inputValue)), 1))\n .replace(limitToken, inputValue);\n });\n };\n\n // Init the input element\n const initInput = (element:HTMLElement, getUrl:(v:string) => string) => {\n const input = <HTMLInputElement>element.querySelector(\"input\"),\n link = <HTMLAnchorElement>element.querySelector(\"a\"),\n initial = input.value,\n action = () => {\n if (input.value === initial) { return } // not changed\n const [min, val, max] = [input.min, input.value, input.max].map(n => parseInt(n) || 0);\n if (val < min || val > max) { // reset invalid/out-of-range\n input.value = initial;\n input.select();\n return;\n }\n link.href = getUrl(input.value);\n link.click();\n };\n input.addEventListener(\"focus\", () => input.select());\n input.addEventListener(\"focusout\", action);\n input.addEventListener(\"keypress\", e => { if (e.key == \"Enter\") { action() } });\n };\n\n // Public interface\n return {\n version: \"43.2.6\",\n\n // Scan for elements with a \"data-pagy\" attribute and call their init functions with the decoded args\n init(arg?:HTMLElement) {\n const target = arg instanceof HTMLElement ? arg : document,\n elements = target.querySelectorAll(\"[data-pagy]\");\n for (const element of <NodeListOf<HTMLElement>>elements) {\n try {\n const [helperId, ...args] = <InitArgs>JSON.parse(B64Decode(<string>element.getAttribute(\"data-pagy\")));\n if (helperId == \"k\") {\n // @ts-expect-error spread 2 arguments, not 3 as it complains about\n void augmentKeynav(element, ...<KeynavArgs><unknown>args);\n } else if (helperId == \"snj\") {\n buildNavJs(<NavJsElement>element, <SeriesNavJsArgs><unknown>args);\n } else if (helperId == \"inj\") {\n void initInputNavJs(element, <InputNavJsArgs><unknown>args);\n } else if (helperId == \"ltj\") {\n initLimitTagJs(element, <LimitTagJsArgs><unknown>args);\n }\n // else { console.warn(\"Pagy.init: %o\\nUnknown helperId '%s'\", element, helperId) }\n } catch (err) { console.warn(\"Pagy.init: %o\\n%s\", element, err) }\n }\n }\n };\n})();\n"
5
+ "interface SyncData {\n from?: number\n to?: number\n key: string\n str?: string\n}\ntype InitArgs = [\"k\", KeynavArgs] | // series_nav[_js] with keynav instance\n [\"snj\", SeriesNavJsArgs] | // series_nav_js\n [\"inj\", InputNavJsArgs] | // input_nav_js\n [\"ltj\", LimitTagJsArgs] // limit_tag_js\ntype AugmentKeynav = (nav:HTMLElement, keynavArgs:KeynavArgs) => Promise<((page: string) => string)>\ntype KeynavArgs = readonly [storageKey: string | null,\n rootKey: string | null,\n pageKey: string,\n last: number,\n spliceArgs?: SpliceArgs]\ntype SpliceArgs = readonly [start: number,\n deleteCount: number, // it would be optional, but ts complains\n ...items: Cutoff[]]\ntype Cutoff = readonly (string | number | boolean)[]\ntype AugmentedPage = [browserId: string,\n storageKey: string,\n pageNumber: number,\n pages: number,\n priorCutoff: Cutoff | null,\n pageCutoff: Cutoff | null]\ntype SeriesNavJsArgs = readonly [NavJsTokens, pageToken: string, NavJsSeries, KeynavArgs?]\ntype NavJsSeries = readonly [widths: number[],\n series: (string | number)[][],\n labels: string[][] | null]\ntype InputNavJsArgs = readonly [urlToken: string,\n pageToken: string,\n KeynavArgs?]\ntype LimitTagJsArgs = readonly [from: number,\n urlToken: string,\n pageToken: string,\n limitToken: string]\ntype NavJsTokens = readonly [before: string,\n anchor: string,\n current: string,\n gap: string,\n after: string]\ninterface NavJsElement extends HTMLElement {\n render(): void\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst Pagy = (() => {\n const storageSupport = 'sessionStorage' in window && 'BroadcastChannel' in window;\n // eslint-disable-next-line prefer-const\n let pagy = \"pagy\", storage: Storage, sync: BroadcastChannel, tabId: number;\n if (storageSupport) {\n storage = sessionStorage; // shorten the compiled size\n sync = new BroadcastChannel(pagy);\n tabId = Date.now();\n // Sync the sessionStorage keys for the cutoffs opened in a new tab/window\n sync.addEventListener(\"message\", (e:MessageEvent<SyncData>) => {\n if (e.data.from) { // request cutoffs\n const cutoffs = storage.getItem(e.data.key);\n if (cutoffs) {\n sync.postMessage(<SyncData>{to: e.data.from, key: e.data.key, str: cutoffs});\n } // send response\n } else if (e.data.to) { // receive cutoffs\n if (e.data.to == tabId) {\n storage.setItem(e.data.key, <string>e.data.str);\n }\n }\n });\n }\n // The observer instance for responsive navs\n const rjsObserver = new ResizeObserver(\n entries => entries.forEach(e => {\n e.target.querySelectorAll<NavJsElement>(\".pagy-rjs\").forEach(el => el.render());\n }));\n\n /* Full set of B64 functions\n const B64Encode = (unicode:string) => btoa(String.fromCharCode(...(new TextEncoder).encode(unicode))),\n B64Safe = (unsafe:string) => unsafe.replace(/[+/=]/g, (m) => m == \"+\" ? \"-\" : m == \"/\" ? \"_\" : \"\"),\n B64SafeEncode = (unicode:string) => B64Safe(B64Encode(unicode)),\n B64Decode = (base64:string) => (new TextDecoder()).decode(Uint8Array.from(atob(base64), c => c.charCodeAt(0))),\n B64Unsafe = (safe:string) => safe.replace(/[-_]/g, (match) => match == \"-\" ? \"+\" : \"/\"),\n B64SafeDecode = (base64:string) => B64Decode(B64Unsafe(base64))\n */\n const B64SafeEncode = (unicode:string) => btoa(String.fromCharCode(...(new TextEncoder).encode(unicode)))\n .replace(/[+/=]/g, (m) => m == \"+\" ? \"-\" : m == \"/\" ? \"_\" : \"\"),\n B64Decode = (base64:string) => (new TextDecoder()).decode(Uint8Array.from(atob(base64), c => c.charCodeAt(0)));\n\n // Return a random key: 3 chars max, base-36 number < 36**3\n const randKey = () => Math.floor(Math.random() * 36 ** 3).toString(36);\n\n // Manage the page augmentation for Keynav, called only if storageSupport\n const augmentKeynav: AugmentKeynav = async (nav, [storageKey, rootKey, pageKey, last, spliceArgs]) => {\n let augmentPage:(page: string) => string;\n const browserKey = document.cookie.split(/;\\s+/) // it works even if malformed\n .find((row) => row.startsWith(pagy + \"=\"))\n ?.split(\"=\")[1] ?? randKey();\n document.cookie = pagy + \"=\" + browserKey; // Smaller .min size: set the cookie without checking\n if (storageKey && !(storageKey in storage)) {\n // Sync the sessiongStorage from other tabs/windows (e.g., open page in the new tab/window)\n sync.postMessage(<SyncData>{ from: tabId, key: storageKey });\n // Wait for the listener to copy the cutoffs in the current sessionStorage\n await new Promise<string|null>((resolve) => setTimeout(() => resolve(\"\"), 100));\n if (!(storageKey in storage)) { // the storageKey didn't get copied: fallback to countless pagination\n augmentPage = (page: string) => page + '+' + last;\n }\n }\n // @ts-expect-error If it is not assigned it means it supports keynav\n if (!augmentPage) { // regular keynav pagination\n if (!storageKey) { do { storageKey = randKey() } while (storageKey in storage) } // no dup keys\n const data = storage.getItem(storageKey),\n cutoffs = <Cutoff[]>(data ? JSON.parse(data) : [undefined]);\n if (spliceArgs) {\n cutoffs.splice(...spliceArgs);\n storage.setItem(storageKey, JSON.stringify(cutoffs));\n }\n // Augment function\n augmentPage = (page:string) => {\n const pageNum = parseInt(page);\n return B64SafeEncode(JSON.stringify(\n <AugmentedPage>[browserKey,\n storageKey,\n pageNum,\n cutoffs.length, // pages/last\n cutoffs[pageNum - 1], // priorCutoff\n cutoffs[pageNum]])); // pageCutoff\n };\n }\n const search = (rootKey) ? `${rootKey}%5B${pageKey}%5D` : pageKey;\n const re = new RegExp(`(?<=\\\\?.*)(\\\\b${search}=)(\\\\d+)`);\n // Augment the page param of each href\n for (const a of <NodeListOf<HTMLAnchorElement>><unknown>nav.querySelectorAll('a[href]')) {\n a.href = a.href.replace(re, (_match, prefix, digit): string => `${prefix}${augmentPage(<string>digit)}`);\n }\n // Return the augment function for further augmentation (i.e., url token in input_nav_js)\n return augmentPage;\n };\n\n // Build the series_nav_js helper\n const buildNavJs = (nav:NavJsElement, [[before, anchor, current, gap, after], pageToken,\n [widths, series, labels], keynavArgs]:SeriesNavJsArgs) => {\n const parent = <HTMLElement>nav.parentElement;\n let lastWidth = -1;\n (nav.render = () => {\n const index = widths.findIndex(w => w < parent.clientWidth);\n if (widths[index] === lastWidth) { return } // no change: abort\n\n let html = before;\n series[index].forEach((item, i) => {\n // Avoid the if blocks and chain the results (shorter pagy.min.js and easier reading)\n html += item == \"gap\" ? gap :\n // @ts-expect-error the item may be a number, but the 'replace' converts it to string (shorter pagy.min.js)\n (typeof item == \"number\" ? anchor.replace(pageToken, item) : current)\n .replace(\"L<\", labels?.[index][i] ?? item + \"<\");\n });\n html += after;\n nav.innerHTML = \"\";\n nav.insertAdjacentHTML(\"afterbegin\", html);\n lastWidth = widths[index];\n if (keynavArgs && storageSupport) { void augmentKeynav(nav, keynavArgs) }\n })();\n if (nav.classList.contains(pagy + \"-rjs\")) { rjsObserver.observe(parent) }\n };\n\n // Init the input_nav_js helpers\n const initInputNavJs = async (nav:HTMLElement, [url_token, pageToken, keynavArgs]:InputNavJsArgs) => {\n const augment = keynavArgs && storageSupport\n ? await augmentKeynav(nav, keynavArgs)\n : (page: string) => page;\n initInput(nav, inputValue => url_token.replace(pageToken, augment(inputValue)));\n };\n\n // Init the limit_tag_js helper\n const initLimitTagJs = (span:HTMLSpanElement, [from, url_token, page_token, limitToken]:LimitTagJsArgs) => {\n initInput(span, inputValue => {\n // @ts-expect-error the page is a number, but the 'replace' converts it to string (shorter pagy.min.js)\n return url_token.replace(page_token, Math.max(Math.ceil(from / parseInt(inputValue)), 1))\n .replace(limitToken, inputValue);\n });\n };\n\n // Init the input element\n const initInput = (element:HTMLElement, getUrl:(v:string) => string) => {\n const input = <HTMLInputElement>element.querySelector(\"input\"),\n link = <HTMLAnchorElement>element.querySelector(\"a\"),\n initial = input.value,\n action = () => {\n if (input.value === initial) { return } // not changed\n const [min, val, max] = [input.min, input.value, input.max].map(n => parseInt(n) || 0);\n if (val < min || val > max) { // reset invalid/out-of-range\n input.value = initial;\n input.select();\n return;\n }\n link.href = getUrl(input.value);\n link.click();\n };\n input.addEventListener(\"focus\", () => input.select());\n input.addEventListener(\"focusout\", action);\n input.addEventListener(\"keypress\", e => { if (e.key == \"Enter\") { action() } });\n };\n\n // Public interface\n return {\n version: \"43.2.7\",\n\n // Scan for elements with a \"data-pagy\" attribute and call their init functions with the decoded args\n init(arg?:HTMLElement) {\n const target = arg instanceof HTMLElement ? arg : document,\n elements = target.querySelectorAll(\"[data-pagy]\");\n for (const element of <NodeListOf<HTMLElement>>elements) {\n try {\n const [helperId, ...args] = <InitArgs>JSON.parse(B64Decode(<string>element.getAttribute(\"data-pagy\")));\n if (helperId == \"k\") {\n // @ts-expect-error spread 2 arguments, not 3 as it complains about\n void augmentKeynav(element, ...<KeynavArgs><unknown>args);\n } else if (helperId == \"snj\") {\n buildNavJs(<NavJsElement>element, <SeriesNavJsArgs><unknown>args);\n } else if (helperId == \"inj\") {\n void initInputNavJs(element, <InputNavJsArgs><unknown>args);\n } else if (helperId == \"ltj\") {\n initLimitTagJs(element, <LimitTagJsArgs><unknown>args);\n }\n // else { console.warn(\"Pagy.init: %o\\nUnknown helperId '%s'\", element, helperId) }\n } catch (err) { console.warn(\"Pagy.init: %o\\n%s\", element, err) }\n }\n }\n };\n})();\n"
6
6
  ],
7
7
  "mappings": "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",
8
- "debugId": "A3F308FA5DB8330B64756E2164756E21",
8
+ "debugId": "2F5F1987603BEC1464756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,2 +1,2 @@
1
- window.Pagy=(()=>{let B="sessionStorage"in window&&"BroadcastChannel"in window,L="pagy",Z,O,W;if(B)Z=sessionStorage,O=new BroadcastChannel(L),W=Date.now(),O.addEventListener("message",(q)=>{if(q.data.from){let z=Z.getItem(q.data.key);if(z)O.postMessage({to:q.data.from,key:q.data.key,str:z})}else if(q.data.to){if(q.data.to==W)Z.setItem(q.data.key,q.data.str)}});let P=new ResizeObserver((q)=>q.forEach((z)=>{z.target.querySelectorAll(".pagy-rjs").forEach((C)=>C.render())})),V=(q)=>btoa(String.fromCharCode(...new TextEncoder().encode(q))).replace(/[+/=]/g,(z)=>z=="+"?"-":z=="/"?"_":""),_=(q)=>new TextDecoder().decode(Uint8Array.from(atob(q),(z)=>z.charCodeAt(0))),S=()=>Math.floor(Math.random()*46656).toString(36),J=async(q,[z,C,F,H,G])=>{let Q,X=document.cookie.split(/;\s+/).find((M)=>M.startsWith(L+"="))?.split("=")[1]??S();if(document.cookie=L+"="+X,z&&!(z in Z)){if(O.postMessage({from:W,key:z}),await new Promise((M)=>setTimeout(()=>M(""),100)),!(z in Z))Q=(M)=>M+"+"+H}if(!Q){if(!z)do z=S();while(z in Z);let M=Z.getItem(z),Y=M?JSON.parse(M):[void 0];if(G)Y.splice(...G),Z.setItem(z,JSON.stringify(Y));Q=($)=>{let R=parseInt($);return V(JSON.stringify([X,z,R,Y.length,Y[R-1],Y[R]]))}}let D=C?`${C}%5B${F}%5D`:F,E=new RegExp(`(?<=\\?.*)(\\b${D}=)(\\d+)`);for(let M of q.querySelectorAll("a[href]"))M.href=M.href.replace(E,(Y,$,R)=>`${$}${Q(R)}`);return Q},x=(q,[[z,C,F,H,G],Q,[X,D,E],M])=>{let Y=q.parentElement,$=-1;if((q.render=()=>{let R=X.findIndex((j)=>j<Y.clientWidth);if(X[R]===$)return;let U=z;if(D[R].forEach((j,I)=>{U+=j=="gap"?H:(typeof j=="number"?C.replace(Q,j):F).replace("L<",E?.[R][I]??j+"<")}),U+=G,q.innerHTML="",q.insertAdjacentHTML("afterbegin",U),$=X[R],M&&B)J(q,M)})(),q.classList.contains(L+"-rjs"))P.observe(Y)},T=async(q,[z,C,F])=>{let H=F&&B?await J(q,F):(G)=>G;N(q,(G)=>z.replace(C,H(G)))},A=(q,[z,C,F,H])=>{N(q,(G)=>{return C.replace(F,Math.max(Math.ceil(z/parseInt(G)),1)).replace(H,G)})},N=(q,z)=>{let C=q.querySelector("input"),F=q.querySelector("a"),H=C.value,G=()=>{if(C.value===H)return;let[Q,X,D]=[C.min,C.value,C.max].map((E)=>parseInt(E)||0);if(X<Q||X>D){C.value=H,C.select();return}F.href=z(C.value),F.click()};C.addEventListener("focus",()=>C.select()),C.addEventListener("focusout",G),C.addEventListener("keypress",(Q)=>{if(Q.key=="Enter")G()})};return{version:"43.2.6",init(q){let z=q instanceof HTMLElement?q:document,C=z.querySelectorAll("[data-pagy]");for(let F of C)try{let[H,...G]=JSON.parse(_(F.getAttribute("data-pagy")));if(H=="k")J(F,...G);else if(H=="snj")x(F,G);else if(H=="inj")T(F,G);else if(H=="ltj")A(F,G)}catch(H){console.warn(`Pagy.init: %o
1
+ window.Pagy=(()=>{let B="sessionStorage"in window&&"BroadcastChannel"in window,L="pagy",Z,O,W;if(B)Z=sessionStorage,O=new BroadcastChannel(L),W=Date.now(),O.addEventListener("message",(q)=>{if(q.data.from){let z=Z.getItem(q.data.key);if(z)O.postMessage({to:q.data.from,key:q.data.key,str:z})}else if(q.data.to){if(q.data.to==W)Z.setItem(q.data.key,q.data.str)}});let P=new ResizeObserver((q)=>q.forEach((z)=>{z.target.querySelectorAll(".pagy-rjs").forEach((C)=>C.render())})),V=(q)=>btoa(String.fromCharCode(...new TextEncoder().encode(q))).replace(/[+/=]/g,(z)=>z=="+"?"-":z=="/"?"_":""),_=(q)=>new TextDecoder().decode(Uint8Array.from(atob(q),(z)=>z.charCodeAt(0))),S=()=>Math.floor(Math.random()*46656).toString(36),J=async(q,[z,C,F,H,G])=>{let Q,X=document.cookie.split(/;\s+/).find((M)=>M.startsWith(L+"="))?.split("=")[1]??S();if(document.cookie=L+"="+X,z&&!(z in Z)){if(O.postMessage({from:W,key:z}),await new Promise((M)=>setTimeout(()=>M(""),100)),!(z in Z))Q=(M)=>M+"+"+H}if(!Q){if(!z)do z=S();while(z in Z);let M=Z.getItem(z),Y=M?JSON.parse(M):[void 0];if(G)Y.splice(...G),Z.setItem(z,JSON.stringify(Y));Q=($)=>{let R=parseInt($);return V(JSON.stringify([X,z,R,Y.length,Y[R-1],Y[R]]))}}let D=C?`${C}%5B${F}%5D`:F,E=new RegExp(`(?<=\\?.*)(\\b${D}=)(\\d+)`);for(let M of q.querySelectorAll("a[href]"))M.href=M.href.replace(E,(Y,$,R)=>`${$}${Q(R)}`);return Q},x=(q,[[z,C,F,H,G],Q,[X,D,E],M])=>{let Y=q.parentElement,$=-1;if((q.render=()=>{let R=X.findIndex((j)=>j<Y.clientWidth);if(X[R]===$)return;let U=z;if(D[R].forEach((j,I)=>{U+=j=="gap"?H:(typeof j=="number"?C.replace(Q,j):F).replace("L<",E?.[R][I]??j+"<")}),U+=G,q.innerHTML="",q.insertAdjacentHTML("afterbegin",U),$=X[R],M&&B)J(q,M)})(),q.classList.contains(L+"-rjs"))P.observe(Y)},T=async(q,[z,C,F])=>{let H=F&&B?await J(q,F):(G)=>G;N(q,(G)=>z.replace(C,H(G)))},A=(q,[z,C,F,H])=>{N(q,(G)=>{return C.replace(F,Math.max(Math.ceil(z/parseInt(G)),1)).replace(H,G)})},N=(q,z)=>{let C=q.querySelector("input"),F=q.querySelector("a"),H=C.value,G=()=>{if(C.value===H)return;let[Q,X,D]=[C.min,C.value,C.max].map((E)=>parseInt(E)||0);if(X<Q||X>D){C.value=H,C.select();return}F.href=z(C.value),F.click()};C.addEventListener("focus",()=>C.select()),C.addEventListener("focusout",G),C.addEventListener("keypress",(Q)=>{if(Q.key=="Enter")G()})};return{version:"43.2.7",init(q){let z=q instanceof HTMLElement?q:document,C=z.querySelectorAll("[data-pagy]");for(let F of C)try{let[H,...G]=JSON.parse(_(F.getAttribute("data-pagy")));if(H=="k")J(F,...G);else if(H=="snj")x(F,G);else if(H=="inj")T(F,G);else if(H=="ltj")A(F,G)}catch(H){console.warn(`Pagy.init: %o
2
2
  %s`,F,H)}}}})();
data/javascripts/pagy.mjs CHANGED
@@ -125,7 +125,7 @@ const Pagy = (() => {
125
125
  });
126
126
  };
127
127
  return {
128
- version: "43.2.6",
128
+ version: "43.2.7",
129
129
  init(arg) {
130
130
  const target = arg instanceof HTMLElement ? arg : document, elements = target.querySelectorAll("[data-pagy]");
131
131
  for (const element of elements) {
@@ -55,6 +55,8 @@ class Pagy
55
55
  @keyset = @options[:keyset] || extract_keyset
56
56
  raise InternalError, 'the set must be ordered' if @keyset.empty?
57
57
 
58
+ @identifiers = quoted_identifiers(@set.model.table_name)
59
+
58
60
  assign_page
59
61
  self.next
60
62
  end
@@ -109,22 +111,28 @@ class Pagy
109
111
  def compose_predicate(prefix = nil)
110
112
  operator = { asc: '>', desc: '<' }
111
113
  directions = @keyset.values
112
- identifier = quoted_identifiers(@set.model.table_name)
114
+ identifier = @identifiers
113
115
  placeholder = @keyset.to_h { |column| [column, ":#{prefix}#{column}"] }
116
+
114
117
  if @options[:tuple_comparison] && (directions.all?(:asc) || directions.all?(:desc))
115
118
  "(#{identifier.values.join(', ')}) #{operator[directions.first]} (#{placeholder.values.join(', ')})"
116
119
  else
117
120
  keyset = @keyset.to_a
118
- union = []
121
+ ors = []
122
+
119
123
  until keyset.empty?
120
- last_column, last_direction = keyset.pop
121
- intersection = +'('
122
- intersection << (keyset.map { |column, _d| "#{identifier[column]} = #{placeholder[column]}" } \
123
- << "#{identifier[last_column]} #{operator[last_direction]} #{placeholder[last_column]}").join(' AND ')
124
- intersection << ')'
125
- union << intersection
124
+ column, direction = keyset.pop
125
+ ands = keyset.map { |k, _| "#{identifier[k]} = #{placeholder[k]}" }
126
+ ands << "#{identifier[column]} #{operator[direction]} #{placeholder[column]}"
127
+ ors << "(#{ands.join(' AND ')})"
126
128
  end
127
- union.join(' OR ')
129
+ query = ors.join(' OR ')
130
+ return query unless @keyset.size > 1
131
+
132
+ # Add hint predicate for DB optimizers that struggle with ORs
133
+ column, direction = @keyset.first
134
+ hint = "#{identifier[column]} #{operator[direction]}= #{placeholder[column]}"
135
+ "#{hint} AND (#{query})"
128
136
  end
129
137
  end
130
138
 
@@ -15,8 +15,8 @@ class Pagy
15
15
 
16
16
  def urlsafe_encode(bin)
17
17
  str = encode(bin)
18
- str.chomp!('==') or str.chomp!('=')
19
18
  str.tr!('+/', '-_')
19
+ str.delete!('=')
20
20
 
21
21
  str
22
22
  end
@@ -8,6 +8,7 @@ class Pagy
8
8
 
9
9
  def plural_for(n = 0)
10
10
  mod100 = n % 100
11
+
11
12
  case
12
13
  when n == 0 # rubocop:disable Style/NumericPredicate
13
14
  :zero
@@ -9,6 +9,7 @@ class Pagy
9
9
  def plural_for(n = 0)
10
10
  mod10 = n % 10
11
11
  mod100 = n % 100
12
+
12
13
  case
13
14
  when mod10 == 1 && mod100 != 11
14
15
  :one
@@ -9,6 +9,7 @@ class Pagy
9
9
  def plural_for(n = 0)
10
10
  mod10 = n % 10
11
11
  mod100 = n % 100
12
+
12
13
  case
13
14
  when n == 1
14
15
  :one
@@ -4,22 +4,18 @@ require_relative 'support/a_lambda' # inheritable
4
4
 
5
5
  class Pagy
6
6
  # Return the enabled/disabled previous page anchor tag
7
- def previous_tag(a = nil, text: I18n.translate('pagy.previous'),
8
- aria_label: I18n.translate('pagy.aria_label.previous'), **)
9
- if @previous
10
- (a || a_lambda(**)).(@previous, text, aria_label:)
11
- else
12
- %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
13
- end
14
- end
7
+ def previous_tag(...) = anchor_tag_for(:previous, ...)
15
8
 
16
9
  # Return the enabled/disabled next page anchor tag
17
- def next_tag(a = nil, text: I18n.translate('pagy.next'),
18
- aria_label: I18n.translate('pagy.aria_label.next'), **)
19
- if @next
20
- (a || a_lambda(**)).(@next, text, aria_label:)
21
- else
22
- %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
23
- end
10
+ def next_tag(...) = anchor_tag_for(:next, ...)
11
+
12
+ private
13
+
14
+ def anchor_tag_for(which, a = nil, text: I18n.translate("pagy.#{which}"),
15
+ aria_label: I18n.translate("pagy.aria_label.#{which}"), **)
16
+ page = send(which)
17
+ return (a || a_lambda(**)).(page.to_i, text, aria_label:) if page
18
+
19
+ %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
24
20
  end
25
21
  end
@@ -9,9 +9,11 @@ class Pagy
9
9
  # Javascript combo pagination for bootstrap: it returns a nav with a data-pagy attribute used by the pagy.js file
10
10
  def bootstrap_input_nav_js(classes: 'pagination', **)
11
11
  a_lambda = a_lambda(**)
12
+
12
13
  input = %(<input name="page" type="number" min="1" max="#{last}" value="#{@page}" aria-current="page" ) +
13
14
  %(style="text-align: center; width: #{@page.to_s.length + 1}rem; padding: 0; border-radius: .25rem; ) +
14
15
  %(border: none; display: inline-block;" class="page-link active">#{A_TAG})
16
+
15
17
  html = %(<ul class="#{classes}">#{
16
18
  bootstrap_html_for(:previous, a_lambda)
17
19
  }<li class="page-item"><label class="page-link">#{
@@ -19,6 +21,7 @@ class Pagy
19
21
  }</label></li>#{
20
22
  bootstrap_html_for(:next, a_lambda)
21
23
  }</ul>)
24
+
22
25
  wrap_input_nav_js(html, 'pagy-bootstrap input-nav-js', **)
23
26
  end
24
27
  end
@@ -9,7 +9,8 @@ class Pagy
9
9
  # Pagination for bootstrap: it returns the html with the series of links to the pages
10
10
  def bootstrap_series_nav(classes: 'pagination', **)
11
11
  a_lambda = a_lambda(**)
12
- html = %(<ul class="#{classes}">#{bootstrap_html_for(:previous, a_lambda)})
12
+
13
+ html = %(<ul class="#{classes}">#{bootstrap_html_for(:previous, a_lambda)})
13
14
  series(**).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
14
15
  html << case item
15
16
  when Integer
@@ -24,6 +25,7 @@ class Pagy
24
25
  end
25
26
  end
26
27
  html << %(#{bootstrap_html_for(:next, a_lambda)}</ul>)
28
+
27
29
  wrap_series_nav(html, 'pagy-bootstrap series-nav', **)
28
30
  end
29
31
  end
@@ -9,6 +9,7 @@ class Pagy
9
9
  # Javascript pagination for bootstrap: it returns a nav with a data-pagy attribute used by the pagy.js file
10
10
  def bootstrap_series_nav_js(classes: 'pagination', **)
11
11
  a_lambda = a_lambda(**)
12
+
12
13
  tokens = { before: %(<ul class="#{classes}">#{bootstrap_html_for(:previous, a_lambda)}),
13
14
  anchor: %(<li class="page-item">#{a_lambda.(PAGE_TOKEN, LABEL_TOKEN, classes: 'page-link')}</li>),
14
15
  current: %(<li class="page-item active"><a role="link" class="page-link" ) +
@@ -16,6 +17,7 @@ class Pagy
16
17
  gap: %(<li class="page-item gap disabled"><a role="link" class="page-link" aria-disabled="true">#{
17
18
  I18n.translate('pagy.gap')}</a></li>),
18
19
  after: %(#{bootstrap_html_for(:next, a_lambda)}</ul>) }
20
+
19
21
  wrap_series_nav_js(tokens, 'pagy-bootstrap series-nav-js', **)
20
22
  end
21
23
  end
@@ -9,13 +9,16 @@ class Pagy
9
9
  # Javascript combo pagination for bulma: it returns a nav with a data-pagy attribute used by the pagy.js file
10
10
  def bulma_input_nav_js(classes: 'pagination', **)
11
11
  a_lambda = a_lambda(**)
12
+
12
13
  input = %(<input name="page" type="number" min="1" max="#{@last}" value="#{@page}" aria-current="page") +
13
14
  %(style="text-align: center; width: #{@page.to_s.length + 1}rem; line-height: 1.2rem; ) +
14
15
  %(border: none; border-radius: .25rem; padding: .0625rem; color: white; ) +
15
16
  %(background-color: #485fc7;">#{A_TAG})
17
+
16
18
  html = %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)}<li class="pagination-link"><label>#{
17
19
  I18n.translate('pagy.input_nav_js', page_input: input, pages: @last)
18
20
  }</label></li>#{bulma_html_for(:next, a_lambda)}</ul>)
21
+
19
22
  wrap_input_nav_js(html, "pagy-bulma input-nav-js #{classes}", **)
20
23
  end
21
24
  end
@@ -9,7 +9,8 @@ class Pagy
9
9
  # Pagination for bulma: it returns the html with the series of links to the pages
10
10
  def bulma_series_nav(classes: 'pagination', **)
11
11
  a_lambda = a_lambda(**)
12
- html = %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)})
12
+
13
+ html = %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)})
13
14
  series(**).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
14
15
  html << case item
15
16
  when Integer
@@ -23,6 +24,7 @@ class Pagy
23
24
  end
24
25
  end
25
26
  html << %(#{bulma_html_for(:next, a_lambda)}</ul>)
27
+
26
28
  wrap_series_nav(html, "pagy-bulma series-nav #{classes}", **)
27
29
  end
28
30
  end
@@ -9,12 +9,14 @@ class Pagy
9
9
  # Javascript pagination for bulma: it returns a nav with a data-pagy attribute used by the Pagy.nav javascript
10
10
  def bulma_series_nav_js(classes: 'pagination', **)
11
11
  a_lambda = a_lambda(**)
12
+
12
13
  tokens = { before: %(<ul class="pagination-list">#{bulma_html_for(:previous, a_lambda)}),
13
14
  anchor: %(<li>#{a_lambda.(PAGE_TOKEN, LABEL_TOKEN, classes: 'pagination-link')}</li>),
14
15
  current: %(<li><a role="link" class="pagination-link is-current" ) +
15
16
  %(aria-current="page" aria-disabled="true">#{LABEL_TOKEN}</a></li>),
16
17
  gap: %(<li><span class="pagination-ellipsis">#{I18n.translate('pagy.gap')}</span></li>),
17
18
  after: %(#{bulma_html_for(:next, a_lambda)}</ul>) }
19
+
18
20
  wrap_series_nav_js(tokens, "pagy-bulma series-nav-js #{classes}", **)
19
21
  end
20
22
  end
@@ -6,8 +6,9 @@ class Pagy
6
6
 
7
7
  # Generate a hash of the wanted internal data
8
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 }
9
+ template = compose_page_url(PAGE_TOKEN, **)
10
+ to_url = ->(page) { template.sub(PAGE_TOKEN, page.to_s) if page }
11
+
11
12
  data_keys -= %i[count limit] if calendar?
12
13
 
13
14
  data_keys.each_with_object({}) do |key, data|
@@ -8,11 +8,13 @@ class Pagy
8
8
  return send(:"#{style}_input_nav_js", **) if style
9
9
 
10
10
  a_lambda = a_lambda(**)
11
- input = %(<input name="page" type="number" min="1" max="#{@last}" value="#{@page}" aria-current="page" ) +
12
- %(style="text-align: center; width: #{@page.to_s.length + 1}rem; padding: 0;">#{A_TAG})
13
- html = %(#{previous_tag(a_lambda)}<label>#{
14
- I18n.translate('pagy.input_nav_js', page_input: input, pages: @last)}</label>#{
15
- next_tag(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)})
16
18
 
17
19
  wrap_input_nav_js(html, 'pagy input-nav-js', **)
18
20
  end
@@ -9,7 +9,8 @@ class Pagy
9
9
 
10
10
  limit_input = %(<input name="limit" type="number" min="1" max="#{client_max_limit}" value="#{
11
11
  @limit}" style="padding: 0; text-align: center; width: #{@limit.to_s.length + 1}rem;">#{A_TAG})
12
- url_token = compose_page_url(PAGE_TOKEN, limit: LIMIT_TOKEN)
12
+
13
+ url_token = compose_page_url(PAGE_TOKEN, limit: LIMIT_TOKEN)
13
14
 
14
15
  %(<span#{%( id="#{id}") if id} class="pagy limit-tag-js" #{
15
16
  data_pagy_attribute(:ltj, @from, url_token, PAGE_TOKEN, LIMIT_TOKEN)
@@ -8,9 +8,9 @@ class Pagy
8
8
  return send(:"#{style}_series_nav", **) if style
9
9
 
10
10
  a_lambda = a_lambda(**)
11
- html = previous_tag(a_lambda)
12
- series(**).each do |item|
13
- # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
11
+
12
+ html = previous_tag(a_lambda)
13
+ series(**).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
14
14
  html << case item
15
15
  when Integer
16
16
  a_lambda.(item)
@@ -24,10 +24,12 @@ class Pagy
24
24
  info_key = count.zero? ? 'pagy.info_tag.no_items' : 'pagy.info_tag.single_page'
25
25
  %( title="#{I18n.translate(info_key, item_name: I18n.translate('pagy.item_name', count:), count:)}")
26
26
  end
27
- rel = case page
28
- when @previous then %( rel="prev")
29
- when @next then %( rel="next")
30
- end
27
+
28
+ rel = case page
29
+ when @previous then %( rel="prev")
30
+ when @next then %( rel="next")
31
+ end
32
+
31
33
  %(#{left}#{page}#{right}#{title}#{
32
34
  %( class="#{classes}") if classes}#{rel}#{%( aria-label="#{aria_label}") if aria_label}>#{text}</a>)
33
35
  end
@@ -9,7 +9,12 @@ class Pagy
9
9
 
10
10
  # Compose the data-pagy attribute, with the base64 encoded JSON-serialized args. Use the faster oj gem if defined.
11
11
  def data_pagy_attribute(*args)
12
- data = defined?(Oj) ? Oj.dump(args, mode: :compat) : JSON.dump(args)
12
+ data = if defined?(Oj)
13
+ Oj.dump(args, mode: :compat)
14
+ else
15
+ JSON.dump(args)
16
+ end
17
+
13
18
  %(data-pagy="#{B64.encode(data)}")
14
19
  end
15
20
  end
@@ -5,8 +5,7 @@ class Pagy
5
5
  private
6
6
 
7
7
  # Compose the aria label attribute for the nav
8
- def nav_aria_label_attribute(aria_label: nil)
9
- aria_label ||= I18n.translate('pagy.aria_label.nav', count: @last)
8
+ def nav_aria_label_attribute(aria_label: I18n.translate('pagy.aria_label.nav', count: @last))
10
9
  %(aria-label="#{aria_label}")
11
10
  end
12
11
  end
@@ -7,8 +7,7 @@ class Pagy
7
7
 
8
8
  # Return the array of page numbers and :gap e.g. [1, :gap, 8, "9", 10, :gap, 36]
9
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
10
+ raise OptionError.new(self, :slots, 'to be an Integer >= 0', slots) unless slots.is_a?(Integer) && slots >= 0
12
11
  return [] if slots.zero?
13
12
 
14
13
  [].tap do |series|
@@ -12,6 +12,7 @@ class Pagy
12
12
  # Build the nav tag, with the specific inner html for the style
13
13
  def wrap_series_nav(html, nav_classes, id: nil, aria_label: nil, **)
14
14
  data = %( #{data_pagy_attribute(:k, @update)}) if keynav?
15
+
15
16
  %(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{nav_aria_label_attribute(aria_label:)}#{data}>#{html}</nav>)
16
17
  end
17
18
  end
@@ -20,13 +20,18 @@ class Pagy
20
20
 
21
21
  # Support for the Calendar API
22
22
  def page_labels(series)
23
- series.map { |s| s.map { |item| item == :gap ? :gap : page_label(item) } } if calendar?
23
+ return unless calendar?
24
+
25
+ series.map do |s|
26
+ s.map { _1 == :gap ? :gap : page_label(_1) }
27
+ end
24
28
  end
25
29
 
26
30
  # Build the nav_js tag, with the specific tokens for the style
27
31
  def wrap_series_nav_js(tokens, nav_classes, id: nil, aria_label: nil, **)
28
- sequels = sequels(**)
32
+ sequels = sequels(**)
29
33
  nav_classes = "pagy-rjs #{nav_classes}" if sequels[0].size > 1
34
+
30
35
  %(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{
31
36
  nav_aria_label_attribute(aria_label:)} #{
32
37
  data = [:snj, tokens.values, PAGE_TOKEN, sequels]
@@ -8,8 +8,10 @@ class Pagy
8
8
  def paginate(context, collection, config)
9
9
  context.instance_eval do
10
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?
11
+
12
+ unless config.is_a?(Hash) && (config.keys - allowed_options).empty?
13
+ raise ArgumentError, "keys must be in #{allowed_options.inspect}"
14
+ end
13
15
 
14
16
  config[:offset] ||= {}
15
17
 
@@ -15,8 +15,8 @@ class Pagy
15
15
  options[:page] = page
16
16
  end
17
17
 
18
- setup_options(count, epoch, collection, options)
19
18
  options[:limit] = options[:request].resolve_limit
19
+ setup_options(count, epoch, collection, options)
20
20
 
21
21
  pagy = Offset::Countish.new(**options)
22
22
  [pagy, pagy.records(collection)]
@@ -24,13 +24,12 @@ class Pagy
24
24
 
25
25
  # Get the count from the page and set epoch when ttl (Time To Live) requires it
26
26
  def setup_options(count, epoch, collection, options)
27
- now = Time.now.to_i
27
+ now = Time.now.to_i
28
+ ongoing = !options[:ttl] || (epoch && epoch <= now && now < (epoch + options[:ttl]))
28
29
 
29
- if !options[:count] && count && (!options[:ttl] ||
30
- (epoch && epoch <= now && now < (epoch + options[:ttl]))) # ongoing
30
+ if !options[:count] && count && ongoing
31
31
  options[:count] = count
32
32
  options[:epoch] = epoch if options[:ttl]
33
-
34
33
  else # recount
35
34
  options[:count] ||= Countable.get_count(collection, options)
36
35
  options[:epoch] = now if options[:ttl]
@@ -12,6 +12,7 @@ class Pagy
12
12
 
13
13
  Searcher.wrap(search, options) do
14
14
  model, query_or_payload, search_options = search
15
+
15
16
  search_options[:size] = options[:limit]
16
17
  search_options[:from] = options[:limit] * ((options[:page] || 1) - 1)
17
18
 
@@ -8,8 +8,8 @@ class Pagy
8
8
  def paginate(set, options)
9
9
  options[:page] ||= options[:request].resolve_page(force_integer: false) # allow nil
10
10
  options[:limit] = options[:request].resolve_limit
11
- pagy = Keyset.new(set, **options)
12
11
 
12
+ pagy = Keyset.new(set, **options)
13
13
  [pagy, pagy.records]
14
14
  end
15
15
  end
@@ -12,6 +12,7 @@ class Pagy
12
12
 
13
13
  Searcher.wrap(search, options) do
14
14
  model, term, search_options = search
15
+
15
16
  search_options[:hits_per_page] = options[:limit]
16
17
  search_options[:page] = options[:page]
17
18
 
@@ -12,8 +12,9 @@ class Pagy
12
12
 
13
13
  Searcher.wrap(search, options) do
14
14
  model, term, search_options, block = search
15
- search_options[:per_page] = options[:limit]
16
- search_options[:page] = options[:page]
15
+
16
+ search_options[:per_page] = options[:limit]
17
+ search_options[:page] = options[:page]
17
18
 
18
19
  method = options[:search_method] || Searchkick::DEFAULT[:search_method]
19
20
  results = model.send(method, term || '*', **search_options, &block)
data/lib/pagy.rb CHANGED
@@ -8,7 +8,7 @@ require_relative 'pagy/toolbox/helpers/loader'
8
8
 
9
9
  # Top superclass: it defines only what's common to all the subclasses
10
10
  class Pagy
11
- VERSION = '43.2.6'
11
+ VERSION = '43.2.7'
12
12
  ROOT = Pathname.new(__dir__).parent.freeze
13
13
  DEFAULT = { limit: 20, limit_key: 'limit', page_key: 'page' }.freeze
14
14
  PAGE_TOKEN = EscapedValue.new('P ')
@@ -49,8 +49,13 @@ class Pagy
49
49
  # Validates and assign the passed options: they must be present and value.to_i must be >= min
50
50
  def assign_and_check(name_min)
51
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
52
+ value = @options[name]
53
+
54
+ if value.respond_to?(:to_i) && (integer = value.to_i) >= min
55
+ instance_variable_set(:"@#{name}", integer)
56
+ else
57
+ raise OptionError.new(self, name, ">= #{min}", value)
58
+ end
54
59
  end
55
60
  end
56
61
 
@@ -59,10 +64,13 @@ class Pagy
59
64
  @request = options.delete(:request) # internal object
60
65
  default = {}
61
66
  current = self.class
67
+
62
68
  begin
63
69
  default = current::DEFAULT.merge(default)
64
70
  current = current.superclass
65
71
  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
72
+
73
+ clean_options = options.delete_if { |k, v| default.key?(k) && (v.nil? || v == '') }
74
+ @options = default.merge!(clean_options).freeze
67
75
  end
68
76
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pagy
3
3
  version: !ruby/object:Gem::Version
4
- version: 43.2.6
4
+ version: 43.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Domizio Demichelis