pagy 43.2.9 → 43.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a812612414d796ebf9f7862669b70c2056810d25691d96e56890f39a9e5aadbd
4
- data.tar.gz: 7caede80d86e132ca9b2f3674d40e6c4ef36af4e7a43922947153a3adef70116
3
+ metadata.gz: 384cf9e4eb056f151267e5628f61a18cda32dd4243234ca57f3dc98db3bada58
4
+ data.tar.gz: f8d58e124b0d6ffcef9bbd36e575ef14db069488180728ec73460a4c3ff17563
5
5
  SHA512:
6
- metadata.gz: f7bd731ba55e11de086ac989ab62cbd009f4b8f9d858eb025f740638e1f9bf16b44b75a11ffee73c65eee3d949795e2e719d7fe61dbd64f14468bac0f28e562d
7
- data.tar.gz: 7682b96dcbbf89bb413a00f1fad091383674624d6f1e03a3520ff1a5febc00907bc41c8a8913266ce8ee258e533537054049e7ce7fa136e19a8dd5f7cc71dd62
6
+ metadata.gz: 685a523bf3c3acfd3a6a3117aa38bf058ca7a2dacd2dd22a87c363edcc43f2fdbcb41c291a957f0730512d18f2b2407807dce54f902b6d88996d3bcd2cc943cb
7
+ data.tar.gz: e292dd6152f1889afbbfac79b7b13d046d60dd2398ee067093935db77d059ebc8fd992b080123a9e8a76cd8de506ef5797d2f8cf61b8169e4b6b2c4c7752c62b
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.9'
19
+ VERSION = '43.3.0'
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.9'
22
+ VERSION = '43.3.0'
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")
@@ -46,7 +46,7 @@ SECTIONS = { pagy: { css_anchor: 'pagy-css' },
46
46
  require 'sinatra/base'
47
47
 
48
48
  # Pagy init
49
- Pagy.options[:client_max_limit] = 100
49
+ Pagy::OPTIONS[:client_max_limit] = 100
50
50
 
51
51
  # Sinatra application
52
52
  class PagyDemo < Sinatra::Base
@@ -86,7 +86,7 @@ class PagyDemo < Sinatra::Base
86
86
  end
87
87
 
88
88
  PAGY_LIKE_HEAD =
89
- %(#{Pagy.dev_tools if ENV['CY_TEST'] != 'true'}
89
+ %(#{Pagy.dev_tools if ENV['E2E_TEST'] != 'true'}
90
90
  <style>
91
91
  /* black/white backdrop color based on --B */
92
92
  .pagy { background-color: hsl(0 0 calc(100 * var(--B))) !important; }
data/apps/index.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # PagyApp module
3
+ # PagyApps module
4
4
  module PagyApps
5
5
  # Return the hash of app name/path
6
6
  INDEX = Dir[File.expand_path('./*.ru', __dir__)].to_h do |f|
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.9'
19
+ VERSION = '43.3.0'
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")
@@ -52,7 +52,7 @@ class PagyKeynav < Sinatra::Base
52
52
  get '/' do
53
53
  Time.zone = 'UTC'
54
54
 
55
- @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }
55
+ @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }.freeze
56
56
  @pagy1, @pets1 = pagy(:keynav_js, Pet.order(@order), limit: 4, root_key: 'animal1')
57
57
  @ids1 = @pets1.pluck(:id)
58
58
  @pagy2, @pets2 = pagy(:keynav_js, Pet.order(@order), limit: 4, root_key: 'animal2')
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.9'
19
+ VERSION = '43.3.0'
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")
@@ -52,7 +52,7 @@ class PagyKeynav < Sinatra::Base
52
52
  get '/' do
53
53
  Time.zone = 'UTC'
54
54
 
55
- @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }
55
+ @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }.freeze
56
56
  @pagy, @pets = pagy(:keynav_js, Pet.order(@order), limit: 4, client_max_limit: 100)
57
57
  # Support also root_key for replacing url in javascript
58
58
  # @pagy, @pets = pagy(:keynav_js, Pet.order(@order), limit: 4, client_max_limit: 100, root_key: 'animal')
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.9'
19
+ VERSION = '43.3.0'
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")
@@ -42,7 +42,7 @@ class PagyKeyset < Sinatra::Base
42
42
  get '/' do
