netzke-basepack 0.4.2 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +1 -0
- data/.gitignore +6 -0
- data/{CHANGELOG → CHANGELOG.rdoc} +26 -0
- data/README.rdoc +11 -11
- data/Rakefile +37 -11
- data/TODO.rdoc +8 -0
- data/VERSION +1 -0
- data/javascripts/basepack.js +71 -28
- data/lib/app/models/netzke_auto_column.rb +56 -0
- data/lib/netzke-basepack.rb +5 -3
- data/lib/netzke/accordion_panel.rb +69 -67
- data/lib/netzke/active_record/basepack.rb +104 -0
- data/lib/netzke/active_record/data_accessor.rb +33 -0
- data/lib/netzke/basic_app.rb +233 -124
- data/lib/netzke/border_layout_panel.rb +97 -98
- data/lib/netzke/configuration_panel.rb +24 -0
- data/lib/netzke/data_accessor.rb +71 -0
- data/lib/netzke/ext.rb +6 -0
- data/lib/netzke/field_model.rb +1 -1
- data/lib/netzke/fields_configurator.rb +62 -37
- data/lib/netzke/form_panel.rb +161 -51
- data/lib/netzke/form_panel_api.rb +74 -0
- data/lib/netzke/form_panel_js.rb +129 -0
- data/lib/netzke/grid_panel.rb +385 -80
- data/lib/netzke/grid_panel_api.rb +352 -0
- data/lib/netzke/grid_panel_extras/javascripts/rows-dd.js +280 -0
- data/lib/netzke/grid_panel_js.rb +721 -0
- data/lib/netzke/masquerade_selector.rb +53 -0
- data/lib/netzke/panel.rb +9 -0
- data/lib/netzke/plugins/configuration_tool.rb +121 -0
- data/lib/netzke/property_editor.rb +95 -7
- data/lib/netzke/property_editor_extras/helper_model.rb +55 -34
- data/lib/netzke/search_panel.rb +62 -0
- data/lib/netzke/tab_panel.rb +97 -37
- data/lib/netzke/table_editor.rb +49 -44
- data/lib/netzke/tree_panel.rb +15 -16
- data/lib/netzke/wrapper.rb +29 -5
- data/netzke-basepack.gemspec +151 -19
- data/stylesheets/basepack.css +5 -0
- data/test/app_root/app/models/book.rb +1 -1
- data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +1 -1
- data/test/unit/accordion_panel_test.rb +1 -2
- data/test/unit/active_record_basepack_test.rb +54 -0
- data/test/unit/grid_panel_test.rb +8 -12
- data/test/unit/helper_model_test.rb +30 -0
- metadata +69 -78
- data/Manifest +0 -86
- data/TODO +0 -3
- data/lib/app/models/netzke_hash_record.rb +0 -180
- data/lib/app/models/netzke_layout_item.rb +0 -11
- data/lib/netzke/ar_ext.rb +0 -269
- data/lib/netzke/configuration_tool.rb +0 -80
- data/lib/netzke/container.rb +0 -77
- data/lib/netzke/db_fields.rb +0 -44
- data/lib/netzke/fields_configurator_old.rb +0 -62
- data/lib/netzke/form_panel_extras/interface.rb +0 -56
- data/lib/netzke/form_panel_extras/js_builder.rb +0 -134
- data/lib/netzke/grid_panel_extras/interface.rb +0 -206
- data/lib/netzke/grid_panel_extras/js_builder.rb +0 -352
- data/test/unit/ar_ext_test.rb +0 -53
- data/test/unit/netzke_hash_record_test.rb +0 -52
- data/test/unit/netzke_layout_item_test.rb +0 -28
data/.autotest
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'autotest/redgreen'
|
data/.gitignore
ADDED
@@ -1,3 +1,29 @@
|
|
1
|
+
v0.5.1 - 2009-09-11
|
2
|
+
Fix: crash when FormPanel has no data_class_name specified.
|
3
|
+
New: DataAccessor widgets (Form/GridPanel) now let the underlying model know which widget (i.e. which instance) accesses its data. Can be useful in virtual attributes for generating widget-specific HTML.
|
4
|
+
Fix: DataAccessor widgets (Form/GridPanel) now don't crash when calculating default columns/fields for the underlying model that has polymorphic columns.
|
5
|
+
Fix: TabPanel was sending redundant "tabchange" event to server when initially instantiated.
|
6
|
+
Fix: column filters were making GridPanel crash when the column editor was set to "textarea".
|
7
|
+
Fix: dongling comma and "delete" object properties caused problems in IE and Safari.
|
8
|
+
Fix: a stand-alone TabPanel would not render the active item.
|
9
|
+
New: BasicApp: masquerading as "World". In this mode all the "touched" persistent preferences will be overwritten for all roles and users.
|
10
|
+
Impr: configuration panel's header now shows the underlying model's name for convenience.
|
11
|
+
Fix: MasqueradeSelector widget added.
|
12
|
+
|
13
|
+
v0.5.0
|
14
|
+
2009-09-06
|
15
|
+
Major refactoring and code reorganization.
|
16
|
+
Compatibility with netzke-core v0.4.0.
|
17
|
+
New: GridPanel now supports adding/editing records in a form and extended configurable search.
|
18
|
+
New: GridPanel now can be loaded along with initial data (saves a request to the server).
|
19
|
+
New: context menu in GridPanel
|
20
|
+
New: "scopes" configuration option added to GridPanel to specify the searchlogic-compatible scope for the data.
|
21
|
+
New: "strong_default_attrs" config option added to GridPanel to specify the attributes that will be assigned to each record that is created or modified by the grid.
|
22
|
+
Usability: GridPanel's actions now get enabled/disabled according to the current selection.
|
23
|
+
Configuration panel for grids and forms now works more consistently.
|
24
|
+
New: some smart defaults for column/fields in Grid/FormPanel.
|
25
|
+
New: BasicApp supports masquerading and application-wide AJAX activity indicator.
|
26
|
+
|
1
27
|
v0.4.2
|
2
28
|
2009-05-07
|
3
29
|
Fix: afterlayout event bind removed completely because of some tricky inconsistent behavior of Ext. BasicApp initializing code put directly into js_after_constructor.
|
data/README.rdoc
CHANGED
@@ -5,7 +5,7 @@ Note that if you would like to modify this code or experiment with it, you may b
|
|
5
5
|
|
6
6
|
= Prerequisites
|
7
7
|
1. Rails >= 2.2
|
8
|
-
2. Ext JS >= 2.0: its root *must* be accessible as RAILS_ROOT/public/extjs. You may symlink your Ext JS library here like this (from your app folder):
|
8
|
+
2. Ext JS >= 2.0: its root *must* be accessible as RAILS_ROOT/public/extjs. You may symlink your Ext JS library here like this (from your app folder):
|
9
9
|
cd public && ln -s ~/Developer/extjs/ext-2.2 extjs
|
10
10
|
|
11
11
|
3. acts_as_list plugin must be installed:
|
@@ -13,10 +13,10 @@ Note that if you would like to modify this code or experiment with it, you may b
|
|
13
13
|
|
14
14
|
= Installation
|
15
15
|
Install the gem:
|
16
|
-
gem install netzke-basepack
|
16
|
+
gem install skozlov-netzke-basepack
|
17
17
|
|
18
18
|
Include it into environment.rb:
|
19
|
-
config.gem "netzke-basepack"
|
19
|
+
config.gem "skozlov-netzke-basepack"
|
20
20
|
|
21
21
|
Include mapping for Netzke controller providing *.js and *.css (in routes.rb):
|
22
22
|
map.netzke
|
@@ -24,14 +24,13 @@ Include mapping for Netzke controller providing *.js and *.css (in routes.rb):
|
|
24
24
|
= Usage
|
25
25
|
First, run the generators to have the necessary migrations:
|
26
26
|
./script/generate netzke_core
|
27
|
-
./script/generate netzke_grid_panel
|
28
27
|
|
29
28
|
Do the migrations:
|
30
29
|
rake db:migrate
|
31
30
|
|
32
31
|
The following example will provide you with a grid-based scaffold for ActiveRecord-model called Book. You may generate it like this:
|
33
32
|
./script/generate model Book title:string amount:integer
|
34
|
-
(don't forget to run the migrations after it)
|
33
|
+
(don't forget to re-run the migrations after it)
|
35
34
|
|
36
35
|
In the controller declare the widget:
|
37
36
|
|
@@ -39,10 +38,10 @@ In the controller declare the widget:
|
|
39
38
|
netzke :books, :widget_class_name => 'GridPanel', :data_class_name => 'Book'
|
40
39
|
end
|
41
40
|
|
42
|
-
After a widget is declared in the controller, it can be accessed in 3 different ways: 1) loaded by means of an automatically created controller action which will produce a basic HTML-page with the widget (handy for testing), 2) embedded directly into a view (by means of helpers), 3) dynamically loaded by other widgets (
|
41
|
+
After a widget is declared in the controller, it can be accessed in 3 different ways: 1) loaded by means of an automatically created controller action which will produce a basic HTML-page with the widget (handy for testing), 2) embedded directly into a view (by means of helpers), 3) dynamically loaded by other widgets (like BasicApp-derived, if you want a desktop-like, AJAX-driven web-app).
|
43
42
|
|
44
43
|
== Using automatically created controller action
|
45
|
-
Without writing any more code, you can access the widget by http://localhost:3000/welcome/books_test. That is to say, you simply append _test to your widget's name (as declared in the controller) to get the action name.
|
44
|
+
Without writing any more code, you can access the widget by http://localhost:3000/welcome/books_test. That is to say, you simply append _test to your widget's name (as declared in the controller) to get the action's name.
|
46
45
|
|
47
46
|
== Embedding a widget into a view
|
48
47
|
netzke-core plugin provides the following 2 helpers to put inside your head-tag (use it in your layout):
|
@@ -52,8 +51,9 @@ netzke-core plugin provides the following 2 helpers to put inside your head-tag
|
|
52
51
|
|
53
52
|
Declaring a widget in the controller provides you with a couple of helpers that can be used in the view:
|
54
53
|
|
55
|
-
1. books_class_definition will contain the
|
56
|
-
2. books_widget_instance will declare a local
|
54
|
+
1. books_class_definition will contain the JavaScript code defining the code for the JS class.
|
55
|
+
2. books_widget_instance will declare a local JavaScript variable called 'book'.
|
56
|
+
3. books_widget_render will provide the JavaScript code that calls the "render" method on the variable declared by books_widget_instance.
|
57
57
|
|
58
58
|
Use these helpers inside of the script-tag like the following (in the view):
|
59
59
|
|
@@ -61,7 +61,7 @@ Use these helpers inside of the script-tag like the following (in the view):
|
|
61
61
|
<%= books_class_definition %>
|
62
62
|
Ext.onReady(function(){
|
63
63
|
<%= books_widget_instance %>
|
64
|
-
|
64
|
+
<%= books_widget_render %>
|
65
65
|
})
|
66
66
|
</script>
|
67
67
|
<div id="books">the widget will be rendered in this div</div>
|
@@ -84,4 +84,4 @@ TODO: this part will be covered later
|
|
84
84
|
= Credentials
|
85
85
|
Testing done with the help of http://github.com/pluginaweek/plugin_test_helper
|
86
86
|
|
87
|
-
Copyright (c) 2008-2009 Sergei Kozlov, released under the
|
87
|
+
Copyright (c) 2008-2009 Sergei Kozlov, released under the MIT license
|
data/Rakefile
CHANGED
@@ -1,14 +1,40 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "netzke-basepack"
|
5
|
+
gemspec.summary = "Pre-built Netzke widgets for your RIA"
|
6
|
+
gemspec.description = "Pre-built Netzke widgets for your RIA"
|
7
|
+
gemspec.email = "sergei@playcode.nl"
|
8
|
+
gemspec.homepage = "http://github.com/skozlov/netzke-basepack"
|
9
|
+
gemspec.rubyforge_project = "netzke-basepack"
|
10
|
+
gemspec.authors = ["Sergei Kozlov"]
|
11
|
+
gemspec.add_dependency "netzke-core"
|
12
|
+
gemspec.add_dependency "searchlogic"
|
13
|
+
end
|
14
|
+
Jeweler::RubyforgeTasks.new do |rubyforge|
|
15
|
+
rubyforge.doc_task = "rdoc"
|
16
|
+
end
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/rdoctask'
|
22
|
+
Rake::RDocTask.new do |rdoc|
|
23
|
+
if File.exist?('VERSION')
|
24
|
+
version = File.read('VERSION')
|
25
|
+
else
|
26
|
+
version = ""
|
27
|
+
end
|
2
28
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
p.runtime_dependencies = ["searchlogic >=1.6.2", "netzke-core >= 0.3.1"]
|
9
|
-
p.development_dependencies = []
|
10
|
-
p.test_pattern = 'test/**/*_test.rb'
|
29
|
+
rdoc.rdoc_dir = 'rdoc'
|
30
|
+
rdoc.title = "netzke-basepack #{version}"
|
31
|
+
rdoc.rdoc_files.include('README*')
|
32
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
33
|
+
end
|
11
34
|
|
12
|
-
|
13
|
-
|
35
|
+
require 'rake/testtask'
|
36
|
+
Rake::TestTask.new(:test) do |test|
|
37
|
+
test.libs << 'lib' << 'test'
|
38
|
+
test.pattern = 'test/**/*_test.rb'
|
39
|
+
test.verbose = true
|
14
40
|
end
|
data/TODO.rdoc
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.1
|
data/javascripts/basepack.js
CHANGED
@@ -8,24 +8,28 @@ Ext.reg('passfield', Ext.netzke.PassField);
|
|
8
8
|
|
9
9
|
// Combobox that knows to talk to the server side (used in both grids and panels)
|
10
10
|
Ext.netzke.ComboBox = Ext.extend(Ext.form.ComboBox, {
|
11
|
-
mode : 'remote',
|
12
11
|
displayField : 'id',
|
13
12
|
valueField : 'id',
|
14
13
|
triggerAction : 'all',
|
15
14
|
typeAhead : true,
|
16
|
-
selectOnFocus : true,
|
17
15
|
|
18
16
|
initComponent : function(){
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
}
|
17
|
+
if (this.options) {
|
18
|
+
Ext.apply(this, {
|
19
|
+
store : this.options,
|
20
|
+
mode : "local"
|
21
|
+
});
|
22
|
+
} else {
|
23
|
+
var row = Ext.data.Record.create([{name:'id'}]);
|
24
|
+
var store = new Ext.data.Store({
|
25
|
+
proxy : new Ext.data.HttpProxy({url:this.parentId+"__get_combobox_options", jsonData:{column:this.name}}),
|
26
|
+
reader : new Ext.data.ArrayReader({root:'data', id:0}, row)
|
27
|
+
});
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
Ext.apply(this, {
|
30
|
+
store : store
|
31
|
+
});
|
32
|
+
}
|
29
33
|
|
30
34
|
Ext.netzke.ComboBox.superclass.initComponent.apply(this, arguments);
|
31
35
|
|
@@ -38,38 +42,58 @@ Ext.netzke.ComboBox = Ext.extend(Ext.form.ComboBox, {
|
|
38
42
|
});
|
39
43
|
}
|
40
44
|
});
|
45
|
+
|
41
46
|
Ext.reg('combobox', Ext.netzke.ComboBox);
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
48
|
+
/*
|
49
|
+
Accepts a string which either contains:
|
50
|
+
Ext.util.Format method (e.g. 'usMoney'),
|
51
|
+
or
|
52
|
+
JSON-encoded array, where the first element is Ext.util.Format method (e.g. 'ellipsis'),
|
53
|
+
and the rest of the elements - configuration parameters that should be passed to this method besids
|
54
|
+
the value to be rendered (e.g. '2');
|
55
|
+
|
56
|
+
Example of the latter: ["defaultValue", "MyDefaultValue"]
|
57
|
+
*/
|
58
|
+
Ext.netzke.normalizedRenderer = function(config) {
|
59
|
+
res = null;
|
46
60
|
|
47
|
-
if (
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
61
|
+
if (config) {
|
62
|
+
try{
|
63
|
+
config = Ext.decode(config); // it's an array consisting of renderer's name *and* eventual options
|
64
|
+
} catch(e) {
|
65
|
+
// leave config as is - it's supposed to be the renderer's name
|
66
|
+
}
|
67
|
+
|
68
|
+
if (Ext.isArray(config)) {
|
69
|
+
res = function(v){
|
70
|
+
var formatMethod = config[0];
|
71
|
+
var valueAndOptions = config.slice(1);
|
72
|
+
valueAndOptions.unshift(v);
|
73
|
+
// call the Format function with the argument *and* configuration
|
74
|
+
return Ext.util.Format[formatMethod] ? Ext.util.Format[formatMethod].apply(this, valueAndOptions) : "Unknown renderer";
|
75
|
+
}
|
76
|
+
} else {
|
77
|
+
res = Ext.util.Format[config] ? Ext.util.Format[config] : function(v){return "Unknown renderer"}
|
59
78
|
}
|
60
79
|
}
|
80
|
+
|
81
|
+
return res;
|
82
|
+
};
|
61
83
|
|
62
|
-
|
84
|
+
Ext.util.Format.mask = function(v){
|
85
|
+
return "********";
|
63
86
|
};
|
64
87
|
|
65
88
|
// Mapping of editor field to grid filters
|
66
89
|
Ext.netzke.filterMap = {
|
67
90
|
numberfield:'Numeric',
|
68
91
|
textfield:'String',
|
92
|
+
textarea:'String',
|
69
93
|
xdatetime:'String',
|
70
94
|
checkbox:'Boolean',
|
71
95
|
combobox:'String',
|
72
|
-
|
96
|
+
datefield:'Date'
|
73
97
|
};
|
74
98
|
|
75
99
|
Ext.override(Ext.StatusBar, {
|
@@ -109,6 +133,24 @@ Ext.data.RecordArrayReader = Ext.extend(Ext.data.JsonReader, {
|
|
109
133
|
}
|
110
134
|
});
|
111
135
|
|
136
|
+
Ext.netzke.JsonField = Ext.extend(Ext.form.TextField, {
|
137
|
+
validator: function(value) {
|
138
|
+
try{
|
139
|
+
var d = Ext.decode(value);
|
140
|
+
return true;
|
141
|
+
} catch(e) {
|
142
|
+
return "Invalid JSON"
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
,setValue: function(value) {
|
147
|
+
this.setRawValue(Ext.encode(value));
|
148
|
+
}
|
149
|
+
|
150
|
+
});
|
151
|
+
|
152
|
+
Ext.reg('jsonfield', Ext.netzke.JsonField);
|
153
|
+
|
112
154
|
/**
|
113
155
|
* @class Ext.ux.form.DateTime
|
114
156
|
* @extends Ext.form.Field
|
@@ -742,3 +784,4 @@ Ext.ux.form.DateTime = Ext.extend(Ext.form.Field, {
|
|
742
784
|
Ext.reg('xdatetime', Ext.ux.form.DateTime);
|
743
785
|
|
744
786
|
// eof
|
787
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'acts_as_list'
|
2
|
+
class NetzkeAutoColumn < ActiveRecord::Base
|
3
|
+
|
4
|
+
acts_as_list
|
5
|
+
default_scope :order => "position"
|
6
|
+
|
7
|
+
# Returns an array of column configuration hashes (without the "id" attribute)
|
8
|
+
def self.all_columns
|
9
|
+
self.all.map do |c|
|
10
|
+
column_hash = c.attributes.reject{ |k,v| k == 'id' }
|
11
|
+
column_hash.each_pair do |k,v|
|
12
|
+
# try to detect JSON format
|
13
|
+
begin
|
14
|
+
normalized_value = v.is_a?(String) ? ActiveSupport::JSON.decode(v) : v
|
15
|
+
rescue ActiveSupport::JSON::ParseError
|
16
|
+
normalized_value = v
|
17
|
+
end
|
18
|
+
column_hash[k] = normalized_value
|
19
|
+
end
|
20
|
+
column_hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Build the table with columns for this widget
|
25
|
+
def self.rebuild_table
|
26
|
+
connection.drop_table('netzke_auto_columns') if table_exists?
|
27
|
+
|
28
|
+
normalized_config_columns = []
|
29
|
+
|
30
|
+
@@widget.class.config_columns.each do |mc|
|
31
|
+
column_hash = mc.is_a?(Symbol) ? {:name => mc} : mc
|
32
|
+
column_hash[:type] ||= :string
|
33
|
+
normalized_config_columns << column_hash
|
34
|
+
end
|
35
|
+
|
36
|
+
# create the table with the fields
|
37
|
+
self.connection.create_table('netzke_auto_columns') do |t|
|
38
|
+
normalized_config_columns.each do |mc|
|
39
|
+
t.column mc[:name], mc[:type], :default => mc[:default]
|
40
|
+
end
|
41
|
+
t.column :position, :integer
|
42
|
+
end
|
43
|
+
|
44
|
+
# populate the table with data
|
45
|
+
NetzkeAutoColumn.create @@widget.normalized_columns.map(&:deebeefy_values)
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.widget=(widget)
|
50
|
+
@@widget = widget
|
51
|
+
if Netzke::Base.session["netzke_auto_column_last_widget"] != @@widget.id_name
|
52
|
+
rebuild_table
|
53
|
+
Netzke::Base.session["netzke_auto_column_last_widget"] = @@widget.id_name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/netzke-basepack.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# External dependencies
|
2
2
|
require 'netzke-core'
|
3
3
|
require 'searchlogic'
|
4
|
+
require 'will_paginate'
|
4
5
|
|
5
|
-
require 'netzke/
|
6
|
+
require 'netzke/active_record/basepack'
|
7
|
+
require 'netzke/ext'
|
6
8
|
|
7
9
|
%w{ models }.each do |dir|
|
8
10
|
path = File.join(File.dirname(__FILE__), 'app', dir)
|
@@ -11,10 +13,10 @@ require 'netzke/ar_ext'
|
|
11
13
|
ActiveSupport::Dependencies.load_once_paths.delete(path)
|
12
14
|
end
|
13
15
|
|
14
|
-
# Make this plugin reloadable for easier development
|
16
|
+
# Make this plugin reloadable at app restart for easier development
|
15
17
|
ActiveSupport::Dependencies.load_once_paths.delete(File.join(File.dirname(__FILE__)))
|
16
18
|
|
17
19
|
# Include javascript & styles required by all basepack widgets.
|
18
20
|
# These files will get loaded at the initial load of the framework (along with Ext and Netzke-core).
|
19
21
|
Netzke::Base.config[:javascripts] << "#{File.dirname(__FILE__)}/../javascripts/basepack.js"
|
20
|
-
Netzke::Base.config[:stylesheets] << "#{File.dirname(__FILE__)}/../stylesheets/basepack.css"
|
22
|
+
Netzke::Base.config[:stylesheets] << "#{File.dirname(__FILE__)}/../stylesheets/basepack.css"
|
@@ -1,111 +1,113 @@
|
|
1
1
|
module Netzke
|
2
|
-
#
|
3
|
-
# AccordionPanel
|
2
|
+
# == AccordionPanel
|
4
3
|
#
|
5
|
-
# Features:
|
4
|
+
# == Features:
|
6
5
|
# * Dynamically loads widgets for the panels that get expanded for the first time
|
7
6
|
# * Is loaded along with the active widget - saves a request to the server
|
8
7
|
#
|
9
|
-
#
|
8
|
+
# Future features:
|
10
9
|
# * Stores the last active panel in persistent_config
|
11
|
-
#
|
12
10
|
class AccordionPanel < Base
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
:listeners => {
|
23
|
-
# every item gets an expand event set, which dynamically loads a widget into this item
|
24
|
-
:add => {
|
25
|
-
:fn => <<-JS.l
|
26
|
-
function(self, comp){
|
27
|
-
comp.on('expand', this.loadItemWidget, self)
|
28
|
-
}
|
29
|
-
JS
|
30
|
-
}
|
31
|
-
}
|
32
|
-
})
|
33
|
-
end
|
11
|
+
|
12
|
+
# JavaScript part
|
13
|
+
def self.js_extend_properties
|
14
|
+
{
|
15
|
+
:layout => 'accordion',
|
16
|
+
:defaults => {:layout => 'fit'},
|
17
|
+
:init_component => <<-END_OF_JAVASCRIPT.l,
|
18
|
+
function(){
|
19
|
+
Ext.netzke.cache.#{short_widget_class_name}.superclass.initComponent.call(this);
|
34
20
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
function(panel) {
|
40
|
-
if (!panel.getWidget()) panel.loadWidget(this.id + "__" + panel.widget + "__get_widget");
|
41
|
-
}
|
42
|
-
JS
|
43
|
-
|
44
|
-
:on_widget_load => <<-JS.l
|
45
|
-
function(){
|
46
|
-
// immediately instantiate the active panel
|
47
|
-
var activePanel = this.findById(this.id + "_active");
|
21
|
+
// Set events
|
22
|
+
this.items.each(function(i){
|
23
|
+
// Set the expand event
|
24
|
+
i.on('expand', this.loadItemWidget, this);
|
48
25
|
|
49
|
-
|
50
|
-
if (
|
51
|
-
|
26
|
+
// If not collapsed, add the active aggregatee (item) into it
|
27
|
+
if (!i.collapsed) {
|
28
|
+
var preloadedItemConfig = this[i.widget.camelize(true) + "Config"];
|
29
|
+
i.add(new Ext.netzke.cache[preloadedItemConfig.widgetClassName](preloadedItemConfig));
|
30
|
+
i.doLayout(); // always needed after adding a component
|
52
31
|
}
|
32
|
+
}, this);
|
33
|
+
}
|
34
|
+
END_OF_JAVASCRIPT
|
35
|
+
|
36
|
+
# Loads widget into the panel if it wasn't loaded yet
|
37
|
+
:load_item_widget => <<-END_OF_JAVASCRIPT.l,
|
38
|
+
function(panel) {
|
39
|
+
// if (!panel.getWidget()) panel.loadWidget(this.id + "__" + panel.widget + "__get_widget");
|
40
|
+
var preloadedItemConfig = this[panel.widget.camelize(true) + "Config"];
|
41
|
+
|
42
|
+
if (preloadedItemConfig){
|
43
|
+
// preloaded widget only needs to be instantiated, as its class and configuration have already been loaded
|
44
|
+
panel.add(new Ext.netzke.cache[preloadedItemConfig.widgetClassName](preloadedItemConfig));
|
45
|
+
panel.doLayout(); // always needed after adding a component
|
46
|
+
} else {
|
47
|
+
// load the widget from the server
|
48
|
+
this.loadAggregatee({id:panel.widget, container:panel.id});
|
53
49
|
}
|
54
|
-
|
55
|
-
|
56
|
-
|
50
|
+
|
51
|
+
}
|
52
|
+
END_OF_JAVASCRIPT
|
53
|
+
}
|
57
54
|
end
|
58
|
-
|
59
|
-
|
60
|
-
# some configuration normalization
|
55
|
+
|
56
|
+
# Some normalization of config
|
61
57
|
def initialize(*args)
|
62
58
|
super
|
63
59
|
|
64
60
|
seen_active = false
|
65
|
-
|
61
|
+
|
66
62
|
config[:items].each_with_index do |item, i|
|
67
63
|
# if some items are provided without names, give them generated names
|
68
64
|
item[:name] ||= "item#{i}"
|
69
|
-
|
65
|
+
|
70
66
|
# remove duplucated :active configuration
|
71
67
|
if item[:active]
|
72
68
|
item[:active] = nil if seen_active
|
73
|
-
seen_active
|
69
|
+
seen_active ||= true
|
74
70
|
end
|
75
71
|
end
|
76
72
|
end
|
73
|
+
|
74
|
+
# Returns items configs
|
75
|
+
def items
|
76
|
+
@items ||= config[:items]
|
77
|
+
end
|
77
78
|
|
79
|
+
# Provides configs for fit panels (which will effectively be accordion panels)
|
78
80
|
def js_config
|
79
|
-
expanded_widget_config = config[:items].detect{|i| i[:active]}
|
80
81
|
super.merge({
|
81
|
-
|
82
|
-
:
|
82
|
+
# these "items" are not related to the "items" of the config, rather these are the items required by the the accordion panel
|
83
|
+
:items => fit_panels
|
83
84
|
})
|
84
85
|
end
|
85
86
|
|
86
|
-
#
|
87
|
-
def initial_aggregatees
|
88
|
-
res = {}
|
89
|
-
config[:items].each_with_index do |item, i|
|
90
|
-
item[:late_aggregation] = !item[:active]
|
91
|
-
res.merge!(item[:name].to_sym => item)
|
92
|
-
end
|
93
|
-
res
|
94
|
-
end
|
95
|
-
|
96
|
-
# fit-panels - panels of layout 'fit' that will contain the widgets (items)
|
87
|
+
# "Fit-panels" - panels of layout 'fit' (effectively the accordion panels) that will contain the widgets ("items")
|
97
88
|
def fit_panels
|
98
89
|
res = []
|
99
90
|
config[:items].each_with_index do |item, i|
|
100
91
|
res << {
|
101
|
-
:id => item[:active] && id_name + '_active', # to mark the fit-panel which will contain the active widget
|
102
|
-
:title => item[:title] || (item[:name] && item[:name].humanize),
|
92
|
+
# :id => item[:active] && id_name + '_active', # to mark the fit-panel which will contain the active widget
|
93
|
+
:title => item[:title] || (item[:name] && item[:name].to_s.humanize),
|
103
94
|
:widget => item[:name], # to know which fit panel will load which widget
|
104
95
|
:collapsed => !(item[:active] || false)
|
105
96
|
}
|
106
97
|
end
|
107
98
|
res
|
108
99
|
end
|
100
|
+
|
101
|
+
# All items become *late* aggregatees, besides the ones that are marked "active"
|
102
|
+
def initial_aggregatees
|
103
|
+
res = {}
|
104
|
+
config[:items].each_with_index do |item, i|
|
105
|
+
item[:late_aggregation] = !item[:active]
|
106
|
+
res.merge!(item[:name].to_sym => item)
|
107
|
+
end
|
108
|
+
res
|
109
|
+
end
|
110
|
+
|
109
111
|
|
110
112
|
end
|
111
113
|
end
|