rails_pagination_ultimate 1.0.1
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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +193 -0
- data/app/assets/javascripts/rails_pagination_ultimate/loader.js +133 -0
- data/app/assets/stylesheets/rails_pagination_ultimate/main.css +113 -0
- data/app/views/rails_pagination_ultimate/_tailwind_pro.html.erb +35 -0
- data/config/locales/en.yml +12 -0
- data/lib/generators/rails_pagination_ultimate/tailwind_generator.rb +17 -0
- data/lib/rails_pagination_ultimate/action_view_helper.rb +203 -0
- data/lib/rails_pagination_ultimate/active_record_extension.rb +98 -0
- data/lib/rails_pagination_ultimate/configuration.rb +24 -0
- data/lib/rails_pagination_ultimate/engine.rb +20 -0
- data/lib/rails_pagination_ultimate/version.rb +5 -0
- data/lib/rails_pagination_ultimate.rb +10 -0
- metadata +100 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 465576ccd1cde103ef4befb850236d17d3105ef14c61cbba792f8ceff7f2aa6e
|
|
4
|
+
data.tar.gz: 9f35198d69e339ea5fd7c6535198cdb3bdd068338d65d074afd00cd33e3429fc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 77e93931d1fbf86e6e3476438a726aaa15aeb6482183040ca7b2f57324c2334bf621ac060dc64a9da78aa981c57f45ca75041abc1ab6508b97fdbed0c7a49ed9
|
|
7
|
+
data.tar.gz: b70adad1aab1788d8626ffb6605d30bd64e1464c9ed79c6c17a4783939923fe262e5d405e7b4feb3ff5a677b3638bbfef944adc3a783566af0d3f2df25aa49a2
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shiboshree Roy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# 💎 RailsPaginationUltimate
|
|
2
|
+
|
|
3
|
+
**The ultra-premium, high-performance pagination powerhouse for modern Rails applications.**
|
|
4
|
+
|
|
5
|
+
RailsPaginationUltimate is built for developers who need more than just simple page links. It is a feature-rich, "Best-in-Class" replacement for Kaminari and WillPaginate, offering pro-grade UI components, native Tailwind CSS support, and advanced interactivity out of the box.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🚀 Key Features
|
|
10
|
+
|
|
11
|
+
* **⚡ Pro Performance**: `without_count` mode for lightning-fast paging on multi-million row tables.
|
|
12
|
+
* **🎨 Premium UI Styles**: Standard numbers, "Load More" buttons, and automated "Infinite Scroll".
|
|
13
|
+
* **🌊 Native Tailwind Support**: First-class support for Tailwind CSS utility classes.
|
|
14
|
+
* **⌨️ Keyboard Navigation**: Use Left/Right arrows to flip through pages.
|
|
15
|
+
* **🔄 History API**: AJAX updates automatically sync with the browser's URL and Back button.
|
|
16
|
+
* **🌍 100% Localizable**: Full I18n support for all labels, icons, and result strings.
|
|
17
|
+
* **🛠 Advanced Helpers**: Integrated `page_entries_info` and `per_page_select` components.
|
|
18
|
+
* **🧩 Total Customization**: Easy-to-use generator to export and modify HTML templates.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
Add this line to your application's `Gemfile`:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
gem 'rails_pagination_ultimate', github: 'shiboshreeroy/rails_pagination_ultimate'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
And then execute:
|
|
31
|
+
```bash
|
|
32
|
+
bundle install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 📖 Usage
|
|
38
|
+
|
|
39
|
+
### 1. Basic Pagination
|
|
40
|
+
In your controller:
|
|
41
|
+
```ruby
|
|
42
|
+
@posts = Post.page(params[:page]).per(25)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
In your view:
|
|
46
|
+
```erb
|
|
47
|
+
<%= paginate @posts %>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. Pro Navigation Modes
|
|
51
|
+
|
|
52
|
+
#### 📱 Infinite Scroll (Modern Feed)
|
|
53
|
+
Automatically loads content as the user scrolls. Requires a `target` CSS selector.
|
|
54
|
+
```erb
|
|
55
|
+
<div id="posts-container">
|
|
56
|
+
<%= render @posts %>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<%= paginate @posts, type: :infinite_scroll, target: "#posts-container" %>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### 🖱 "Load More" Button
|
|
63
|
+
A premium button that appends results without a full page reload.
|
|
64
|
+
```erb
|
|
65
|
+
<%= paginate @posts, type: :load_more, target: "#posts-container" %>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### ⌨️ Keyboard & History Support
|
|
69
|
+
Enable pro-level accessibility and shareable URLs for AJAX pagination:
|
|
70
|
+
```erb
|
|
71
|
+
<%= paginate @posts, theme: :tailwind, keyboard: true, history: true %>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## ⚡ Performance Optimization
|
|
77
|
+
|
|
78
|
+
### Fast Mode (`without_count`)
|
|
79
|
+
Traditional pagination runs a `COUNT(*)` query which can be slow on large tables. RailsPaginationUltimate's "Fast Mode" skips this count entirely and uses an efficient look-ahead check for the next page.
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# Controller
|
|
83
|
+
@posts = Post.without_count.page(params[:page]).per(50)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 🎨 Customization & Theming
|
|
89
|
+
|
|
90
|
+
### A. Tailwind CSS (Recommended)
|
|
91
|
+
Use pro-grade Tailwind components by simply passing `theme: :tailwind`. No extra CSS files required!
|
|
92
|
+
```erb
|
|
93
|
+
<%= paginate @posts, theme: :tailwind, first_last: true, window: 3 %>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### B. CSS Variables (Default Theme)
|
|
97
|
+
If you're not using Tailwind, the default theme is built with clean CSS variables for instant global styling:
|
|
98
|
+
```css
|
|
99
|
+
:root {
|
|
100
|
+
--rp-primary-color: #3b82f6; /* Your Brand Color */
|
|
101
|
+
--rp-border-radius: 0.5rem;
|
|
102
|
+
--rp-text-color: #1f2937;
|
|
103
|
+
--rp-bg-color: #ffffff;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### C. Total UI Control (Generator)
|
|
108
|
+
Need to change the HTML? Export the Pro Tailwind template to your app:
|
|
109
|
+
```bash
|
|
110
|
+
rails generate rails_pagination_ultimate:tailwind
|
|
111
|
+
```
|
|
112
|
+
This creates `app/views/rails_pagination_ultimate/_pagination.html.erb`. You can then use it like this:
|
|
113
|
+
```erb
|
|
114
|
+
<%= paginate @posts, template: "rails_pagination_ultimate/pagination" %>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 🛠 Results Management
|
|
120
|
+
|
|
121
|
+
### Detailed Information
|
|
122
|
+
Displays "Showing 1-25 of 150 results".
|
|
123
|
+
```erb
|
|
124
|
+
<%= page_entries_info @posts %>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Dynamic "Per Page" Selector
|
|
128
|
+
Let users choose how many items they want to see.
|
|
129
|
+
```erb
|
|
130
|
+
<%= per_page_select @posts, choices: [25, 50, 100], label: "Items per page:" %>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 🌍 Localization (I18n)
|
|
136
|
+
|
|
137
|
+
Everything is translatable in `config/locales/en.yml`:
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
en:
|
|
141
|
+
rails_pagination_ultimate:
|
|
142
|
+
previous: "← Previous"
|
|
143
|
+
next: "Next →"
|
|
144
|
+
first: "« First"
|
|
145
|
+
last: "Last »"
|
|
146
|
+
info:
|
|
147
|
+
other: "Showing <b>%{first}-%{last}</b> of <b>%{total}</b> results"
|
|
148
|
+
without_count: "Showing <b>%{first}-%{last}</b> results"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 📋 Options Reference
|
|
154
|
+
|
|
155
|
+
| Option | Type | Default | Description |
|
|
156
|
+
| :--- | :--- | :--- | :--- |
|
|
157
|
+
| `theme` | Symbol | `nil` | Set to `:tailwind` for Tailwind UI. |
|
|
158
|
+
| `type` | Symbol | `:standard` | `:standard`, `:load_more`, or `:infinite_scroll`. |
|
|
159
|
+
| `target` | String | `nil` | CSS selector for where to append new items (AJAX). |
|
|
160
|
+
| `window` | Integer | `2` | Number of page links around the current page. |
|
|
161
|
+
| `first_last`| Boolean | `false` | Show "First" and "Last" page links. |
|
|
162
|
+
| `keyboard` | Boolean | `false` | Enable Left/Right arrow key navigation. |
|
|
163
|
+
| `history` | Boolean | `false` | Update browser URL/history on AJAX paging. |
|
|
164
|
+
| `template` | String | `nil` | Path to a custom partial for rendering. |
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## ⚖️ Why switch from Kaminari?
|
|
169
|
+
|
|
170
|
+
| Feature | RailsPaginationUltimate | Kaminari |
|
|
171
|
+
| :--- | :---: | :---: |
|
|
172
|
+
| **Native Tailwind UI** | ✅ Built-in | ❌ No |
|
|
173
|
+
| **Infinite Scroll** | ✅ Built-in | ❌ No |
|
|
174
|
+
| **Fast Mode (No Count)** | ✅ Native | ❌ Plugin required |
|
|
175
|
+
| **Keyboard Shortcuts** | ✅ Built-in | ❌ No |
|
|
176
|
+
| **History API Sync** | ✅ Built-in | ❌ No |
|
|
177
|
+
| **Interactive Per-Page** | ✅ Built-in | ❌ No |
|
|
178
|
+
| **Modern Flexbox CSS** | ✅ Yes | ❌ No |
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 👨💻 Author & Credits
|
|
183
|
+
|
|
184
|
+
**RailsPaginationUltimate** is developed and maintained by **Shiboshree Roy**.
|
|
185
|
+
|
|
186
|
+
- **Developer & Author**: [Shiboshree Roy](https://github.com/shiboshreeroy)
|
|
187
|
+
- **Powered By**: Shiboshree Roy
|
|
188
|
+
|
|
189
|
+
Built with ❤️ for the Rails community.
|
|
190
|
+
|
|
191
|
+
## 📄 License
|
|
192
|
+
|
|
193
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
document.addEventListener("click", function(event) {
|
|
3
|
+
const target = event.target.closest("[data-type='load-more']");
|
|
4
|
+
if (!target) return;
|
|
5
|
+
|
|
6
|
+
event.preventDefault();
|
|
7
|
+
const url = target.href;
|
|
8
|
+
const targetSelector = target.dataset.target;
|
|
9
|
+
|
|
10
|
+
if (!targetSelector) {
|
|
11
|
+
console.warn("RailsPaginationUltimate: No target selector provided for load-more button.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
target.classList.add("loading");
|
|
16
|
+
target.innerText = "Loading...";
|
|
17
|
+
|
|
18
|
+
fetch(url, {
|
|
19
|
+
headers: {
|
|
20
|
+
"Accept": "text/vnd.turbo-stream.html, text/html, application/xhtml+xml"
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
.then(response => response.text())
|
|
24
|
+
.then(html => {
|
|
25
|
+
const parser = new DOMParser();
|
|
26
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
27
|
+
const newItems = doc.querySelector(targetSelector);
|
|
28
|
+
const newButton = doc.querySelector(".rp-load-more-container");
|
|
29
|
+
|
|
30
|
+
if (newItems) {
|
|
31
|
+
document.querySelector(targetSelector).insertAdjacentHTML("beforeend", newItems.innerHTML);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const container = target.closest(".rp-load-more-container");
|
|
35
|
+
if (newButton) {
|
|
36
|
+
container.outerHTML = newButton.outerHTML;
|
|
37
|
+
} else {
|
|
38
|
+
container.remove();
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.catch(error => {
|
|
42
|
+
console.error("RailsPaginationUltimate error:", error);
|
|
43
|
+
target.classList.remove("loading");
|
|
44
|
+
target.innerText = "Load More";
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Infinite Scroll Implementation
|
|
49
|
+
const initInfiniteScroll = () => {
|
|
50
|
+
const containers = document.querySelectorAll("[data-controller='rp-infinite-scroll']");
|
|
51
|
+
|
|
52
|
+
containers.forEach(container => {
|
|
53
|
+
if (container.dataset.observed) return;
|
|
54
|
+
|
|
55
|
+
const url = container.dataset.rpInfiniteScrollUrlValue;
|
|
56
|
+
const targetSelector = container.dataset.rpInfiniteScrollTargetValue;
|
|
57
|
+
|
|
58
|
+
const observer = new IntersectionObserver((entries) => {
|
|
59
|
+
if (entries[0].isIntersecting) {
|
|
60
|
+
observer.unobserve(container);
|
|
61
|
+
|
|
62
|
+
fetch(url, {
|
|
63
|
+
headers: { "Accept": "text/html" }
|
|
64
|
+
})
|
|
65
|
+
.then(response => response.text())
|
|
66
|
+
.then(html => {
|
|
67
|
+
const parser = new DOMParser();
|
|
68
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
69
|
+
const newItems = doc.querySelector(targetSelector);
|
|
70
|
+
const newContainer = doc.querySelector("[data-controller='rp-infinite-scroll']");
|
|
71
|
+
|
|
72
|
+
if (newItems) {
|
|
73
|
+
document.querySelector(targetSelector).insertAdjacentHTML("beforeend", newItems.innerHTML);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (newContainer) {
|
|
77
|
+
container.outerHTML = newContainer.outerHTML;
|
|
78
|
+
initInfiniteScroll(); // Re-init for the new container
|
|
79
|
+
} else {
|
|
80
|
+
container.remove();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}, { threshold: 0.1 });
|
|
85
|
+
|
|
86
|
+
observer.observe(container);
|
|
87
|
+
container.dataset.observed = "true";
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Keyboard Navigation & History Support
|
|
92
|
+
const initAdvancedFeatures = () => {
|
|
93
|
+
const navs = document.querySelectorAll("[data-controller='rails-pagination']");
|
|
94
|
+
|
|
95
|
+
navs.forEach(nav => {
|
|
96
|
+
if (nav.dataset.advancedObserved) return;
|
|
97
|
+
|
|
98
|
+
// Keyboard Navigation
|
|
99
|
+
if (nav.dataset.keyboard === "true") {
|
|
100
|
+
document.addEventListener("keydown", (event) => {
|
|
101
|
+
if (event.key === "ArrowLeft") {
|
|
102
|
+
const prev = nav.querySelector("[data-rel='prev']");
|
|
103
|
+
if (prev) prev.click();
|
|
104
|
+
} else if (event.key === "ArrowRight") {
|
|
105
|
+
const next = nav.querySelector("[data-rel='next']");
|
|
106
|
+
if (next) next.click();
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// History API for AJAX links
|
|
112
|
+
if (nav.dataset.history === "true") {
|
|
113
|
+
nav.querySelectorAll("a").forEach(link => {
|
|
114
|
+
link.addEventListener("click", () => {
|
|
115
|
+
if (!link.dataset.remote && !link.dataset.turbo) return;
|
|
116
|
+
window.history.pushState({}, "", link.href);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
nav.dataset.advancedObserved = "true";
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
document.addEventListener("turbo:load", () => {
|
|
126
|
+
initInfiniteScroll();
|
|
127
|
+
initAdvancedFeatures();
|
|
128
|
+
});
|
|
129
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
130
|
+
initInfiniteScroll();
|
|
131
|
+
initAdvancedFeatures();
|
|
132
|
+
});
|
|
133
|
+
})();
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--rp-primary-color: #3b82f6;
|
|
3
|
+
--rp-primary-hover: #2563eb;
|
|
4
|
+
--rp-text-color: #374151;
|
|
5
|
+
--rp-bg-color: #ffffff;
|
|
6
|
+
--rp-border-color: #e5e7eb;
|
|
7
|
+
--rp-border-radius: 0.5rem;
|
|
8
|
+
--rp-gap-color: #9ca3af;
|
|
9
|
+
--rp-disabled-color: #d1d5db;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.rp-pagination {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
gap: 0.5rem;
|
|
17
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
18
|
+
margin: 2rem 0;
|
|
19
|
+
user-select: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.rp-page {
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
min-width: 2.5rem;
|
|
27
|
+
height: 2.5rem;
|
|
28
|
+
padding: 0 0.5rem;
|
|
29
|
+
border: 1px solid var(--rp-border-color);
|
|
30
|
+
border-radius: var(--rp-border-radius);
|
|
31
|
+
background-color: var(--rp-bg-color);
|
|
32
|
+
color: var(--rp-text-color);
|
|
33
|
+
text-decoration: none;
|
|
34
|
+
font-size: 0.875rem;
|
|
35
|
+
font-weight: 500;
|
|
36
|
+
transition: all 0.2s;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.rp-page:hover:not(.disabled):not(.current):not(.gap) {
|
|
41
|
+
border-color: var(--rp-primary-color);
|
|
42
|
+
color: var(--rp-primary-color);
|
|
43
|
+
background-color: #f8fafc;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.rp-page.current {
|
|
47
|
+
background-color: var(--rp-primary-color);
|
|
48
|
+
border-color: var(--rp-primary-color);
|
|
49
|
+
color: white;
|
|
50
|
+
cursor: default;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.rp-page.disabled {
|
|
54
|
+
color: var(--rp-disabled-color);
|
|
55
|
+
border-color: var(--rp-border-color);
|
|
56
|
+
cursor: not-allowed;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.rp-page.gap {
|
|
60
|
+
border: none;
|
|
61
|
+
color: var(--rp-gap-color);
|
|
62
|
+
cursor: default;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.rp-page-links {
|
|
66
|
+
display: flex;
|
|
67
|
+
gap: 0.25rem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.rp-prev, .rp-next {
|
|
71
|
+
padding: 0 1rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.rp-load-more-container {
|
|
75
|
+
display: flex;
|
|
76
|
+
justify-content: center;
|
|
77
|
+
margin: 2rem 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.rp-load-more-button {
|
|
81
|
+
display: inline-flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
padding: 0.75rem 2rem;
|
|
85
|
+
background-color: var(--rp-primary-color);
|
|
86
|
+
color: white;
|
|
87
|
+
border-radius: var(--rp-border-radius);
|
|
88
|
+
font-weight: 600;
|
|
89
|
+
text-decoration: none;
|
|
90
|
+
transition: background-color 0.2s;
|
|
91
|
+
border: none;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.rp-load-more-button:hover {
|
|
96
|
+
background-color: var(--rp-primary-hover);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.rp-load-more-button.loading {
|
|
100
|
+
opacity: 0.7;
|
|
101
|
+
cursor: wait;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@media (max-width: 640px) {
|
|
105
|
+
.rp-page-links {
|
|
106
|
+
display: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.rp-pagination {
|
|
110
|
+
justify-content: space-between;
|
|
111
|
+
width: 100%;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<nav class="flex items-center justify-between border-t border-gray-200 px-4 sm:px-0">
|
|
2
|
+
<div class="-mt-px flex w-0 flex-1">
|
|
3
|
+
<% unless collection.first_page? %>
|
|
4
|
+
<%= link_to template.url_for(template.params.to_unsafe_h.merge(page: collection.prev_page)), class: "inline-flex items-center border-t-2 border-transparent pr-1 pt-4 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
|
5
|
+
<svg class="mr-3 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
6
|
+
<path fill-rule="evenodd" d="M18 10a.75.75 0 01-.75.75H4.66l2.1 1.95a.75.75 0 11-1.02 1.1l-3.5-3.25a.75.75 0 010-1.1l3.5-3.25a.75.75 0 111.02 1.1l-2.1 1.95h12.59A.75.75 0 0118 10z" clip-rule="evenodd" />
|
|
7
|
+
</svg>
|
|
8
|
+
Previous
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="hidden md:-mt-px md:flex">
|
|
14
|
+
<% windowed_page_numbers.each do |n| %>
|
|
15
|
+
<% if n == :gap %>
|
|
16
|
+
<span class="inline-flex items-center border-t-2 border-transparent px-4 pt-4 text-sm font-medium text-gray-500">...</span>
|
|
17
|
+
<% elsif n == collection.current_page %>
|
|
18
|
+
<span class="inline-flex items-center border-t-2 border-blue-500 px-4 pt-4 text-sm font-medium text-blue-600" aria-current="page"><%= n %></span>
|
|
19
|
+
<% else %>
|
|
20
|
+
<%= link_to n, template.url_for(template.params.to_unsafe_h.merge(page: n)), class: "inline-flex items-center border-t-2 border-transparent px-4 pt-4 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" %>
|
|
21
|
+
<% end %>
|
|
22
|
+
<% end %>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="-mt-px flex w-0 flex-1 justify-end">
|
|
26
|
+
<% unless collection.last_page? %>
|
|
27
|
+
<%= link_to template.url_for(template.params.to_unsafe_h.merge(page: collection.next_page)), class: "inline-flex items-center border-t-2 border-transparent pl-1 pt-4 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
|
28
|
+
Next
|
|
29
|
+
<svg class="ml-3 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
30
|
+
<path fill-rule="evenodd" d="M2 10a.75.75 0 01.75-.75h12.59l-2.1-1.95a.75.75 0 111.02-1.1l3.5 3.25a.75.75 0 010 1.1l-3.5 3.25a.75.75 0 11-1.02-1.1l2.1-1.95H2.75A.75.75 0 012 10z" clip-rule="evenodd" />
|
|
31
|
+
</svg>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% end %>
|
|
34
|
+
</div>
|
|
35
|
+
</nav>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
en:
|
|
2
|
+
rails_pagination_ultimate:
|
|
3
|
+
previous: "« Previous"
|
|
4
|
+
next: "Next »"
|
|
5
|
+
first: "« First"
|
|
6
|
+
last: "Last »"
|
|
7
|
+
truncate: "..."
|
|
8
|
+
info:
|
|
9
|
+
one: "Displaying <b>1</b> %{entry_name}"
|
|
10
|
+
other: "Displaying %{entry_name} <b>%{first} - %{last}</b> of <b>%{total}</b>"
|
|
11
|
+
all: "Displaying <b>all %{total}</b> %{entry_name}"
|
|
12
|
+
without_count: "Displaying %{entry_name} <b>%{first} - %{last}</b>"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module RailsPaginationUltimate
|
|
6
|
+
module Generators
|
|
7
|
+
class TailwindGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("../../../app/views/rails_pagination_ultimate", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Copies RailsPaginationUltimate Tailwind views to your application."
|
|
11
|
+
|
|
12
|
+
def copy_views
|
|
13
|
+
copy_file "_tailwind_pro.html.erb", "app/views/rails_pagination_ultimate/_pagination.html.erb"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsPaginationUltimate
|
|
4
|
+
module ActionViewHelper
|
|
5
|
+
def paginate(collection, options = {})
|
|
6
|
+
return unless collection.respond_to?(:total_pages)
|
|
7
|
+
|
|
8
|
+
paginator = Paginator.new(self, collection, options)
|
|
9
|
+
paginator.render
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def page_entries_info(collection, options = {})
|
|
13
|
+
entry_name = options[:entry_name] || collection.model_name.human.downcase
|
|
14
|
+
entry_name = entry_name.pluralize if collection.total_count != 1
|
|
15
|
+
|
|
16
|
+
if collection.total_pages == Float::INFINITY
|
|
17
|
+
first = collection.offset_value + 1
|
|
18
|
+
last = collection.offset_value + collection.length
|
|
19
|
+
return I18n.t("rails_pagination_ultimate.info.without_count", first: first, last: last, entry_name: entry_name).html_safe
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if collection.total_pages < 2
|
|
23
|
+
I18n.t("rails_pagination_ultimate.info.all", total: collection.total_count, entry_name: entry_name).html_safe
|
|
24
|
+
else
|
|
25
|
+
first = collection.offset_value + 1
|
|
26
|
+
last = [collection.offset_value + collection.limit_value, collection.total_count].min
|
|
27
|
+
I18n.t("rails_pagination_ultimate.info.other", first: first, last: last, total: collection.total_count, entry_name: entry_name).html_safe
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def per_page_select(collection, options = {})
|
|
32
|
+
choices = options[:choices] || [10, 25, 50, 100]
|
|
33
|
+
current_per = collection.limit_value
|
|
34
|
+
|
|
35
|
+
template.content_tag(:div, class: "rp-per-page-container #{options[:class]}") do
|
|
36
|
+
template.concat template.content_tag(:span, options[:label] || "Show", class: "rp-label")
|
|
37
|
+
template.concat template.select_tag(:per, template.options_for_select(choices, current_per),
|
|
38
|
+
class: "rp-select",
|
|
39
|
+
onchange: "const url = new URL(window.location.href); url.searchParams.set('per', this.value); url.searchParams.set('page', 1); window.location.href = url.toString();")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Paginator
|
|
45
|
+
attr_reader :template, :collection, :options, :current_page, :total_pages
|
|
46
|
+
|
|
47
|
+
def initialize(template, collection, options = {})
|
|
48
|
+
@template = template
|
|
49
|
+
@collection = collection
|
|
50
|
+
@options = options
|
|
51
|
+
@current_page = collection.current_page
|
|
52
|
+
@total_pages = collection.total_pages
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def render
|
|
56
|
+
return "" if total_pages <= 1 && total_pages != Float::INFINITY
|
|
57
|
+
|
|
58
|
+
if options[:template]
|
|
59
|
+
return template.render(partial: options[:template], locals: {
|
|
60
|
+
collection: collection,
|
|
61
|
+
options: options,
|
|
62
|
+
template: template,
|
|
63
|
+
windowed_page_numbers: windowed_page_numbers
|
|
64
|
+
})
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
case options[:type]
|
|
68
|
+
when :load_more
|
|
69
|
+
render_load_more
|
|
70
|
+
when :infinite_scroll
|
|
71
|
+
render_infinite_scroll
|
|
72
|
+
else
|
|
73
|
+
render_standard
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def render_standard
|
|
80
|
+
nav_class = options[:theme] == :tailwind ? "flex items-center justify-center space-x-2 my-8" : "rp-pagination rp-standard #{options[:class]}"
|
|
81
|
+
|
|
82
|
+
data = {
|
|
83
|
+
controller: "rails-pagination",
|
|
84
|
+
keyboard: options[:keyboard],
|
|
85
|
+
history: options[:history]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
template.content_tag(:nav, class: nav_class, aria: { label: "Pagination" }, data: data) do
|
|
89
|
+
template.concat first_link if options[:first_last] && total_pages != Float::INFINITY
|
|
90
|
+
template.concat prev_link
|
|
91
|
+
template.concat page_links if total_pages != Float::INFINITY
|
|
92
|
+
template.concat next_link
|
|
93
|
+
template.concat last_link if options[:first_last] && total_pages != Float::INFINITY
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def first_link
|
|
98
|
+
label = options[:first_label] || I18n.t("rails_pagination_ultimate.first").html_safe
|
|
99
|
+
if collection.first_page?
|
|
100
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-400 cursor-not-allowed hidden sm:inline-flex" : "rp-page rp-first disabled"
|
|
101
|
+
template.content_tag(:span, label, class: cls)
|
|
102
|
+
else
|
|
103
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-700 hover:bg-gray-50 transition hidden sm:inline-flex" : "rp-page rp-first"
|
|
104
|
+
template.link_to(label, template.url_for(template.params.to_unsafe_h.merge(page: 1)), class: cls, data: { rel: "first" })
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def last_link
|
|
109
|
+
label = options[:last_label] || I18n.t("rails_pagination_ultimate.last").html_safe
|
|
110
|
+
if collection.last_page?
|
|
111
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-400 cursor-not-allowed hidden sm:inline-flex" : "rp-page rp-last disabled"
|
|
112
|
+
template.content_tag(:span, label, class: cls)
|
|
113
|
+
else
|
|
114
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-700 hover:bg-gray-50 transition hidden sm:inline-flex" : "rp-page rp-last"
|
|
115
|
+
template.link_to(label, template.url_for(template.params.to_unsafe_h.merge(page: total_pages)), class: cls, data: { rel: "last" })
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def render_load_more
|
|
120
|
+
return if collection.last_page?
|
|
121
|
+
|
|
122
|
+
template.content_tag(:div, class: "rp-pagination rp-load-more-container #{options[:class]}") do
|
|
123
|
+
template.link_to("Load More", template.url_for(template.params.to_unsafe_h.merge(page: collection.next_page)),
|
|
124
|
+
class: "rp-load-more-button",
|
|
125
|
+
remote: true,
|
|
126
|
+
data: {
|
|
127
|
+
type: "load-more",
|
|
128
|
+
target: options[:target],
|
|
129
|
+
turbo_frame: options[:turbo_frame]
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def render_infinite_scroll
|
|
136
|
+
return if collection.last_page?
|
|
137
|
+
|
|
138
|
+
template.content_tag(:div, class: "rp-pagination rp-infinite-scroll-container #{options[:class]}",
|
|
139
|
+
data: {
|
|
140
|
+
controller: "rp-infinite-scroll",
|
|
141
|
+
rp_infinite_scroll_url_value: template.url_for(template.params.to_unsafe_h.merge(page: collection.next_page)),
|
|
142
|
+
rp_infinite_scroll_target_value: options[:target]
|
|
143
|
+
}) do
|
|
144
|
+
template.content_tag(:div, options[:loading_text] || "Loading more...", class: "rp-infinite-scroll-loader")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def prev_link
|
|
149
|
+
label = options[:prev_label] || I18n.t("rails_pagination_ultimate.previous").html_safe
|
|
150
|
+
if collection.first_page?
|
|
151
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-400 cursor-not-allowed" : "rp-page rp-prev disabled"
|
|
152
|
+
template.content_tag(:span, label, class: cls)
|
|
153
|
+
else
|
|
154
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-700 hover:bg-gray-50 transition" : "rp-page rp-prev"
|
|
155
|
+
template.link_to(label, template.url_for(template.params.to_unsafe_h.merge(page: collection.prev_page)), class: cls, data: { turbo_frame: options[:turbo_frame], rel: "prev" })
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def next_link
|
|
160
|
+
label = options[:next_label] || I18n.t("rails_pagination_ultimate.next").html_safe
|
|
161
|
+
if collection.last_page?
|
|
162
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-400 cursor-not-allowed" : "rp-page rp-next disabled"
|
|
163
|
+
template.content_tag(:span, label, class: cls)
|
|
164
|
+
else
|
|
165
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-700 hover:bg-gray-50 transition" : "rp-page rp-next"
|
|
166
|
+
template.link_to(label, template.url_for(template.params.to_unsafe_h.merge(page: collection.next_page)), class: cls, data: { turbo_frame: options[:turbo_frame], rel: "next" })
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def page_links
|
|
171
|
+
container_cls = options[:theme] == :tailwind ? "flex space-x-1" : "rp-page-links"
|
|
172
|
+
template.content_tag(:div, class: container_cls) do
|
|
173
|
+
windowed_page_numbers.each do |n|
|
|
174
|
+
if n == :gap
|
|
175
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 text-gray-400" : "rp-page gap"
|
|
176
|
+
template.concat template.content_tag(:span, "...", class: cls)
|
|
177
|
+
elsif n == current_page
|
|
178
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 bg-blue-600 text-white rounded-md font-medium" : "rp-page current"
|
|
179
|
+
template.concat template.content_tag(:span, n, class: cls)
|
|
180
|
+
else
|
|
181
|
+
cls = options[:theme] == :tailwind ? "px-4 py-2 border rounded-md text-gray-700 hover:border-blue-600 hover:text-blue-600 transition" : "rp-page"
|
|
182
|
+
template.concat template.link_to(n, template.url_for(template.params.to_unsafe_h.merge(page: n)), class: cls, data: { turbo_frame: options[:turbo_frame] })
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def windowed_page_numbers
|
|
189
|
+
return [] if total_pages == Float::INFINITY
|
|
190
|
+
window = options[:window] || 2
|
|
191
|
+
pages = []
|
|
192
|
+
|
|
193
|
+
(1..total_pages).each do |n|
|
|
194
|
+
if n <= 1 || n >= total_pages || (n - current_page).abs <= window
|
|
195
|
+
pages << n
|
|
196
|
+
else
|
|
197
|
+
pages << :gap if pages.last != :gap
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
pages
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module RailsPaginationUltimate
|
|
6
|
+
module ActiveRecordExtension
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
scope :page, ->(num) {
|
|
11
|
+
num = num.to_i
|
|
12
|
+
num = 1 if num < 1
|
|
13
|
+
limit(default_per_page).offset(default_per_page * (num - 1)).extending(ActiveRecordModelExtension)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
scope :without_count, -> {
|
|
17
|
+
extending(WithoutCountExtension)
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module WithoutCountExtension
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module ClassMethods
|
|
25
|
+
def default_per_page
|
|
26
|
+
RailsPaginationUltimate.config.default_per_page
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module ActiveRecordModelExtension
|
|
31
|
+
def is_without_count?
|
|
32
|
+
singleton_class.included_modules.include?(WithoutCountExtension)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def per(num)
|
|
36
|
+
num = num.to_i
|
|
37
|
+
limit(num).offset(num * (current_page - 1))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def current_page
|
|
41
|
+
limit_val = limit_value || RailsPaginationUltimate.config.default_per_page
|
|
42
|
+
offset_val = offset_value || 0
|
|
43
|
+
(offset_val / limit_val) + 1
|
|
44
|
+
rescue StandardError
|
|
45
|
+
1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def total_pages
|
|
49
|
+
return Float::INFINITY if is_without_count?
|
|
50
|
+
|
|
51
|
+
limit_val = limit_value || RailsPaginationUltimate.config.default_per_page
|
|
52
|
+
(total_count.to_f / limit_val).ceil
|
|
53
|
+
rescue StandardError
|
|
54
|
+
0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def total_count
|
|
58
|
+
return 0 if is_without_count?
|
|
59
|
+
|
|
60
|
+
@total_count ||= begin
|
|
61
|
+
c = except(:offset, :limit, :order)
|
|
62
|
+
c = c.count
|
|
63
|
+
c.is_a?(Hash) ? c.count : c
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def first_page?
|
|
68
|
+
current_page == 1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def last_page?
|
|
72
|
+
if is_without_count?
|
|
73
|
+
return !has_next_page?
|
|
74
|
+
end
|
|
75
|
+
current_page >= total_pages
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def next_page
|
|
79
|
+
current_page + 1 unless last_page?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def prev_page
|
|
83
|
+
current_page - 1 unless first_page?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def has_next_page?
|
|
89
|
+
@_has_next_page ||= begin
|
|
90
|
+
limit_val = limit_value || RailsPaginationUltimate.config.default_per_page
|
|
91
|
+
# Fetch one extra record to see if there is a next page
|
|
92
|
+
records_count = except(:limit).limit(limit_val + 1).offset(offset_value).to_a.length
|
|
93
|
+
records_count > limit_val
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsPaginationUltimate
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :default_per_page
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@default_per_page = 25
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :configuration
|
|
14
|
+
|
|
15
|
+
def configure
|
|
16
|
+
self.configuration ||= Configuration.new
|
|
17
|
+
yield(configuration)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def config
|
|
21
|
+
self.configuration ||= Configuration.new
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsPaginationUltimate
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
isolate_namespace RailsPaginationUltimate
|
|
6
|
+
|
|
7
|
+
initializer "rails_pagination_ultimate.active_record" do
|
|
8
|
+
ActiveSupport.on_load(:active_record) do
|
|
9
|
+
include RailsPaginationUltimate::ActiveRecordExtension
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
initializer "rails_pagination_ultimate.action_view" do
|
|
14
|
+
ActiveSupport.on_load(:action_view) do
|
|
15
|
+
require_relative "action_view_helper"
|
|
16
|
+
include RailsPaginationUltimate::ActionViewHelper
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rails_pagination_ultimate/version"
|
|
4
|
+
require_relative "rails_pagination_ultimate/configuration"
|
|
5
|
+
require_relative "rails_pagination_ultimate/active_record_extension"
|
|
6
|
+
require_relative "rails_pagination_ultimate/engine" if defined?(Rails)
|
|
7
|
+
|
|
8
|
+
module RailsPaginationUltimate
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails_pagination_ultimate
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Shiboshree Roy
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activerecord
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '6.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '6.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: actionview
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '6.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '6.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: railties
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '6.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '6.0'
|
|
54
|
+
description: RailsPaginationUltimate provides a powerful and easy-to-use pagination
|
|
55
|
+
solution with modern UI/UX, responsive designs, and seamless Turbo/Hotwire integration.
|
|
56
|
+
email:
|
|
57
|
+
- shiboshreeroy169@gmail.com
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- CHANGELOG.md
|
|
63
|
+
- LICENSE.txt
|
|
64
|
+
- README.md
|
|
65
|
+
- app/assets/javascripts/rails_pagination_ultimate/loader.js
|
|
66
|
+
- app/assets/stylesheets/rails_pagination_ultimate/main.css
|
|
67
|
+
- app/views/rails_pagination_ultimate/_tailwind_pro.html.erb
|
|
68
|
+
- config/locales/en.yml
|
|
69
|
+
- lib/generators/rails_pagination_ultimate/tailwind_generator.rb
|
|
70
|
+
- lib/rails_pagination_ultimate.rb
|
|
71
|
+
- lib/rails_pagination_ultimate/action_view_helper.rb
|
|
72
|
+
- lib/rails_pagination_ultimate/active_record_extension.rb
|
|
73
|
+
- lib/rails_pagination_ultimate/configuration.rb
|
|
74
|
+
- lib/rails_pagination_ultimate/engine.rb
|
|
75
|
+
- lib/rails_pagination_ultimate/version.rb
|
|
76
|
+
homepage: https://github.com/shiboshreeroy/rails_pagination_ultimate
|
|
77
|
+
licenses:
|
|
78
|
+
- MIT
|
|
79
|
+
metadata:
|
|
80
|
+
homepage_uri: https://github.com/shiboshreeroy/rails_pagination_ultimate
|
|
81
|
+
source_code_uri: https://github.com/shiboshreeroy/rails_pagination_ultimate
|
|
82
|
+
changelog_uri: https://github.com/shiboshreeroy/rails_pagination_ultimate/blob/main/CHANGELOG.md
|
|
83
|
+
rdoc_options: []
|
|
84
|
+
require_paths:
|
|
85
|
+
- lib
|
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: 3.0.0
|
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
requirements: []
|
|
97
|
+
rubygems_version: 4.0.11
|
|
98
|
+
specification_version: 4
|
|
99
|
+
summary: A premium, responsive, and advanced pagination gem for Rails.
|
|
100
|
+
test_files: []
|