43
43
  Time.zone = 'UTC'
44
44
 
45
- @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }
45
+ @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }.freeze
46
46
  @pagy, @pets = pagy(:keyset, Pet.order(@order), limit: 10, client_max_limit: 100)
47
47
  # response.headers.merge!(@pagy.headers_hash)
48
48
  erb :main
@@ -16,7 +16,7 @@
16
16
  # URL
17
17
  # http://127.0.0.1:8000
18
18
 
19
- VERSION = '43.2.9'
19
+ VERSION = '43.3.0'
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")
@@ -41,7 +41,7 @@ class PagyKeysetSequel < Sinatra::Base
41
41
 
42
42
  # Root route/action
43
43
  get '/' do
44
- @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }
44
+ @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }.freeze
45
45
  @pagy, @pets = pagy(:keyset, Pet.order(:animal, :name, Sequel.desc(:birthdate), :id),
46
46
  limit: 10, client_max_limit: 100)
47
47
  erb :main
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.9'
19
+ VERSION = '43.3.0'
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.9'
19
+ VERSION = '43.3.0'
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")
@@ -34,7 +34,7 @@ end
34
34
 
35
35
  # Edit this section adding the legacy as needed
36
36
  # Pagy initializer
37
- Pagy.options[:client_max_limit] = 100
37
+ Pagy::OPTIONS[:client_max_limit] = 100
38
38
 
39
39
  # Sinatra setup
40
40
  require 'sinatra/base'
data/config/pagy.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Pagy initializer file (43.2.9)
3
+ # Pagy initializer file (43.3.0)
4
4
  # See https://ddnexus.github.io/pagy/resources/initializer/
5
5
 
6
6
  ############ Global Options ################################################################
@@ -8,11 +8,12 @@
8
8
  # Add your global options below. They will be applied globally.
9
9
  # For example:
10
10
  #
11
- # Pagy.options[:limit] = 10 # Limit the items per page
12
- # Pagy.options[:client_max_limit] = 100 # The client can request a limit up to 100
13
- # Pagy.options[:max_pages] = 200 # Allow only 200 pages
14
- # Pagy.options[:jsonapi] = true # Use JSON:API compliant URLs
11
+ # Pagy::OPTIONS[:limit] = 10 # Limit the items per page
12
+ # Pagy::OPTIONS[:client_max_limit] = 100 # The client can request a limit up to 100
13
+ # Pagy::OPTIONS[:max_pages] = 200 # Allow only 200 pages
14
+ # Pagy::OPTIONS[:jsonapi] = true # Use JSON:API compliant URLs
15
15
 
16
+ Pagy::OPTIONS.freeze
16
17
 
17
18
  ############ JavaScript ####################################################################
18
19
  # See https://ddnexus.github.io/pagy/resources/javascript/ for details.
data/javascripts/pagy.js CHANGED
@@ -126,7 +126,7 @@ window.Pagy = (() => {
126
126
  });
127
127
  };
