magic_grid 0.12.4 → 0.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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