magic_grid 0.12.4 → 0.12.5
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +1 -1
- data/README.md +29 -2
- data/Rakefile +1 -1
- data/lib/magic_grid/collection.rb +21 -24
- data/lib/magic_grid/column.rb +7 -2
- data/lib/magic_grid/definition.rb +90 -61
- data/lib/magic_grid/html_grid.rb +118 -147
- data/lib/magic_grid/order.rb +69 -0
- data/lib/magic_grid/version.rb +1 -1
- data/spec/collection_spec.rb +17 -9
- data/spec/definition_spec.rb +1 -15
- data/spec/helpers_spec.rb +5 -0
- data/spec/html_grid_spec.rb +19 -13
- data/spec/order_spec.rb +30 -0
- data/spec/spec_helper.rb +1 -1
- metadata +150 -163
data/lib/magic_grid/html_grid.rb
CHANGED
@@ -1,128 +1,131 @@
|
|
1
1
|
require 'magic_grid/definition'
|
2
|
+
require 'magic_grid/order'
|
2
3
|
|
3
4
|
module MagicGrid
|
4
5
|
class HtmlGrid
|
6
|
+
attr_reader :view, :grid
|
7
|
+
private :view, :grid
|
5
8
|
|
6
9
|
def initialize(grid_definition, view, controller = nil)
|
7
10
|
@grid = grid_definition
|
11
|
+
@spinner_drawn = false
|
8
12
|
@view ||= view
|
9
|
-
|
10
|
-
@current_url = controller.request.fullpath
|
11
|
-
else
|
12
|
-
@current_url = nil
|
13
|
-
end
|
13
|
+
@current_url = controller && controller.request.fullpath
|
14
14
|
end
|
15
15
|
|
16
16
|
def render(&row_renderer)
|
17
|
-
@
|
18
|
-
grid_data = {
|
19
|
-
:searcher => @grid.searcher,
|
20
|
-
:current => @current_url,
|
21
|
-
:live_search => @grid.options[:live_search],
|
22
|
-
:listeners => @grid.options[:listeners],
|
23
|
-
:remote => @grid.options[:remote],
|
24
|
-
:default_ajax_handler => @grid.options[:default_ajax_handler],
|
25
|
-
:params => @grid.base_params,
|
26
|
-
}
|
17
|
+
@row_renderer = row_renderer || method(:grid_row)
|
27
18
|
table_options = {
|
28
|
-
:class =>
|
29
|
-
:id
|
30
|
-
:data
|
19
|
+
:class => "magic_grid #{grid.options[:class]}",
|
20
|
+
:id => grid.magic_id,
|
21
|
+
:data => {
|
22
|
+
:searcher => grid.searcher,
|
23
|
+
:current => @current_url,
|
24
|
+
:live_search => grid.options[:live_search],
|
25
|
+
:listeners => grid.options[:listeners],
|
26
|
+
:remote => grid.options[:remote],
|
27
|
+
:default_ajax_handler => grid.options[:default_ajax_handler],
|
28
|
+
:params => grid.base_params,
|
29
|
+
}.reject {|_,v| v.nil? }
|
31
30
|
}
|
32
|
-
|
33
|
-
thead + tbody(&row_renderer) + tfoot
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def thead
|
38
|
-
@view.content_tag('thead', :data => {:params => @grid.base_params}) do
|
39
|
-
magic_grid_head
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def tbody(&row_renderer)
|
44
|
-
@view.content_tag('tbody', :class => "ui-widget-content") do
|
45
|
-
magic_rows &row_renderer
|
46
|
-
end
|
31
|
+
table(table_options)
|
47
32
|
end
|
48
33
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
34
|
+
def table(options)
|
35
|
+
view.content_tag('table', options) do
|
36
|
+
view.content_tag('thead',
|
37
|
+
:data => {:params => grid.base_params},
|
38
|
+
&method(:magic_grid_head)) <<
|
39
|
+
view.content_tag('tbody',
|
40
|
+
:class => "ui-widget-content",
|
41
|
+
&method(:magic_rows)) <<
|
42
|
+
view.content_tag('tfoot',
|
43
|
+
&method(:magic_grid_foot))
|
52
44
|
end
|
53
45
|
end
|
54
46
|
|
55
|
-
def
|
47
|
+
def render_spinner
|
56
48
|
unless @spinner_drawn
|
57
49
|
@spinner_drawn = true
|
58
|
-
|
59
|
-
:id => (
|
50
|
+
view.tag('span',
|
51
|
+
:id => (grid.magic_id.to_s + "_spinner"),
|
60
52
|
:class => "magic_grid_spinner")
|
61
53
|
end
|
62
54
|
end
|
63
55
|
|
64
56
|
def magic_grid_head
|
65
|
-
spinner = self.method(:spinner_generator)
|
66
57
|
thead = []
|
67
|
-
if
|
68
|
-
thead <<
|
58
|
+
if grid.has_title?
|
59
|
+
thead << render_title(grid.title)
|
69
60
|
end
|
70
|
-
if
|
71
|
-
thead <<
|
61
|
+
if grid.needs_searcher?
|
62
|
+
thead << searcher_block
|
72
63
|
end
|
73
|
-
if
|
74
|
-
thead <<
|
64
|
+
if grid.options[:per_page] and grid.options[:top_pager]
|
65
|
+
thead << magic_pager_block(true)
|
66
|
+
end
|
67
|
+
if thead.empty? and not grid.options[:collapse_emtpy_header]
|
68
|
+
thead << filler_block(&method(:render_spinner))
|
75
69
|
end
|
76
70
|
thead << magic_column_headers
|
77
71
|
thead.join.html_safe
|
78
72
|
end
|
79
73
|
|
74
|
+
def render_title(title)
|
75
|
+
view.content_tag 'tr' do
|
76
|
+
view.content_tag('th', title,
|
77
|
+
:class => 'full-width ui-widget-header',
|
78
|
+
:colspan => grid.columns.count)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
80
82
|
def magic_grid_foot
|
81
|
-
if
|
83
|
+
if grid.options[:per_page] and grid.options[:bottom_pager]
|
82
84
|
magic_pager_block
|
83
|
-
elsif not
|
85
|
+
elsif not grid.options[:collapse_emtpy_footer]
|
84
86
|
filler_block
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
88
|
-
def filler_block(
|
89
|
-
|
90
|
-
|
90
|
+
def filler_block(&block)
|
91
|
+
view.content_tag 'tr' do
|
92
|
+
view.content_tag('td', nil,
|
91
93
|
:class => 'full-width ui-widget-header',
|
92
|
-
:colspan =>
|
94
|
+
:colspan => grid.columns.count,
|
93
95
|
&block)
|
94
96
|
end
|
95
97
|
end
|
96
98
|
|
97
99
|
def magic_column_headers
|
98
|
-
|
99
|
-
|
100
|
+
view.content_tag 'tr' do
|
101
|
+
grid.columns.reduce(''.html_safe) do |acc, col|
|
100
102
|
classes = ['ui-state-default'] << col.html_classes
|
101
103
|
acc <<
|
102
104
|
if col.sortable?
|
103
105
|
sortable_header(col)
|
104
106
|
else
|
105
|
-
|
107
|
+
view.content_tag 'th', col.label.html_safe, :class => classes.join(' ')
|
106
108
|
end
|
107
109
|
end
|
108
110
|
end
|
109
111
|
end
|
110
112
|
|
111
|
-
def magic_rows
|
112
|
-
rows =
|
113
|
+
def magic_rows
|
114
|
+
rows = grid.collection.map(&@row_renderer)
|
113
115
|
if rows.empty?
|
114
|
-
|
116
|
+
render_empty_collection(grid.options[:if_empty]).html_safe
|
117
|
+
else
|
118
|
+
rows.join.html_safe
|
115
119
|
end
|
116
|
-
rows.join.html_safe
|
117
120
|
end
|
118
121
|
|
119
122
|
def render_empty_collection(fallback)
|
120
123
|
if fallback
|
121
|
-
|
122
|
-
|
124
|
+
view.content_tag 'tr' do
|
125
|
+
view.content_tag('td', :colspan => grid.columns.count,
|
123
126
|
:class => 'if-empty') do
|
124
127
|
if fallback.respond_to? :call
|
125
|
-
fallback.call(
|
128
|
+
fallback.call(grid).to_s
|
126
129
|
else
|
127
130
|
fallback
|
128
131
|
end
|
@@ -131,18 +134,14 @@ module MagicGrid
|
|
131
134
|
end
|
132
135
|
end
|
133
136
|
|
134
|
-
def grid_row(record
|
135
|
-
|
136
|
-
|
137
|
-
else
|
138
|
-
@view.content_tag 'tr', :class => @view.cycle('odd', 'even') do
|
139
|
-
@grid.columns.map { |c| grid_cell(c, record) }.join.html_safe
|
140
|
-
end
|
137
|
+
def grid_row(record)
|
138
|
+
view.content_tag 'tr', :class => view.cycle('odd', 'even') do
|
139
|
+
grid.columns.map { |c| grid_cell(c, record) }.join.html_safe
|
141
140
|
end
|
142
141
|
end
|
143
142
|
|
144
143
|
def grid_cell(column, record)
|
145
|
-
|
144
|
+
view.content_tag('td', :class => column.html_classes) do
|
146
145
|
method = column.reader
|
147
146
|
if method.respond_to? :call
|
148
147
|
method.call(record).to_s
|
@@ -154,114 +153,86 @@ module MagicGrid
|
|
154
153
|
end
|
155
154
|
end
|
156
155
|
|
157
|
-
def
|
158
|
-
-
|
159
|
-
end
|
160
|
-
|
161
|
-
def reverse_order(order)
|
162
|
-
order.to_i == 0 ? 1 : 0
|
163
|
-
end
|
164
|
-
|
165
|
-
def order_icon(order = -1)
|
166
|
-
@view.content_tag 'span', '', :class => "ui-icon #{order_icon_class(order)}"
|
156
|
+
def order_icon(order = Order::Unordered)
|
157
|
+
view.content_tag 'span', '', :class => "ui-icon #{order.icon_class}"
|
167
158
|
end
|
168
159
|
|
169
|
-
def
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
def order_class(order = -1)
|
178
|
-
case order.to_i
|
179
|
-
when 0 then 'sort-asc'
|
180
|
-
when 1 then 'sort-desc'
|
181
|
-
else 'sort-none'
|
160
|
+
def column_link_params(col)
|
161
|
+
id = col.id
|
162
|
+
my_params = grid.base_params.merge(grid.param_key(:col) => id)
|
163
|
+
params = HashWithIndifferentAccess.new(my_params)
|
164
|
+
if id.to_s == grid.current_sort_col.to_s
|
165
|
+
params[grid.param_key(:order)] = grid.current_order.reverse.to_param
|
166
|
+
else
|
167
|
+
params.delete(grid.param_key(:order))
|
182
168
|
end
|
169
|
+
params
|
183
170
|
end
|
184
171
|
|
185
172
|
def sortable_header(col)
|
186
173
|
id = col.id
|
187
174
|
label = col.label || id.titleize
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
classes = ['sorter ui-state-default'] << col.html_classes
|
195
|
-
if id.to_s == @grid.current_sort_col.to_s
|
196
|
-
order = @grid.current_order
|
197
|
-
classes << "sort-current" << order_class(order)
|
198
|
-
column_link_params[@grid.param_key(:order)] = reverse_order(order)
|
199
|
-
else
|
200
|
-
column_link_params.delete @grid.param_key(:order)
|
201
|
-
end
|
202
|
-
if column_link_params[@grid.param_key(:order)].to_i == default_sort_order.to_i
|
203
|
-
column_link_params.delete(@grid.param_key(:order))
|
204
|
-
end
|
205
|
-
@view.content_tag 'th', :class => classes.join(' ') do
|
206
|
-
@view.link_to column_link_params, :remote => @grid.options[:remote] do
|
207
|
-
label.html_safe << order_icon(order)
|
175
|
+
params = column_link_params(col)
|
176
|
+
classes = %w{sorter ui-state-default} << col.html_classes
|
177
|
+
classes << 'sort-current' if col.order.sorted?
|
178
|
+
view.content_tag 'th', :class => classes.join(' ') do
|
179
|
+
view.link_to params, :remote => grid.options[:remote] do
|
180
|
+
label.html_safe << order_icon(col.order)
|
208
181
|
end
|
209
182
|
end
|
210
183
|
end
|
211
184
|
|
212
|
-
def searcher_block
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
searcher_input
|
185
|
+
def searcher_block
|
186
|
+
view.content_tag('tr') do
|
187
|
+
view.content_tag('td', :class => 'searcher full-width ui-widget-header',
|
188
|
+
:colspan => grid.columns.count) do
|
189
|
+
searcher_input
|
217
190
|
end
|
218
191
|
end
|
219
192
|
end
|
220
193
|
|
221
|
-
def searcher_input
|
194
|
+
def searcher_input
|
222
195
|
searcher_data = {
|
223
|
-
:min_length =>
|
224
|
-
:current
|
196
|
+
:min_length => grid.options[:min_search_length],
|
197
|
+
:current => grid.current_search,
|
225
198
|
}
|
226
|
-
searcher =
|
227
|
-
|
228
|
-
searcher <<
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
if
|
235
|
-
searcher <<
|
236
|
-
|
199
|
+
searcher = view.label_tag(grid.searcher.to_sym,
|
200
|
+
grid.options[:searcher_label])
|
201
|
+
searcher << view.search_field_tag(grid.searcher.to_sym,
|
202
|
+
grid.param(:q),
|
203
|
+
:placeholder => grid.options[:searcher_tooltip],
|
204
|
+
:size => grid.options[:searcher_size],
|
205
|
+
:data => searcher_data,
|
206
|
+
:form => "a form that doesn't exist")
|
207
|
+
if grid.options[:search_button]
|
208
|
+
searcher << view.button_tag(grid.options[:searcher_button],
|
209
|
+
:class => 'magic-grid-search-button')
|
237
210
|
end
|
238
|
-
searcher <<
|
239
|
-
searcher
|
211
|
+
searcher << render_spinner
|
240
212
|
end
|
241
213
|
|
242
214
|
def magic_pager(collection, opts={})
|
243
|
-
if
|
215
|
+
if view.respond_to? :will_paginate
|
244
216
|
# WillPaginate
|
245
|
-
|
246
|
-
elsif
|
217
|
+
view.will_paginate collection.collection, opts
|
218
|
+
elsif view.respond_to? :paginate
|
247
219
|
#Kaminari, or something else..
|
248
|
-
|
220
|
+
view.paginate collection.collection, opts
|
249
221
|
else
|
250
222
|
("<!-- page #{collection.current_page} of #{collection.total_pages} -->" +
|
251
223
|
'<!-- INSTALL WillPaginate or Kaminari for a pager! -->').html_safe
|
252
224
|
end
|
253
225
|
end
|
254
226
|
|
255
|
-
def magic_pager_block(
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
pager = magic_pager(
|
260
|
-
|
261
|
-
|
262
|
-
)
|
227
|
+
def magic_pager_block(spinner = false)
|
228
|
+
view.content_tag('tr') do
|
229
|
+
view.content_tag('td', :class => 'full-width ui-widget-header magic-pager',
|
230
|
+
:colspan => grid.columns.count) do
|
231
|
+
pager = magic_pager(grid.magic_collection,
|
232
|
+
:param_name => grid.param_key(:page),
|
233
|
+
:params => grid.base_params)
|
263
234
|
if spinner
|
264
|
-
pager <<
|
235
|
+
pager << render_spinner
|
265
236
|
end
|
266
237
|
pager
|
267
238
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module MagicGrid
|
2
|
+
module Order
|
3
|
+
class Unordered
|
4
|
+
def self.css_class
|
5
|
+
'sort-none'
|
6
|
+
end
|
7
|
+
def self.icon_class
|
8
|
+
'ui-icon-carat-2-n-s'
|
9
|
+
end
|
10
|
+
def self.to_sql
|
11
|
+
'ASC'
|
12
|
+
end
|
13
|
+
def self.to_param
|
14
|
+
0
|
15
|
+
end
|
16
|
+
def self.sorted?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
def self.reverse
|
20
|
+
Descending
|
21
|
+
end
|
22
|
+
end
|
23
|
+
class Unsortable < Unordered; end
|
24
|
+
class Ascending < Unordered
|
25
|
+
def self.css_class
|
26
|
+
'sort-asc'
|
27
|
+
end
|
28
|
+
def self.icon_class
|
29
|
+
'ui-icon-triangle-1-n'
|
30
|
+
end
|
31
|
+
def self.sorted?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
def self.reverse
|
35
|
+
Descending
|
36
|
+
end
|
37
|
+
end
|
38
|
+
class Descending < Unordered
|
39
|
+
def self.css_class
|
40
|
+
'sort-desc'
|
41
|
+
end
|
42
|
+
def self.icon_class
|
43
|
+
'ui-icon-triangle-1-s'
|
44
|
+
end
|
45
|
+
def self.to_sql
|
46
|
+
'DESC'
|
47
|
+
end
|
48
|
+
def self.to_param
|
49
|
+
1
|
50
|
+
end
|
51
|
+
def self.sorted?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
def self.reverse
|
55
|
+
Ascending
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def self.from_param(something)
|
59
|
+
case something
|
60
|
+
when 1, "1", :desc, :DESC, "desc", "DESC", Descending
|
61
|
+
Descending
|
62
|
+
#when 0, "0", :asc, :ASC, "asc", "ASC", Ascending
|
63
|
+
# Ascending
|
64
|
+
else
|
65
|
+
Ascending
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/magic_grid/version.rb
CHANGED