128
128
  return {
129
- version: "43.2.9",
129
+ version: "43.3.0",
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=CB82DC132B8F99C764756E2164756E21
153
+ //# debugId=975696F2DDC5F9D164756E2164756E21
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.9\",\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.3.0\",\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": "CB82DC132B8F99C764756E2164756E21",
8
+ "debugId": "975696F2DDC5F9D164756E2164756E21",
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.9",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.3.0",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.9",
128
+ version: "43.3.0",
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) {
@@ -8,6 +8,7 @@ require 'active_support/core_ext/integer/time'
8
8
 
9
9
  class Pagy
10
10
  # Calendar class
11
+ # noinspection RubyMismatchedArgumentType
11
12
  class Calendar < Hash
12
13
  path = Pathname.new(__dir__)
13
14
  autoload :Unit, path.join('unit')
@@ -9,8 +9,8 @@ class Pagy
9
9
  end
10
10
 
11
11
  # Collect the search arguments to pass to the actual search
12
- def pagy_search(term = nil, **options, &block)
13
- Arguments.new([self, term, options, block])
12
+ def pagy_search(*arguments, **options, &block)
13
+ Arguments.new([self, arguments, options, block])
14
14
  end
15
15
  end
16
16
 
@@ -29,4 +29,6 @@ class Pagy
29
29
  end
30
30
 
31
31
  class Searchkick < SearchBase; end
32
+
33
+ class TypesenseRails < SearchBase; end
32
34
  end
@@ -27,13 +27,13 @@ class Pagy
27
27
  end
28
28
 
29
29
  def resolve_limit
30
- limit_key = @options[:limit_key] || DEFAULT[:limit_key]
31
30
  default = @options[:limit] || DEFAULT[:limit]
32
31
  max_limit = @options[:client_max_limit]
33
32
  return default unless max_limit
34
33
 
35
- limit = @params.dig(@options[:root_key], limit_key) || @params[limit_key]
36
- limit ? [limit.to_i, max_limit].min : default
34
+ limit_key = @options[:limit_key] || DEFAULT[:limit_key]
35
+ limit = (@params.dig(@options[:root_key], limit_key) || @params[limit_key]).to_s.to_i
36
+ limit.zero? ? default : [limit, max_limit].min
37
37
  end
38
38
 
39
39
  private
data/lib/pagy/cli.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'optparse'
4
4
  require 'fileutils'
5
5
  require 'rbconfig'
6
- require 'pagy'
6
+ require_relative '../pagy'
7
7
  require_relative '../../apps/index'
8
8
 
9
9
  class Pagy
@@ -42,9 +42,10 @@ class Pagy
42
42
  end
43
43
 
44
44
  opts.separator "\nRackup options"
45
- opts.on('-e', '--env ENV', 'Environment') { |v| options[:env] = v }
46
- opts.on('-o', '--host HOST', 'Host') { |v| options[:host] = v }
47
- opts.on('-p', '--port PORT', 'Port') { |v| options[:port] = v }
45
+ opts.on('-e', '--env ENV', 'Environment') { |v| options[:env] = v }
46
+ opts.on('-o', '--host HOST', 'Host') { |v| options[:host] = v }
47
+ opts.on('-p', '--port PORT', 'Port') { |v| options[:port] = v }
48
+ opts.on('-t', '--threads THREADS', 'Threads') { |v| options[:threads] = v }
48
49
 
49
50
  opts.separator "\nOther options"
50
51
  opts.on('-q', '--quiet', 'Quiet mode for development') { options[:quiet] = true }
@@ -115,6 +116,7 @@ class Pagy
115
116
 
116
117
  gem_dir = File.expand_path('../..', __dir__)
117
118
  rackup = "rackup -I #{gem_dir}/lib -r pagy -o #{options[:host]} -p #{options[:port]} -E #{options[:env]} #{file}"
119
+ rackup << " -O Threads=#{options[:threads]}" if options[:threads]
118
120
  rackup << ' -q' if options[:quiet]
119
121
 
120
122
  exec(rackup)
@@ -12,8 +12,8 @@ class Pagy
12
12
 
13
13
  pagy, results = yield
14
14
 
15
- arguments = search_arguments[4..]
16
- results = results.send(*arguments) unless arguments.empty?
15
+ called = search_arguments[4..]
16
+ results = results.send(*called) unless called.empty?
17
17
 
18
18
  [pagy, results]
19
19
  end
@@ -16,14 +16,14 @@ class Pagy
16
16
  headers_map.each_with_object('link' => links) do |(key, name), hash|
17
17
  next unless name
18
18
 
19
- # :nocov:
20
19
  value = case key
20
+ # :nocov:
21
21
  when :page then @page
22
22
  when :limit then @limit unless calendar?
23
23
  when :pages then @last if @count
24
24
  when :count then @count
25
+ # :nocov:
25
26
  end
26
- # :nocov:
27
27
  hash[name] = value.to_s if value
28
28
  end
29
29
  end
@@ -5,7 +5,7 @@ require_relative 'support/wrap_input_nav_js'
5
5
  class Pagy
6
6
  # JavaScript input pagination: it returns a nav with a data-pagy attribute used by the pagy.js file
7
7
  def input_nav_js(style = nil, **)
8
- return send(:"#{style}_input_nav_js", **) if style
8
+ return send(:"#{style}_input_nav_js", **) if style && style.to_s != 'pagy'
9
9
 
10
10
  a_lambda = a_lambda(**)
11
11
 
@@ -5,7 +5,7 @@ require_relative 'support/wrap_series_nav'
5
5
  class Pagy
6
6
  # Return the HTML with the series of links to the pages
7
7
  def series_nav(style = nil, **)
8
- return send(:"#{style}_series_nav", **) if style
8
+ return send(:"#{style}_series_nav", **) if style && style.to_s != 'pagy'
9
9
 
10
10
  a_lambda = a_lambda(**)
11
11
 
@@ -5,7 +5,7 @@ require_relative 'support/wrap_series_nav_js'
5
5
  class Pagy
6
6
  # Return a nav with a data-pagy attribute used by the pagy.js file
7
7
  def series_nav_js(style = nil, **)
8
- return send(:"#{style}_series_nav_js", **) if style
8
+ return send(:"#{style}_series_nav_js", **) if style && style.to_s != 'pagy'
9
9
 
10
10
  a_lambda = a_lambda(**)
11
11
  tokens = { before: previous_tag(a_lambda),
@@ -5,7 +5,8 @@ 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: I18n.translate('pagy.aria_label.nav', count: @last))
8
+ def nav_aria_label_attribute(aria_label: nil)
9
+ aria_label ||= I18n.translate('pagy.aria_label.nav', count: @last)
9
10
  %(aria-label="#{aria_label}")
10
11
  end
11
12
  end
@@ -11,13 +11,13 @@ class Pagy
11
11
  if search.is_a?(Search::Arguments) # Active mode
12
12
 
13
13
  Searcher.wrap(search, options) do
14
- model, query_or_payload, search_options = search
14
+ model, arguments, search_options = search
15
15
 
16
16
  search_options[:size] = options[:limit]
17
17
  search_options[:from] = options[:limit] * ((options[:page] || 1) - 1)
18
18
 
19
19
  method = options[:search_method] || ElasticsearchRails::DEFAULT[:search_method]
20
- response_object = model.send(method, query_or_payload, **search_options)
20
+ response_object = model.send(method, *arguments, **search_options)
21
21
  options[:count] = total_count_from(response_object)
22
22
 
23
23
  [ElasticsearchRails.new(**options), response_object]
@@ -11,13 +11,13 @@ class Pagy
11
11
  if search.is_a?(Search::Arguments) # Active mode
12
12
 
13
13
  Searcher.wrap(search, options) do
14
- model, term, search_options = search
14
+ model, arguments, search_options = search
15
15
 
16
16
  search_options[:hits_per_page] = options[:limit]
17
17
  search_options[:page] = options[:page]
18
18
 
19
19
  method = options[:search_method] || Meilisearch::DEFAULT[:search_method]
20
- results = model.send(method, term, search_options)
20
+ results = model.send(method, *arguments, search_options)
21
21
  options[:count] = results.raw_answer['totalHits']
22
22
 
23
23
  [Meilisearch.new(**options), results]
@@ -11,7 +11,8 @@ class Pagy
11
11
  calendar: :CalendarPaginator,
12
12
  elasticsearch_rails: :ElasticsearchRailsPaginator,
13
13
  meilisearch: :MeilisearchPaginator,
14
- searchkick: :SearchkickPaginator }.freeze
14
+ searchkick: :SearchkickPaginator,
15
+ typesense_rails: :TypesenseRailsPaginator }.freeze
15
16
 
