ariadne_view_components 0.0.94.4 → 0.0.94.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/app/assets/javascripts/ariadne_view_components.js +9 -9
- data/app/assets/javascripts/ariadne_view_components.js.br +0 -0
- data/app/assets/javascripts/ariadne_view_components.js.gz +0 -0
- data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
- data/app/assets/stylesheets/ariadne_view_components.css +1 -1
- data/app/assets/stylesheets/ariadne_view_components.css.br +0 -0
- data/app/assets/stylesheets/ariadne_view_components.css.gz +0 -0
- data/app/components/ariadne/ui/pagination/component.html.erb +46 -26
- data/app/components/ariadne/ui/pagination/component.rb +104 -11
- data/app/frontend/controllers/pagination_controller.ts +42 -0
- data/app/lib/ariadne/pagination_calculator.rb +78 -0
- data/config/initializers/pagy.rb +15 -0
- data/lib/ariadne/view_components/version.rb +1 -1
- metadata +5 -2
@@ -6,23 +6,116 @@ module Ariadne
|
|
6
6
|
module Pagination
|
7
7
|
# Pagination is a horizontal set of links to navigate paginated content.
|
8
8
|
class Component < Ariadne::BaseComponent
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
# Required props:
|
10
|
+
# - total_count: Total number of items to paginate.
|
11
|
+
# - current_page: Current page number.
|
12
|
+
# - items_per_page: Number of items per page.
|
13
|
+
# - label: Main label for screen readers (e.g., "Pagination").
|
14
|
+
# - goto_label: Label prefix for page links (e.g., "Go to page").
|
15
|
+
# - callback_page_url: Lambda that receives a page number and returns the URL.
|
16
|
+
# - callback_per_page_change: Lambda that receives a per_page value and returns the URL.
|
17
|
+
# - per_page_options: Array of allowed page sizes (e.g., [10, 25, 50, 100]).
|
18
|
+
# - frame_id: Turbo frame identifier (if applicable).
|
19
|
+
# - show_results_counter: Boolean indicating whether to display the results counter.
|
14
20
|
|
21
|
+
# Use dry-initializer for props
|
22
|
+
option :total_count
|
23
|
+
option :current_page, default: proc { 1 }
|
24
|
+
option :items_per_page, default: proc { 10 }
|
25
|
+
option :label, default: proc { I18n.t("pagy.aria_label.nav", default: "Pagination") }
|
26
|
+
option :goto_label, default: proc { I18n.t("pagy.goto_label", default: "Go to page") }
|
15
27
|
option :callback_page_url
|
28
|
+
option :callback_per_page_change
|
29
|
+
option :per_page_options
|
30
|
+
option :frame_id
|
31
|
+
option :show_results_counter, default: proc { true }
|
32
|
+
|
33
|
+
def initialize(**options)
|
34
|
+
super
|
35
|
+
@calculator = Ariadne::PaginationCalculator.new(
|
36
|
+
total_count: total_count,
|
37
|
+
page: current_page,
|
38
|
+
items_per_page: items_per_page,
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :calculator
|
16
45
|
|
17
46
|
def page_links
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
47
|
+
calculator.page_series
|
48
|
+
end
|
49
|
+
|
50
|
+
def component_classes
|
51
|
+
merge_tailwind_classes(
|
52
|
+
"ariadne-flex ariadne-items-center ariadne-justify-between ariadne-w-full",
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def link_classes(is_active = false)
|
57
|
+
base_classes = "ariadne-inline-flex ariadne-items-center ariadne-justify-center ariadne-min-w-[2rem] ariadne-h-8 ariadne-px-2 ariadne-text-sm ariadne-font-medium ariadne-rounded-md"
|
58
|
+
|
59
|
+
active_classes = if is_active
|
60
|
+
"ariadne-bg-gray-100 dark:ariadne-bg-gray-700 ariadne-text-gray-900 dark:ariadne-text-white ariadne-cursor-default"
|
61
|
+
else
|
62
|
+
"ariadne-transition-colors ariadne-text-gray-600 hover:ariadne-bg-gray-50 dark:ariadne-text-gray-300 dark:hover:ariadne-bg-gray-700 ariadne-cursor-pointer"
|
63
|
+
end
|
64
|
+
|
65
|
+
merge_tailwind_classes([base_classes, active_classes])
|
66
|
+
end
|
67
|
+
|
68
|
+
def disabled_link_classes
|
69
|
+
merge_tailwind_classes(
|
70
|
+
"ariadne-inline-flex ariadne-items-center ariadne-justify-center ariadne-min-w-[2rem] ariadne-h-8 ariadne-px-2 ariadne-text-sm ariadne-font-medium ariadne-rounded-md ariadne-text-gray-300 dark:ariadne-text-gray-600 ariadne-cursor-not-allowed",
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def ellipsis_classes
|
75
|
+
merge_tailwind_classes(
|
76
|
+
"ariadne-inline-flex ariadne-items-center ariadne-justify-center ariadne-min-w-[2rem] ariadne-h-8 ariadne-px-1 ariadne-text-sm ariadne-font-medium ariadne-text-gray-500 dark:ariadne-text-gray-400",
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def select_classes
|
81
|
+
merge_tailwind_classes(
|
82
|
+
"ariadne-h-8 ariadne-pl-2 ariadne-pr-8 ariadne-text-sm ariadne-font-medium ariadne-rounded-md ariadne-border ariadne-border-gray-300 dark:ariadne-border-gray-600 ariadne-bg-white dark:ariadne-bg-gray-800 ariadne-text-gray-700 dark:ariadne-text-gray-200 hover:ariadne-border-primary-500 focus:ariadne-outline-none focus:ariadne-ring-1 focus:ariadne-ring-primary-500 focus:ariadne-border-primary-500",
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
def counter_classes
|
87
|
+
merge_tailwind_classes(
|
88
|
+
"ariadne-text-sm ariadne-text-gray-700 dark:ariadne-text-gray-300 ariadne-font-medium",
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
def formatted_info
|
93
|
+
{
|
94
|
+
text: "#{calculator.from}-#{calculator.to} of #{total_count}",
|
95
|
+
role: "status",
|
96
|
+
aria: { live: "polite" },
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def select_options
|
101
|
+
per_page_options.map do |size|
|
102
|
+
{
|
103
|
+
value: callback_per_page_change.call(size),
|
104
|
+
label: "#{size} per page",
|
105
|
+
selected: size == items_per_page,
|
106
|
+
}
|
24
107
|
end
|
25
108
|
end
|
109
|
+
|
110
|
+
def current_per_page_option
|
111
|
+
select_options.find { |option| option[:selected] } || select_options.first
|
112
|
+
end
|
113
|
+
|
114
|
+
def nav_section_classes
|
115
|
+
merge_tailwind_classes(
|
116
|
+
"ariadne-flex ariadne-items-center ariadne-gap-1 ariadne-justify-center",
|
117
|
+
)
|
118
|
+
end
|
26
119
|
end
|
27
120
|
end
|
28
121
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import {controllerFactory} from '@utils/createController'
|
2
|
+
|
3
|
+
export default class PaginationController extends controllerFactory<HTMLElement>()({
|
4
|
+
targets: {
|
5
|
+
perPage: HTMLSelectElement,
|
6
|
+
},
|
7
|
+
}) {
|
8
|
+
connect(): void {
|
9
|
+
|
10
|
+
}
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Handles changes to the per-page selector
|
14
|
+
*/
|
15
|
+
perPageChange(event: Event): void {
|
16
|
+
event.preventDefault()
|
17
|
+
const select = event.target as HTMLSelectElement
|
18
|
+
const value = select.value
|
19
|
+
|
20
|
+
// Get the current URL and update it
|
21
|
+
const url = new URL(window.location.href)
|
22
|
+
const newParams = new URLSearchParams(value.split('?')[1])
|
23
|
+
|
24
|
+
// Update the URL with new parameters
|
25
|
+
newParams.forEach((value, key) => {
|
26
|
+
url.searchParams.set(key, value)
|
27
|
+
})
|
28
|
+
|
29
|
+
window.location.href = url.toString()
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Handles clicks on pagination links
|
34
|
+
*/
|
35
|
+
navigate(event: Event): void {
|
36
|
+
const link = (event.target as HTMLElement).closest('a')
|
37
|
+
if (!link) return
|
38
|
+
|
39
|
+
event.preventDefault()
|
40
|
+
window.location.href = link.href
|
41
|
+
}
|
42
|
+
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Ariadne
|
5
|
+
# Handles pagination calculations and data structure
|
6
|
+
class PaginationCalculator
|
7
|
+
attr_reader :total_count, :page, :items_per_page, :pages
|
8
|
+
|
9
|
+
def initialize(total_count:, page: 1, items_per_page: 10)
|
10
|
+
@total_count = total_count
|
11
|
+
@items_per_page = items_per_page
|
12
|
+
@pages = calculate_total_pages
|
13
|
+
@page = handle_page_overflow(page)
|
14
|
+
end
|
15
|
+
|
16
|
+
def from
|
17
|
+
return 0 if total_count.zero?
|
18
|
+
|
19
|
+
(page - 1) * items_per_page + 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def to
|
23
|
+
return 0 if total_count.zero?
|
24
|
+
|
25
|
+
[page * items_per_page, total_count].min
|
26
|
+
end
|
27
|
+
|
28
|
+
def next_page
|
29
|
+
page < pages ? page + 1 : nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def prev_page
|
33
|
+
page > 1 ? page - 1 : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def page_series
|
37
|
+
return [] if pages <= 1
|
38
|
+
return (1..pages).to_a if pages <= 5
|
39
|
+
|
40
|
+
links = []
|
41
|
+
links << 1
|
42
|
+
|
43
|
+
if page <= 3
|
44
|
+
# Near the beginning: 1 2 3 4 5 6 7 8 ... 20
|
45
|
+
(2..8).each { |n| links << n if n <= pages }
|
46
|
+
links << nil if pages > 8
|
47
|
+
links << pages if pages > 8
|
48
|
+
elsif page >= pages - 2
|
49
|
+
# Near the end: 1 ... 13 14 15 16 17 18 19 20
|
50
|
+
links << nil
|
51
|
+
((pages - 7)..pages).each { |n| links << n if n > 1 }
|
52
|
+
else
|
53
|
+
# Middle range: 1 ... 9 10 11 12 13 14 ... 20
|
54
|
+
links << nil
|
55
|
+
((page - 2)..(page + 2)).each { |n| links << n }
|
56
|
+
links << nil
|
57
|
+
links << pages
|
58
|
+
end
|
59
|
+
|
60
|
+
links
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def calculate_total_pages
|
66
|
+
return 1 if total_count.zero?
|
67
|
+
|
68
|
+
(total_count.to_f / items_per_page).ceil
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_page_overflow(requested_page)
|
72
|
+
return 1 if requested_page < 1
|
73
|
+
return pages if requested_page > pages
|
74
|
+
|
75
|
+
requested_page
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pagy"
|
4
|
+
|
5
|
+
# Only require the necessary extras:
|
6
|
+
require "pagy/extras/overflow" # Handles overflow scenarios gracefully.
|
7
|
+
require "pagy/extras/metadata" # Provides metadata for JSON/client-side rendering.
|
8
|
+
require "pagy/extras/headers" # Useful for API responses (if needed).
|
9
|
+
require "pagy/extras/keyset" # For efficient keyset pagination with large data sets.
|
10
|
+
require "pagy/extras/array" # Required for series method
|
11
|
+
|
12
|
+
# Customize default settings:
|
13
|
+
Pagy::DEFAULT[:limit] = 10 # Default items per page.
|
14
|
+
Pagy::DEFAULT[:size] = 9 # Number of links in the nav bar.
|
15
|
+
Pagy::DEFAULT[:overflow] = :last_page
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ariadne_view_components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.94.
|
4
|
+
version: 0.0.94.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garen J. Torikian
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tailwind_merge
|
@@ -265,6 +265,7 @@ files:
|
|
265
265
|
- app/frontend/ariadne/theme.ts
|
266
266
|
- app/frontend/controllers/form_autosubmit_controller.ts
|
267
267
|
- app/frontend/controllers/form_validity_controller.ts
|
268
|
+
- app/frontend/controllers/pagination_controller.ts
|
268
269
|
- app/frontend/controllers/tooltip_controller.ts
|
269
270
|
- app/frontend/entrypoints/application.ts
|
270
271
|
- app/frontend/stylesheets/ariadne_view_components.css
|
@@ -281,9 +282,11 @@ files:
|
|
281
282
|
- app/lib/ariadne/form.rb
|
282
283
|
- app/lib/ariadne/icon_helper.rb
|
283
284
|
- app/lib/ariadne/logger_helper.rb
|
285
|
+
- app/lib/ariadne/pagination_calculator.rb
|
284
286
|
- app/lib/ariadne/view_component/html_attrs.rb
|
285
287
|
- app/lib/ariadne/view_component/style_variants.rb
|
286
288
|
- app/lib/ariadne/view_helper.rb
|
289
|
+
- config/initializers/pagy.rb
|
287
290
|
- lib/ariadne/accessibility.rb
|
288
291
|
- lib/ariadne/forms/acts_as_component.rb
|
289
292
|
- lib/ariadne/forms/base.html.erb
|