netzke-communitypack 0.1.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +2 -5
  3. data/Rakefile +36 -1
  4. data/lib/netzke-communitypack.rb +9 -4
  5. data/lib/netzke-communitypack/version.rb +2 -9
  6. data/lib/netzke/communitypack/google_map_panel.rb +22 -22
  7. data/lib/netzke/communitypack/google_map_panel/javascript/GMapPanel.js +2 -1
  8. data/lib/netzke/communitypack/live_search_grid_panel.rb +39 -36
  9. data/lib/netzke/communitypack/model_explorer.rb +68 -0
  10. data/lib/netzke/communitypack/one_to_many_explorer.rb +78 -0
  11. data/lib/netzke/communitypack/one_to_many_explorer/javascripts/one_to_many_explorer.js +14 -0
  12. data/lib/netzke/communitypack/workspace.rb +94 -0
  13. data/lib/netzke/communitypack/workspace/javascripts/workspace.js +73 -0
  14. data/lib/tasks/netzke-communitypack_tasks.rake +4 -0
  15. data/test/{rails_app → dummy}/Rakefile +2 -2
  16. data/test/dummy/app/assets/javascripts/application.js +9 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  18. data/test/{rails_app → dummy}/app/controllers/application_controller.rb +0 -0
  19. data/test/{rails_app → dummy}/app/helpers/application_helper.rb +0 -0
  20. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/test/{rails_app → dummy}/config.ru +1 -1
  22. data/test/{rails_app → dummy}/config/application.rb +12 -12
  23. data/test/dummy/config/boot.rb +10 -0
  24. data/test/{rails_app → dummy}/config/database.yml +4 -1
  25. data/test/{rails_app → dummy}/config/environment.rb +1 -1
  26. data/test/{rails_app → dummy}/config/environments/development.rb +9 -5
  27. data/test/{rails_app → dummy}/config/environments/production.rb +25 -14
  28. data/test/{rails_app → dummy}/config/environments/test.rb +7 -3
  29. data/test/{rails_app → dummy}/config/initializers/backtrace_silencers.rb +0 -0
  30. data/test/{rails_app → dummy}/config/initializers/inflections.rb +0 -0
  31. data/test/{rails_app → dummy}/config/initializers/mime_types.rb +0 -0
  32. data/test/{rails_app → dummy}/config/initializers/secret_token.rb +1 -1
  33. data/test/dummy/config/initializers/session_store.rb +8 -0
  34. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  35. data/test/{rails_app → dummy}/config/locales/en.yml +1 -1
  36. data/test/{rails_app → dummy}/config/routes.rb +2 -5
  37. data/test/{rails_app → dummy}/public/404.html +0 -0
  38. data/test/{rails_app → dummy}/public/422.html +0 -0
  39. data/test/{rails_app → dummy}/public/500.html +0 -0
  40. data/test/{rails_app → dummy}/public/favicon.ico +0 -0
  41. data/test/{rails_app → dummy}/script/rails +0 -0
  42. data/test/netzke-communitypack_test.rb +7 -0
  43. data/test/test_helper.rb +10 -0
  44. metadata +134 -134
  45. data/.gitignore +0 -4
  46. data/CHANGELOG.rdoc +0 -3
  47. data/Gemfile +0 -4
  48. data/lib/netzke/communitypack/viewport.rb +0 -4
  49. data/netzke-communitypack.gemspec +0 -26
  50. data/test/rails_app/.gitignore +0 -4
  51. data/test/rails_app/Gemfile +0 -34
  52. data/test/rails_app/Gemfile.lock +0 -89
  53. data/test/rails_app/README +0 -256
  54. data/test/rails_app/app/components/test_panel.rb +0 -3
  55. data/test/rails_app/app/controllers/components_controller.rb +0 -5
  56. data/test/rails_app/app/helpers/components_helper.rb +0 -2
  57. data/test/rails_app/app/views/components/show.html.erb +0 -13
  58. data/test/rails_app/app/views/layouts/application.html.erb +0 -17
  59. data/test/rails_app/config/boot.rb +0 -13
  60. data/test/rails_app/config/initializers/session_store.rb +0 -8
  61. data/test/rails_app/db/seeds.rb +0 -7
  62. data/test/rails_app/doc/README_FOR_APP +0 -2
  63. data/test/rails_app/lib/tasks/.gitkeep +0 -0
  64. data/test/rails_app/public/images/rails.png +0 -0
  65. data/test/rails_app/public/index.html +0 -239
  66. data/test/rails_app/public/javascripts/application.js +0 -2
  67. data/test/rails_app/public/javascripts/controls.js +0 -965
  68. data/test/rails_app/public/javascripts/dragdrop.js +0 -974
  69. data/test/rails_app/public/javascripts/effects.js +0 -1123
  70. data/test/rails_app/public/javascripts/prototype.js +0 -6001
  71. data/test/rails_app/public/javascripts/rails.js +0 -175
  72. data/test/rails_app/public/robots.txt +0 -5
  73. data/test/rails_app/public/stylesheets/.gitkeep +0 -0
  74. data/test/rails_app/test/functional/components_controller_test.rb +0 -8
  75. data/test/rails_app/test/performance/browsing_test.rb +0 -9
  76. data/test/rails_app/test/test_helper.rb +0 -13
  77. data/test/rails_app/test/unit/helpers/components_helper_test.rb +0 -4
  78. data/test/rails_app/vendor/plugins/.gitkeep +0 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
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.
@@ -1,6 +1,3 @@
1
- = Netzke components submitted by the community
1
+ = Netzke Communitypack
2
2
 