16
17
  path = Pathname.new(__dir__)
17
18
  paginators.each { |symbol, name| autoload name, path.join(symbol.to_s) }
@@ -24,7 +25,7 @@ class Pagy
24
25
  arguments = if paginator == :calendar
25
26
  [self, collection, options]
26
27
  else
27
- [collection, options = Pagy.options.merge(options)]
28
+ [collection, options = Pagy::OPTIONS.merge(options)]
28
29
  end
29
30
 
30
31
  options[:root_key] = 'page' if options[:jsonapi] # enforce 'page' root_key for JSON:API
@@ -11,13 +11,13 @@ class Pagy
11
11
  if search.is_a?(Search::Arguments) # Active mode
12
12
 
13
13
  Searcher.wrap(search, options) do
14
- model, term, search_options, block = search
14
+ model, arguments, search_options, block = search
15
15
 
16
16
  search_options[:per_page] = options[:limit]
17
17
  search_options[:page] = options[:page]
18
18
 
19
19
  method = options[:search_method] || Searchkick::DEFAULT[:search_method]
20
- results = model.send(method, term || '*', **search_options, &block)
20
+ results = model.send(method, *arguments || '*', **search_options, &block)
21
21
  options[:count] = results.total_count
22
22
 
23
23
  [Searchkick.new(**options), results]
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/searcher'
4
+
5
+ class Pagy
6
+ module TypesenseRailsPaginator
7
+ module_function
8
+
9
+ # Paginate from the search object
10
+ def paginate(search, options)
11
+ if search.is_a?(Search::Arguments) # Active mode
12
+
13
+ Searcher.wrap(search, options) do
14
+ model, arguments, search_options = search
15
+
16
+ search_options[:per_page] = options[:limit]
17
+ search_options[:page] = options[:page]
18
+
19
+ method = options[:search_method] || TypesenseRails::DEFAULT[:search_method]
20
+ results = model.send(method, *arguments, search_options)
21
+ options[:count] = results.raw_answer['found']
22
+
23
+ [TypesenseRails.new(**options), results]
24
+ end
25
+
26
+ else # Passive mode
27
+ options[:limit] = search.raw_answer['request_params']['per_page']
28
+ options[:page] = search.raw_answer['page']
29
+ options[:count] = search.raw_answer['found']
30
+
31
+ TypesenseRails.new(**options)
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/pagy.rb CHANGED
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'pathname'
4
+
4
5
  require_relative 'pagy/classes/exceptions'
