netzke-basepack 0.12.9 → 1.0.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +75 -44
- data/Gemfile +4 -2
- data/LICENSE +2 -6
- data/README.md +22 -24
- data/javascripts/basepack.js +0 -8
- data/javascripts/{columns.js → grid/columns.js} +59 -71
- data/javascripts/grid/event_handlers.js +218 -0
- data/javascripts/netzkeremotecombo.js +5 -13
- data/javascripts/tristate.js +62 -0
- data/javascripts/xdatetime.js +8 -37
- data/lib/netzke-basepack.rb +3 -2
- data/lib/netzke/basepack.rb +1 -1
- data/lib/netzke/basepack/action_column.rb +6 -23
- data/lib/netzke/basepack/active_record.rb +0 -6
- data/lib/netzke/basepack/attr_config.rb +20 -11
- data/lib/netzke/basepack/attribute_config.rb +10 -0
- data/lib/netzke/basepack/attributes.rb +196 -0
- data/lib/netzke/basepack/column_config.rb +47 -39
- data/lib/netzke/basepack/columns.rb +127 -97
- data/lib/netzke/basepack/data_accessor.rb +7 -48
- data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +15 -17
- data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +111 -90
- data/lib/netzke/basepack/dynamic_tab_panel.rb +3 -5
- data/lib/netzke/basepack/dynamic_tab_panel/{javascripts → client}/dynamic_tab_panel.js +6 -5
- data/lib/netzke/basepack/field_config.rb +30 -19
- data/lib/netzke/basepack/fields.rb +22 -12
- data/lib/netzke/basepack/grid_live_search.rb +1 -4
- data/lib/netzke/basepack/grid_live_search/{javascripts → client}/grid_live_search.js +9 -7
- data/lib/netzke/basepack/item_persistence.rb +3 -3
- data/lib/netzke/basepack/item_persistence/events_plugin.rb +8 -10
- data/lib/netzke/basepack/paging_form.rb +7 -11
- data/lib/netzke/basepack/paging_form/{javascripts → client}/paging_form.js +4 -4
- data/lib/netzke/basepack/query_builder.rb +12 -10
- data/lib/netzke/basepack/query_builder/{javascripts → client}/query_builder.js +14 -12
- data/lib/netzke/basepack/record_form_window.rb +8 -8
- data/lib/netzke/basepack/search_panel.rb +4 -6
- data/lib/netzke/basepack/search_panel/{javascripts → client}/condition_field.js +13 -16
- data/lib/netzke/basepack/search_panel/{javascripts → client}/search_panel.js +7 -7
- data/lib/netzke/basepack/search_window.rb +6 -6
- data/lib/netzke/basepack/simple_app/{javascripts → client}/statusbar_ext.js +0 -0
- data/lib/netzke/basepack/version.rb +1 -1
- data/lib/netzke/form/base.rb +166 -0
- data/lib/netzke/{basepack/form/javascripts/form.js → form/base/client/base.js} +77 -38
- data/lib/netzke/form/base/client/readonly_mode.css +4 -0
- data/lib/netzke/{basepack/form/javascripts → form/base/client}/readonly_mode.js +5 -5
- data/lib/netzke/form/endpoints.rb +33 -0
- data/lib/netzke/form/services.rb +74 -0
- data/lib/netzke/grid/actions.rb +52 -0
- data/lib/netzke/grid/base.rb +289 -0
- data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/advanced_search.js +5 -1
- data/lib/netzke/{basepack/grid/javascripts/grid.js → grid/base/client/base.js} +61 -53
- data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/extensions.js +19 -13
- data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/remember_selection.js +0 -1
- data/lib/netzke/grid/client.rb +8 -0
- data/lib/netzke/grid/components.rb +55 -0
- data/lib/netzke/grid/configuration.rb +72 -0
- data/lib/netzke/grid/endpoints.rb +99 -0
- data/lib/netzke/grid/permissions.rb +18 -0
- data/lib/netzke/grid/services.rb +141 -0
- data/lib/netzke/tree/base.rb +173 -0
- data/lib/netzke/{basepack/tree/javascripts/tree.js → tree/base/client/base.js} +55 -26
- data/lib/netzke/{basepack/tree/javascripts → tree/base/client}/extensions.js +7 -7
- data/lib/netzke/tree/endpoints.rb +34 -0
- data/lib/netzke/{basepack/viewport.rb → viewport/base.rb} +3 -3
- data/lib/netzke/{basepack/window.rb → window/base.rb} +7 -8
- data/lib/netzke/window/base/client/base.js +26 -0
- data/locales/de.yml +49 -33
- data/locales/en.yml +32 -39
- data/locales/es.yml +39 -25
- data/locales/nl.yml +39 -25
- data/locales/ru.yml +38 -25
- data/locales/uk.yml +40 -26
- data/stylesheets/basepack.css +10 -0
- metadata +48 -45
- data/javascripts/mixins/grid_event_handlers.js +0 -139
- data/lib/netzke/basepack/accordion.rb +0 -45
- data/lib/netzke/basepack/active_record/relation_extensions.rb +0 -27
- data/lib/netzke/basepack/form.rb +0 -131
- data/lib/netzke/basepack/form/endpoints.rb +0 -35
- data/lib/netzke/basepack/form/services.rb +0 -74
- data/lib/netzke/basepack/form/stylesheets/readonly_mode.css +0 -14
- data/lib/netzke/basepack/grid.rb +0 -570
- data/lib/netzke/basepack/grid/endpoints.rb +0 -111
- data/lib/netzke/basepack/grid/javascripts/edit_in_form.js +0 -51
- data/lib/netzke/basepack/grid/services.rb +0 -148
- data/lib/netzke/basepack/tab_panel.rb +0 -22
- data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +0 -11
- data/lib/netzke/basepack/tree.rb +0 -269
- data/lib/netzke/basepack/window/javascripts/window.js +0 -26
- data/lib/netzke/basepack/wrap_lazy_loaded.rb +0 -29
@@ -1,41 +1,50 @@
|
|
1
1
|
module Netzke
|
2
2
|
module Basepack
|
3
|
-
#
|
3
|
+
# Base for FieldConfig and ColumnConfig
|
4
4
|
class AttrConfig < ActiveSupport::OrderedOptions
|
5
|
-
def initialize(c,
|
5
|
+
def initialize(c, model_adapter)
|
6
6
|
c = {name: c.to_s} if c.is_a?(Symbol) || c.is_a?(String)
|
7
7
|
c[:name] = c[:name].to_s
|
8
8
|
self.replace(c)
|
9
9
|
|
10
|
-
@
|
10
|
+
@model_adapter = model_adapter
|
11
11
|
end
|
12
12
|
|
13
13
|
def primary?
|
14
|
-
@
|
14
|
+
@model_adapter.primary_key_attr?(self)
|
15
15
|
end
|
16
16
|
|
17
17
|
def association?
|
18
|
-
@
|
18
|
+
@model_adapter.association_attr?(self)
|
19
19
|
end
|
20
20
|
|
21
|
-
def set_defaults
|
22
|
-
set_read_only
|
21
|
+
def set_defaults
|
22
|
+
set_read_only if read_only.nil?
|
23
23
|
end
|
24
24
|
|
25
|
-
def set_read_only
|
25
|
+
def set_read_only
|
26
26
|
self.read_only = primary? ||
|
27
27
|
!responded_to_by_model? &&
|
28
28
|
!association?
|
29
|
+
self.delete(:read_only) if read_only == false
|
29
30
|
end
|
30
31
|
|
31
32
|
private
|
32
33
|
|
34
|
+
def default_label
|
35
|
+
if association?
|
36
|
+
@model_adapter.human_attribute_name(name.split("__").first)
|
37
|
+
else
|
38
|
+
@model_adapter.human_attribute_name(name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
33
42
|
def responded_to_by_model?
|
34
43
|
# if no model class is provided, assume the attribute is being responded to
|
35
|
-
@
|
44
|
+
@model_adapter.model.nil? ||
|
36
45
|
!setter.nil? ||
|
37
|
-
@
|
38
|
-
@
|
46
|
+
@model_adapter.model.instance_methods.include?(:"#{name}=") ||
|
47
|
+
@model_adapter.model.attribute_names.include?(name)
|
39
48
|
end
|
40
49
|
end
|
41
50
|
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
module Netzke
|
2
|
+
module Basepack
|
3
|
+
# This module is encluded in +Grid+, +Form+, and +Tree+. It allows configuring specific model attributes.
|
4
|
+
#
|
5
|
+
# To override default configuration for a model attribute (e.g. to change its label or read-only property) use the
|
6
|
+
# +attribute_overrides+ configuration option for the component, or the +attribute+ DSL method. This will have effect
|
7
|
+
# on both columns and form fields.
|
8
|
+
#
|
9
|
+
# For example, to make the address attribute read-only:
|
10
|
+
#
|
11
|
+
# class Users < Netzke::Grid::Base
|
12
|
+
# def configure(c)
|
13
|
+
# super
|
14
|
+
# c.model = User
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# attribute :address do |c|
|
18
|
+
# c.read_only = true
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Using the +attribute_overrides+ config option may be handy when building composite components. E.g. in a tab panel
|
23
|
+
# nesting multiple grids, you may want to override specific attributes for a specific grid:
|
24
|
+
#
|
25
|
+
# class ManagmentPanel < Netzke::Base
|
26
|
+
# client_class do |c|
|
27
|
+
# c.extend = "Ext.tab.Panel"
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# def configure(c)
|
31
|
+
# super
|
32
|
+
# c.items = [:users, :roles]
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# component :users do |c|
|
36
|
+
# c.attribute_overrides = {
|
37
|
+
# birth_date: {
|
38
|
+
# excluded: true # exclude this column from the grid and forms
|
39
|
+
# }
|
40
|
+
# }
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# component :roles
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# The following attribute config options are available:
|
47
|
+
#
|
48
|
+
# [read_only]
|
49
|
+
#
|
50
|
+
# A boolean that defines whether the attribute should be editable via grid/form.
|
51
|
+
#
|
52
|
+
# [getter]
|
53
|
+
#
|
54
|
+
# A lambda that receives a record as a parameter, and is expected to return the value used in the grid cell or
|
55
|
+
# form field, e.g.:
|
56
|
+
#
|
57
|
+
# getter: lambda {|r| [r.first_name, r.last_name].join }
|
58
|
+
#
|
59
|
+
# In case of relation used in relation, passes the last record to lambda, e.g.:
|
60
|
+
#
|
61
|
+
# name: author__books__first__name, getter: lambda {|r| r.title }
|
62
|
+
# r #=> author.books.first
|
63
|
+
#
|
64
|
+
# [setter]
|
65
|
+
#
|
66
|
+
# A lambda that receives a record as first parameter, and the value passed from the cell/field as the second parameter,
|
67
|
+
# and is expected to modify the record accordingly, e.g.:
|
68
|
+
#
|
69
|
+
# setter: lambda {|r,v| r.first_name, r.last_name = v.split(" ") }
|
70
|
+
#
|
71
|
+
# [scope]
|
72
|
+
#
|
73
|
+
# The scope for association attribute. Same syntax applies as for scoping out records for the grid.
|
74
|
+
#
|
75
|
+
# [filter_association_with]
|
76
|
+
#
|
77
|
+
# A Proc object that receives the relation and the value to filter by. Example:
|
78
|
+
#
|
79
|
+
# column :author__name do |c|
|
80
|
+
# c.filter_association_with = lambda {|rel, value| rel.where("first_name like ? or last_name like ?", "%#{value}%", "%#{value}%" ) }
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# [format]
|
84
|
+
#
|
85
|
+
# The format to display data in case of date and datetime attributes, e.g. 'Y-m-d g:i:s'.
|
86
|
+
#
|
87
|
+
# [excluded]
|
88
|
+
#
|
89
|
+
# When true, this attribute will not be used
|
90
|
+
#
|
91
|
+
# [meta]
|
92
|
+
#
|
93
|
+
# When set to +true+, the data for this column will be available in the grid store, but the actual column won't be
|
94
|
+
# created (as if +excluded+ were set to +true+).
|
95
|
+
#
|
96
|
+
# [type]
|
97
|
+
#
|
98
|
+
# When adding a virtual attribute to the grid, it may be useful to specify its type, so the column editor (and the
|
99
|
+
# form field) are configured properly.
|
100
|
+
#
|
101
|
+
# [column_config]
|
102
|
+
#
|
103
|
+
# Configuration specific for the corresponding grid column. For example:
|
104
|
+
#
|
105
|
+
# attribute :address do |c|
|
106
|
+
# c.column_config = { width: 200 }
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# [field_config]
|
110
|
+
#
|
111
|
+
# Configuration for the corresponding form field. For example:
|
112
|
+
#
|
113
|
+
# attribute :address do |c|
|
114
|
+
# c.field_config = { xtype: :displayfield }
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# [editor_config]
|
118
|
+
#
|
119
|
+
# Additional configuration for column editor and form field (which are usually represented by the same Ext field
|
120
|
+
# component). Any common Ext config option like `min_chars` and `format` are accepted. Besides, Netzke extends it
|
121
|
+
# with some extras:
|
122
|
+
#
|
123
|
+
# [blank_line]
|
124
|
+
#
|
125
|
+
# The blank line for one-to-many association columns, defaults to "---". Set to false to exclude completely.
|
126
|
+
#
|
127
|
+
# [date_format]
|
128
|
+
#
|
129
|
+
# In case of datetime type, the format date must be entered in the editor.
|
130
|
+
#
|
131
|
+
# [time_format]
|
132
|
+
#
|
133
|
+
# In case of datetime type, the format time must be entered in the editor.
|
134
|
+
module Attributes
|
135
|
+
extend ActiveSupport::Concern
|
136
|
+
|
137
|
+
ATTRIBUTE_METHOD_NAME = "%s_attribute"
|
138
|
+
|
139
|
+
included do
|
140
|
+
class_attribute :declared_attribute_names
|
141
|
+
self.declared_attribute_names = []
|
142
|
+
end
|
143
|
+
|
144
|
+
module ClassMethods
|
145
|
+
# Adds/overrides an attribute config, e.g.:
|
146
|
+
#
|
147
|
+
# attribute :price do |c|
|
148
|
+
# c.read_only = true
|
149
|
+
# end
|
150
|
+
def attribute(name, &block)
|
151
|
+
method_name = ATTRIBUTE_METHOD_NAME % name
|
152
|
+
define_method(method_name, &block)
|
153
|
+
|
154
|
+
# we *must* use a writer here
|
155
|
+
self.declared_attribute_names = declared_attribute_names + [name]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def attribute_overrides
|
160
|
+
return @attribute_overrides if @attribute_overrides
|
161
|
+
|
162
|
+
declared = self.class.declared_attribute_names.reduce({}) do |res, name|
|
163
|
+
c = AttributeConfig.new(name)
|
164
|
+
augment_attribute_config(c)
|
165
|
+
res.merge!(name => c)
|
166
|
+
end
|
167
|
+
|
168
|
+
@attribute_overrides = (config.attribute_overrides || {}).deep_merge(declared)
|
169
|
+
end
|
170
|
+
|
171
|
+
def augment_attribute_config(c)
|
172
|
+
send(ATTRIBUTE_METHOD_NAME % c.name, c)
|
173
|
+
end
|
174
|
+
|
175
|
+
def association_attr?(attr)
|
176
|
+
!!attr[:name].to_s.index("__")
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns a hash of association attribute default values. Used when creating new records with association attributes that have a default value.
|
180
|
+
def association_value_defaults(cols)
|
181
|
+
@_default_association_values ||= {}.tap do |values|
|
182
|
+
cols.each do |c|
|
183
|
+
next unless association_attr?(c) && c[:default_value]
|
184
|
+
|
185
|
+
assoc_name, assoc_method = c[:name].split '__'
|
186
|
+
assoc_class = model_adapter.class_for(assoc_name)
|
187
|
+
assoc_data_adapter = Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(assoc_class).new(assoc_class)
|
188
|
+
::Rails.logger.debug "\n!!! assoc_data_adapter.class: #{assoc_data_adapter.class.inspect}\n"
|
189
|
+
assoc_instance = assoc_data_adapter.find_record c[:default_value]
|
190
|
+
values[c[:name]] = assoc_instance.send(assoc_method)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Netzke
|
2
2
|
module Basepack
|
3
|
-
# Takes care of automatic column configuration in {
|
3
|
+
# Takes care of automatic column configuration in {Grid::Base}
|
4
4
|
class ColumnConfig < AttrConfig
|
5
5
|
# These config options can be ommitted from config, as they are assumed by default at the JS side
|
6
6
|
COMMON_DEFAULTS = {
|
@@ -12,20 +12,30 @@ module Netzke
|
|
12
12
|
assoc: false
|
13
13
|
}
|
14
14
|
|
15
|
-
def
|
15
|
+
def merge_attribute(attr)
|
16
|
+
self.merge!(attr)
|
17
|
+
|
18
|
+
self.text = delete(:label) if self.has_key?(:label)
|
19
|
+
|
20
|
+
self.merge!(delete(:column_config)) if self.has_key?(:column_config)
|
21
|
+
|
22
|
+
self.delete(:field_config) if self.has_key?(:field_config)
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_defaults
|
16
26
|
super
|
17
27
|
|
18
|
-
self.
|
28
|
+
self.type ||= @model_adapter.attr_type(name)
|
19
29
|
|
20
|
-
set_xtype
|
30
|
+
set_xtype if xtype.nil?
|
21
31
|
|
22
|
-
self.virtual = @
|
32
|
+
self.virtual = @model_adapter.virtual_attribute?(self)
|
23
33
|
|
24
|
-
self.text ||= label ||
|
34
|
+
self.text ||= label || default_label
|
25
35
|
|
26
|
-
set_editor
|
36
|
+
set_editor
|
27
37
|
|
28
|
-
set_width
|
38
|
+
set_width if width.nil?
|
29
39
|
|
30
40
|
self.hidden = primary? if hidden.nil?
|
31
41
|
|
@@ -35,81 +45,79 @@ module Netzke
|
|
35
45
|
|
36
46
|
self.assoc = association? # used at the JS side
|
37
47
|
|
38
|
-
remove_defaults
|
48
|
+
remove_defaults # options that are implied by Ext JS by default, thus don't have to be passed
|
39
49
|
end
|
40
50
|
|
41
|
-
def set_xtype
|
51
|
+
def set_xtype
|
42
52
|
# if user set those manually, we don't mess with column xtype
|
43
53
|
return if renderer || editor
|
44
|
-
xtype =
|
54
|
+
xtype = xtype_for_type(type)
|
45
55
|
self.xtype = xtype unless xtype.nil?
|
46
56
|
end
|
47
57
|
|
48
|
-
def
|
58
|
+
def xtype_for_type(type)
|
49
59
|
{ :boolean => :checkcolumn,
|
50
60
|
:date => :datecolumn
|
51
61
|
}[type]
|
52
62
|
end
|
53
63
|
|
54
|
-
def set_editor
|
64
|
+
def set_editor
|
55
65
|
# if shouldn't be editable, don't set any default editor
|
56
66
|
return if read_only
|
57
67
|
|
58
68
|
passed_editor = editor
|
59
69
|
|
60
70
|
if association?
|
61
|
-
set_default_association_editor
|
71
|
+
set_default_association_editor
|
62
72
|
else
|
63
|
-
self.editor =
|
73
|
+
self.editor = editor_for_type(type)
|
64
74
|
end
|
65
75
|
|
66
76
|
self.editor.merge!(passed_editor) if passed_editor
|
67
77
|
end
|
68
78
|
|
69
79
|
# Detects an association column and sets up the proper editor.
|
70
|
-
def set_default_association_editor
|
80
|
+
def set_default_association_editor
|
71
81
|
assoc, assoc_method = name.split('__')
|
72
82
|
|
73
|
-
assoc_method_type = @
|
83
|
+
assoc_method_type = @model_adapter.get_assoc_property_type assoc, assoc_method
|
74
84
|
|
75
85
|
# if association column is boolean, display a checkbox (or alike), otherwise - a combobox (or alike)
|
76
86
|
if nested_attribute
|
77
|
-
self.editor =
|
87
|
+
self.editor = editor_for_type(assoc_method_type)
|
78
88
|
else
|
79
|
-
self.editor = assoc_method_type == :boolean ?
|
89
|
+
self.editor = assoc_method_type == :boolean ? editor_for_type(:boolean) : {xtype: :netzkeremotecombo}
|
80
90
|
end
|
81
91
|
end
|
82
92
|
|
83
93
|
# Column editor config for attribute type.
|
84
|
-
def
|
85
|
-
{xtype:
|
94
|
+
def editor_for_type(type)
|
95
|
+
{xtype: type_to_editor_xtype(type)}
|
96
|
+
end
|
97
|
+
|
98
|
+
def type_to_editor_xtype(type)
|
99
|
+
type_to_editor_xtype_map[type] || :textfield
|
86
100
|
end
|
87
101
|
|
88
102
|
# Hash that maps a column type to the editor xtype. Override if you want different editors.
|
89
|
-
def
|
90
|
-
{
|
91
|
-
:
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
96
|
-
|
103
|
+
def type_to_editor_xtype_map
|
104
|
+
{
|
105
|
+
integer: :numberfield,
|
106
|
+
boolean: :checkbox,
|
107
|
+
date: :datefield,
|
108
|
+
datetime: :xdatetime,
|
109
|
+
text: :textarea,
|
110
|
+
string: :textfield
|
111
|
+
}
|
97
112
|
end
|
98
113
|
|
99
|
-
def set_width
|
100
|
-
self.width =
|
101
|
-
when :boolean
|
102
|
-
50
|
103
|
-
when :datetime
|
104
|
-
150
|
105
|
-
else
|
106
|
-
120
|
107
|
-
end
|
114
|
+
def set_width
|
115
|
+
self.width = 150 if type == :datetime
|
108
116
|
end
|
109
117
|
|
110
118
|
##
|
111
119
|
# @return self
|
112
|
-
def remove_defaults
|
120
|
+
def remove_defaults
|
113
121
|
COMMON_DEFAULTS.each_pair {|k,v| self.delete(k) if v == self[k]}
|
114
122
|
self
|
115
123
|
end
|
@@ -1,7 +1,48 @@
|
|
1
1
|
module Netzke
|
2
2
|
module Basepack
|
3
3
|
# Takes care of grid column configuration, as well as the grid's default form fields
|
4
|
-
#
|
4
|
+
# +Grid::Base+ extends common Ext JS column options with the following ones:
|
5
|
+
#
|
6
|
+
# [sorting_scope]
|
7
|
+
#
|
8
|
+
# A Proc object used for sorting by the column. This can be useful for sorting by a virtual column. The Proc
|
9
|
+
# object will get the relation as the first parameter, and the sorting direction as the second. Example:
|
10
|
+
#
|
11
|
+
# columns => [{ name: "complete_user_name", sorting_scope: lambda {|rel, dir| order("users.first_name #{dir.to_s}, users.last_name #{dir.to_s}") }, ...]
|
12
|
+
#
|
13
|
+
# [filter_with]
|
14
|
+
#
|
15
|
+
# A Proc object that receives the relation, the value to filter by and the operator. This allows for more flexible
|
16
|
+
# handling of basic filters and enables filtering of virtual columns. Example:
|
17
|
+
#
|
18
|
+
# columns => [{ name: "complete_user_name", filter_with: lambda{|rel, value, op| rel.where("first_name like ? or last_name like ?", "%#{value}%", "%#{value}%" ) } }, ...]
|
19
|
+
#
|
20
|
+
# [filterable]
|
21
|
+
#
|
22
|
+
# Set to false to disable filtering on this column
|
23
|
+
#
|
24
|
+
# [editor]
|
25
|
+
#
|
26
|
+
# A hash that will override the automatic editor configuration. For example, for one-to-many association column
|
27
|
+
# you may set it to +{min_chars: 1}+, which will be passed to the combobox and make it query its remote data after
|
28
|
+
# entering 1 character (instead of default 4).
|
29
|
+
#
|
30
|
+
# === Configuring default filters on grid columns
|
31
|
+
#
|
32
|
+
# Default Filters can either be configured on the grid itself
|
33
|
+
#
|
34
|
+
# def configure(c)
|
35
|
+
# super
|
36
|
+
# c.default_filters = [{name: "Mark"}, {age: {gt: 10}}]
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# or as a component configuration
|
40
|
+
#
|
41
|
+
# component :tasks |c|
|
42
|
+
# c.klass = TaskGrid
|
43
|
+
# c.default_filters = [{due_date: {before: Time.now}}]
|
44
|
+
# end
|
45
|
+
#
|
5
46
|
module Columns
|
6
47
|
extend ActiveSupport::Concern
|
7
48
|
|
@@ -13,11 +54,6 @@ module Netzke
|
|
13
54
|
end
|
14
55
|
|
15
56
|
module ClassMethods
|
16
|
-
def inherited(klass)
|
17
|
-
klass.class_attribute :declared_columns
|
18
|
-
klass.declared_columns = []
|
19
|
-
end
|
20
|
-
|
21
57
|
# Adds/overrides a column config, e.g.:
|
22
58
|
#
|
23
59
|
# column :title do |c|
|
@@ -28,53 +64,47 @@ module Netzke
|
|
28
64
|
def column(name, &block)
|
29
65
|
method_name = COLUMN_METHOD_NAME % name
|
30
66
|
define_method(method_name, &block)
|
31
|
-
self.declared_columns
|
67
|
+
self.declared_columns = [*declared_columns, name]
|
32
68
|
end
|
33
69
|
end
|
34
70
|
|
35
71
|
# Returns the list of (non-normalized) columns to be used. By default returns the list of model column names and declared columns.
|
36
72
|
# Can be overridden.
|
37
73
|
def columns
|
38
|
-
config.columns ||
|
39
|
-
end
|
40
|
-
|
41
|
-
# Columns from model + columns declared with DSL
|
42
|
-
def default_columns
|
43
|
-
(data_adapter.model_attributes + self.class.declared_columns).uniq
|
74
|
+
config.columns || model_adapter.model_attributes
|
44
75
|
end
|
45
76
|
|
46
77
|
# An array of complete columns configs ready to be passed to the JS side.
|
47
|
-
|
48
|
-
|
49
|
-
# * :with_meta - when true, include the meta column
|
50
|
-
def final_columns(options = {})
|
51
|
-
# memoize
|
52
|
-
@_final_columns ||= {}
|
53
|
-
@_final_columns[options] ||= [].tap do |cols|
|
78
|
+
def final_columns
|
79
|
+
@final_columns ||= [].tap do |cols|
|
54
80
|
has_primary_column = false
|
55
81
|
|
56
82
|
columns.each do |c|
|
57
|
-
c =
|
58
|
-
|
59
|
-
# merge with column declaration
|
60
|
-
send(:"#{c.name}_column", c) if respond_to?(:"#{c.name}_column")
|
83
|
+
c = build_column_config(c)
|
84
|
+
next if c.excluded
|
61
85
|
|
62
|
-
# detect primary key column
|
63
86
|
has_primary_column ||= c.primary?
|
64
|
-
|
65
|
-
if (!c.excluded || options[:with_excluded]) && (!c.meta || options[:with_meta])
|
66
|
-
# set the defaults as lowest priority
|
67
|
-
augment_column_config(c)
|
68
|
-
|
69
|
-
cols << c # if options[:with_excluded] || !c.excluded
|
70
|
-
end
|
87
|
+
cols << c
|
71
88
|
end
|
72
89
|
|
73
|
-
insert_primary_column(cols)
|
74
|
-
|
90
|
+
insert_primary_column(cols) unless has_primary_column
|
91
|
+
append_association_values_column(cols)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_column_config(c)
|
96
|
+
Netzke::Basepack::ColumnConfig.new(c, model_adapter).tap do |c|
|
97
|
+
attribute_config = attribute_overrides[c.name.to_sym]
|
98
|
+
c.merge_attribute(attribute_config) if attribute_config
|
99
|
+
augment_column_config(c)
|
75
100
|
end
|
76
101
|
end
|
77
102
|
|
103
|
+
# Array of complete config hashes for non-meta columns
|
104
|
+
def non_meta_columns
|
105
|
+
@non_meta_columns ||= final_columns.reject{|c| c[:meta]}
|
106
|
+
end
|
107
|
+
|
78
108
|
# Columns as a hash, for easier access to a specific column
|
79
109
|
def final_columns_hash
|
80
110
|
@_final_columns_hash ||= final_columns.inject({}){|r,c| r.merge(c[:name].to_sym => c)}
|
@@ -82,89 +112,107 @@ module Netzke
|
|
82
112
|
|
83
113
|
# Columns that have to be used by the JS side of the grid
|
84
114
|
def js_columns
|
85
|
-
final_columns
|
115
|
+
final_columns.map do |c|
|
86
116
|
# we are removing the editor on this last step, so that the editor config is still being passed from the
|
87
117
|
# column config to the form editor; refactor!
|
88
|
-
c.delete(:editor)
|
118
|
+
c.delete(:editor) unless config.edit_inline
|
89
119
|
c
|
90
120
|
end
|
91
121
|
end
|
92
122
|
|
93
|
-
def
|
94
|
-
cols <<
|
95
|
-
c.
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
)
|
102
|
-
c[:default_value] = meta_default_data if meta_default_data.present?
|
123
|
+
def append_association_values_column(cols)
|
124
|
+
cols << Netzke::Basepack::AttrConfig.new("association_values", model_adapter).tap do |c|
|
125
|
+
c.meta = true
|
126
|
+
c.getter = lambda do |r|
|
127
|
+
model_adapter.assoc_values(r, final_columns_hash).netzke_literalize_keys
|
128
|
+
end
|
129
|
+
defaults = association_value_defaults(cols).netzke_literalize_keys
|
130
|
+
c.default_value = defaults if defaults.present?
|
103
131
|
end
|
104
132
|
end
|
105
133
|
|
106
134
|
def insert_primary_column(cols)
|
107
|
-
primary_key =
|
108
|
-
raise "Model #{
|
109
|
-
c = Netzke::Basepack::ColumnConfig.new(
|
110
|
-
|
135
|
+
primary_key = model_adapter.primary_key
|
136
|
+
raise "Model #{model_adapter.model.name} does not have a primary column" if primary_key.blank?
|
137
|
+
c = Netzke::Basepack::ColumnConfig.new(model_adapter.primary_key, model_adapter)
|
138
|
+
c.merge_attribute(attribute_overrides[c.name.to_sym]) if attribute_overrides.has_key?(c.name.to_sym)
|
111
139
|
augment_column_config(c)
|
112
140
|
cols.insert(0, c)
|
113
141
|
end
|
114
142
|
|
115
|
-
#
|
116
|
-
def
|
117
|
-
|
143
|
+
# Default form items (non-normalized) that will be displayed in the Add/Edit forms
|
144
|
+
def default_form_items
|
145
|
+
non_meta_columns.map{|c| c.name.to_sym}
|
118
146
|
end
|
119
147
|
|
120
|
-
#
|
121
|
-
def
|
122
|
-
|
148
|
+
# ATM the same attributes are used as in forms
|
149
|
+
def attributes_for_search
|
150
|
+
non_meta_columns.map do |c|
|
151
|
+
{name: c.name, text: c.text, type: c.type}.tap do |a|
|
152
|
+
if c[:assoc]
|
153
|
+
a[:text].sub!(" ", " ")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
123
157
|
end
|
124
158
|
|
125
|
-
|
159
|
+
# Form items that will be used by the Add/Edit forms. May be useful overriding it.
|
160
|
+
def form_items
|
161
|
+
config.form_items || default_form_items
|
162
|
+
end
|
126
163
|
|
127
|
-
|
128
|
-
|
129
|
-
def
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
164
|
+
private
|
165
|
+
|
166
|
+
def populate_columns_with_filters(c)
|
167
|
+
c.default_filters.each do |filter|
|
168
|
+
c.columns[:items].each do |column|
|
169
|
+
if column[:name].to_sym == filter[:column].to_sym
|
170
|
+
extend_column_with_filter(column, filter)
|
134
171
|
end
|
135
|
-
f[:field_label] = c.text || c.header
|
136
172
|
end
|
137
173
|
end
|
174
|
+
c.delete(:default_filters)
|
138
175
|
end
|
139
176
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
a[:text].sub!(" ", " ")
|
146
|
-
end
|
177
|
+
def extend_column_with_filter(column, filter)
|
178
|
+
if filter[:value].is_a?(Hash)
|
179
|
+
val = {}
|
180
|
+
filter[:value].each do |k,v|
|
181
|
+
val[k] = (v.is_a?(Time) || v.is_a?(Date) || v.is_a?(ActiveSupport::TimeWithZone)) ? Netzke::Core::JsonLiteral.new("new Date('#{v.strftime("%m/%d/%Y")}')") : v
|
147
182
|
end
|
183
|
+
else
|
184
|
+
val = filter[:value]
|
185
|
+
end
|
186
|
+
new_filter = {value: val, active: true}
|
187
|
+
if column[:filter]
|
188
|
+
column[:filter].merge! new_filter
|
189
|
+
else
|
190
|
+
column[:filter] = new_filter
|
148
191
|
end
|
149
192
|
end
|
150
193
|
|
151
|
-
|
194
|
+
# Extends passed column config with DSL declaration for this column
|
195
|
+
def apply_column_dsl(c)
|
196
|
+
method_name = COLUMN_METHOD_NAME % c.name
|
197
|
+
send(method_name, c) if respond_to?(method_name)
|
198
|
+
end
|
152
199
|
|
153
200
|
# Based on initial column config, e.g.:
|
154
201
|
#
|
155
|
-
# {:name=>"author__name", :
|
202
|
+
# {:name=>"author__name", :type=>:string}
|
156
203
|
#
|
157
204
|
# augment it with additional configuration params, e.g.:
|
158
205
|
#
|
159
|
-
# {:name=>"author__name", :
|
206
|
+
# {:name=>"author__name", :type=>:string, :editor=>{:xtype=>:netzkeremotecombo}, :assoc=>true, :virtual=>true, :header=>"Author", :sortable=>false, :filterable=>false}
|
160
207
|
#
|
161
208
|
# It may be handy to override it.
|
162
209
|
def augment_column_config(c)
|
163
|
-
c
|
210
|
+
apply_column_dsl(c)
|
211
|
+
c.set_defaults
|
164
212
|
end
|
165
213
|
|
166
214
|
def initial_columns_order
|
167
|
-
|
215
|
+
non_meta_columns.map do |c|
|
168
216
|
# copy the values that are not null
|
169
217
|
{name: c[:name]}.tap do |r|
|
170
218
|
r[:width] = c[:width] if c[:width]
|
@@ -188,32 +236,14 @@ module Netzke
|
|
188
236
|
init_column_names != stored_column_names
|
189
237
|
end
|
190
238
|
|
191
|
-
# Selects those columns that make sense to be shown in forms
|
192
|
-
def columns_taken_over_to_forms
|
193
|
-
final_columns.select {|c| include_in_forms?(c) && !exclude_from_forms?(c)}
|
194
|
-
end
|
195
|
-
|
196
|
-
def include_in_forms?(c)
|
197
|
-
!c.getter.nil? ||
|
198
|
-
!c.setter.nil? ||
|
199
|
-
data_adapter.attribute_names.include?(c[:name]) ||
|
200
|
-
data_class.instance_methods.include?(c[:name].to_sym) ||
|
201
|
-
data_class.instance_methods.include?(:"#{c[:name]}=") ||
|
202
|
-
association_attr?(c)
|
203
|
-
end
|
204
|
-
|
205
|
-
def exclude_from_forms?(c)
|
206
|
-
c[:type] == :action
|
207
|
-
end
|
208
|
-
|
209
239
|
def columns_default_values
|
210
|
-
|
240
|
+
non_meta_columns.inject({}) do |r,c|
|
211
241
|
assoc_name, assoc_method = c[:name].split '__'
|
212
242
|
if c[:default_value].nil?
|
213
243
|
r
|
214
244
|
else
|
215
245
|
if assoc_method
|
216
|
-
r.merge(
|
246
|
+
r.merge(model_adapter.foreign_key_for(assoc_name) || model_adapter.foreign_key_for(assoc_name) => c[:default_value])
|
217
247
|
else
|
218
248
|
r.merge(c[:name] => c[:default_value])
|
219
249
|
end
|