listpress 0.1.11 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3695595a23f6a3377c6248a0c9e01d9c6702e1a526567daebbb9d4724392f226
4
- data.tar.gz: 0fb82ad6524ed2a9c80d8cf6f8d78751c4b5c14f80adce353e127e37e5098573
3
+ metadata.gz: 44127d0ef0815c1b06377df68c5bc866a0f86a0577a26dc92e5e9a2fcd2f11ce
4
+ data.tar.gz: c84824e3c3ee06e60eaea668286cef834b15ce46cd8bdaa6edc4d0cb7d7c0771
5
5
  SHA512:
6
- metadata.gz: f3d0826640b1c3e4ba011a71ea41e0bd4203ffe98b730aca7535d48a9b0809e39d05ae4a374aab133ca295aba71d2d62c1ef20a8612bbfb452276b5a9a3e8982
7
- data.tar.gz: ad69433776dbf5fc5d008416277df82556bc2a9d628301c609eda730b7c139dd80be1715206520d221bbc49c1a979c1f906d945f77e61cbd9e257b70ff839582
6
+ metadata.gz: 3173e635d38f88c5b895cf5c6155cf5a5d13393f8b56b3540487e7eaa30e4c489144991c0952c160e8bd84e8ecc14c7f4aec6736641d58d944335b2408c548bd
7
+ data.tar.gz: 74cc9b038c9c67db10d63b13dd7f4a1657744416e04a8ff669a764c0df276b0f81f35c59d2326e64c2101210df851464a9c3e1998a6a97732741c6975ba37d4c
data/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Listpress
2
2
 
3
- Listing helper for Timepress projects
3
+ Listing helper for Timepress projects.
4
+
5
+ ## Requirements
6
+
7
+ - Listpress requires jQuery to run.
8
+ - Listpress styles are built on top of Bootstrap, but all templates and looks can be customized if you need to use something else.
4
9
 
5
10
  ## Installation
6
11
 
@@ -14,11 +19,23 @@ And then execute:
14
19
 
15
20
  $ bundle install
16
21
 
17
- Add include to your `application.js`
22
+ Add listpress to your `application.js`
23
+
24
+ import "listpress"
25
+
26
+ Add listpress to your `app/config/importmap.rb`
27
+
28
+ pin "listpress", to: "listpress.js" # from gem
29
+
30
+ And `assets/config/manifest.js`
31
+
32
+ //= link listpress.js
33
+
34
+ Listpress uses Rails importmaps to import jQuery, use `bin/importmap` to make jQuery available if you didn't do so yet.
18
35
 
19
- //= require listpress
36
+ bin/importmap pin jquery@3.5.1 --download
20
37
 
21
- And styles to your `application.scss`
38
+ And styles to your `application.scss`. Styles are built on top of Boostrap 5.
22
39
 
23
40
  @import 'listpress';
24
41
 
@@ -27,20 +44,31 @@ And styles to your `application.scss`
27
44
  In your view:
28
45
 
29
46
  ```erb
30
- <%= listing @messages, per_page: 100, default_sort: "date:desc", default_filter: { resolved: false }, table_options: { class: "compact"} do |l| %>
47
+ <%#
48
+ create listing for @messages
49
+ `name` is optional but required to differentiate between multiple listings on the same page if present
50
+ `per_page` activates paging
51
+ `default_sort` "name_of_column:desc" or "name_of_column:asc" - works only if column has sort enabled
52
+ `default_filter` { name_of_filter: "value of filter" }
53
+ %>
54
+ <%= listing @messages, name: :messages, per_page: 100, default_sort: "date:desc", default_filter: { resolved: false } do |l| %>
31
55
  <%# :code filter will use where(code: filter_value), because model has this attribute %>
32
- <% l.filter :code, as: :select, collection: Message.distinct.order(:code).pluck(:code).collect{|c| [c, c]} %>
56
+ <% l.filter :code, as: :select, collection: Message.distinct.order(:code).pluck(:code) %>
33
57
 
34
58
  <%# :resolved filter will call method "resolved" on collection passing filter value as argument, (instead of attribute :resolved) %>
35
59
  <% l.filter :resolved, as: :boolean, method: :resolved %>
36
60
 
37
- <%# will use search method %>
61
+ <%# custom filter - block returns filtered collection %>
62
+ <% l.filter(:code_ends_with, as: :text) {|collection, value| collection.where('code LIKE ?', "%#{value}")} %>
63
+
64
+ <%# will use search method on the collection %>
38
65
  <% l.filter :search, as: :search %>
39
66
 
40
- <%# set attributes for whole row %>
41
- <% l.row_attributes {|item| { class: item.red? "red" : "" }} %>
67
+ <%# set html attributes for whole item (for <tr> tag) %>
68
+ <% l.item_attributes {|item| { class: item.red? "red" : "" }} %>
42
69
 
43
70
  <%# add class "nowrap", allow sorting by :date, custom output specified with a block %>
71
+ <%# th_options sets html attributes for column header, td_options for column values %>
44
72
  <% l.column(:date, class: "nowrap", sort: true, th_options: {title: "Wow such dates"}, td_options: {title: "Very short"}) {|m| lf m.date, format: :short} %>
45
73
 
46
74
  <%# passes the output (even if given by block) through format_helper() %>
@@ -74,21 +102,51 @@ In your controller:
74
102
  end
75
103
  ```
76
104
 
77
- ### Config
78
-
79
- You can change some setting in `Listpress::Config` class (for example in initializers).
80
-
81
- ```ruby
82
- Listpress::Config.color = "green" # affects class of paginate links
83
- Listpress::Config.paginate_links_renderer = MaterializePaginateRenderer # affects how WillPaginate renders the paging links
84
- ```
85
-
86
105
  ## Overriding views
87
106
 
88
- You can copy and override these templates:
107
+ You can copy and override these templates to update the markup or add new filter types.
89
108
 
