volt-table 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/CODE_OF_CONDUCT.md +13 -0
  5. data/Gemfile +8 -0
  6. data/Guardfile +24 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +134 -0
  9. data/Rakefile +1 -0
  10. data/app/table/assets/css/font-awesome.min.css +4 -0
  11. data/app/table/assets/css/main.scss +66 -0
  12. data/app/table/assets/fonts/FontAwesome.otf +0 -0
  13. data/app/table/assets/fonts/fontawesome-webfont.eot +0 -0
  14. data/app/table/assets/fonts/fontawesome-webfont.svg +655 -0
  15. data/app/table/assets/fonts/fontawesome-webfont.ttf +0 -0
  16. data/app/table/assets/fonts/fontawesome-webfont.woff +0 -0
  17. data/app/table/assets/fonts/fontawesome-webfont.woff2 +0 -0
  18. data/app/table/config/dependencies.rb +1 -0
  19. data/app/table/config/initializers/boot.rb +10 -0
  20. data/app/table/config/routes.rb +1 -0
  21. data/app/table/controllers/columns_controller.rb +101 -0
  22. data/app/table/controllers/footers_controller.rb +19 -0
  23. data/app/table/controllers/headers_controller.rb +98 -0
  24. data/app/table/controllers/main_controller.rb +135 -0
  25. data/app/table/tasks/count_task.rb +15 -0
  26. data/app/table/views/columns/column_head.html +4 -0
  27. data/app/table/views/columns/filter_head.html +36 -0
  28. data/app/table/views/columns/index.html +17 -0
  29. data/app/table/views/footers/index.html +14 -0
  30. data/app/table/views/headers/index.html +62 -0
  31. data/app/table/views/headers/modal.html +34 -0
  32. data/app/table/views/main/body.html +26 -0
  33. data/app/table/views/main/footers.html +1 -0
  34. data/app/table/views/main/index.html +7 -0
  35. data/app/table/views/wrapper/index.html +10 -0
  36. data/lib/volt/table.rb +18 -0
  37. data/lib/volt/table/version.rb +5 -0
  38. data/spec/dummy/.gitignore +9 -0
  39. data/spec/dummy/README.md +4 -0
  40. data/spec/dummy/app/main/assets/css/app.css.scss +1 -0
  41. data/spec/dummy/app/main/config/dependencies.rb +13 -0
  42. data/spec/dummy/app/main/config/initializers/boot.rb +10 -0
  43. data/spec/dummy/app/main/config/routes.rb +14 -0
  44. data/spec/dummy/app/main/controllers/main_controller.rb +67 -0
  45. data/spec/dummy/app/main/lib/seed_db.rb +32 -0
  46. data/spec/dummy/app/main/models/user.rb +15 -0
  47. data/spec/dummy/app/main/views/main/about.html +16 -0
  48. data/spec/dummy/app/main/views/main/index.html +25 -0
  49. data/spec/dummy/app/main/views/main/main.html +29 -0
  50. data/spec/dummy/config.ru +4 -0
  51. data/spec/dummy/config/app.rb +137 -0
  52. data/spec/dummy/config/base/index.html +15 -0
  53. data/spec/dummy/config/initializers/boot.rb +4 -0
  54. data/spec/factories.rb +31 -0
  55. data/spec/integration/table_integration_spec.rb +171 -0
  56. data/spec/spec_helper.rb +19 -0
  57. data/volt-table.gemspec +46 -0
  58. metadata +399 -0