3
- Includes the following components:
4
-
5
- * LiveSearchGridPanel - GridPanel extension with a configurable live-search textfield
6
- * GoogleMapPanel - Panel showing a Google map
3
+ A bunch of community-written Netzke components.
data/Rakefile CHANGED
@@ -1,2 +1,37 @@
1
- require 'bundler'
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'NetzkeCommunitypack'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
2
25
  Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task :default => :test
@@ -1,5 +1,10 @@
1
- module Netzke
2
- module Communitypack
3
- # Your code goes here...
4
- end
1
+ # External dependencies
2
+ require 'netzke-core'
3
+ require 'active_support/dependencies'
4
+
5
+ # Make components auto-loadable
6
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
7
+
8
+ module NetzkeCommunitypack
9
+
5
10
  end
@@ -1,10 +1,3 @@
1
- module Netzke
2
- module Communitypack
3
- MAJOR = 0
4
- MINOR = 1
5
- PATCH = 3
6
-
7
- STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
- VERSION = STRING
9
- end
1
+ module NetzkeCommunitypack
2
+ VERSION = "0.7.0" # Keep the major and minor versions synced with those of Netzke Core gem.
10
3
  end
@@ -1,30 +1,30 @@
1
1
  # Creates a google map panel. The panel additionaly has the options of google maps, i.e.:
2
2
  # * +zoom_level+ - The initial zoom level
3
- # * +gmap_type+ -
4
- # * +map_conf_opts+ -
3
+ # * +gmap_type+ -
4
+ # * +map_conf_opts+ -
5
5
  # * +map_controlls+ -
6
6
  # * +set_center+ - The initial map position
7
7
  # * +markers+ - Initial markers on the page
8
8
  # ...
9
9
  class Netzke::Communitypack::GoogleMapPanel < ::Netzke::Base