90
109
  ```
91
110
  app/views/shared/_listing.html.erb
92
111
  app/views/shared/_listing_filters.html.erb
93
112
  app/views/shared/_listing_table.html.erb
94
- ```
113
+ ```
114
+
115
+ ## In-line editing
116
+
117
+ For in-line editing SimpleForm gem is required. Then you can specify editable columns with `:edit` option like this:
118
+
119
+ ```erb
120
+ <%= listing @pages, per_page: 100, default_sort: "date:desc" do |l| %>
121
+ <%# produces: f.input :title %>
122
+ <% l.column :title, edit: true %>
123
+
124
+ <%# produces: f.input :published, as: :boolean %>
125
+ <% l.column :published, helper: :bool, edit: { as: :boolean } %>
126
+
127
+ <%# produces: f.association :author, as: :select, collection: User.all.pluck(:name, :id) %>
128
+ <% l.column :author, helper: :bool, edit: { association: true, as: :select, collection: User.all.pluck(:name, :id) } %>
129
+
130
+ <%# uses this column to place the editing form and "Save" and "Cancel" buttons %>
131
+ <% l.column edit: :actions do |p| %>
132
+ <%= link_to "Edit", edit_page_path(p) %>
133
+ <% end %>
134
+ <% end %>
135
+ ```
136
+
137
+ `edit: true` turns on editing with default SimpleForm `f.input` field for attribute of the same name as column.
138
+ If you specify `edit: hash`, the hash is passed as options to `f.input`.
139
+
140
+ If you need to use `f.association` field, use `edit: {associtation: true}`
141
+
142
+ Use `edit: :actions` to specify a column where the form and it's submit buttons should be placed. This should be specified exactly once.
143
+
144
+ ## Javascript events
145
+
146
+ - `update.listpress` (data: `url`) - is triggered on `.listing` after the listing is updated with AJAX - you can listen to this to initialize active components in table or react to document location changes - current state url is passed as data.
147
+ - `refresh.listpress` - trigger this on a listing item `.listing-item` to reload the item - causes AJAX request that returns only this item and overwrite item in listing - filters are ignored
148
+ - `edit.listpress` (data: `index`) - trigger this on a listing item `.listing-item` to load an in-line editing form for this item. `index` is index of `.editable` (resp. `.editing`) cell that should be focused when loaded.
149
+
150
+ ## I18n
151
+
152
+ Listpress defines I18n keys in `listpress` scope for `cs` and `en` languages.
@@ -0,0 +1,256 @@
1
+ import $ from "jquery"
2
+
3
+ export function updateUrlWithParams(url, params) {
4
+ var anchor, query, pos;
5
+
6
+ // split anchor from url
7
+ pos = url.indexOf('#');
8
+ if (pos > 0) {
9
+ anchor = url.substring(pos + 1);
10
+ url = url.substr(0, pos);
11
+ } else {
12
+ anchor = "";
13
+ }
14
+
15
+ // split query from url
16
+ pos = url.indexOf('?');
17
+ if (pos > 0) {
18
+ query = url.substring(pos + 1);
19
+ url = url.substr(0, pos);
20
+ } else {
21
+ query = "";
22
+ }
23
+
24
+ // parse query into an object
25
+ var parsed_query = {};
26
+ query.replace(/&*([^=&]+)=([^=&]*)/gi, function(str,key,value) {
27
+ key = decodeURIComponent(key.replace(/\+/g, ' '));
28
+ value = decodeURIComponent(value.replace(/\+/g, ' '));
29
+
30
+ if (!parsed_query.hasOwnProperty(key)) parsed_query[key] = [];
31
+ parsed_query[key].push(value);
32
+ });
33
+
34
+ // aggregate params by name
35
+ var parsed_params = {};
36
+ $.each(params, function (i, pair) {
37
+ if (!parsed_params.hasOwnProperty(pair[0])) parsed_params[pair[0]] = [];
38
+ parsed_params[pair[0]].push(pair[1]);
39
+ });
40
+
41
+ // update query with parsed_params
42
+ $.each(parsed_params, function (k, v) {
43
+ parsed_query[k] = v;
44
+ });
45
+
46
+ // create new URL
47
+ var sep = '?';
48
+ $.each(parsed_query, function (k, values) {
49
+ $.each(values, function (i, val) {
50
+ url += sep + encodeURIComponent(k) + '=' + encodeURIComponent(val);
51
+ sep = '&'
52
+ });
53
+ });
54
+
55
+ if (anchor !== '') {
56
+ url += '#' + anchor
57
+ }
58
+
59
+ return url;
60
+ }
61
+
62
+ $(document).ready(function () {
63
+ // timeout used so URL is not updated too often on typing
64
+ var pushStateTimeout = null;
65
+
66
+ // url that represents the last known state
67
+ var currentStateUrl = location.href;
68
+
69
+ // currently running AJAX
70
+ var currentXhr = null;
71
+
72
+ $('.listing').each(function () {
73
+ var $listing = $(this);
74
+ var $filters = $listing.find('.filters');
75
+ var $content = $listing.find('.listing-content');
76
+ var $pages = $listing.find('.pages');
77
+ var name = $listing.data('name');
78
+
79
+ // factory for success function for AJAX updates
80
+ var updateFn = function (delayedPush) {
81
+ return function (data) {
82
+ // update html
83
+ $content.html(data.content);
84
+ $pages.html(data.pages);
85
+
86
+ // update current state according to params sent from Rails
87
+ currentStateUrl = updateUrlWithParams(location.href, data.params);
88
+
89
+ // push state to history
90
+ if (pushStateTimeout) clearTimeout(pushStateTimeout);
91
+ if (delayedPush) {
92
+ pushStateTimeout = setTimeout(function () {history.pushState(location.href, "listing", currentStateUrl)}, 300);
93
+ } else {
94
+ history.pushState(location.href, "listing", currentStateUrl);
95
+ }
96
+
97
+ $listing.trigger('update.listpress', currentStateUrl);
98
+ $listing.removeClass('loading');
99
+ }
100
+ };
101
+
102
+ // makes AJAX request and handles the response
103
+ var doRequest = function (url, delayedPush) {
104
+ if (currentXhr) currentXhr.abort();
105
+
106
+ $listing.addClass('loading');
107
+
108
+ currentXhr = $.ajax(url, {
109
+ method: 'GET',
110
+ dataType: 'json',
111
+ success: updateFn(delayedPush),
112
+ error: function (xhr, status, error) {
113
+ if (status != "abort") {
114
+ console.log("AJAX failed:", xhr, status, error);
115
+ window.location = url;
116
+ }
117
+ }
118
+ });
119
+ };
120
+
121
+ // handle paging links
122
+ $pages.on('click.listpress', 'a[href]', function () {
123
+ doRequest(updateUrlWithParams(currentStateUrl, [["listing", name], [name + "[page]", $(this).data('page')]]));
124
+ return false;
125
+ });
126
+
127
+ // handle sorting links
128
+ $content.on('click.listpress', 'a.sort', function () {
129
+ doRequest(updateUrlWithParams(currentStateUrl, [["listing", name], [name + "[sort]", $(this).data('sort')]]));
130
+ return false;
131
+ });
132
+
133
+ // refresh - refresh a single item
134
+ // edit - load editing version of the item (with in-line editing form)
135
+ $content.on('refresh.listpress edit.listpress', '.listing-item', function (e, edit_data) {
136
+ var id = $(this).data('id');
137
+
138
+ var params = [["listing", name], ["listing_item_id", id]];
139
+ if (e.type == "edit") params.push(["listing_edit", "1"]);
140
+
141
+ var url = updateUrlWithParams(currentStateUrl, params);
142
+
143
+ $.ajax(url, {
144
+ method: 'GET',
145
+ dataType: 'json',
146
+ success: function (data) {
147
+ // replace item with an item loaded with ajax
148
+ var $item = $(data.content).find('.listing-item').eq(0);
149
+ $content.find('.listing-item[data-id=' + id + ']').replaceWith($item);
150
+
151
+ if (e.type == "edit") {
152
+ // activate form field in the same cell as has been clicked - correct index must be sent in edit_data
153
+ setTimeout(function () {
154
+ var $first_input = $item.find('.editing').eq(edit_data).find('input, select, textarea').filter(':visible').first();
155
+ $first_input.focus();
156
+ if ($first_input.is('[type=text]')) {
157
+ $first_input[0].select();
158
+ }
159
+ }, 0);
160
+ }
161
+ $listing.trigger('update.listpress', currentStateUrl);
162
+ },
163
+ error: function (xhr, status, error) {
164
+ if (status != "abort") {
165
+ console.log("AJAX failed:", xhr, status, error);
166
+ window.location = currentStateUrl;
167
+ }
168
+ }
169
+ });
170
+ });
171
+
172
+ // handle filters
173
+ var filterTimeout = null;
174
+
175
+ $filters.on('submit.listpress', 'form', function () {return false;}); // prevent manual form submit
176
+
177
+ var submitFilters = function () {
178
+ // collect form data as params
179
+ var data = [];
180
+ $.each($filters.find('form').serializeArray(), function (i, o) {
181
+ if (o.name.substr(0, name.length + 8) == name + '[filter]') {
182
+ data.push([o.name, o.value])
183
+ }
184
+ });
185
+ data.push(['listing', name]);
186
+ data.push([name + '[page]', 1]);
187
+
188
+ // send
189
+ doRequest(updateUrlWithParams(currentStateUrl, data), true);
190
+ };
191
+
192
+ $filters.on('input.listpress', 'input[type=text],textarea', function () {
193
+ if (filterTimeout) clearTimeout(filterTimeout);
194
+ filterTimeout = setTimeout(submitFilters, 500);
195
+ });
196
+ $filters.on('change.listpress', 'select', submitFilters);
197
+
198
+ // load edit form when .editable is clicked
199
+ $content.on('click.listpress', '.editable', function (e) {
200
+ if ($(e.target).is('input,a[href]')) return;
201
+
202
+ var $item = $(this).closest('.listing-item');
203
+ var cell_index = $item.find('.editable').index($(this));
204
+ $item.trigger('edit.listpress', cell_index);
205
+ });
206
+
207
+ // close form when cancel button is clicked
208
+ $content.on('click.listpress', '.editing-actions a.btn.cancel', function () {
209
+ $(this).closest('.listing-item').trigger('refresh.listpress');
210
+ return false;
211
+ });
212
+
213
+ // submit form on enter, close it on escape
214
+ $content.on("keydown.listpress", ".editing input,.editing select,.editing textarea", function (e) {
215
+ if (e.which == 13 && !$(this).is('textarea')) { // Enter
216
+ $(this.form).submit();
217
+ return false;
218
+ } else if (e.which == 27) { // ESC
219
+ $(this).closest('.listing-item').trigger('refresh.listpress');
220
+ return false;
221
+ }
222
+ });
223
+
224
+ // handle form submit
225
+ $content.on("submit.listpress", ".editing-actions form", function () {
226
+ var $form = $(this);
227
+ var actionUrl = this.action; // get absolute URL
228
+ var xhr = new XMLHttpRequest(); // we must pass this one to get access to its responseURL
229
+
230
+ $.ajax({
231
+ type: "POST",
232
+ url: actionUrl,
233
+ data: $form.serialize(),
234
+ xhr: function () {
235
+ return xhr;
236
+ },
237
+ success: function() {
238
+ // we expect redirect on successful submit
239
+ if (xhr.responseURL == actionUrl) { // not redirected = fail - submit the form without ajax so user can see what's wrong
240
+ $form.trigger("submit.not-listpress");
241
+ } else { // redirected = success - refresh the item
242
+ $form.closest('.listing-item').trigger('refresh.listpress');
243
+ }
244
+ }
245
+ });
246
+
247
+ return false;
248
+ });
249
+
250
+ });
251
+
252
+ // reload page when back button is pressed
253
+ $(window).on('popstate.listpress', function () {
254
+ location.reload();
255
+ });
256
+ });
@@ -1,13 +1,37 @@
1
1
  .listing {
2
2
  .sort-direction { font-size: 0.8rem; position: relative; top: -2px; }
3
- .pages { padding: 15px 0;
4
- .pagination { margin: 0; }
5
- .pagination-info { float: right; font-size: 1.1rem; line-height: 2.0; margin: 0px 0; }
3
+
4
+ .pages { @extend .mb-2;
5
+ .pagination { margin: 0;
6
+ .page-item { @extend .shadow-sm; }
7
+ }
8
+ .pagination-info { float: right; line-height: 1.5; padding: 0.5rem 0; }
9
+ .gap { display: inline-block; padding: $pagination-padding-y $pagination-padding-x; }
10
+ }
11
+
12
+ form { justify-content: right;
13
+ .col-form-label { @extend .me-1; }
6
14
  }
7
15
 
8
- form { float: right; clear: right;
9
- .input-field.inline { margin-top: -5px; margin-bottom: 0; }
16
+ .listing-wrapper { @extend .mb-2; position: relative;
17
+ .listing-content .listing-table { clear: both; margin-bottom: 0;
18
+ thead {
19
+ th { background-color: $primary; border: none; color: color-contrast($primary); font-weight: normal;
20
+ a { color: color-contrast($primary); font-weight: bold; }
21
+ }
22
+ }
23
+ tbody { border-top: none;
24
+ tr:last-child td { border-bottom: none; }
25
+ }
26
+ th, td { padding: 0.25rem 0.5rem; }
27
+ .list-empty td { height: 100px; line-height: 100px; text-align: center; vertical-align: middle; }
28
+ }
29
+ .spinner-border { display: none; }
30
+ }
31
+ &.loading {
32
+ .listing-wrapper {
33
+ .listing-content { opacity: 0.5; }
34
+ .spinner-border { display: block; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; }
35
+ }
10
36
  }
11
- table { clear: both; }
12
- .list-empty td {height: 100px;line-height: 100px;text-align: center;vertical-align: middle;}
13
37
  }
@@ -3,16 +3,19 @@
3
3
  <%# render and capture filters %>
4
4
  <%= list.captures[:filters] = capture do %>
5
5
  <% if list.filters.any? %>
6
- <%= render "shared/listing_filters", list: list %>
6
+ <%= render list.filters_view, list: list %>
7
7
  <% end %>
8
- <% end %>
8
+ <% end unless list.item_refresh? %>
9
9
  </div>
10
10
 
11
- <div class="table">
12
- <%# render and capture table %>
13
- <%= list.captures[:table] = capture do %>
14
- <%= render "shared/listing_table", list: list %>
15
- <% end %>
11
+ <div class="listing-wrapper">
12
+ <div class="listing-content">
13
+ <%# render and capture table %>
14
+ <%= list.captures[:content] = capture do %>
15
+ <%= render list.listing_view, list: list %>
16
+ <% end %>
17
+ </div>
18
+ <div class="spinner-border"></div>
16
19
  </div>
17
20
 
18
21
  <div class="pages">
@@ -20,9 +23,8 @@
20
23
  <%= list.captures[:pages] = capture do %>
21
24
  <% if list.paginate? %>
22
25
  <div class="pagination-info"><%= page_entries_info list.collection, model: list.collection.model %></div>
23
- <%= will_paginate list.collection, renderer: Listpress::Config.paginate_link_renderer, param_name: list.page_param_name %>
26
+ <%= will_paginate list.collection, renderer: Listpress::PaginateRenderer, param_name: list.page_param_name %>
24
27
  <% end %>
25
- <% end %>
28
+ <% end unless list.item_refresh? %>
26
29
  </div>
27
30
  </div>
28
-
@@ -1,7 +1,7 @@
1
- <%= form_with url: request.params, method: :get do |f| %>
1
+ <%= form_with url: request.params, method: :get, class: "row row-cols-auto g-0" do |f| %>
2
2
  <%# make hidden fields with all the GET params so they don't get lost on submit %>
3
3
  <% list.flatten_params(request.GET).each do |k, v| %>
4
- <%= f.hidden_field k, value: v unless ['utf8', list.page_param_name].include?(k) %>
4
+ <%= f.hidden_field k, value: v, id: nil unless ['utf8', list.page_param_name].include?(k) %>
5
5
  <% end %>
6
6
 
7
7
  <%# reset page when changing filters %>
@@ -9,26 +9,54 @@
9
9
 
10
10
  <%# render filter fields %>
11
11
  <% list.filters.each do |filter| %>
12
- <% field_name = "#{list.name}[filter][#{filter[:name]}]" %>
13
- <% field_value = list.filter_value(filter[:name]) %>
14
- <% case filter[:as] %>
15
- <% when :boolean %>
16
- <div class="input-field inline">
17
- <%= f.select field_name, [[t('listpress.do_not_filter'), nil], [t('listpress.yes'), true], [t('listpress.no'), false]], selected: field_value %>
18
- <label><%= list.human_name(filter[:name]) %></label>
19
- </div>
20
- <% when :select %>
21
- <div class="input-field inline">
22
- <%= f.select field_name, [[t('listpress.do_not_filter'), ""]] + filter[:collection], selected: field_value %>
23
- <label><%= list.human_name(filter[:name]) %></label>
24
- </div>
25
- <% when :search %>
26
- <div class="input-field inline">
27
- <i class="material-icons prefix">search</i>
28
- <%= f.text_field field_name, value: field_value, placeholder: t('listpress.search') %>
29
- </div>
30
- <% else %>
31
- Unknown filter type <%= filter[:as] %>
32
- <% end %>
12
+ <div class="col row row-cols-auto g-0 ps-2 mb-2 <%= "filter-#{filter[:name].to_s.parameterize}" %>">
13
+ <% field_name = "#{list.name}[filter][#{filter[:name]}]" %>
14
+ <% field_value = list.filter_value(filter[:name]) %>
15
+ <% case filter[:as] %>
16
+ <% when :boolean %>
17
+ <label class="col-form-label"><%= list.human_name(filter[:name]) %></label>
18
+ <div class="shadow-sm">
19
+ <%= f.select field_name, [[t('listpress.do_not_filter'), nil], [t('listpress.yes'), true], [t('listpress.no'), false]], { selected: field_value }, class: "form-select" %>
20
+ </div>
21
+ <% when :select %>
22
+ <label class="col-form-label"><%= list.human_name(filter[:name]) %></label>
23
+ <div class="shadow-sm">
24
+ <%= f.select field_name, [[t('listpress.do_not_filter'), ""]] + filter[:collection], { selected: field_value }, class: "form-select" %>
25
+ </div>
26
+ <% when :multiple %>
27
+ <% field_value = Array.wrap(field_value).collect {|s| s.split(',').collect(&:presence).compact }.sum([])%>
28
+ <label class="col-form-label"><%= list.human_name(filter[:name]) %></label>
29
+ <div class="shadow-sm">
30
+ <%= f.select field_name, [[t('listpress.do_not_filter'), ""]] + filter[:collection], { selected: field_value }, class: "form-select", multiple: true %>
31
+ </div>
32
+ <% when :grouped_select %>
33
+ <label class="col-form-label"><%= list.human_name(filter[:name]) %></label>
34
+ <div class="shadow-sm">
35
+ <%= f.select field_name, grouped_options_for_select(filter[:collection], field_value, prompt: t('listpress.do_not_filter')), {}, class: "form-control" %>
36
+ </div>
37
+ <% when :search %>
38
+ <div class="input-group shadow-sm">
39
+ <div class="input-group-text"><i class="fas fa-search"></i></div>
40
+ <%= f.text_field field_name, value: field_value, placeholder: t('listpress.search'), class: "form-control" %>
41
+ </div>
42
+ <% when :date %>
43
+ <label class="col-form-label"><%= list.human_name(filter[:name]) %></label>
44
+ <div class="shadow-sm">
45
+ <%= f.text_field field_name, value: field_value, class: "form-control date", size: 8 %>
46
+ </div>
47
+ <% when :text %>
48
+ <label class="col-form-label"><%= list.human_name(filter[:name]) %></label>
49
+ <div class="shadow-sm">
50
+ <%= f.text_field field_name, value: field_value, class: "form-control" %>
51
+ </div>
52
+ <% when :multiline %>
53
+ <label class="col-form-label"><%= list.human_name(filter[:name]) %></label>
54
+ <div class="shadow-sm">
55
+ <%= f.text_area field_name, value: field_value.presence, class: "form-control one-line", rows: 1, cols: 20 %>
56
+ </div>
57
+ <% else %>
58
+ <label class="ms-2 col-form-label">Unknown filter type <%= filter[:as] %></label>
59
+ <% end %>
60
+ </div>
33
61
  <% end %>
34
62
  <% end %>
@@ -1,37 +1,62 @@
1
- <%= content_tag :table, list.options[:table_options] do %>
2
- <thead>
3
- <tr>
4
- <%# render column headers with sorting indicator and links %>
5
- <% sort_attr, sort_dir = list.sort %>
6
- <% list.columns.each do |col| %>
7
- <%= content_tag :th, (col[:th_options] || {}).merge(class: col[:class]) do %>
8
- <% if col[:sort] %>
9
- <% sort = sort_attr == col[:attr] && sort_dir == :asc ? "#{col[:attr]}:desc" : "#{col[:attr]}:asc" %>
10
- <%= link_to list.human_name(col[:attr]), request.params.deep_merge(list.name => {sort: sort}), class: "sort", "data-sort": sort %>
11
- <% if col[:attr] == sort_attr %>
12
- <span class="sort-direction"><%= sort_dir == :asc ? "" : "" %></span>
1
+ <div class="bg-white shadow table-responsive rounded">
2
+ <table class="table table-hover table-sm align-middle listing-table">
3
+ <% unless list.item_refresh? %>
4
+ <thead>
5
+ <tr>
6
+ <%# render column headers with sorting indicator and links %>
7
+ <% sort_attr, sort_dir = list.sort %>
8
+ <% list.columns.each do |col| %>
9
+ <%= content_tag :th, (col[:th_options] || {}).merge(class: col[:class]) do %>
10
+ <% if col[:sort] %>
11
+ <% sort = sort_attr == col[:attr] && sort_dir == :asc ? "#{col[:attr]}:desc" : "#{col[:attr]}:asc" %>
12
+ <%= link_to list.human_name(col[:attr]), request.params.deep_merge(list.name => {sort: sort}), class: "sort", "data-sort": sort %>
13
+ <% if col[:attr] == sort_attr %>
14
+ <span class="sort-direction"><%= sort_dir == :asc ? "▲" : "▼" %></span>
15
+ <% end %>
16
+ <% else %>
17
+ <%= list.human_name(col[:attr]) %>
18
+ <% end %>
13
19
  <% end %>
14
- <% else %>
15
- <%= list.human_name(col[:attr]) %>
16
20
  <% end %>
17
- <% end %>
21
+ </tr>
22
+ </thead>
18
23
  <% end %>
19
- </tr>
20
- </thead>
21
- <tbody>
22
- <% list.collection.each do |item| %>
23
- <%= content_tag :tr, list.row_attributes[:proc]&.call(item) || list.row_attributes do %>
24
- <%# render each row according to it's definition %>
25
- <% list.columns.each do |col| %>
26
- <%= content_tag :td, (col[:td_options] || {}).merge(class: col[:class]) do %>
27
- <% v = col[:proc] ? capture { col[:proc].call(item).to_s } : item.public_send(col[:attr]) %>
28
- <%= col[:helper] ? send(col[:helper], v) : v %>
24
+ <tbody>
25
+ <% list.collection.each do |item| %>
26
+ <%= content_tag :tr, list.item_attributes_for(item) do %>
27
+ <%# render each item according to it's definition %>
28
+
29
+ <% form_html = nil %>
30
+ <% form = nil %>
31
+ <% if list.edit? %>
32
+ <% form_html = simple_form_for(item, namespace: [list.name.to_s.underscore, item.id].join("_")) do |f| %>
33
+ <% form = f %>
34
+ <%= f.button :submit, t('listpress.save') %>
35
+ <%= button_link t('listpress.cancel'), "#", class: "cancel", btn_class: "btn btn-secondary" %>
36
+ <% end %>
37
+ <% end %>
38
+
39
+ <% list.columns_with_td_options.each do |col, td_options| %>
40
+ <%= content_tag :td, td_options do %>
41
+ <% if list.edit? && col[:edit] %>
42
+ <% if col[:edit] == :actions %>
43
+ <%= form_html %>
44
+ <% else %>
45
+ <%= listing_input(form, col) %>
46
+ <% end %>
47
+ <% else %>
48
+ <% v = col[:proc] ? capture { col[:proc].call(item).to_s } : item.public_send(col[:attr]) %>
49
+ <%= col[:helper] ? send(col[:helper], v) : v %>
50
+ <% end %>
51
+ <% end %>
29
52
  <% end %>
30
53
  <% end %>
31
54
  <% end %>
32
- <% end %>
33
- <% if list.collection.empty? %>
34
- <tr class="list-empty"><td colspan="<%= list.columns.size %>"><%= t('listpress.no_results') %></td></tr>
35
- <% end %>
36
- </tbody>
37
- <% end %>
55
+ <% if list.collection.empty? %>
56
+ <tr class="list-empty">
57
+ <td colspan="<%= list.columns.size %>"><%= t('listpress.no_results') %></td>
58
+ </tr>
59
+ <% end %>
60
+ </tbody>
61
+ </table>
62
+ </div>
@@ -9,6 +9,9 @@ module Listpress
9
9
 
10
10
  # apply filters to collection
11
11
  def filter(collection)
12
+ # skip filters if we need to retrieve a specific item
13
+ return collection.where(id: listing.params[:item_id]) if listing.params[:item_id]
14
+
12
15
  listing.filters.each do |filter|
13
16
  val = listing.filter_value(filter[:name])
14
17
 
@@ -52,7 +55,7 @@ module Listpress
52
55
  end
53
56
 
54
57
  def page(collection)
55
- if listing.paginate?
58
+ if listing.paginate? && !listing.item_refresh?
56
59
  collection = collection.paginate(page: listing.params[:page] || 1, per_page: listing.options[:per_page])
57
60
  end
58
61
 
@@ -10,9 +10,9 @@ module Listpress
10
10
  #
11
11
  # Options:
12
12
  # name: Identifies listing within a page - required for multiple listings on one page, (default: :list)
13
- # per_page: Limit output to this many items and turn paging on
14
13
  # params: Use these params instead of parsing them from request
15
- # default_sort: Use this sort if none specified in params (eg: "id:asc")
14
+ #
15
+ # See Listing.new for other options
16
16
  #
17
17
  def listing(collection, options = {}, &block)
18
18
  options = options.dup
@@ -21,9 +21,12 @@ module Listpress
21
21
  pars = options.delete(:params) || params[name] || {}
22
22
 
23
23
  if @_listing_only.nil? || @_listing_only == name
24
+ pars = pars.merge(@_listing_control) if @_listing_control
25
+
24
26
  list = Listing.new(name, collection, options, pars)
25
27
  yield list
26
- output = render 'shared/listing', list: list
28
+
29
+ output = render list.view, list: list
27
30
 
28
31
  if controller.respond_to?(:listing_content)
29
32
  controller.listing_content[name] = list.captures
@@ -45,14 +48,20 @@ module Listpress
45
48
  respond_to do |format|
46
49
  format.html { render action }
47
50
  format.json {
48
- render_listing(action, name = params[:listing].presence&.to_sym || :list)
49
- render json: listing_content[name].merge(params: Listing.flatten_params(request.GET.except(:listing)))
51
+ name = params[:listing].presence&.to_sym || :list
52
+ control = {
53
+ item_id: params[:listing_item_id].presence,
54
+ edit: params[:listing_edit].to_i == 1,
55
+ }
56
+
57
+ render_listing(action, name, control)
58
+ render json: listing_content[name].merge(params: Listing.flatten_params(request.GET.except(:listing, :listing_item_id, :listing_edit)))
50
59
  }
51
60
  end
52
61
  end
53
62
 
54
- def render_listing(action = :index, name = nil)
55
- list_only(name)
63
+ def render_listing(action = :index, name = nil, control = {})
64
+ list_only(name, control)
56
65
  render_to_string action: action, formats: [:html], layout: nil
57
66
  end
58
67
 
@@ -66,9 +75,25 @@ module Listpress
66
75
  @_listing_instances ||= {}
67
76
  end
68
77
 
69
- # Used in controller to limit listing rendering only for +name+
70
- def list_only(name)
78
+ # Used in controller to limit listing rendering only for +name+ listing and pass control options +control+
79
+ def list_only(name, control)
71
80
  @_listing_only = name
81
+ @_listing_control = control
82
+ end
83
+
84
+ # View helper to render input for using SimpleForm builder +form+ and column +col+
85
+ def listing_input(form, col)
86
+ options = col[:edit].is_a?(Hash) ? col[:edit].dup : {}
87
+ options = options.reverse_merge(label: false, grid_wrapper_html: {class: "col-sm-12 ms-0"})
88
+ options[:input_html] ||= {}
89
+ options[:input_html][:form] = form.id
90
+
91
+ if options.delete(:association)
92
+ form.association col[:attr], options
93
+ else
94
+ form.input col[:attr], options
95
+ end
72
96
  end
97
+
73
98
  end
74
99
  end
@@ -1,7 +1,23 @@
1
1
  module Listpress
2
+ # definition of a listing - made from DSL in a View
2
3
  class Listing
3
4
  attr_reader :name, :collection, :filters, :options, :columns, :captures, :params
4
5
 
6
+ # Args:
7
+ # name: name of the listing
8
+ # collection: collection of items to list - usually AR relation
9
+ # options: configuration of listing given as specified in template
10
+ # params: request params for this specific listing
11
+ #
12
+ # Options:
13
+ # per_page: Limit output to this many items and turn paging on
14
+ # default_sort: Use this sort if none specified in params (eg: "id:asc")
15
+ # default_filter: Hash with default values for filters
16
+ # resolver: Specify custom Resolver class - Resolver applies filters and sorting to collection - default Listpress::DefaultResolver
17
+ # view: Partial used to render the listing component, default 'shared/listing'
18
+ # listing_view: Partial used to show the listed items, default 'shared/listing_table'
19
+ # filters_view: Partial used to show filters, default 'shared/listing_filters'
20
+ #
5
21
  def initialize(name, collection, options = {}, params = {})
6
22
  @name = name
7
23
  @collection = collection
@@ -10,7 +26,7 @@ module Listpress
10
26
  @columns = []
11
27
  @filters = []
12
28
  @captures = {}
13
- @row_attributes = {}
29
+ @item_attributes = {}
14
30
  end
15
31
 
16
32
  # Defines a column
@@ -18,12 +34,22 @@ module Listpress
18
34
  # Usage:
19
35
  # <%= listing collection, options do |l| %>
20
36
  # <% l.column :id, class: "right-align", sort: true %>
21
- # <% l.column :name, sort: true, helper: :shorten %>
37
+ # <% l.column :name, sort: true, helper: :shorten, edit: true %>
22
38
  # <% l.column :special, th_options: { title: "Special column"}, td_options: { title: "With special cells" } %>
39
+ # <% l.column(:block) { |item| link_to item.name, edit_item_path(item) } %>
23
40
  #
24
41
  # Options:
25
- # class: Used as HTML class for this column's cells
26
- # sort: Allows sorting by this attribute
42
+ # - attr: First argument if given is attribute name - unless a block is specified, this attribute will be called on the model to get content of the cell
43
+ # - label: Label of the column, defaults to human_attribute_name of `attr`
44
+ # - class: Used as HTML class for this column's cells
45
+ # - sort: Allow sorting of this column
46
+ # - true to sort by the `attr` (must be an SQL column)
47
+ # - column name to sort by other SQL column
48
+ # - Arel.sql() to sort by arbitrary SQL
49
+ # - edit: true to activate in-line editing with SimpleForm, hash to pass options to SimpleForm `input` builder method. Use association: true to call SimpleForm `association` instead.
50
+ # - th_options: HTML attributes for column header
51
+ # - td_options: HTML attributes for column cells
52
+ # - helper: use this helper to format cell value - if cell value given by block, applies to block result
27
53
  def column(*args, &block)
28
54
  opts = args.extract_options!
29
55
  attr = args[0] || ""
@@ -31,22 +57,26 @@ module Listpress
31
57
  column = opts.merge(attr: attr)
32
58
  column[:proc] = block if block_given?
33
59
 
60
+ if column[:edit]
61
+ SimpleForm rescue raise("To use in-line editing simple_form gem is required.")
62
+ end
63
+
34
64
  @columns << column
35
65
  end
36
66
 
37
- # Defines attributes for each row (tr)
67
+ # Defines attributes for each item (tr).
38
68
  #
39
69
  # Usage:
40
70
  # <%= listing collection, options do |l| %>
41
- # <% l.row_attributes {|item| { class: item.red? "red" : "" }} %>
71
+ # <% l.item_attributes {|item| { class: item.red? ? "red" : "" }} %>
42
72
  # ...
43
- def row_attributes(*args, &block)
73
+ def item_attributes(*args, &block)
44
74
  if block_given?
45
- @row_attributes[:proc] = block
75
+ @item_attributes[:proc] = block
46
76
  elsif !args.empty?
47
- @row_attributes = args.extract_options!
77
+ @item_attributes = args.extract_options!
48
78
  else
49
- @row_attributes
79
+ @item_attributes
50
80
  end
51
81
  end
52
82
 
@@ -102,10 +132,52 @@ module Listpress
102
132
  options[:per_page]
103
133
  end
104
134
 
135
+ def item_refresh?
136
+ params[:item_id].present?
137
+ end
138
+
139
+ def edit?
140
+ params[:edit]
141
+ end
142
+
105
143
  def page_param_name
106
144
  "#{name}[page]"
107
145
  end
108
146
 
147
+ def columns_with_td_options
148
+ columns.collect do |col|
149
+ td_options = col[:td_options].deep_dup || {}
150
+ td_options[:class] = Array.wrap(td_options[:class]) + Array.wrap(col[:class])
151
+ if col[:edit]
152
+ if col[:edit] == :actions
153
+ td_options[:class] << (edit? ? "editing-actions" : "editable-actions")
154
+ else
155
+ td_options[:class] << (edit? ? "editing" : "editable")
156
+ end
157
+ end
158
+
159
+ [col, td_options]
160
+ end
161
+ end
162
+
163
+ def item_attributes_for(item)
164
+ if item_attributes[:proc]
165
+ attributes = item_attributes[:proc].call(item)
166
+ else
167
+ attributes = item_attributes.deep_dup
168
+ end
169
+
170
+ if item.respond_to?(:id)
171
+ attributes[:data] ||= {}
172
+ attributes[:data][:id] = item.id
173
+ end
174
+
175
+ attributes[:class] = Array.wrap(attributes[:class])
176
+ attributes[:class] << 'listing-item'
177
+
178
+ attributes
179
+ end
180
+
109
181
  # returns representation of arbitrarily nested params as list of pairs [field_name, value], that
110
182
  # can be used in forms to recreate the params
111
183
  def self.flatten_params(params, level=0)
@@ -157,5 +229,20 @@ module Listpress
157
229
  nil
158
230
  end
159
231
  end
232
+
233
+ # partial used to render the whole component
234
+ def view
235
+ options[:view] || 'shared/listing'
236
+ end
237
+
238
+ # partial used to render filters
239
+ def filters_view
240
+ options[:filters_view] || 'shared/listing_filters'
241
+ end
242
+
243
+ # partial used to render the listing of items
244
+ def listing_view
245
+ options[:listing_view] || 'shared/listing_table'
246
+ end
160
247
  end
161
248
  end
@@ -5,3 +5,5 @@ cs:
5
5
  "yes": Ano
6
6
  "no": Ne
7
7
  search: Hledat
8
+ save: Uložit
9
+ cancel: Zrušit
@@ -5,3 +5,6 @@ en:
5
5
  "yes": Yes
6
6
  "no": No
7
7
  search: Search
8
+ save: Save
9
+ cancel: Cancel
10
+
@@ -2,7 +2,7 @@ require 'will_paginate'
2
2
  require 'will_paginate/view_helpers/action_view'
3
3
 
4
4
  module Listpress
5
- class BootstrapPaginateRenderer < WillPaginate::ActionView::LinkRenderer
5
+ class PaginateRenderer < WillPaginate::ActionView::LinkRenderer
6
6
  def html_container(html)
7
7
  tag(:nav, tag(:ul, html, class: "pagination"))
8
8
  end
@@ -1,3 +1,3 @@
1
1
  module Listpress
2
- VERSION = "0.1.11"
2
+ VERSION = "0.4.2"
3
3
  end
data/lib/listpress.rb CHANGED
@@ -1,8 +1,6 @@
1
1
  require "listpress/version"
2
- require "listpress/materialize_paginate_renderer"
3
- require "listpress/bootstrap_paginate_renderer"
2
+ require "listpress/paginate_renderer"
4
3
 
5
- require "listpress/config"
6
4
  require "listpress/engine"
7
5
  require "listpress/default_resolver"
8
6
  require "listpress/listing"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: listpress
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Petr Sedivy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-08 00:00:00.000000000 Z
11
+ date: 2023-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -63,7 +63,7 @@ files:
63
63
  - Gemfile
64
64
  - README.md
65
65
  - Rakefile
66
- - app/assets/javascript/listpress.js
66
+ - app/assets/javascripts/listpress.js
67
67
  - app/assets/stylesheets/listpress.scss
68
68
  - app/views/shared/_listing.html.erb
69
69
  - app/views/shared/_listing_filters.html.erb
@@ -71,15 +71,13 @@ files:
71
71
  - bin/console
72
72
  - bin/setup
73
73
  - lib/listpress.rb
74
- - lib/listpress/bootstrap_paginate_renderer.rb
75
- - lib/listpress/config.rb
76
74
  - lib/listpress/default_resolver.rb
77
75
  - lib/listpress/engine.rb
78
76
  - lib/listpress/helper.rb
79
77
  - lib/listpress/listing.rb
80
78
  - lib/listpress/locale/cs.yml
81
79
  - lib/listpress/locale/en.yml
82
- - lib/listpress/materialize_paginate_renderer.rb
80
+ - lib/listpress/paginate_renderer.rb
83
81
  - lib/listpress/version.rb
84
82
  - listpress.gemspec
85
83
  homepage: https://git.timepress.cz/timepress/listpress
@@ -102,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
100
  - !ruby/object:Gem::Version
103
101
  version: '0'
104
102
  requirements: []
105
- rubygems_version: 3.0.3
103
+ rubygems_version: 3.0.8
106
104
  signing_key:
107
105
  specification_version: 4
108
106
  summary: Listing helper for Timepress projects
@@ -1,130 +0,0 @@
1
- $(document).ready(function () {
2
- function updateUrlWithParams(url, params) {
3
- var query;
4
-
5
- // split query from url
6
- var pos = url.indexOf('?');
7
- if (pos > 0) {
8
- query = url.substring(pos + 1);
9
- url = url.substr(0, pos);
10
- } else {
11
- query = "";
12
- }
13
-
14
- // parse query into an object
15
- var parsed_query = {};
16
- query.replace(/&*([^=&]+)=([^=&]*)/gi, function(str,key,value) {parsed_query[decodeURIComponent(key.replace(/\+/g, ' '))] = decodeURIComponent(value.replace(/\+/g, ' '))});
17
-
18
- // update query with params
19
- $.each(params, function (i, param) {
20
- parsed_query[param[0]] = param[1];
21
- });
22
-
23
- // create new URL
24
- var sep = '?';
25
- $.each(parsed_query, function (k, val) {
26
- url += sep + encodeURIComponent(k) + '=' + encodeURIComponent(val);
27
- sep = '&'
28
- });
29
-
30
- return url;
31
- }
32
-
33
- // timeout used so URL is not updated too often on typing
34
- var pushStateTimeout = null;
35
-
36
- // url that represents the last known state
37
- var currentStateUrl = location.href;
38
-
39
- // currently running AJAX
40
- var currentXhr = null;
41
-
42
- $('.listing').each(function () {
43
- var $listing = $(this);
44
- var $filters = $listing.find('.filters');
45
- var $table = $listing.find('.table');
46
- var $pages = $listing.find('.pages');
47
- var name = $listing.data('name');
48
-
49
- // factory for success function
50
- var updateFn = function (delayedPush) {
51
- return function (data) {
52
- // update html
53
- $table.html(data.table);
54
- $pages.html(data.pages);
55
-
56
- // update current state according to params sent from Rails
57
- currentStateUrl = updateUrlWithParams(location.href, data.params);
58
-
59
- // push state to history
60
- if (pushStateTimeout) clearTimeout(pushStateTimeout);
61
- if (delayedPush) {
62
- pushStateTimeout = setTimeout(function () {history.pushState(location.href, "listing", currentStateUrl)}, 300);
63
- } else {
64
- history.pushState(location.href, "listing", currentStateUrl);
65
- }
66
-
67
- $listing.trigger('update', currentStateUrl);
68
- }
69
- };
70
-
71
- // makes AJAX request and handles the response
72
- var doRequest = function (url, delayedPush) {
73
- if (currentXhr) currentXhr.abort();
74
-
75
- currentXhr = $.ajax(url, {
76
- method: 'GET',
77
- dataType: 'json',
78
- success: updateFn(delayedPush),
79
- error: function (xhr, status, error) {
80
- if (status != "abort") {
81
- console.log("AJAX failed:", xhr, status, error);
82
- window.location = url;
83
- }
84
- }
85
- });
86
- };
87
-
88
- // handle paging links
89
- $pages.on('click', 'a[href]', function () {
90
- doRequest(updateUrlWithParams(currentStateUrl, [["listing", name], [name + "[page]", $(this).data('page')]]));
91
- return false;
92
- });
93
-
94
- // handle sorting links
95
- $table.on('click', 'a.sort', function () {
96
- doRequest(updateUrlWithParams(currentStateUrl, [["listing", name], [name + "[sort]", $(this).data('sort')]]));
97
- return false;
98
- });
99
-
100
- // handle filters
101
- var filterTimeout = null;
102
-
103
- $filters.on('submit', 'form', function () {return false;}); // prevent manual form submit
104
-
105
- var submitFilters = function () {
106
- // collect form data as params
107
- var data = [];
108
- $.each($filters.find('form').serializeArray(), function (i, o) {
109
- if (o.name.substr(0, name.length + 8) == name + '[filter]') {
110
- data.push([o.name, o.value])
111
- }
112
- });
113
- data.push(['listing', name]);
114
- data.push([name + '[page]', 1]);
115
-
116
- // send
117
- doRequest(updateUrlWithParams(currentStateUrl, data), true);
118
- };
119
-
120
- $filters.on('input', 'input[type=text],textarea', function () {
121
- if (filterTimeout) clearTimeout(filterTimeout);
122
- filterTimeout = setTimeout(submitFilters, 200);
123
- });
124
- $filters.on('change', 'select', submitFilters);
125
- });
126
-
127
- $(window).on('popstate', function (e) {
128
- location.reload()
129
- })
130
- });
@@ -1,10 +0,0 @@
1
- module Listpress
2
- class Config
3
- @color = "green"
4
- @paginate_link_renderer = MaterializePaginateRenderer
5
-
6
- class << self
7
- attr_accessor :color, :paginate_link_renderer
8
- end
9
- end
10
- end
@@ -1,36 +0,0 @@
1
- require 'will_paginate'
2
- require 'will_paginate/view_helpers/action_view'
3
-
4
- module Listpress
5
- class MaterializePaginateRenderer < WillPaginate::ActionView::LinkRenderer
6
- def html_container(html)
7
- tag(:ul, html, class: "pagination")
8
- end
9
-
10
- def page_number(page)
11
- if page == current_page
12
- "<li class=\"#{Config.color} active\">" + link(page, page, rel: rel_value(page), "data-page": page) + "</li>"
13
- else
14
- "<li class=\"waves-effect\">" + link(page, page, rel: rel_value(page), "data-page": page) + "</li>"
15
- end
16
- end
17
-
18
- def previous_page
19
- num = @collection.current_page > 1 && @collection.current_page - 1
20
- previous_or_next_page(num, "<i class=\"material-icons\">chevron_left</i>")
21
- end
22
-
23
- def next_page
24
- num = @collection.current_page < total_pages && @collection.current_page + 1
25
- previous_or_next_page(num, "<i class=\"material-icons\">chevron_right</i>")
26
- end
27
-
28
- def previous_or_next_page(page, text)
29
- if page
30
- "<li class=\"waves-effect\">" + link(text, page, "data-page": page) + "</li>"
31
- else
32
- "<li class=\"disabled\"><a>" + text + "</a></li>"
33
- end
34
- end
35
- end
36
- end