active-list 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +46 -0
- data/VERSION +1 -0
- data/lib/active-list.rb +37 -0
- data/lib/active-list/action_pack.rb +48 -0
- data/lib/active-list/columns/action_column.rb +70 -0
- data/lib/active-list/columns/data_column.rb +138 -0
- data/lib/active-list/columns/field_column.rb +29 -0
- data/lib/active-list/compass/stylesheets/_active-list.scss +7 -0
- data/lib/active-list/compass/stylesheets/active-list/_background.scss +37 -0
- data/lib/active-list/compass/stylesheets/active-list/_minimal.scss +89 -0
- data/lib/active-list/compass/stylesheets/active-list/_theme.scss +161 -0
- data/lib/active-list/definition.rb +103 -0
- data/lib/active-list/exporters.rb +71 -0
- data/lib/active-list/exporters/csv_exporter.rb +30 -0
- data/lib/active-list/exporters/excel_csv_exporter.rb +36 -0
- data/lib/active-list/exporters/open_document_spreadsheet_exporter.rb +81 -0
- data/lib/active-list/finder.rb +133 -0
- data/lib/active-list/generator.rb +88 -0
- data/lib/active-list/rails/engine.rb +11 -0
- data/lib/active-list/renderers.rb +28 -0
- data/lib/active-list/renderers/simple_renderer.rb +333 -0
- data/lib/assets/images/active-list.png +0 -0
- data/lib/assets/javascripts/active-list.jquery.js +128 -0
- data/lib/assets/stylesheets/active-list.css.scss +7 -0
- metadata +156 -0
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.
|
data/README.rdoc
ADDED
@@ -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
|
data/lib/active-list.rb
ADDED
@@ -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,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
|
+
}
|