10
- js_include "#{File.dirname(__FILE__)}/google_map_panel/javascript/GMapPanel.js"
11
-
12
- # default configuration
13
- config do
14
- {
15
- :zoom_level => 14,
16
- :gmap_type => 'map',
17
- :layout => :fit,
18
- :map_conf_opts => ['enableScrollWheelZoom','enableDoubleClickZoom','enableDragging'],
19
- :map_controls => ['GSmallMapControl','GMapTypeControl','NonExistantControl'],
20
- :set_center => {
21
- :geo_code_addr => 'Flottwellstr. 4-5, 10785 Berlin, Germany',
22
- :marker => {
23
- :title => 'pme Familienservice GmbH'
24
- }
25
- }
26
- }
27
- end
28
-
29
- js_base_class 'Ext.ux.GMapPanel'
10
+ js_base_class 'Ext.ux.GMapPanel'
11
+
12
+ js_include "#{File.dirname(__FILE__)}/google_map_panel/javascript/GMapPanel.js"
13
+
14
+ # default configuration
15
+ def configuration
16
+ super.merge({
17
+ :zoom_level => 14,
18
+ :gmap_type => 'map',
19
+ :layout => :fit,
20
+ :map_conf_opts => ['enableScrollWheelZoom','enableDoubleClickZoom','enableDragging'],
21
+ :map_controls => ['GSmallMapControl','GMapTypeControl','NonExistantControl'],
22
+ :set_center => {
23
+ :geo_code_addr => 'Flottwellstr. 4-5, 10785 Berlin, Germany',
24
+ :marker => {
25
+ :title => 'pme Familienservice GmbH'
26
+ }
27
+ }
28
+ })
29
+ end
30
30
  end
