smart_listing 0.9.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +211 -4
- data/app/assets/javascripts/{smart_listing/smart_listing.coffee.erb → smart_listing.coffee.erb} +35 -18
- data/app/helpers/smart_listing/helper.rb +50 -36
- data/app/views/kaminari/smart_listing/_first_page.html.erb +13 -0
- data/app/views/kaminari/smart_listing/_gap.html.erb +8 -0
- data/app/views/kaminari/smart_listing/_last_page.html.erb +13 -0
- data/app/views/kaminari/smart_listing/_next_page.html.erb +13 -0
- data/app/views/kaminari/smart_listing/_page.html.erb +12 -0
- data/app/views/kaminari/smart_listing/_paginator.html.erb +25 -0
- data/app/views/kaminari/smart_listing/_prev_page.html.erb +13 -0
- data/app/views/smart_listing/_action_custom.html.erb +1 -1
- data/app/views/smart_listing/_action_delete.html.erb +2 -1
- data/app/views/smart_listing/_action_edit.html.erb +2 -1
- data/app/views/smart_listing/_action_show.html.erb +2 -1
- data/app/views/smart_listing/_pagination_per_page_link.html.erb +1 -1
- data/app/views/smart_listing/_sortable.html.erb +3 -3
- data/app/views/smart_listing/item/_create.js.erb +1 -0
- data/app/views/smart_listing/item/_create_continue.js.erb +1 -1
- data/config/locales/en.yml +2 -0
- data/lib/generators/smart_listing/templates/initializer.rb +16 -1
- data/lib/smart_listing.rb +123 -51
- data/lib/smart_listing/config.rb +35 -6
- data/lib/smart_listing/version.rb +1 -1
- metadata +12 -8
- data/app/assets/javascripts/smart_listing/application.js +0 -14
- data/app/assets/stylesheets/smart_listing/application.css +0 -13
- data/app/views/smart_listing/_action_inactive.html.erb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41dcdfa429d72c12038175a0eb1252ad399b2a71
|
4
|
+
data.tar.gz: d277995fe1f1474795326b8db38c0fd07b307c0f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6feb82a31cbe7dfdad359b3427325447a4ea63ad425189c752da1e93a9e1612ca32adb2d0f08f489193f9a29854721cf849b6e78f33c67f8789e8546d937e24c
|
7
|
+
data.tar.gz: c958ec953a3b49097490e90affb1785af82fa62c8318feafc7d3a72f37ebd86e8ca977dd9e47171dda0a289633e72c6819befa15995ce3ca0e857b526354544a
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# SmartListing
|
2
2
|
|
3
|
-
SmartListing helps creating
|
3
|
+
SmartListing helps creating AJAX-enabled lists of ActiveRecord collections or arrays with pagination, filtering, sorting and in-place editing.
|
4
|
+
|
5
|
+
[See it in action](http://showcase.sology.eu/smart_listing)
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -16,23 +18,228 @@ Then run:
|
|
16
18
|
$ bundle install
|
17
19
|
```
|
18
20
|
|
21
|
+
Also, you need to add SmartListing to your asset pipeline:
|
22
|
+
|
23
|
+
```
|
24
|
+
//= require smart_listing
|
25
|
+
```
|
26
|
+
|
27
|
+
### Initializer
|
28
|
+
|
19
29
|
Optionally you can also install some configuration initializer:
|
20
30
|
|
21
31
|
```sh
|
22
32
|
$ rails generate smart_listing:install
|
23
33
|
```
|
24
34
|
|
25
|
-
|
35
|
+
It will be placed in `config/initializers/smart_listing.rb` and will allow you to tweak some configuration settings like HTML classes and data attributes names.
|
36
|
+
|
37
|
+
### Custom views
|
38
|
+
|
39
|
+
SmartListing comes with some built-in views which are by default compatible with Bootstrap 3. You can easily change them after installing:
|
26
40
|
|
27
41
|
```sh
|
28
42
|
$ rails generate smart_listing:views
|
29
43
|
```
|
30
44
|
|
45
|
+
Files will be placed in `app/views/smart_listing`.
|
46
|
+
|
31
47
|
## Usage
|
32
48
|
|
33
|
-
|
49
|
+
Let's start with a controller. In order to use SmartListing, in most cases you need to include controller extensions and SmartListing helper methods:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
include SmartListing::Helper::ControllerExtensions
|
53
|
+
helper SmartListing::Helper
|
54
|
+
```
|
55
|
+
|
56
|
+
Next, put following code in controller action you desire:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
@users = smart_listing_create(:users, User.active, partial: "users/listing")
|
60
|
+
```
|
61
|
+
|
62
|
+
This will create SmartListing named `:users` consisting of ActiveRecord scope `User.active` elements and rendered by partial `users/listing`. You can also use arrays instead of ActiveRecord collections. Just put @array: true@ option just like for Kaminari.
|
63
|
+
|
64
|
+
In the main view (typically something like `index.html.erb` or `index.html.haml`), use this method to render listing:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
smart_listing_render(:users)
|
68
|
+
```
|
69
|
+
|
70
|
+
`smart_listing_render` does some magic and renders `users/listing` partial which may look like this (in HAML):
|
71
|
+
|
72
|
+
```haml
|
73
|
+
- unless smart_listing.empty?
|
74
|
+
%table
|
75
|
+
%thead
|
76
|
+
%tr
|
77
|
+
%th User name
|
78
|
+
%th Email
|
79
|
+
%tbody
|
80
|
+
- smart_listing.collection.each do |user|
|
81
|
+
%tr
|
82
|
+
%td= user.name
|
83
|
+
%td= user.email
|
84
|
+
|
85
|
+
= smart_listing.paginate
|
86
|
+
- else
|
87
|
+
%p.warning No records!
|
88
|
+
```
|
89
|
+
|
90
|
+
You can see that listing template has access to special `smart_listing` local variable which is basically an instance of `SmartListing::Helper::Builder`. It provides you with some helper methods that ease rendering of SmartListing:
|
91
|
+
|
92
|
+
* `Builder#paginate` - renders Kaminari pagination,
|
93
|
+
* `Builder#pagination_per_page_links` - display some link that allow you to customize Kaminari's `per_page`,
|
94
|
+
* `Builder#collection` - accesses underlying list of items,
|
95
|
+
* `Builder#empty?` - checks if collection is empty,
|
96
|
+
* `Builder#count` - returns collection count,
|
97
|
+
* `Builder#render` - basic template's `render` wrapper that automatically adds `smart_listing` local variable,
|
98
|
+
|
99
|
+
There are also other methods that will be described in detail below.
|
100
|
+
|
101
|
+
If you are using SmartListing with AJAX on (by default), one last thing required to make pagination (and other features) work is to create JS template for main view (typically something like `index.js.erb`):
|
102
|
+
|
103
|
+
```erb
|
104
|
+
<%= smart_listing_update(:users) %>
|
105
|
+
```
|
106
|
+
|
107
|
+
### Sorting
|
108
|
+
|
109
|
+
SmartListing supports two modes of sorting: implicit and explicit. Implicit mode is enabled by default. In this mode, you define sort columns directly in the view:
|
110
|
+
|
111
|
+
```haml
|
112
|
+
- unless smart_listing.empty?
|
113
|
+
%table
|
114
|
+
%thead
|
115
|
+
%tr
|
116
|
+
%th= smart_listing.sortable "User name", "name"
|
117
|
+
%th= smart_listing.sortable "Email", "email"
|
118
|
+
%tbody
|
119
|
+
- smart_listing.collection.each do |user|
|
120
|
+
%tr
|
121
|
+
%td= user.name
|
122
|
+
%td= user.email
|
123
|
+
|
124
|
+
= smart_listing.paginate
|
125
|
+
- else
|
126
|
+
%p.warning No records!
|
127
|
+
```
|
128
|
+
|
129
|
+
In this case `"name"` and `"email"` are sorting column names. `Builder#sortable` renders special link containing column name and sort order (either `asc`, `desc`, or empty value).
|
130
|
+
|
131
|
+
You can also specify default sort order in the controller:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
@users = smart_listing_create(:users, User.active, partial: "users/listing", default_sort: {:name: "asc"})
|
135
|
+
```
|
136
|
+
|
137
|
+
Implicit mode is convenient with simple data sets. In case you want to sort by joined column names, we advise you to use explicit sorting:
|
138
|
+
```ruby
|
139
|
+
@users = smart_listing_create :users, User.active.joins(:stats), partial: "users/listing",
|
140
|
+
sort_attributes: [[:last_signin, "stats.last_signin_at"]],
|
141
|
+
default_sort: {last_signin: "desc"}
|
142
|
+
```
|
143
|
+
|
144
|
+
Note that `:sort_attributes` are array which of course means, that order of attributes matters.
|
145
|
+
|
146
|
+
### List item management and in-place editing
|
147
|
+
|
148
|
+
In order to allow managing and editing list items, we need to reorganize our views a bit. Basically, each item needs to have its own partial:
|
149
|
+
|
150
|
+
```haml
|
151
|
+
- unless smart_listing.empty?
|
152
|
+
%table
|
153
|
+
%thead
|
154
|
+
%tr
|
155
|
+
%th= smart_listing.sortable "User name", "name"
|
156
|
+
%th= smart_listing.sortable "Email", "email"
|
157
|
+
%th
|
158
|
+
%tbody
|
159
|
+
- smart_listing.collection.each do |user|
|
160
|
+
%tr.editable{data: {id: user.id}}
|
161
|
+
= smart_listing.render partial: 'users/user', locals: {user: user}
|
162
|
+
= smart_listing.item_new colspan: 3, link: new_user_path
|
163
|
+
|
164
|
+
= smart_listing.paginate
|
165
|
+
- else
|
166
|
+
%p.warning No records!
|
167
|
+
```
|
168
|
+
|
169
|
+
`<tr>` has now `editable` class and `data-id` attribute. These are essential to make it work. We've used also a new helper: `Builder#new_item`. It renders new row which is used for adding new items. `:link` needs to be valid url to new resource action which renders JS:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
<%= smart_listing_item :users, :new, @new_user, "users/form" %>
|
173
|
+
```
|
174
|
+
|
175
|
+
Note that `new` action does not need to create SmartListing (via `smart_listing_create`). It just initializes `@new_user` and renders JS view.
|
176
|
+
|
177
|
+
New partial for user (`users/user`) may look like this:
|
178
|
+
```haml
|
179
|
+
%td= user.name
|
180
|
+
%td= user.email
|
181
|
+
%td.actions= smart_listing_item_actions [{name: :edit, url: edit_user_path(user)}, {name: :destroy, url: user_path(user)}]
|
182
|
+
```
|
183
|
+
|
184
|
+
`smart_listing_item_actions` renders here links that allow to edit and destroy user item. `:edit` and `:destroy` are built-in actions, you can also define your `:custom` actions. Again. `<td>`'s class `actions` is important.
|
185
|
+
|
186
|
+
Controller actions referenced by above urls are again plain Ruby on Rails actions that render JS like:
|
187
|
+
|
188
|
+
```erb
|
189
|
+
<%= smart_listing_item :users, :new, @user, "users/form" %>
|
190
|
+
<%= smart_listing_item :users, :edit, @user, "users/form" %>
|
191
|
+
<%= smart_listing_item :users, :destroy, @user %>
|
192
|
+
```
|
193
|
+
|
194
|
+
Partial name supplied to `smart_listing_item` (`users/form`) references `@user` as `object` and may look like this:
|
195
|
+
|
196
|
+
```haml
|
197
|
+
%td{colspan: 3}
|
198
|
+
- if object.persisted?
|
199
|
+
%p Edit user
|
200
|
+
- else
|
201
|
+
%p Add user
|
202
|
+
|
203
|
+
= form_for object, url: object.new_record? ? users_path : user_path(object), remote: true do |f|
|
204
|
+
%p
|
205
|
+
Name:
|
206
|
+
= f.text_field :name
|
207
|
+
%p
|
208
|
+
Email:
|
209
|
+
= f.text_field :email
|
210
|
+
%p= f.submit "Save"
|
211
|
+
```
|
212
|
+
|
213
|
+
And one last thing are `create` and `update` controller actions JS view:
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
<%= smart_listing_item :users, :create, @user, @user.persisted? ? "users/user" : "users/form" %>
|
217
|
+
<%= smart_listing_item :users, :update, @user, @user.valid? ? "users/user" : "users/form" %>
|
218
|
+
```
|
219
|
+
|
220
|
+
### Controls (filtering)
|
221
|
+
|
222
|
+
SmartListing controls allow you to change somehow presented data. This is typically used for filtering records. Let's see how view with controls may look like:
|
223
|
+
|
224
|
+
```haml
|
225
|
+
= smart_listing_controls_for(:users) do
|
226
|
+
.filter.input-append
|
227
|
+
= text_field_tag :filter, '', class: "search", placeholder: "Type name here", autocomplete: "off"
|
228
|
+
%button.btn.disabled{type: "submit"}
|
229
|
+
%i.icon.icon-search
|
230
|
+
```
|
231
|
+
|
232
|
+
This gives you nice Bootstrap-enabled filter field with keychange handler. Of course you can use any other form fields in controls too.
|
233
|
+
|
234
|
+
When form field changes its value, form is submitted and request is made. This needs to be handled in controller:
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
users_scope = User.active.joins(:stats)
|
238
|
+
users_scope = users_scope.like(params[:filter]) if params[:filter]
|
239
|
+
@users = smart_listing_create :users, users_scope, partial: "users/listing"
|
240
|
+
```
|
34
241
|
|
35
|
-
|
242
|
+
Then, JS view is rendered and your SmartListing updated. That's it!
|
36
243
|
|
37
244
|
## Credits
|
38
245
|
|
data/app/assets/javascripts/{smart_listing/smart_listing.coffee.erb → smart_listing.coffee.erb}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Useful when SmartListing target url is different than current one
|
2
2
|
$.rails.href = (element) ->
|
3
|
-
element.attr('href') || element.data('
|
3
|
+
element.attr('href') || element.data('<%= SmartListing.config.data_attributes(:href) %>')
|
4
4
|
|
5
5
|
class SmartListing
|
6
6
|
constructor: (e) ->
|
@@ -25,6 +25,8 @@ class SmartListing
|
|
25
25
|
else
|
26
26
|
editable.remove()
|
27
27
|
|
28
|
+
@container.trigger("smart_listing:destroy", editable)
|
29
|
+
|
28
30
|
@changeItemCount(-1)
|
29
31
|
@refresh()
|
30
32
|
|
@@ -45,16 +47,7 @@ class SmartListing
|
|
45
47
|
false
|
46
48
|
|
47
49
|
@container.on 'click', '.<%= SmartListing.config.classes(:item_actions) %> a[data-<%= SmartListing.config.data_attributes(:confirmation) %>]', (event) =>
|
48
|
-
|
49
|
-
if(@confirmed != event.currentTarget)
|
50
|
-
# We need confirmation
|
51
|
-
$.fn.smart_listing.showConfirmation $(event.currentTarget), $(event.currentTarget).data('<%= SmartListing.config.data_attributes(:confirmation) %>'), (confirm_elem) =>
|
52
|
-
@confirmed = confirm_elem[0]
|
53
|
-
false
|
54
|
-
else
|
55
|
-
# Confirmed, reset flag and go ahead with deletion
|
56
|
-
@confirmed = null
|
57
|
-
true
|
50
|
+
$.fn.smart_listing.confirm $(event.currentTarget), $(event.currentTarget).data('<%= SmartListing.config.data_attributes(:confirmation) %>')
|
58
51
|
|
59
52
|
@container.on 'click', '.<%= SmartListing.config.classes(:item_actions) %> a[data-<%= SmartListing.config.data_attributes(:popover) %>]', (event) =>
|
60
53
|
name = $(event.currentTarget).data('<%= SmartListing.config.data_attributes(:popover) %>')
|
@@ -92,7 +85,7 @@ class SmartListing
|
|
92
85
|
parseInt(@container.data('<%= SmartListing.config.data_attributes(:max_count) %>'))
|
93
86
|
|
94
87
|
setAutoshow: (v) =>
|
95
|
-
@container.
|
88
|
+
@container.data('<%= SmartListing.config.data_attributes(:autoshow) %>', v)
|
96
89
|
|
97
90
|
changeItemCount: (value) =>
|
98
91
|
@container.find('.<%= SmartListing.config.classes(:pagination_per_page) %> .<%= SmartListing.config.classes(:pagination_count) %>').html(@itemCount() + value)
|
@@ -123,7 +116,7 @@ class SmartListing
|
|
123
116
|
@container.find('.<%= SmartListing.config.classes(:new_item_placeholder) %>').addClass('<%= SmartListing.config.classes(:hidden) %>')
|
124
117
|
@container.find('.<%= SmartListing.config.classes(:new_item_action) %>').addClass('<%= SmartListing.config.classes(:hidden) %>')
|
125
118
|
else
|
126
|
-
if @container.
|
119
|
+
if @container.data('<%= SmartListing.config.data_attributes(:autoshow) %>')
|
127
120
|
@container.find('.<%= SmartListing.config.classes(:new_item_placeholder) %>').removeClass('<%= SmartListing.config.classes(:hidden) %>')
|
128
121
|
@container.find('.<%= SmartListing.config.classes(:new_item_action) %>').addClass('<%= SmartListing.config.classes(:hidden) %>')
|
129
122
|
else
|
@@ -146,7 +139,7 @@ class SmartListing
|
|
146
139
|
|
147
140
|
registerPopover: (name, callback) =>
|
148
141
|
@popovers[name] = callback
|
149
|
-
|
142
|
+
|
150
143
|
#################################################################################################
|
151
144
|
# Methods executed by rails UJS:
|
152
145
|
|
@@ -178,11 +171,15 @@ class SmartListing
|
|
178
171
|
new_item.html(content)
|
179
172
|
new_item_placeholder.before(new_item)
|
180
173
|
|
174
|
+
@container.trigger("smart_listing:create:success", new_item)
|
175
|
+
|
181
176
|
@changeItemCount(1)
|
182
177
|
@refresh()
|
183
178
|
else
|
184
179
|
new_item_placeholder.html(content)
|
185
180
|
|
181
|
+
@container.trigger("smart_listing:create:fail", new_item_placeholder)
|
182
|
+
|
186
183
|
@fadeLoaded()
|
187
184
|
|
188
185
|
edit: (id, content) =>
|
@@ -196,6 +193,8 @@ class SmartListing
|
|
196
193
|
editable.html(content)
|
197
194
|
editable.addClass('<%= SmartListing.config.classes(:inline_editing) %>')
|
198
195
|
|
196
|
+
@container.trigger("smart_listing:edit", editable)
|
197
|
+
|
199
198
|
@fadeLoaded()
|
200
199
|
|
201
200
|
update: (id, success, content) =>
|
@@ -205,10 +204,14 @@ class SmartListing
|
|
205
204
|
editable.removeData('<%= SmartListing.config.data_attributes(:inline_edit_backup) %>')
|
206
205
|
editable.html(content)
|
207
206
|
|
207
|
+
@container.trigger("smart_listing:update:success", editable)
|
208
|
+
|
208
209
|
@refresh()
|
209
210
|
else
|
210
211
|
editable.html(content)
|
211
212
|
|
213
|
+
@container.trigger("smart_listing:update:fail", editable)
|
214
|
+
|
212
215
|
@fadeLoaded()
|
213
216
|
|
214
217
|
destroy: (id, destroyed) =>
|
@@ -217,6 +220,8 @@ class SmartListing
|
|
217
220
|
remove: (id) =>
|
218
221
|
editable = @container.find(".<%= SmartListing.config.classes(:editable) %>[data-<%= SmartListing.config.data_attributes(:id) %>=#{id}]").first()
|
219
222
|
editable.remove()
|
223
|
+
|
224
|
+
@container.trigger("smart_listing:remove", editable)
|
220
225
|
|
221
226
|
update_list: (content, data) =>
|
222
227
|
$.each data, (key, value) =>
|
@@ -294,6 +299,17 @@ $.fn.smart_listing.showConfirmation = (confirmation_elem, msg, confirm_callback)
|
|
294
299
|
|
295
300
|
$.fn.smart_listing.showPopover confirmation_elem, buildPopover(confirmation_elem, msg)
|
296
301
|
|
302
|
+
$.fn.smart_listing.confirm = (elem, msg) ->
|
303
|
+
if !elem.data("confirmed")
|
304
|
+
# We need confirmation
|
305
|
+
$.fn.smart_listing.showConfirmation elem, msg, (confirm_elem) =>
|
306
|
+
confirm_elem.data("confirmed", true)
|
307
|
+
false
|
308
|
+
else
|
309
|
+
# Confirmed, reset flag and go ahead with deletion
|
310
|
+
elem.data("confirmed", false)
|
311
|
+
true
|
312
|
+
|
297
313
|
$.fn.smart_listing.onLoading = (content, loader) ->
|
298
314
|
content.stop(true).fadeTo(500, 0.2)
|
299
315
|
loader.show()
|
@@ -335,9 +351,9 @@ $.fn.smart_listing_controls = () ->
|
|
335
351
|
|
336
352
|
$.fn.smart_listing_controls.filter = (filter) ->
|
337
353
|
form = filter.closest('form')
|
338
|
-
button =
|
339
|
-
icon =
|
340
|
-
field =
|
354
|
+
button = form.find('<%= SmartListing.config.selectors(:filtering_button) %>')
|
355
|
+
icon = form.find('<%= SmartListing.config.selectors(:filtering_icon) %>')
|
356
|
+
field = form.find('<%= SmartListing.config.selectors(:filtering_input) %>')
|
341
357
|
|
342
358
|
$.fn.smart_listing.observeField(field,
|
343
359
|
onFilled: ->
|
@@ -352,10 +368,11 @@ $.fn.smart_listing_controls.filter = (filter) ->
|
|
352
368
|
form.submit()
|
353
369
|
)
|
354
370
|
|
355
|
-
|
371
|
+
button.click ->
|
356
372
|
if field.val().length > 0
|
357
373
|
field.val('')
|
358
374
|
field.trigger('keydown')
|
375
|
+
return false
|
359
376
|
|
360
377
|
$ ->
|
361
378
|
$('.<%= SmartListing.config.classes(:main) %>').smart_listing()
|
@@ -30,7 +30,7 @@ module SmartListing
|
|
30
30
|
|
31
31
|
def paginate options = {}
|
32
32
|
if @smart_listing.collection.respond_to? :current_page
|
33
|
-
@template.paginate @smart_listing.collection, {:remote =>
|
33
|
+
@template.paginate @smart_listing.collection, {:remote => @smart_listing.remote?, :param_name => @smart_listing.param_name(:page), :params => UNSAFE_PARAMS}.merge(@smart_listing.kaminari_options)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -38,6 +38,7 @@ module SmartListing
|
|
38
38
|
@smart_listing.collection
|
39
39
|
end
|
40
40
|
|
41
|
+
# Check if smart list is empty
|
41
42
|
def empty?
|
42
43
|
@smart_listing.count == 0
|
43
44
|
end
|
@@ -59,7 +60,7 @@ module SmartListing
|
|
59
60
|
|
60
61
|
def pagination_per_page_link page
|
61
62
|
if @smart_listing.per_page.to_i != page
|
62
|
-
url = @template.url_for(sanitize_params(@template.params.merge(@smart_listing.
|
63
|
+
url = @template.url_for(sanitize_params(@template.params.merge(@smart_listing.all_params(:per_page => page, :page => 1))))
|
63
64
|
end
|
64
65
|
|
65
66
|
locals = {
|
@@ -71,17 +72,16 @@ module SmartListing
|
|
71
72
|
end
|
72
73
|
|
73
74
|
def sortable title, attribute, options = {}
|
74
|
-
|
75
|
+
dirs = [nil, "asc", "desc"]
|
76
|
+
next_index = (dirs.index(@smart_listing.sort_order(attribute)) + 1) % dirs.length
|
75
77
|
|
76
78
|
sort_params = {
|
77
|
-
|
78
|
-
@smart_listing.param_names[:sort_order] => (@smart_listing.sort_order == "asc") ? "desc" : "asc",
|
79
|
-
@smart_listing.param_names[:sort_extra] => extra
|
79
|
+
attribute => dirs[next_index]
|
80
80
|
}
|
81
81
|
|
82
82
|
locals = {
|
83
|
-
:
|
84
|
-
:url => @template.url_for(sanitize_params(@template.params.merge(sort_params))),
|
83
|
+
:order => @smart_listing.sort_order(attribute),
|
84
|
+
:url => @template.url_for(sanitize_params(@template.params.merge(@smart_listing.all_params(:sort => sort_params)))),
|
85
85
|
:container_classes => [SmartListing.config.classes(:sortable)],
|
86
86
|
:attribute => attribute,
|
87
87
|
:title => title
|
@@ -125,10 +125,10 @@ module SmartListing
|
|
125
125
|
locals = {
|
126
126
|
:colspan => options.delete(:colspan),
|
127
127
|
:no_items_classes => no_records_classes,
|
128
|
-
:no_items_text => options.delete(:no_items_text),
|
128
|
+
:no_items_text => options.delete(:no_items_text) || @template.t("smart_listing.msgs.no_items"),
|
129
129
|
:new_item_button_url => options.delete(:link),
|
130
130
|
:new_item_button_classes => new_item_button_classes,
|
131
|
-
:new_item_button_text => options.delete(:text),
|
131
|
+
:new_item_button_text => options.delete(:text) || @template.t("smart_listing.actions.new"),
|
132
132
|
:new_item_autoshow => block_given?,
|
133
133
|
:new_item_content => nil,
|
134
134
|
}
|
@@ -149,9 +149,8 @@ module SmartListing
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
-
|
153
|
-
|
154
|
-
@smart_listing.count == 0
|
152
|
+
def count
|
153
|
+
@smart_listing.count
|
155
154
|
end
|
156
155
|
|
157
156
|
# Check if smart list reached its item max count
|
@@ -173,6 +172,7 @@ module SmartListing
|
|
173
172
|
|
174
173
|
# Outputs smart list container
|
175
174
|
def smart_listing_for name, *args, &block
|
175
|
+
puts args.to_yaml
|
176
176
|
raise ArgumentError, "Missing block" unless block_given?
|
177
177
|
name = name.to_sym
|
178
178
|
options = args.extract_options!
|
@@ -202,46 +202,54 @@ module SmartListing
|
|
202
202
|
output
|
203
203
|
end
|
204
204
|
|
205
|
+
def smart_listing_render name, *args
|
206
|
+
smart_listing_for(name, *args) do |smart_listing|
|
207
|
+
concat(smart_listing.render_list)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def smart_listing_controls_for name, *args, &block
|
212
|
+
smart_listing = @smart_listings.try(:[], name)
|
213
|
+
|
214
|
+
classes = [SmartListing.config.classes(:controls), args.first.try(:[], :class)]
|
215
|
+
|
216
|
+
form_tag(smart_listing.try(:href) || {}, :remote => smart_listing.try(:remote?) || true, :method => :get, :class => classes, :data => {:smart_listing => name}) do
|
217
|
+
concat(content_tag(:div, :style => "margin:0;padding:0;display:inline") do
|
218
|
+
concat(hidden_field_tag("#{smart_listing.try(:base_param)}[_]", 1, :id => nil)) # this forces smart_listing_update to refresh the list
|
219
|
+
end)
|
220
|
+
concat(capture(&block))
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
205
224
|
# Render item action buttons (ie. edit, destroy and custom ones)
|
206
225
|
def smart_listing_item_actions actions = []
|
207
226
|
content_tag(:span) do
|
208
227
|
actions.each do |action|
|
209
228
|
next unless action.is_a?(Hash)
|
210
229
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
230
|
+
locals = {
|
231
|
+
:action_if => action.has_key?(:if) ? action[:if] : true,
|
232
|
+
:url => action.delete(:url),
|
233
|
+
:icon => action.delete(:icon),
|
234
|
+
:title => action.delete(:title),
|
235
|
+
}
|
236
|
+
locals[:icon] = [locals[:icon], SmartListing.config.classes(:muted)] if !locals[:action_if]
|
217
237
|
|
218
238
|
action_name = action[:name].to_sym
|
219
239
|
case action_name
|
220
240
|
when :show
|
221
|
-
locals = {
|
222
|
-
:url => action.delete(:url),
|
223
|
-
:icon => action.delete(:icon),
|
224
|
-
}
|
225
241
|
concat(render(:partial => 'smart_listing/action_show', :locals => locals))
|
226
242
|
when :edit
|
227
|
-
locals = {
|
228
|
-
:url => action.delete(:url),
|
229
|
-
:icon => action.delete(:icon),
|
230
|
-
}
|
231
243
|
concat(render(:partial => 'smart_listing/action_edit', :locals => locals))
|
232
244
|
when :destroy
|
233
|
-
locals
|
234
|
-
:url => action.delete(:url),
|
235
|
-
:icon => action.delete(:icon),
|
245
|
+
locals.merge!(
|
236
246
|
:confirmation => action.delete(:confirmation),
|
237
|
-
|
247
|
+
)
|
238
248
|
concat(render(:partial => 'smart_listing/action_delete', :locals => locals))
|
239
249
|
when :custom
|
240
|
-
locals
|
241
|
-
:url => action.delete(:url),
|
242
|
-
:icon => action.delete(:icon),
|
250
|
+
locals.merge!(
|
243
251
|
:html_options => action,
|
244
|
-
|
252
|
+
)
|
245
253
|
concat(render(:partial => 'smart_listing/action_custom', :locals => locals))
|
246
254
|
else
|
247
255
|
concat(render(:partial => "smart_listing/action_#{action_name}", :locals => {:action => action}))
|
@@ -261,9 +269,15 @@ module SmartListing
|
|
261
269
|
# JS helpers:
|
262
270
|
|
263
271
|
# Updates the smart list
|
264
|
-
def smart_listing_update name
|
272
|
+
def smart_listing_update name, options = {}
|
265
273
|
name = name.to_sym
|
266
274
|
smart_listing = @smart_listings[name]
|
275
|
+
|
276
|
+
# don't update list if params are missing (prevents interfering with other lists)
|
277
|
+
if params.keys.select{|k| k.include?("smart_listing")}.any? && !params[smart_listing.base_param]
|
278
|
+
return unless options[:force]
|
279
|
+
end
|
280
|
+
|
267
281
|
builder = Builder.new(name, smart_listing, self, {}, nil)
|
268
282
|
render(:partial => 'smart_listing/update_list', :locals => {
|
269
283
|
:name => smart_listing.name,
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%# Link to the "First" page
|
2
|
+
- available local variables
|
3
|
+
url: url to the first page
|
4
|
+
current_page: a page object for the currently displayed page
|
5
|
+
total_pages: total number of pages
|
6
|
+
per_page: number of items to fetch per page
|
7
|
+
remote: data-remote
|
8
|
+
-%>
|
9
|
+
<% unless current_page.first? %>
|
10
|
+
<li class="first">
|
11
|
+
<%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote %>
|
12
|
+
</li>
|
13
|
+
<% end %>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%# Non-link tag that stands for skipped pages...
|
2
|
+
- available local variables
|
3
|
+
current_page: a page object for the currently displayed page
|
4
|
+
total_pages: total number of pages
|
5
|
+
per_page: number of items to fetch per page
|
6
|
+
remote: data-remote
|
7
|
+
-%>
|
8
|
+
<li class="page gap disabled"><a href="#" onclick="return false;"><%= raw(t 'views.pagination.truncate') %></a></li>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%# Link to the "Last" page
|
2
|
+
- available local variables
|
3
|
+
url: url to the last page
|
4
|
+
current_page: a page object for the currently displayed page
|
5
|
+
total_pages: total number of pages
|
6
|
+
per_page: number of items to fetch per page
|
7
|
+
remote: data-remote
|
8
|
+
-%>
|
9
|
+
<% unless current_page.last? %>
|
10
|
+
<li class="last next"><%# "next" class present for border styling in twitter bootstrap %>
|
11
|
+
<%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} %>
|
12
|
+
</li>
|
13
|
+
<% end %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%# Link to the "Next" page
|
2
|
+
- available local variables
|
3
|
+
url: url to the next page
|
4
|
+
current_page: a page object for the currently displayed page
|
5
|
+
total_pages: total number of pages
|
6
|
+
per_page: number of items to fetch per page
|
7
|
+
remote: data-remote
|
8
|
+
-%>
|
9
|
+
<% unless current_page.last? %>
|
10
|
+
<li class="next_page">
|
11
|
+
<%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote %>
|
12
|
+
</li>
|
13
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%# Link showing page number
|
2
|
+
- available local variables
|
3
|
+
page: a page object for "this" page
|
4
|
+
url: url to this page
|
5
|
+
current_page: a page object for the currently displayed page
|
6
|
+
total_pages: total number of pages
|
7
|
+
per_page: number of items to fetch per page
|
8
|
+
remote: data-remote
|
9
|
+
-%>
|
10
|
+
<li class="page<%= ' active' if page.current? %>">
|
11
|
+
<%= link_to page, url, opts = {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} %>
|
12
|
+
</li>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<%# The container tag
|
2
|
+
- available local variables
|
3
|
+
current_page: a page object for the currently displayed page
|
4
|
+
total_pages: total number of pages
|
5
|
+
per_page: number of items to fetch per page
|
6
|
+
remote: data-remote
|
7
|
+
paginator: the paginator that renders the pagination tags inside
|
8
|
+
-%>
|
9
|
+
<%= paginator.render do -%>
|
10
|
+
<div class="text-center">
|
11
|
+
<ul class="pagination">
|
12
|
+
<%= first_page_tag unless current_page.first? %>
|
13
|
+
<%= prev_page_tag unless current_page.first? %>
|
14
|
+
<% each_page do |page| -%>
|
15
|
+
<% if page.left_outer? || page.right_outer? || page.inside_window? -%>
|
16
|
+
<%= page_tag page %>
|
17
|
+
<% elsif !page.was_truncated? -%>
|
18
|
+
<%= gap_tag %>
|
19
|
+
<% end -%>
|
20
|
+
<% end -%>
|
21
|
+
<%= next_page_tag unless current_page.last? %>
|
22
|
+
<%= last_page_tag unless current_page.last? %>
|
23
|
+
</ul>
|
24
|
+
</div>
|
25
|
+
<% end -%>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%# Link to the "Previous" page
|
2
|
+
- available local variables
|
3
|
+
url: url to the previous page
|
4
|
+
current_page: a page object for the currently displayed page
|
5
|
+
total_pages: total number of pages
|
6
|
+
per_page: number of items to fetch per page
|
7
|
+
remote: data-remote
|
8
|
+
-%>
|
9
|
+
<% unless current_page.first? %>
|
10
|
+
<li class="prev">
|
11
|
+
<%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote %>
|
12
|
+
</li>
|
13
|
+
<% end %>
|
@@ -5,6 +5,7 @@
|
|
5
5
|
url: destroy url
|
6
6
|
icon
|
7
7
|
confirmation: confrimation text
|
8
|
+
title
|
8
9
|
-%>
|
9
10
|
|
10
|
-
<%=
|
11
|
+
<%= link_to_if(action_if, content_tag(:i, "", :class => icon || SmartListing.config.classes(:icon_trash)), url, :remote => true, :class => "destroy", :method => :delete, :title => title || t("smart_listing.actions.destroy"), :data => {:confirmation => confirmation || t("smart_listing.msgs.destroy_confirmation")} ) %>
|
@@ -4,6 +4,7 @@
|
|
4
4
|
Available local variables:
|
5
5
|
url
|
6
6
|
icon
|
7
|
+
title
|
7
8
|
-%>
|
8
9
|
|
9
|
-
<%=
|
10
|
+
<%= link_to_if(action_if, content_tag(:i, '', :class => icon || SmartListing.config.classes(:icon_edit)), url, :remote => true, :class => "edit", :title => title || t("smart_listing.actions.edit") ) %>
|
@@ -4,6 +4,7 @@
|
|
4
4
|
Available local variables:
|
5
5
|
url
|
6
6
|
icon
|
7
|
+
title
|
7
8
|
-%>
|
8
9
|
|
9
|
-
<%=
|
10
|
+
<%= link_to_if(action_if, content_tag(:i, '', :class => icon || SmartListing.config.classes(:icon_show)), url, :class => "show", :title => title || t("smart_listing.actions.show") ) %>
|
@@ -6,15 +6,15 @@
|
|
6
6
|
url: sortable link url
|
7
7
|
attribute: current attribute name
|
8
8
|
title: current attribute title
|
9
|
-
|
9
|
+
order: defines column sort order
|
10
10
|
smart_listing: current SmartListing instance
|
11
11
|
builder: current builder instance
|
12
12
|
-%>
|
13
13
|
|
14
14
|
<%= link_to url, :class => container_classes, :data => {:attr => attribute}, :remote => true do %>
|
15
15
|
<%= title %>
|
16
|
-
<% if
|
17
|
-
<span class="<%=
|
16
|
+
<% if order %>
|
17
|
+
<span class="<%= order == "asc" ? SmartListing.config.classes(:icon_sort_up) : SmartListing.config.classes(:icon_sort_down) %>"></span>
|
18
18
|
<% else %>
|
19
19
|
<span class="<%= SmartListing.config.classes(:icon_sort_none) %>"></span>
|
20
20
|
<% end %>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
var smart_listing = $('#<%= name %>').smart_listing();
|
2
|
-
smart_listing.create(<%= id || 0 %>, <%= object.persisted? %>, "<%= escape_javascript(render(:partial => part, :locals => {object_key => object})) %>");
|
3
2
|
smart_listing.setAutoshow(true);
|
3
|
+
smart_listing.create(<%= id || 0 %>, <%= object.persisted? %>, "<%= escape_javascript(render(:partial => part, :locals => {object_key => object})) %>");
|
4
4
|
<% if object.persisted? %>
|
5
5
|
smart_listing.new_item("<%= escape_javascript(render(:partial => new.last, :locals => {object_key => new.first})) %>");
|
6
6
|
<% end %>
|
data/config/locales/en.yml
CHANGED
@@ -1,4 +1,19 @@
|
|
1
1
|
SmartListing.configure do |config|
|
2
|
+
config.global_options({
|
3
|
+
#:param_names => { # param names
|
4
|
+
#:page => :page,
|
5
|
+
#:per_page => :per_page,
|
6
|
+
#:sort => :sort,
|
7
|
+
#},
|
8
|
+
#:array => false, # controls whether smart list should be using arrays or AR collections
|
9
|
+
#:max_count => nil, # limit number of rows
|
10
|
+
#:unlimited_per_page => false, # allow infinite page size
|
11
|
+
#:paginate => true, # allow pagination
|
12
|
+
#:memorize_per_page => false, # save per page settings in the cookie
|
13
|
+
#:page_sizes => DEFAULT_PAGE_SIZES, # set available page sizes array
|
14
|
+
#:kaminari_options => {:theme => "smart_listing"}, # Kaminari's paginate helper options
|
15
|
+
})
|
16
|
+
|
2
17
|
config.constants :classes, {
|
3
18
|
#:main => "smart-listing",
|
4
19
|
#:editable => "editable",
|
@@ -12,7 +27,7 @@ SmartListing.configure do |config|
|
|
12
27
|
#:hidden => "hidden",
|
13
28
|
#:autoselect => "autoselect",
|
14
29
|
#:callback => "callback",
|
15
|
-
#:pagination_per_page => "pagination-per-page",
|
30
|
+
#:pagination_per_page => "pagination-per-page text-center",
|
16
31
|
#:pagination_count => "count",
|
17
32
|
#:inline_editing => "info",
|
18
33
|
#:no_records => "no-records",
|
data/lib/smart_listing.rb
CHANGED
@@ -2,6 +2,23 @@ require 'smart_listing/config'
|
|
2
2
|
require "smart_listing/engine"
|
3
3
|
require "kaminari"
|
4
4
|
|
5
|
+
# Fix parsing nester params
|
6
|
+
module Kaminari
|
7
|
+
module Helpers
|
8
|
+
class Tag
|
9
|
+
def page_url_for(page)
|
10
|
+
@template.url_for @params.deep_merge(page_param(page)).merge(:only_path => true)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def page_param(page)
|
16
|
+
Rack::Utils.parse_nested_query("#{@param_name}=#{page <= 1 ? nil : page}").symbolize_keys
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
5
22
|
module SmartListing
|
6
23
|
class Base
|
7
24
|
if Rails.env.development?
|
@@ -10,32 +27,19 @@ module SmartListing
|
|
10
27
|
DEFAULT_PAGE_SIZES = [10, 20, 50, 100]
|
11
28
|
end
|
12
29
|
|
13
|
-
attr_reader :name, :collection, :options, :per_page, :
|
30
|
+
attr_reader :name, :collection, :options, :per_page, :sort, :page, :partial, :count
|
14
31
|
|
15
32
|
def initialize name, collection, options = {}
|
16
33
|
@name = name
|
17
34
|
|
18
35
|
@options = {
|
19
|
-
:
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
:partial => @name, # smart list partial name
|
27
|
-
:array => false, # controls whether smart list should be using arrays or AR collections
|
28
|
-
:max_count => nil, # limit number of rows
|
29
|
-
:unlimited_per_page => false, # allow infinite page size
|
30
|
-
:sort => true, # allow sorting
|
31
|
-
:paginate => true, # allow pagination
|
32
|
-
:href => nil, # set smart list target url (in case when different than current url)
|
33
|
-
:callback_href => nil, # set smart list callback url (in case when different than current url)
|
34
|
-
:default_sort_attr => nil, # default sort by
|
35
|
-
:memorize_per_page => false,
|
36
|
-
:page_sizes => DEFAULT_PAGE_SIZES, # set available page sizes array
|
37
|
-
:kaminari_options => {}, # Kaminari's paginate helper options
|
38
|
-
}.merge!(options)
|
36
|
+
:partial => @name, # SmartListing partial name
|
37
|
+
:sort_attributes => :implicit, # allow implicitly setting sort attributes
|
38
|
+
:default_sort => {}, # default sorting
|
39
|
+
:href => nil, # set SmartListing target url (in case when different than current url)
|
40
|
+
:remote => true, # SmartListing is remote by default
|
41
|
+
:callback_href => nil, # set SmartListing callback url (in case when different than current url)
|
42
|
+
}.merge(SmartListing.config.global_options).merge(options)
|
39
43
|
|
40
44
|
if @options[:array]
|
41
45
|
@collection = collection.to_a
|
@@ -45,14 +49,16 @@ module SmartListing
|
|
45
49
|
end
|
46
50
|
|
47
51
|
def setup params, cookies
|
48
|
-
@
|
49
|
-
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@
|
53
|
-
@sort_extra = params[param_names[:sort_extra]]
|
52
|
+
@params = params
|
53
|
+
|
54
|
+
@page = get_param :page
|
55
|
+
@per_page = !get_param(:per_page) || get_param(:per_page).empty? ? (@options[:memorize_per_page] && get_param(:per_page, cookies).to_i > 0 ? get_param(:per_page, cookies).to_i : page_sizes.first) : get_param(:per_page).to_i
|
56
|
+
@per_page = page_sizes.first unless page_sizes.include?(@per_page)
|
54
57
|
|
55
|
-
|
58
|
+
@sort = parse_sort(get_param(:sort)) || @options[:default_sort]
|
59
|
+
sort_keys = (@options[:sort_attributes] == :implicit ? @sort.keys.collect{|s| [s, s]} : @options[:sort_attributes])
|
60
|
+
|
61
|
+
set_param(:per_page, @per_page, cookies) if @options[:memorize_per_page]
|
56
62
|
|
57
63
|
@count = @collection.size
|
58
64
|
@count = @count.length if @count.is_a?(Hash)
|
@@ -64,26 +70,29 @@ module SmartListing
|
|
64
70
|
end
|
65
71
|
|
66
72
|
if @options[:array]
|
67
|
-
@
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
yval = yval.upcase if yval.is_a?(String)
|
76
|
-
|
77
|
-
if xval.nil? || yval.nil?
|
78
|
-
xval.nil? ? 1 : -1
|
79
|
-
else
|
80
|
-
if @sort_order == "asc"
|
81
|
-
(xval <=> yval) || (xval && !yval ? 1 : -1)
|
82
|
-
else
|
83
|
-
(yval <=> xval) || (yval && !xval ? 1 : -1)
|
73
|
+
if @sort && @sort.any? # when array we sort only by first attribute
|
74
|
+
i = sort_keys.index{|x| x[0] == @sort.first[0]}
|
75
|
+
@collection = @collection.sort do |x, y|
|
76
|
+
xval = x
|
77
|
+
yval = y
|
78
|
+
sort_keys[i][1].split(".").each do |m|
|
79
|
+
xval = xval.try(m)
|
80
|
+
yval = yval.try(m)
|
84
81
|
end
|
85
|
-
|
86
|
-
|
82
|
+
xval = xval.upcase if xval.is_a?(String)
|
83
|
+
yval = yval.upcase if yval.is_a?(String)
|
84
|
+
|
85
|
+
if xval.nil? || yval.nil?
|
86
|
+
xval.nil? ? 1 : -1
|
87
|
+
else
|
88
|
+
if @sort.first[1] == "asc"
|
89
|
+
(xval <=> yval) || (xval && !yval ? 1 : -1)
|
90
|
+
else
|
91
|
+
(yval <=> xval) || (yval && !xval ? 1 : -1)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
87
96
|
if @options[:paginate] && @per_page > 0
|
88
97
|
@collection = ::Kaminari.paginate_array(@collection).page(@page).per(@per_page)
|
89
98
|
if @collection.length == 0
|
@@ -91,7 +100,9 @@ module SmartListing
|
|
91
100
|
end
|
92
101
|
end
|
93
102
|
else
|
94
|
-
|
103
|
+
# let's sort by all attributes
|
104
|
+
@collection = @collection.order(sort_keys.collect{|s| "#{s[1]} #{@sort[s[0]]}" if @sort[s[0]]}.compact) if @sort && @sort.any?
|
105
|
+
|
95
106
|
if @options[:paginate] && @per_page > 0
|
96
107
|
@collection = @collection.page(@page).per(@per_page)
|
97
108
|
end
|
@@ -106,6 +117,10 @@ module SmartListing
|
|
106
117
|
@options[:param_names]
|
107
118
|
end
|
108
119
|
|
120
|
+
def param_name key
|
121
|
+
"#{base_param}[#{param_names[key]}]"
|
122
|
+
end
|
123
|
+
|
109
124
|
def unlimited_per_page?
|
110
125
|
!!@options[:unlimited_per_page]
|
111
126
|
end
|
@@ -122,6 +137,10 @@ module SmartListing
|
|
122
137
|
@options[:callback_href]
|
123
138
|
end
|
124
139
|
|
140
|
+
def remote?
|
141
|
+
@options[:remote]
|
142
|
+
end
|
143
|
+
|
125
144
|
def page_sizes
|
126
145
|
@options[:page_sizes]
|
127
146
|
end
|
@@ -130,12 +149,65 @@ module SmartListing
|
|
130
149
|
@options[:kaminari_options]
|
131
150
|
end
|
132
151
|
|
133
|
-
def all_params
|
134
|
-
ap = {}
|
152
|
+
def all_params overrides = {}
|
153
|
+
ap = {base_param => {}}
|
135
154
|
@options[:param_names].each do |k, v|
|
136
|
-
|
155
|
+
if overrides[k]
|
156
|
+
ap[base_param][v] = overrides[k]
|
157
|
+
else
|
158
|
+
ap[base_param][v] = self.send(k)
|
159
|
+
end
|
137
160
|
end
|
138
161
|
ap
|
139
162
|
end
|
163
|
+
|
164
|
+
def sort_order attribute
|
165
|
+
@sort && @sort[attribute].present? ? @sort[attribute] : nil
|
166
|
+
end
|
167
|
+
|
168
|
+
def base_param
|
169
|
+
"#{name}_smart_listing"
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def get_param key, store = @params
|
175
|
+
if store.is_a?(ActionDispatch::Cookies::CookieJar)
|
176
|
+
store["#{base_param}_#{param_names[key]}"]
|
177
|
+
else
|
178
|
+
store[base_param].try(:[], param_names[key])
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def set_param key, value, store = @params
|
183
|
+
if store.is_a?(ActionDispatch::Cookies::CookieJar)
|
184
|
+
store["#{base_param}_#{param_names[key]}"] = value
|
185
|
+
else
|
186
|
+
store[base_param] ||= {}
|
187
|
+
store[base_param][param_names[key]] = value
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def parse_sort sort_params
|
192
|
+
sort = nil
|
193
|
+
|
194
|
+
if @options[:sort_attributes] == :implicit
|
195
|
+
sort = sort_params.dup if sort_params.present?
|
196
|
+
elsif @options[:sort_attributes]
|
197
|
+
@options[:sort_attributes].each do |a|
|
198
|
+
k, v = a
|
199
|
+
if sort_params && sort_params[k.to_s]
|
200
|
+
dir = ["asc", "desc", ""].delete(sort_params[k.to_s])
|
201
|
+
|
202
|
+
if dir
|
203
|
+
sort ||= {}
|
204
|
+
sort[k] = dir
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
sort
|
211
|
+
end
|
140
212
|
end
|
141
213
|
end
|
data/lib/smart_listing/config.rb
CHANGED
@@ -8,7 +8,27 @@ module SmartListing
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class Configuration
|
11
|
+
if Rails.env.development?
|
12
|
+
DEFAULT_PAGE_SIZES = [3, 10, 20, 50, 100]
|
13
|
+
else
|
14
|
+
DEFAULT_PAGE_SIZES = [10, 20, 50, 100]
|
15
|
+
end
|
16
|
+
|
11
17
|
DEFAULTS = {
|
18
|
+
:global_options => {
|
19
|
+
:param_names => { # param names
|
20
|
+
:page => :page,
|
21
|
+
:per_page => :per_page,
|
22
|
+
:sort => :sort,
|
23
|
+
},
|
24
|
+
:array => false, # controls whether smart list should be using arrays or AR collections
|
25
|
+
:max_count => nil, # limit number of rows
|
26
|
+
:unlimited_per_page => false, # allow infinite page size
|
27
|
+
:paginate => true, # allow pagination
|
28
|
+
:memorize_per_page => false,
|
29
|
+
:page_sizes => DEFAULT_PAGE_SIZES, # set available page sizes array
|
30
|
+
:kaminari_options => {:theme => "smart_listing"}, # Kaminari's paginate helper options
|
31
|
+
},
|
12
32
|
:constants => {
|
13
33
|
:classes => {
|
14
34
|
:main => "smart-listing",
|
@@ -23,7 +43,7 @@ module SmartListing
|
|
23
43
|
:hidden => "hidden",
|
24
44
|
:autoselect => "autoselect",
|
25
45
|
:callback => "callback",
|
26
|
-
:pagination_per_page => "pagination-per-page",
|
46
|
+
:pagination_per_page => "pagination-per-page text-center",
|
27
47
|
:pagination_count => "count",
|
28
48
|
:inline_editing => "info",
|
29
49
|
:no_records => "no-records",
|
@@ -39,11 +59,12 @@ module SmartListing
|
|
39
59
|
:icon_new => "glyphicon glyphicon-plus",
|
40
60
|
:icon_edit => "glyphicon glyphicon-pencil",
|
41
61
|
:icon_trash => "glyphicon glyphicon-trash",
|
42
|
-
:icon_inactive => "glyphicon glyphicon-circle",
|
62
|
+
:icon_inactive => "glyphicon glyphicon-remove-circle text-muted",
|
43
63
|
:icon_show => "glyphicon glyphicon-share-alt",
|
44
64
|
:icon_sort_none => "glyphicon glyphicon-resize-vertical",
|
45
65
|
:icon_sort_up => "glyphicon glyphicon-chevron-up",
|
46
66
|
:icon_sort_down => "glyphicon glyphicon-chevron-down",
|
67
|
+
:muted => "text-muted",
|
47
68
|
},
|
48
69
|
:data_attributes => {
|
49
70
|
:main => "smart-listing",
|
@@ -64,7 +85,9 @@ module SmartListing
|
|
64
85
|
:edit_cancel => "button.cancel",
|
65
86
|
:row => "tr",
|
66
87
|
:head => "thead",
|
67
|
-
:
|
88
|
+
:filtering_button => "button",
|
89
|
+
:filtering_icon => "button span",
|
90
|
+
:filtering_input => ".filter input"
|
68
91
|
}
|
69
92
|
}
|
70
93
|
}
|
@@ -78,11 +101,10 @@ module SmartListing
|
|
78
101
|
end
|
79
102
|
|
80
103
|
def constants key, value = nil
|
81
|
-
if value
|
82
|
-
@options[:constants][key]
|
83
|
-
else
|
104
|
+
if value
|
84
105
|
@options[:constants][key].merge!(value)
|
85
106
|
end
|
107
|
+
@options[:constants][key]
|
86
108
|
end
|
87
109
|
|
88
110
|
def classes key
|
@@ -96,5 +118,12 @@ module SmartListing
|
|
96
118
|
def selectors key
|
97
119
|
@options[:constants][:selectors][key]
|
98
120
|
end
|
121
|
+
|
122
|
+
def global_options value = nil
|
123
|
+
if value
|
124
|
+
@options[:global_options].merge!(value)
|
125
|
+
end
|
126
|
+
@options[:global_options]
|
127
|
+
end
|
99
128
|
end
|
100
129
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_listing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sology
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.15.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.15.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: sqlite3
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -76,15 +76,19 @@ files:
|
|
76
76
|
- LICENSE
|
77
77
|
- README.md
|
78
78
|
- Rakefile
|
79
|
-
- app/assets/javascripts/smart_listing
|
80
|
-
- app/assets/javascripts/smart_listing/smart_listing.coffee.erb
|
81
|
-
- app/assets/stylesheets/smart_listing/application.css
|
79
|
+
- app/assets/javascripts/smart_listing.coffee.erb
|
82
80
|
- app/helpers/smart_listing/application_helper.rb
|
83
81
|
- app/helpers/smart_listing/helper.rb
|
82
|
+
- app/views/kaminari/smart_listing/_first_page.html.erb
|
83
|
+
- app/views/kaminari/smart_listing/_gap.html.erb
|
84
|
+
- app/views/kaminari/smart_listing/_last_page.html.erb
|
85
|
+
- app/views/kaminari/smart_listing/_next_page.html.erb
|
86
|
+
- app/views/kaminari/smart_listing/_page.html.erb
|
87
|
+
- app/views/kaminari/smart_listing/_paginator.html.erb
|
88
|
+
- app/views/kaminari/smart_listing/_prev_page.html.erb
|
84
89
|
- app/views/smart_listing/_action_custom.html.erb
|
85
90
|
- app/views/smart_listing/_action_delete.html.erb
|
86
91
|
- app/views/smart_listing/_action_edit.html.erb
|
87
|
-
- app/views/smart_listing/_action_inactive.html.erb
|
88
92
|
- app/views/smart_listing/_action_show.html.erb
|
89
93
|
- app/views/smart_listing/_item_new.html.erb
|
90
94
|
- app/views/smart_listing/_pagination_per_page_link.html.erb
|
@@ -1,14 +0,0 @@
|
|
1
|
-
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
-
// listed below.
|
3
|
-
//
|
4
|
-
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
-
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
-
//
|
7
|
-
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
-
// compiled file.
|
9
|
-
//
|
10
|
-
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
-
// about supported directives.
|
12
|
-
//
|
13
|
-
//= require_tree .
|
14
|
-
//= require smart_listing/smart_listing
|
@@ -1,13 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
-
* listed below.
|
4
|
-
*
|
5
|
-
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
-
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
-
*
|
8
|
-
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
-
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
-
*
|
11
|
-
*= require_self
|
12
|
-
*= require_tree .
|
13
|
-
*/
|