5
6
  require_relative 'pagy/modules/abilities/linkable'
6
7
  require_relative 'pagy/modules/abilities/configurable'
7
8
  require_relative 'pagy/toolbox/helpers/loader'
8
9
 
9
10
  # Top superclass: it defines only what's common to all the subclasses
11
+ # noinspection RubyMismatchedArgumentType
10
12
  class Pagy
11
- VERSION = '43.2.9'
13
+ VERSION = '43.3.0'
12
14
  ROOT = Pathname.new(__dir__).parent.freeze
13
15
  DEFAULT = { limit: 20, limit_key: 'limit', page_key: 'page' }.freeze
14
16
  PAGE_TOKEN = EscapedValue.new('P ')
@@ -26,9 +28,11 @@ class Pagy
26
28
  autoload :ElasticsearchRails, path.join('classes/offset/search')
27
29
  autoload :Meilisearch, path.join('classes/offset/search')
28
30
  autoload :Searchkick, path.join('classes/offset/search')
31
+ autoload :TypesenseRails, path.join('classes/offset/search')
29
32
  autoload :Keyset, path.join('classes/keyset/keyset')
30
33
 
31
- def self.options = @options ||= {}
34
+ OPTIONS = {} # rubocop:disable Style/MutableConstant
35
+ def self.options = OPTIONS
32
36
 
33
37
  extend Configurable
34
38
  include Linkable
@@ -65,10 +69,11 @@ class Pagy
65
69
  default = {}
66
70
  current = self.class
67
71
 
68
- begin
72
+ loop do
69
73
  default = current::DEFAULT.merge(default)
70
74
  current = current.superclass
71
- end until current == Object # rubocop:disable Lint/Loop -- see https://github.com/rubocop/rubocop-performance/issues/362
75
+ break if current == Object
76
+ end
72
77
 
73
78
  clean_options = options.delete_if { |k, v| default.key?(k) && (v.nil? || v == '') }
74
79
  @options = default.merge!(clean_options).freeze
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.9
4
+ version: 43.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Domizio Demichelis
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: uri
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: yaml
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -138,6 +152,7 @@ files:
138
152
  - lib/pagy/toolbox/paginators/method.rb
139
153
  - lib/pagy/toolbox/paginators/offset.rb
140
154
  - lib/pagy/toolbox/paginators/searchkick.rb
155
+ - lib/pagy/toolbox/paginators/typesense_rails.rb
141
156
  - locales/ar.yml
142
157
  - locales/be.yml
143
158
  - locales/bg.yml