@@ -72,6 +72,7 @@ Ext.ux.GMapPanel = Ext.extend(Ext.Panel, {
72
72
  this.addMarkers(this.markers);
73
73
  this.addMapControls();
74
74
  this.addOptions();
75
+ this.gmap.checkResize();
75
76
  },
76
77
  onResize : function(w, h){
77
78
 
@@ -212,4 +213,4 @@ Ext.ux.GMapPanel = Ext.extend(Ext.Panel, {
212
213
 
213
214
  });
214
215
 
215
- Ext.reg('gmappanel', Ext.ux.GMapPanel);
216
+ Ext.reg('gmappanel', Ext.ux.GMapPanel);
@@ -7,40 +7,43 @@
7
7
  # * +live_search_scope+ - The scope name for filtering the results by the live search (default: :live_search)
8
8
  #
9
9
  class Netzke::Communitypack::LiveSearchGridPanel < ::Netzke::Basepack::GridPanel
10
- config :tbar => ['->', {
11
- :xtype => 'textfield',
12
- :id => 'live_search_field',
13
- :enable_key_events => true,
14
- :ref => '../live_search_field',
15
- :empty_text => 'Search'
16
- }]
17
-
18
- js_method :init_component, <<-JS
19
- function() {
20
- #{js_full_class_name}.superclass.initComponent.call(this);
21
-
22
- this.liveSearchBuffer = '';
23
- this.live_search_field.on('keydown', function() { this.onLiveSearch(); }, this, { buffer: 500 });
24
- this.live_search_field.on('blur', function() { this.onLiveSearch(); }, this, { buffer: 500 });
25
- }
26
- JS
27
-
28
- js_method :on_live_search, <<-JS
29
- function() {
30
- var search_text = this.getTopToolbar().get('live_search_field').getValue();
31
- if (search_text == this.liveSearchBuffer) return;
32
- this.liveSearchBuffer = search_text;
33
- this.getStore().setBaseParam('live_search', search_text);
34
- this.getStore().load();
35
- }
36
- JS
37
-
38
- def get_data(*args)
39
- params = args.first
40
- search_scope = config[:live_search_scope] || :live_search
41
- data_class.send(search_scope, params && params[:live_search] || '').scoping do
42
- super
43
- end
44
- end
45
-
10
+ def configuration
11
+ super.merge({
12
+ :tbar => ['->', {
13
+ :xtype => 'textfield',
14
+ :enable_key_events => true,
15
+ :ref => '../live_search_field',
16
+ :empty_text => 'Search'
17
+ }]
18
+ })
19
+ end
20
+
21
+ js_method :init_component, <<-JS
22
+ function() {
23
+ #{js_full_class_name}.superclass.initComponent.call(this);
24
+
25
+ this.liveSearchBuffer = '';
26
+ this.live_search_field.on('keydown', function() { this.onLiveSearch(); }, this, { buffer: 500 });
27
+ this.live_search_field.on('blur', function() { this.onLiveSearch(); }, this, { buffer: 500 });
28
+ }
29
+ JS
30
+
31
+ js_method :on_live_search, <<-JS
32
+ function() {
33
+ var search_text = this.live_search_field.getValue();
34
+ if (search_text == this.liveSearchBuffer) return;
35
+ this.liveSearchBuffer = search_text;
36
+ this.getStore().setBaseParam('live_search', search_text);
37
+ this.getStore().load();
38
+ }
39
+ JS
40
+
41
+ def get_data(*args)
42
+ params = args.first
43
+ search_scope = config[:live_search_scope] || :live_search
44
+ data_class.send(search_scope, params && params[:live_search] || '').scoping do
45
+ super
46
+ end
47
+ end
48
+
46
49
  end
@@ -0,0 +1,68 @@
1
+ module Netzke
2
+ module Communitypack
3
+ # 2 regions - "grid" and "form", form displaying the details of the record selected in the grid.
4
+ #
5
+ # Accepts the following config options:
6
+ # * :model - name of the model, e.g. "User"
7
+ # * :grid_config (optional) - a config hash passed to the grid
8
+ # * :form_config (optional) - a config hash passed to the form
9
+ class ModelExplorer < Netzke::Basepack::BorderLayoutPanel
10
+
11
+ delegates_to_dsl :model, :grid_config, :form_config
12
+
13
+ js_properties(
14
+ :prevent_header => true,
15
+ :border => true
16
+ )
17
+
18
+ def configuration
19
+ super.tap do |c|
20
+
21
+ # merge default container and collection config with the one provided by the user
22
+ c[:grid_config] = {
23
+ :region => :west,
24
+ :class_name => "Netzke::Basepack::GridPanel",
25
+ :model => c[:model],
26
+ :item_id => 'grid'
27
+ }.merge(c[:grid_config] || {})
28
+
29
+ c[:form_config] = {
30
+ :class_name => "Netzke::Basepack::FormPanel",
31
+ :model => c[:model],
32
+ :region => :center,
33
+ :item_id => 'form'
34
+ }.merge(c[:form_config] || {})
35
+
36
+ # set default width/height for regions
37
+ c[:grid_config][:width] ||= 300 if [:west, :east].include?(c[:grid_config][:region].to_sym)
38
+ c[:grid_config][:height] ||= 150 if [:north, :south].include?(c[:grid_config][:region].to_sym)
39
+
40
+ c[:items] = [c[:grid_config], c[:form_config]]
41
+ end
42
+ end
43
+
44
+ endpoint :select_record do |params|
45
+ # store selected container record id in the session for this component's instance
46
+ component_session[:selected_record_id] = params[:id]
47
+
48
+ # {:form => {:set_title => "Blah"}}
49
+ end
50
+
51
+ js_method :init_component, <<-JS
52
+ function(){
53
+ // calling superclass's initComponent
54
+ this.callParent();
55
+
56
+ this.grid = this.getComponent('grid');
57
+ this.form = this.getComponent('form');
58
+
59
+ // setting the 'rowclick' event
60
+ this.grid.getView().on('itemclick', function(view, record){
61
+ this.selectRecord({id: record.getId()});
62
+ this.form.netzkeLoad({id: record.getId()});
63
+ }, this);
64
+ }
65
+ JS
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,78 @@
1
+ module Netzke
2
+ module Communitypack
3
+ # 2 grids - "container" and "collection" - bound with a one-to-many relationship.
4
+ # The collection model should implement belongs_to in respect to the container model.
5
+ #
6
+ # Accepts the following config options:
7
+ # * :container_model - name of the container model, e.g. "User"
8
+ # * :collection_model - name of the collection model, e.g. "Issue" (belongs_to :user)
9
+ # * :container_config (optional) - a config hash passed to the container grid
10
+ # * :collection_config (optional) - a config hash passod to the collection grid
11
+ # * :association (optional) - the name of the association used in belongs_to macro. Defaults to the underscored name of the container model.
12
+ class OneToManyExplorer < Netzke::Basepack::BorderLayoutPanel
13
+ js_mixin
14
+
15
+ delegates_to_dsl :container_model, :collection_model, :container_config, :collection_config, :association
16
+
17
+ js_properties(
18
+ :prevent_header => true,
19
+ :border => true
20
+ )
21
+
22
+ def configuration
23
+ super.tap do |c|
24
+
25
+ # merge default container and collection config with the one provided by the user
26
+ c[:container_config] = {
27
+ :region => :west,
28
+ :class_name => "Netzke::Basepack::GridPanel"
29
+ }.merge(c[:container_config] || {})
30
+
31
+ c[:collection_config] = {
32
+ :class_name => "Netzke::Basepack::GridPanel"
33
+ }.merge(c[:collection_config] || {})
34
+
35
+ # set default width/height for regions
36
+ c[:container_config][:width] ||= 300 if [:west, :east].include?(c[:container_config][:region].to_sym)
37
+ c[:container_config][:height] ||= 200 if [:north, :south].include?(c[:container_config][:region].to_sym)
38
+
39
+ # figure out collection_class from config or from the passed component
40
+ container_class = c[:container_config][:model].try(:constantize) || c[:container_config][:class_name].constantize.new.data_class
41
+ collection_class = c[:collection_config][:model].try(:constantize) || c[:collection_config][:class_name].constantize.new.data_class
42
+
43
+ # use the shortcuts for models
44
+ c[:container_config][:model] ||= c[:container_model] || container_class.name
45
+ c[:collection_config][:model] ||= c[:collection_model] || collection_class.name
46
+
47
+ # we need to get the association reflection in order to properly set the collection grid scope
48
+ c[:association] ||= c[:container_config][:model].underscore.to_sym # the belongs_to association, e.g. "user"
49
+
50
+ association = collection_class.reflect_on_association(c[:association])
51
+
52
+ # if we have extra scopes received in the config, take them into account!
53
+ passed_scope = c[:collection_config][:scope] || {}
54
+ passed_strong_default_attrs = c[:collection_config][:strong_default_attrs] || {}
55
+
56
+ c[:items] = [
57
+ c[:container_config].merge(:item_id => 'container'),
58
+
59
+
60
+ c[:collection_config].merge(
61
+ :region => :center,
62
+ :item_id => 'collection',
63
+ :scope => {association.foreign_key.to_sym => component_session[:selected_container_record_id]}.merge(passed_scope),
64
+ :strong_default_attrs => {association.foreign_key.to_sym => component_session[:selected_container_record_id]}.merge(passed_strong_default_attrs),
65
+ :load_inline_data => false
66
+ )
67
+ ]
68
+ end
69
+ end
70
+
71
+ endpoint :select_container_record do |params|
72
+ # store selected container record id in the session for this component's instance
73
+ component_session[:selected_container_record_id] = params[:id]
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,14 @@
1
+ {
2
+ initComponent: function() {
3
+ // calling superclass's initComponent
4
+ this.callParent();
5
+
6
+ // setting the 'rowclick' event
7
+ var view = this.getComponent('container').getView();
8
+ view.on('itemclick', function(view, record){
9
+ // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
10
+ this.selectContainerRecord({id: record.get('id')});
11
+ this.getComponent('collection').getStore().load();
12
+ }, this);
13
+ }
14
+ }
@@ -0,0 +1,94 @@
1
+ module Netzke
2
+ module Communitypack
3
+ # A component that allows for dynamical loading/unloading of other Netzke components in tabs.
4
+ # It can be manipulated by calling the <js>loadChild</js> method, e.g.:
5
+ #
6
+ # workspace.loadChild("UserGrid", {newTab: true})
7
+ #
8
+ # - will load a UserGrid component from the server in a new tab.
9
+ class Workspace < Netzke::Base
10
+ js_base_class "Ext.tab.Panel"
11
+ js_property :prevent_header, true
12
+
13
+ js_mixin
14
+
15
+ action :remove_all
16
+
17
+ def default_config
18
+ super.tap do |c|
19
+ c[:items] = ([dashboard_config] + stored_tabs).each_with_index.map do |tab,i|
20
+ {
21
+ :layout => 'fit',
22
+ :title => tab[:title],
23
+ :closable => i > 0, # all closable except first
24
+ :netzke_component_id => tab[:name],
25
+ :items => !components[tab[:name].to_sym][:lazy_loading] && [tab[:name].to_sym.component]
26
+ }
27
+ end
28
+ end
29
+ end
30
+
31
+ def dashboard_config
32
+ {
33
+ :title => "Dashboard",
34
+ :class_name => "Netzke::Basepack::Panel"
35
+ }.merge!(@passed_config[:dashboard_config] || {}).merge(:name => 'cmp0', :lazy_loading => false)
36
+ end
37
+
38
+ # Overriding this to allow for dynamically declared components
39
+ def components
40
+ stored_tabs.inject({}){ |r,tab| r.merge(tab[:name].to_sym => tab.reverse_merge(:prevent_header => true, :lazy_loading => true, :border => false)) }.merge(:cmp0 => dashboard_config)
41
+ end
42
+
43
+ # Overriding the deliver_component endpoint, to dynamically add tabs and replace components in existing tabs
44
+ def deliver_component_endpoint(params)
45
+ cmp_name = params[:name]
46
+ cmp_index = cmp_name.sub("cmp", "").to_i
47
+
48
+ if params[:component].present?
49
+ current_tabs = stored_tabs
50
+
51
+ # we need to instantiate the newly added child to get access to its title
52
+ cmp_class = constantize_class_name(params[:component])
53
+ raise RuntimeError, "Could not find class #{params[:component]}" if cmp_class.nil?
54
+
55
+ cmp_config = {:name => params[:name], :class_name => cmp_class.name}.merge(params[:config] || {}).symbolize_keys
56
+ cmp_instance = cmp_class.new(cmp_config, self)
57
+ new_tab_short_config = cmp_config.merge(:title => cmp_instance.js_config[:title] || cmp_instance.class.js_properties[:title]) # here we set the title
58
+
59
+ if stored_tabs.empty? || cmp_index > stored_tabs.last[:name].sub("cmp", "").to_i
60
+ # add new tab to persistent storage
61
+ current_tabs << new_tab_short_config
62
+ else
63
+ # replace existing tab in the storage
64
+ current_tabs[current_tabs.index(current_tabs.detect{ |tab| tab[:name] == cmp_name })] = new_tab_short_config
65
+ end
66
+
67
+ component_session[:items] = current_tabs
68
+ @stored_tabs = nil # reset cache
69
+ end
70
+
71
+ super(params)
72
+ end
73
+
74
+ # Clean the session on request. More clean-up may be needed later, as we start using persistent configuration.
75
+ endpoint :server_remove_all do |params|
76
+ component_session[:items] = []
77
+ end
78
+
79
+ # Removes a closed tab's component from the storage.
80
+ endpoint :server_remove_tab do |params|
81
+ component_session[:items].delete_if{ |item| item[:name] == params[:name] }
82
+ {}
83
+ end
84
+
85
+ private
86
+
87
+ # We store these in component_session atm. May as well be in persistent storage, depending on the requirements
88
+ def stored_tabs
89
+ @stored_tabs ||= component_session[:items] || []
90
+ end
91
+
92
+ end
93
+ end
94
+ end