active-list 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2012 Brice Texier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ = ActiveList
2
+
3
+ ActiveList is a simple list widget. It permits to create a controller
4
+ method and view helper to displays lists.
5
+
6
+ The first need was to have a simple component to build easily HTML
7
+ tables. No scaffolds, only listings.
8
+
9
+ ActiveList works only with Rails ≥ 3.1.
10
+
11
+ == Quick start
12
+
13
+ First, the JS code must be added to the pipeline in app/assets/javascripts/application.js:
14
+
15
+ //= require active-list.jquery
16
+
17
+ And get a better, you can add in app/assets/stylesheets/application.css:
18
+
19
+ *= require active-list
20
+
21
+ The simple way to use it is to write in our controller:
22
+
23
+ class PeopleController < ApplicationController
24
+ list
25
+
26
+ def index
27
+ end
28
+ end
29
+
30
+ And in the view app/views/people/index.html.erb:
31
+
32
+ <%=list-%>
33
+
34
+ == Build Status {<img src="https://secure.travis-ci.org/burisu/active-list.png"/>}[http://travis-ci.org/burisu/active-list]
35
+
36
+ == To do
37
+
38
+ * Provides styles as @mixins for a better customization
39
+ * Adds support for Prototype JS
40
+ * Adds implicit exporters JSON, XML,
41
+
42
+ == License
43
+
44
+ ActiveList is released under the MIT license:
45
+
46
+ * http://www.opensource.org/licenses/MIT
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 4.0.0
@@ -0,0 +1,37 @@
1
+ require 'fastercsv'
2
+ require 'csv'
3
+ require 'action_dispatch'
4
+ require 'rails'
5
+ require 'compass'
6
+
7
+ module ActiveList #:nodoc:
8
+
9
+
10
+ CSV = (::CSV.const_defined?(:Reader) ? ::FasterCSV : ::CSV).freeze
11
+
12
+ def self.version
13
+ v = nil
14
+ File.open(File.join(File.dirname(__FILE__), "..", "VERSION")) {|f| v = f.read.strip}
15
+ return v
16
+ end
17
+ VERSION = self.version.freeze
18
+
19
+ def self.assets_path
20
+ File.join(File.dirname(__FILE__), "assets", "images")
21
+ end
22
+
23
+ def self.compass_extension_path
24
+ File.join(File.dirname(__FILE__), "active-list", "compass")
25
+ end
26
+
27
+ end
28
+
29
+ # Compass registration
30
+ Compass::Frameworks.register('active-list', :path => ActiveList.compass_extension_path)
31
+
32
+ require "active-list/definition"
33
+ require "active-list/generator"
34
+ require "active-list/action_pack"
35
+ require "active-list/rails/engine"
36
+
37
+
@@ -0,0 +1,48 @@
1
+ require 'action_controller'
2
+ require 'action_view'
3
+
4
+ module ActiveList
5
+
6
+ module ActionController
7
+
8
+ def self.included(base) #:nodoc:
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ # Permits to define and generate methods to manage dynamic
14
+ # table ActiveList
15
+ def list(*args, &block)
16
+ name, options = nil, {}
17
+ name = args[0] if args[0].is_a? Symbol
18
+ options = args[-1] if args[-1].is_a? Hash
19
+ name ||= self.controller_name.to_sym
20
+ model = (options[:model]||name).to_s.classify.constantize
21
+ options[:controller_method_name] = "list#{'_'+name.to_s if name != self.controller_name.to_sym}"
22
+ options[:view_method_name] = "_#{self.controller_name}_list_#{name}_tag"
23
+ options[:records_variable_name] = "@#{name}"
24
+ table = ActiveList::Table.new(name, model, options)
25
+ if block_given?
26
+ yield table
27
+ else
28
+ table.load_default_columns
29
+ end
30
+
31
+ class_eval(table.send(:generate_controller_method_code), __FILE__, __LINE__)
32
+ ActionView::Base.send(:class_eval, table.send(:generate_view_method_code), __FILE__, __LINE__)
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ module ViewsHelper
40
+ def list(*args, &block)
41
+ name, options = nil, {}
42
+ name = args[0] if args[0].is_a? Symbol
43
+ options = args[-1] if args[-1].is_a? Hash
44
+ self.send("_#{options[:controller]||self.controller_name}_#{__method__}_#{name||self.controller_name}_tag", &block)
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,70 @@
1
+ # coding: utf-8
2
+ module ActiveList
3
+
4
+ class Table
5
+
6
+ # Add a new method in Table which permit to define data columns
7
+ def action(name, options={})
8
+ @columns << ActionColumn.new(self, name, options)
9
+ end
10
+
11
+ end
12
+
13
+ class ActionColumn < Column
14
+
15
+ def header_code
16
+ "'ƒ'"
17
+ end
18
+
19
+
20
+ def operation(record='record')
21
+ link_options = ""
22
+ link_options += ", :confirm=>::I18n.translate('labels.#{@options[:confirm]}')" unless @options[:confirm].nil?
23
+ link_options += ", :method=>#{@options[:method].inspect}" if @options[:method].is_a? Symbol
24
+ action = @name
25
+ format = @options[:format] ? ", :format=>'#{@options[:format]}'" : ""
26
+ if @options[:remote]
27
+ raise Exception.new("Sure to use :remote ?")
28
+ remote_options = @options.dup
29
+ remote_options[:confirm] = ::I18n.translate('labels.'+@options[:confirm].to_s) unless @options[:confirm].nil?
30
+ remote_options.delete :remote
31
+ remote_options.delete :image
32
+ remote_options = remote_options.inspect.to_s
33
+ remote_options = remote_options[1..-2]
34
+ code = "link_to_remote(#{image}"
35
+ code += ", {:url=>{:action=>:"+@name.to_s+", :id=>"+record+".id"+format+"}"
36
+ code += ", "+remote_options+"}"
37
+ code += ", {:title=>::I18n.translate('labels.#{action}')}"
38
+ code += ")"
39
+ elsif @options[:actions]
40
+ raise Exception.new("options[:actions] have to be a Hash.") unless @options[:actions].is_a? Hash
41
+ cases = []
42
+ for a in @options[:actions]
43
+ v = a[1][:action].to_s.split('_')[-1]
44
+ cases << record+"."+@name.to_s+".to_s=="+a[0].inspect+"\nlink_to(::I18n.translate('labels.#{v}')"+
45
+ ", {"+(a[1][:controller] ? ':controller=>:'+a[1][:controller].to_s+', ' : '')+":action=>'"+a[1][:action].to_s+"', :id=>"+record+".id"+format+"}"+
46
+ ", {:id=>'"+@name.to_s+"_'+"+record+".id.to_s"+link_options+", :title=>::I18n.translate('labels.#{v}')}"+
47
+ ")\n"
48
+ end
49
+
50
+ code = "if "+cases.join("elsif ")+"end"
51
+ else
52
+ url = @options[:url] ||= {}
53
+ url[:controller] ||= @options[:controller]||self.table.model.name.underscore.pluralize.to_sym
54
+ url[:action] ||= @name
55
+ url[:id] ||= "RECORD.id"
56
+ url.delete_if{|k, v| v.nil?}
57
+ url = "{"+url.collect{|k, v| ":#{k}=>"+(v.is_a?(String) ? v.gsub(/RECORD/, record) : v.inspect)}.join(", ")+format+"}"
58
+ code = "{:id=>'"+@name.to_s+"_'+"+record+".id.to_s"+link_options+", :title=>::I18n.translate('labels.#{action}')}"
59
+ code = "link_to(::I18n.translate('labels.#{action}'), "+url+", "+code+")"
60
+ end
61
+ code = "if ("+@options[:if].gsub('RECORD', record)+")\n"+code+"\n end" if @options[:if]
62
+ code
63
+ end
64
+
65
+
66
+
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,138 @@
1
+ module ActiveList
2
+
3
+ class Table
4
+
5
+ # Retrieves all columns in database
6
+ def table_columns
7
+ cols = self.model_columns.collect{|c| c.name}
8
+ @columns.select{|c| c.is_a? DataColumn and cols.include? c.name.to_s}
9
+ end
10
+
11
+ def data_columns
12
+ @columns.select{|c| c.is_a? DataColumn}
13
+ end
14
+
15
+ # Add a new method in Table which permit to define data columns
16
+ def column(name, options={})
17
+ @columns << DataColumn.new(self, name, options)
18
+ end
19
+
20
+ end
21
+
22
+ class DataColumn < Column
23
+
24
+ def header_code
25
+ if @options[:label].is_a? String
26
+ "::I18n.translate('labels.#{@options[:label].strip}')"
27
+ elsif through = @options[:through]
28
+ through = [through] unless through.is_a? Array
29
+ model, reflection = @table.model, nil
30
+ for ref in through
31
+ unless reflection.nil?
32
+ model = reflection.class_name.constantize rescue nil
33
+ end
34
+ raise Exception.new("Unknown model #{reflection.class_name}") if model.nil?
35
+ reflection = model.reflections[ref]
36
+ raise Exception.new("Unknown reflection :#{ref} (#{through.inspect}) for the ActiveRecord: "+model.name) if reflection.nil?
37
+ end
38
+ "#{model.name}.human_attribute_name('#{reflection.name}')"
39
+ else
40
+ "#{@table.model.name}.human_attribute_name('#{@name}')"
41
+ end
42
+ end
43
+
44
+
45
+ # Code for rows
46
+ def datum_code(record='rekord', child = false)
47
+ code = if child and @options[:children].is_a? Symbol
48
+ "#{record}.#{@options[:children]}"
49
+ elsif child and @options[:children].is_a? FalseClass
50
+ "nil"
51
+ elsif through = @options[:through] and !child
52
+ through = [through] unless through.is_a?(Array)
53
+ foreign_record = record
54
+ through.each { |x| foreign_record += '.'+x.to_s }
55
+ "(#{foreign_record}.#{@name} rescue nil)"
56
+ else
57
+ "#{record}.#{@name}"
58
+ end
59
+ return code
60
+ end
61
+
62
+ # Code for exportation
63
+ def exporting_datum_code(record='rekord', noview=false)
64
+ datum = self.datum_code(record)
65
+ if self.datatype == :boolean
66
+ datum = "(#{datum} ? ::I18n.translate('list.export.true_value') : ::I18n.translate('list.export.false_value'))"
67
+ elsif self.datatype == :date
68
+ datum = "(#{datum}.nil? ? '' : ::I18n.localize(#{datum}))"
69
+ elsif self.datatype == :decimal and not noview
70
+ currency = nil
71
+ if currency = self.options[:currency]
72
+ currency = currency[:body] if currency.is_a?(Hash)
73
+ currency = :currency if currency.is_a?(TrueClass)
74
+ currency = "RECORD.#{currency}" if currency.is_a?(Symbol)
75
+ raise Exception.new("Option :currency is not valid. Hash, Symbol or true/false") unless currency.is_a?(String)
76
+ end
77
+ datum = "(#{datum}.nil? ? '' : ::I18n.localize(#{datum}#{', :currency=>'+currency.gsub(/RECORD/, record) if currency}))"
78
+ elsif @name.to_s.match(/(^|\_)currency$/) and self.datatype == :string and self.limit == 3
79
+ datum = "(#{datum}.nil? ? '' : ::I18n.currency_label(#{datum}))"
80
+ elsif @name==:country and self.datatype == :string and self.limit == 2
81
+ datum = "(#{datum}.nil? ? '' : ::I18n.translate('countries.'+#{datum}))"
82
+ elsif @name==:language and self.datatype == :string and self.limit <= 8
83
+ datum = "(#{datum}.nil? ? '' : ::I18n.translate('languages.'+#{datum}))"
84
+ end
85
+ return datum
86
+ end
87
+
88
+ # Returns the data type of the column if the column is in the database
89
+ def datatype
90
+ @options[:datatype] || (@column ? @column.type : nil)
91
+ end
92
+
93
+
94
+ def numeric?
95
+ [:decimal, :integer, :float, :numeric].include? self.datatype
96
+ end
97
+
98
+ # Returns the size/length of the column if the column is in the database
99
+ def limit
100
+ @column.limit if @column
101
+ end
102
+
103
+
104
+ # Returns the class name of the used model
105
+ def class_name
106
+ klass = self.table.model
107
+ if through = @options[:through]
108
+ through = [through] unless through.is_a? Array
109
+ for ref in through
110
+ klass = klass.reflections[ref].class_name.constantize
111
+ end
112
+ end
113
+ return klass.name
114
+ end
115
+
116
+ # Defines if column is exportable
117
+ def exportable?
118
+ true
119
+ end
120
+
121
+ # Check if a column is sortable
122
+ def sortable?
123
+ #not self.action? and
124
+ not self.options[:through] and not @column.nil?
125
+ end
126
+
127
+ # Generate code in order to get the (foreign) record of the column
128
+ def record_expr(record='record')
129
+ if @options[:through]
130
+ return ([record]+[@options[:through]]).flatten.join(".")
131
+ else
132
+ return record
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveList
2
+
3
+ class Table
4
+
5
+ # Add a new method in Table which permit to define text_field columns
6
+ def text_field(name, options={})
7
+ @columns << TextFieldColumn.new(self, name, options)
8
+ end
9
+
10
+ # Add a new method in Table which permit to define check_box columns
11
+ def check_box(name, options={})
12
+ @columns << CheckBoxColumn.new(self, name, options)
13
+ end
14
+
15
+ end
16
+
17
+ class FieldColumn < Column
18
+ def header_code
19
+ "#{@table.model.name}.human_attribute_name('#{@name}')"
20
+ end
21
+ end
22
+
23
+ class TextFieldColumn < FieldColumn
24
+ end
25
+
26
+ class CheckBoxColumn < FieldColumn
27
+ end
28
+
29
+ end
@@ -0,0 +1,7 @@
1
+ @import "active-list/minimal";
2
+ @import "active-list/background";
3
+ @import "active-list/theme";
4
+
5
+ .active-list {
6
+ @include active-list-theme;
7
+ }
@@ -0,0 +1,37 @@
1
+ // Provides mixin to generate colors for cells backgrounds
2
+ //
3
+
4
+ @function merge-color($color-1, $color-2) {
5
+ $col2: rgb(red($color-2), green($color-2), blue($color-2));
6
+ $percent: 100*alpha($color-2);
7
+ @return mix($col2, $color-1, $percent);
8
+ }
9
+
10
+ @function merge-colors($color, $color-1: rgba(0, 0, 0, 0), $color-2: rgba(0, 0, 0, 0), $color-3: rgba(0, 0, 0, 0), $color-4: rgba(0, 0, 0, 0), $color-5: rgba(0, 0, 0, 0), $color-6: rgba(0, 0, 0, 0), $color-7: rgba(0, 0, 0, 0), $color-8: rgba(0, 0, 0, 0), $color-9: rgba(0, 0, 0, 0), $color-10: rgba(0, 0, 0, 0)) {
11
+ @return merge-color(merge-color(merge-color(merge-color(merge-color(merge-color(merge-color(merge-color(merge-color(merge-color($color, $color-1), $color-2), $color-3), $color-4), $color-5), $color-6), $color-7), $color-8), $color-9), $color-10);
12
+ }
13
+
14
+ $list-line-backgrounds: ("" rgba(255, 255, 255, 0)) (".odd" rgba(255, 255, 255, 0.5)) (".even" rgba(255, 255, 255, 0.5));
15
+ $list-column-backgrounds: ("" rgba(255, 255, 255, 0)) (".act" rgba(255, 134, 0, 0)) (".sor" rgba(0, 18, 132, 0.05));
16
+ $list-hover-backgrounds: ("" rgba(255, 255, 255, 0)) (":hover" rgba(209, 218, 255, 0.3));
17
+
18
+ @mixin list-colors($bgcolor: #000000, $selector: '&') {
19
+ tr {
20
+ #{$selector} {
21
+ @each $line-background in $list-line-backgrounds {
22
+ &#{nth($line-background, 1)} {
23
+ @each $hover-background in $list-hover-backgrounds {
24
+ &#{nth($hover-background, 1)} {
25
+ @each $col-background in $list-column-backgrounds {
26
+ td#{nth($col-background, 1)} {
27
+ background-color: merge-colors($bgcolor, nth($line-background, 2), nth($col-background, 2), nth($hover-background, 2));
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+
@@ -0,0 +1,89 @@
1
+ // Minimal style to get a working ActiveList
2
+
3
+ div[data-list-source] {
4
+ table.list {
5
+ thead {
6
+ tr {
7
+ th {
8
+ &[data-list-column-sort] {
9
+ cursor: pointer;
10
+ }
11
+
12
+ &.spe {
13
+ padding: 0;
14
+ .list-menu {
15
+ position: relative;
16
+ a { cursor: pointer; }
17
+ .list-menu-start {
18
+ display: block;
19
+ height: 16px;
20
+ padding: 2px;
21
+ }
22
+ &:hover {
23
+ .list-menu-start { z-index:5000; position: relative; top: 0px; }
24
+ & > ul { display: block; }
25
+ }
26
+ ul {
27
+ display: none;
28
+ position:absolute;
29
+ top: 20px;
30
+ padding: 1px;
31
+ margin:0;
32
+ html[dir="ltr"] & { right: -1px;}
33
+ html[dir="rtl"] & { left: -1px;}
34
+ li {
35
+ &[data-list-change-page-size], &[data-list-toggle-column] {
36
+ cursor: pointer;
37
+ display: block;
38
+ padding: 0.3ex;
39
+ }
40
+ list-style-type: none;
41
+ width: 25ex;
42
+ position: relative;
43
+ a {
44
+ display: block;
45
+ padding: 0.3ex;
46
+ }
47
+ ul {
48
+ display: none;
49
+ position: absolute;
50
+ top: -2px;
51
+ html[dir="ltr"] & { right: 25ex; }
52
+ html[dir="rtl"] & { left: 25ex; }
53
+ &:hover { display:block; }
54
+ }
55
+ &:hover ul { display:block; }
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ &.hidden {
62
+ display: none;
63
+ }
64
+
65
+ }
66
+ }
67
+ }
68
+
69
+ tbody {
70
+ tr {
71
+ td.hidden {
72
+ display: none;
73
+ }
74
+ }
75
+ }
76
+
77
+ }
78
+
79
+ div.extras {
80
+ .pagination {
81
+ a[data-list-move-to-page] {
82
+ cursor: pointer;
83
+ }
84
+ a[data-list-move-to-page][disabled] {
85
+ cursor: default;
86
+ }
87
+ }
88
+ }
89
+ }