@@ -0,0 +1 @@
1
+ # Component dependencies
@@ -0,0 +1,10 @@
1
+ # Place any code you want to run when the component is included on the client
2
+ # or server.
3
+
4
+ # To include code only on the client use:
5
+ # if RUBY_PLATFORM == 'opal'
6
+ #
7
+ # To include code only on the server, use:
8
+ # unless RUBY_PLATFORM == 'opal'
9
+ # ^^ this will not send compile in code in the conditional to the client.
10
+ # ^^ this include code required in the conditional.
@@ -0,0 +1 @@
1
+ # Component routes
@@ -0,0 +1,101 @@
1
+ module Table
2
+ class ColumnsController < Volt::ModelController
3
+ reactive_accessor :options
4
+ reactive_accessor :values
5
+
6
+ ##############
7
+ # Actions
8
+ ##############
9
+
10
+ def index
11
+ page._column_filt = []
12
+ end
13
+
14
+ ##############
15
+ # Callbacks
16
+ ##############
17
+
18
+ ##############
19
+ # Data Sources
20
+ ##############
21
+
22
+ ##############
23
+ # Events
24
+ ##############
25
+ def sort(field)
26
+ if field
27
+ toggle_sort_direction(field)
28
+ params._sort_field = field
29
+ end
30
+ end
31
+
32
+ def toggle_sort_direction(field)
33
+ if params._sort_field == field
34
+ if params._sort_direction.to_i == 1
35
+ params._sort_direction = -1
36
+ else
37
+ params._sort_direction = 1
38
+ end
39
+ end
40
+ end
41
+
42
+ def apply_filters(item, event)
43
+ page._column_filt = page._column_filt.reject { |h| item.to_s.include? h._col }
44
+ unless options == nil || values == nil || options == '' || values == ''
45
+ page._column_filt << {col: "#{item}", option: "#{options}", value: "#{values}" }
46
+ end
47
+ `$(#{event.target}).closest('.dropdown').removeClass('open')`
48
+ nil
49
+ end
50
+
51
+ def reset(item, event)
52
+ options = ""
53
+ values = ""
54
+ if page._column_filt.any? { |x| x._col == item.to_s }
55
+ page._column_filt = page._column_filt.reject { |h| item.to_s.include? h._col }
56
+ end
57
+ `$(#{event.target}).closest('.dropdown').removeClass('open')`
58
+ nil
59
+ end
60
+
61
+ ##############
62
+ # Display
63
+ ##############
64
+ def header_sort_klass(label)
65
+ if params._sort_field == label
66
+ params._sort_direction.to_i == 1 ? 'headerSortDown' : 'headerSortUp'
67
+ end
68
+ end
69
+
70
+ def sort_direction(reverse)
71
+ if reverse
72
+ params._sort_direction.to_i == 1 ? 'down' : 'up'
73
+ else
74
+ params._sort_direction.to_i == 1 ? 'up' : 'down'
75
+ end
76
+ end
77
+
78
+ def filter_label(filter)
79
+ if page._column_filt.any? { |x| x._col == filter.to_s }
80
+ index = page._column_filt.index {|x| x._col == filter.to_s }
81
+ if page._column_filt[index]._option.to_s == '$ne'
82
+ "!= #{page._column_filt[index]._value}"
83
+ elsif page._column_filt[index]._option == '$gt'
84
+ "> #{page._column_filt[index]._value}"
85
+ elsif page._column_filt[index]._option == '$lt'
86
+ "< #{page._column_filt[index]._value}"
87
+ elsif page._column_filt[index]._option == '$lte'
88
+ "<= #{page._column_filt[index]._value}"
89
+ elsif page._column_filt[index]._option == '$gte'
90
+ ">= #{page._column_filt[index]._value}"
91
+ elsif page._column_filt[index]._option == '$eq'
92
+ "= #{page._column_filt[index]._value}"
93
+ else
94
+ ""
95
+ end
96
+ else
97
+ ""
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,19 @@
1
+ module Table
2
+ class FootersController < Volt::ModelController
3
+
4
+ def start_offset
5
+ (((params._page || 1).to_i - 1) * params._per_page.to_i)
6
+ end
7
+
8
+ def last_item
9
+ range = start_offset + params._per_page.to_i
10
+ attrs.table_size.then do |size|
11
+ if range >= size
12
+ size
13
+ else
14
+ range
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,98 @@
1
+ module Table
2
+ class HeadersController < Volt::ModelController
3
+ reactive_accessor :search_term
4
+ reactive_accessor :options
5
+ reactive_accessor :values
6
+ before_action :set_default_options
7
+
8
+ ##################
9
+ # Actions
10
+ ##################
11
+ def index
12
+ self.search_term = params._query
13
+ end
14
+
15
+ def modal
16
+ self.options = []
17
+ self.values = []
18
+ end
19
+
20
+ ######################
21
+ # callbacks
22
+ ######################
23
+ def set_default_options
24
+ params._per_page ||= 10
25
+ end
26
+
27
+ ######################
28
+ # Data Sources
29
+ ######################
30
+
31
+ def size
32
+ params._per_page.to_i
33
+ end
34
+
35
+ def set_table_size(size)
36
+ params._per_page = size
37
+ end
38
+
39
+ ####################
40
+ # Events
41
+ ####################
42
+ def toggle_popover
43
+ if `$('#popover').css('display') == 'none'`
44
+ `$('#popover').css('display', 'block')`
45
+ else
46
+ `$('#popover').css('display', 'none')`
47
+ end
48
+ end
49
+
50
+ def apply_filters
51
+ page._column_filt = []
52
+ options.each_with_index do |opt, i|
53
+ unless options[i] == nil || values[i] == nil
54
+ page._column_filt << {col: "#{search_fields[i]['field']}", option: "#{options[i]}", value: "#{values[i]}" }
55
+ end
56
+ end
57
+ `$('#sortModal').modal('hide');`
58
+ end
59
+
60
+ def search_fields
61
+ search_fields = []
62
+ page._table._columns.each do |col|
63
+ unless col._search_field == nil
64
+ search_fields << {title: col._title, field: col._field_name, search: col._search_field}
65
+ end
66
+ end
67
+ search_fields
68
+ end
69
+
70
+ def search
71
+ unless @last_hit.nil? || search_term.length == 1
72
+ if (Time.now - @last_hit) >= 0.5
73
+ params._query = search_term
74
+ else
75
+ timeout unless @timeout
76
+ end
77
+ end
78
+ @last_hit = Time.now
79
+ end
80
+
81
+ def timeout
82
+ unless @timeout
83
+ @timeout = true
84
+ `setTimeout(function(){#{timeout_search}}, 500)`
85
+ end
86
+ end
87
+
88
+ def timeout_search
89
+ @timeout = false
90
+ search
91
+ end
92
+
93
+ def clear_filters
94
+ page._column_filt = []
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,135 @@
1
+ module Table
2
+ class MainController < Volt::ModelController
3
+
4
+ def body
5
+ params._per_page ||= 10
6
+ params._sort_direction ||= 1
7
+ page._column_filt ||= []
8
+ end
9
+
10
+ def trigger_row_click(item_id)
11
+ # event = page._table._default_e_click
12
+ # event ||=
13
+ # trigger('row_click', item_id)
14
+ end
15
+
16
+ def td_click(item_id, col_index)
17
+ event = page._table._columns[col_index]._click_event
18
+ event ||= page._table._default_click_event
19
+ trigger(event, item_id, col_index)
20
+ end
21
+
22
+ def start_offset
23
+ (((params._page || 1).to_i - 1) * params._per_page.to_i)
24
+ end
25
+
26
+ def searched_data
27
+ if params._query && !params._query.empty?
28
+ query = build_query
29
+ else
30
+ query = column_filters
31
+ end
32
+ attrs.source.where(query)
33
+ end
34
+
35
+ def build_query
36
+ if params._query
37
+ ands = []
38
+ # split at commas to get my array of queries (commas are AND)
39
+ and_pieces = params._query.split(',').map(&:strip)
40
+ and_pieces.each_with_index do |piece, i|
41
+ if piece =~ /\|/
42
+ or_query = []
43
+ or_pieces = piece.split('|').map(&:strip)
44
+ or_pieces.each_with_index do |p, i|
45
+ if /:/.match(p)
46
+ or_query.push(field_match(p)).flatten!
47
+ else
48
+ or_query.push(any_match(p)).flatten!
49
+ end
50
+ end
51
+ ands << {'$or' => or_query}
52
+ elsif /:/.match(piece) # if part of the query is a specific field search
53
+ ands << field_match(piece)
54
+ else
55
+ ands << {'$or' => any_match(piece)}
56
+ end
57
+ end
58
+ ands << column_filters if column_filters
59
+ {'$and' => ands}
60
+ end
61
+ end
62
+
63
+ def field_match(query)
64
+ clean_string = query.split(':').map(&:strip)
65
+ if search_fields.has_key?(clean_string[0])
66
+ {search_fields[clean_string[0]] => {"$regex"=>clean_string[1], "$options"=>"i"}}
67
+ else
68
+ {}
69
+ end
70
+ end
71
+
72
+ def any_match(query)
73
+ match = []
74
+ search_fields.values.each do |field|
75
+ match << {field => { '$regex' => "#{query}", '$options' => 'i' }}
76
+ end
77
+ match
78
+ end
79
+
80
+ def column_filters
81
+ ands = []
82
+ page._column_filt.each do |filter|
83
+ ands << {filter._col => {"#{filter._option}" => "#{filter._value}"}}
84
+ end
85
+ ands unless ands.empty?
86
+ end
87
+
88
+ def search_fields
89
+ fields = {}
90
+ page._table._columns.each do |col|
91
+ unless col._search_field == nil
92
+ fields[col._search_field] = col._field_name
93
+ end
94
+ end
95
+ fields
96
+ end
97
+
98
+ def ordered_data
99
+ if params._sort_direction
100
+ searched_data.order(params._sort_field => params._sort_direction.to_i)
101
+ else
102
+ searched_data
103
+ end
104
+ end
105
+
106
+ def current_page
107
+ per_page = params._per_page.to_i
108
+ per_page = 10 unless per_page > 0
109
+ ordered_data.skip(start_offset).limit(per_page)
110
+ end
111
+
112
+ def total_size
113
+ # TODO: volt-mongo loads the entire collection into memory for counts as of 9-7-15
114
+ #attrs.total_size || 500 #attrs.source.count
115
+ query = {}
116
+ attrs.source.path.then do |path|
117
+ CountTask.count(path[0], query)
118
+ end
119
+ end
120
+
121
+ def table_size
122
+ # TODO: volt-mongo loads the entire collection into memory for counts as of 9-7-15
123
+ # searched_data.count
124
+ if params._query && !params._query.empty?
125
+ query = build_query
126
+ else
127
+ query = column_filters
128
+ end
129
+ attrs.source.path.then do |path|
130
+ CountTask.count(path[0], query)
131
+ end
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,15 @@
1
+ class CountTask < Volt::Task
2
+ def count(data, filter)
3
+ puts "Volt::DataStore.fetch.db[#{data.to_s}].find(#{filter})"
4
+ res = Volt::DataStore.fetch.db[data.to_s].find(filter).count
5
+ puts res
6
+ res
7
+ end
8
+
9
+ def total_count(data, filter)
10
+ puts "Volt::DataStore.fetch.db[#{data.to_s}].find(#{filter})"
11
+ res = Volt::DataStore.fetch.db[data.to_s].find(filter).count
12
+ puts res
13
+ res
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ <:Body>
2
+ <th e-click="sort(attrs.sort_name)" class="header {{ header_sort_klass(attrs.sort_name) }}">
3
+ {{ attrs.label }}
4
+ </th>
@@ -0,0 +1,36 @@
1
+ <:Body>
2
+ <th>
3
+ <div class="filter-label dropdown">
4
+ {{ if page._column_filt.any? { |x| x._col == attrs.field_name.to_s } }}
5
+ <span data-toggle="dropdown">{{ filter_label(attrs.field_name) }}</span>
6
+ {{ else }}
7
+ <span class="fa fa-filter muted" data-toggle="dropdown"></span>
8
+ {{ end }}
9
+ <ul class="dropdown-menu">
10
+ <form e-submit="apply_filters(attrs.field_name, event)">
11
+ <li style="padding: 5px;">Filter {{ attrs.label }}</li>
12
+ <li style="padding: 5px;">
13
+ <select value="{{options}}" class="form-control">
14
+ <option value=""></option>
15
+ <option value="$eq">=</option>
16
+ <option value="$ne">!=</option>
17
+ <option value="$gt">&gt;</option>
18
+ <option value="$gte">&gt;=</option>
19
+ <option value="$lt">&lt;</option>
20
+ <option value="$lte">&lt;=</option>
21
+ </select>
22
+ </li>
23
+ <li style="padding: 5px;">
24
+ <input type="text" class="form-control" value="{{ values }}" label=false />
25
+ </li>
26
+ <li role="separator" class="divider"></li>
27
+ <li style="padding: 5px;">
28
+ <button type="reset" e-click="reset(attrs.field_name, event)" class="btn btn-default btn-block">Reset</button>
29
+ </li>
30
+ <li style="padding: 5px;">
31
+ <button type="submit" class="btn btn-default btn-block">Apply</button>
32
+ </li>
33
+ </form>
34
+ </ul>
35
+ </div>
36
+ </th>
@@ -0,0 +1,17 @@
1
+ <:Body>
2
+ <thead>
3
+ <tr>
4
+ {{ page._table._columns.each do |label| }}
5
+ {{ if label._shown }}
6
+ <:column_head label="{{ label._title }}" field_name="{{ label._field_name }}" sort_name="{{ label._sort_name }}" sort_reverse="{{ label._sort_reverse }}"/>
7
+ {{ end }}
8
+ {{ end }}
9
+ </tr>
10
+ <tr>
11
+ {{ page._table._columns.each do |label| }}
12
+ {{ if label._shown }}
13
+ <:filter_head label="{{ label._title }}" field_name="{{ label._field_name }}" sort_name="{{ label._sort_name }}" sort_reverse="{{ label._sort_reverse }}"/>
14
+ {{ end }}
15
+ {{ end }}
16
+ </tr>
17
+ </thead>