Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'scho/0-8' into 0-8
- Loading branch information
Showing
20 changed files
with
853 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
# Ext.tree.TreePanel-based component | ||
# | ||
# TODO: Add documentation for usage | ||
class Netzke::Communitypack::TreePanel < Netzke::Base | ||
|
||
|
||
# Include data accessor module | ||
include ::Netzke::Basepack::DataAccessor | ||
# Include columns module | ||
include ::Netzke::Basepack::GridPanel::Columns | ||
|
||
extend ActiveSupport::Memoizable | ||
|
||
self.default_instance_config = { | ||
:indicate_leafs => true, | ||
:auto_scroll => false, | ||
:root_visible => false, | ||
:load_inline_data => true, | ||
:enable_pagination => true, | ||
:rows_per_page => 30, | ||
:treecolumn => 'tree' # The default name of the column, that will be the treecolumn | ||
} | ||
|
||
js_configure do |c| | ||
c.extend = "Ext.tree.TreePanel" | ||
c.mixin :tree_panel | ||
c.include :paging_tree_store | ||
end | ||
|
||
# Configure dynamic JS properties for instantiation | ||
def js_config | ||
super.tap do |c| | ||
# Hand over inline data to the js config hash | ||
c[:inline_data] = get_data if config[:load_inline_data] | ||
end | ||
end | ||
|
||
def configure(c) #:nodoc: | ||
super | ||
c.title = c.title || self.class.js_config.properties[:title] || data_class.name.pluralize | ||
c.columns = final_columns(with_meta: true) | ||
# Set it to the primary key if not given and camelize it | ||
# Setting it to anything else than the primary key is especially useful when instances of different class are shown in one tree | ||
# because the primary key MUST be unique! | ||
c.pri = (c.pri || data_class.primary_key).to_s.camelize(:lower) | ||
# Add extra fields for a tree: A method ':name(r)' is called for every record to retrieve the value | ||
c.extra_fields ||= [] | ||
c.extra_fields << {:name => 'leaf', :type => 'boolean'} # This will call leaf?(r) for every record | ||
c.extra_fields << {:name => 'expanded', :type => 'boolean'} # This will call expanded?(r) for every record | ||
# only if the node id property is different from the data class' primary key, we need to add and extra field | ||
c.extra_fields << {:name => c.pri.to_s.camelize(:lower), :type => 'string'} if c.pri != data_class.primary_key | ||
end | ||
|
||
# Sets the xtype to 'treecolumn' for the column with name equal to the :treecolumn value of the config | ||
def set_default_xtype(c) | ||
if c[:name] == config[:treecolumn].to_s | ||
c[:xtype] = 'treecolumn' | ||
else | ||
super | ||
end | ||
end | ||
|
||
# Set data_index | ||
# The name of the field configuration of the Ext JS model will be set to this value | ||
# This is neccessary since the data is serialized as a hash (with camelized keys) | ||
# so the data_index must also be camelized | ||
def set_default_data_index(c) | ||
c[:data_index] = c[:name].camelize(:lower) | ||
end | ||
|
||
# Call super and then set the data_index | ||
def augment_column_config(c) | ||
super | ||
set_default_data_index(c) | ||
end | ||
|
||
# @!method get_data_endpoint | ||
# | ||
# Returns something like: | ||
# [ | ||
# { 'id'=> 1, 'text'=> 'A folder Node', 'leaf'=> false }, | ||
# { 'id'=> 2, 'text'=> 'A leaf Node', 'leaf'=> true } | ||
# ] | ||
# | ||
# @param [Hash] params | ||
endpoint :get_data do |params, this| | ||
this.merge! get_data(params) | ||
end | ||
|
||
# Method that is called by the get_data endpoint | ||
# Calls the get_children method and returns the serialized records | ||
# | ||
# @param [] *args takes any arguments | ||
# @return [Hash] all the serialized data | ||
def get_data(*args) | ||
params = args.first || {} # params are optional! | ||
if !config[:prohibit_read] | ||
{}.tap do |res| | ||
# set children to an instance variable in order to access them later | ||
@records = get_children(params) | ||
|
||
# Serialize children | ||
res[:data] = serialize_data(@records) | ||
res[:total] = count_records(params) if config[:enable_pagination] && (params[:id].nil? || params[:id] == 'root') | ||
end | ||
else | ||
flash :error => "You don't have permissions to read data" | ||
{ :netzke_feedback => @flash } | ||
end | ||
end | ||
|
||
# Serializes an array of objects | ||
# | ||
# @param [Array] records | ||
# @return [Array] the serialized data | ||
def serialize_data(records) | ||
records.map { |r| | ||
r.netzke_hash(final_columns(:with_meta => true)).tap { |h| | ||
|
||
config[:extra_fields].each do |f| | ||
name = f[:name].underscore.to_sym | ||
h[name] = send("#{name}#{f[:type] == 'boolean' ? '?' : ''}", r) | ||
end | ||
|
||
inline_children = get_inline_children(r) | ||
h[:data] = serialize_data(inline_children) unless inline_children.nil? | ||
h | ||
} | ||
} | ||
end | ||
|
||
# Retrieves all children for a node | ||
# Note: It's recommended to override this method | ||
# | ||
# @param [Hash] params | ||
# @return [Array] array of records | ||
def get_children(params) | ||
scope_data_class(params) do | ||
params[:limit] = config[:rows_per_page] if config[:enable_pagination] && (params[:id].nil? || params[:id] == 'root') | ||
params[:scope] = config[:scope] | ||
data_adapter.get_records(params, final_columns) | ||
end | ||
end | ||
|
||
# Scopes the data class depending on the config of the parent_key and the node | ||
# | ||
# @param [Hash] params | ||
def scope_data_class(params, &block) | ||
if config[:parent_key] | ||
# The value of the pri property of the expanded node is passed as params[:id] ('root' for the root collection) | ||
if params[:id].nil? || params[:id] == 'root' | ||
data_class.where(config[:parent_key] => nil).scoping do | ||
yield | ||
end | ||
else | ||
data_class.where(config[:parent_key] => params[:id]).scoping do | ||
yield | ||
end | ||
end | ||
else | ||
yield | ||
end | ||
end | ||
|
||
# Counts the total records | ||
# | ||
# @param [Hash] params | ||
# @return [Fixnum] The number of records | ||
def count_records(params) | ||
scope_data_class(params) do | ||
params[:scope] = config[:scope] | ||
data_adapter.count_records(params, final_columns) | ||
end | ||
end | ||
|
||
# Should return all children of the record that should also be serialized in the current request | ||
# Note: It's recommended to override this method | ||
# | ||
# @param [Object] r The record for which the inline children should be loaded | ||
# @return [NilClass, Array] If nil is returned, the tree doesn't know anything about any children, so opening the node will cause another request. | ||
# If an empty array is returned, the tree assumes that there are no children available for this node (and thus you can't open it!) | ||
def get_inline_children(r) | ||
nil | ||
end | ||
|
||
# Is the record a leaf or not? | ||
# Note: It's recommended to override this method | ||
# | ||
# @param [Object] r | ||
# @return [Boolean] Whether the node is a leaf or not | ||
def leaf?(r) | ||
r.children.empty? | ||
end | ||
|
||
# Is the record a expanded or not? | ||
# Note: It's recommended to override this method | ||
# | ||
# @param [Object] r | ||
# @return [Boolean] Whether the node is expanded or not | ||
def expanded?(r) | ||
false | ||
end | ||
|
||
# Is the record a leaf or not? | ||
# Note: It's recommended to override this method | ||
# | ||
# @param [Object] r | ||
# @return [Boolean] Whether the node is a leaf or not | ||
def node_id(r) | ||
r.send(data_class.primary_key) | ||
end | ||
end |
67 changes: 67 additions & 0 deletions
67
lib/netzke/communitypack/tree_panel/javascripts/paging_tree_store.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Extends Ext.data.TreeStore and adds paging to it | ||
Ext.define('Ext.netzke.PagingTreeStore', { | ||
extend: 'Ext.data.TreeStore', | ||
alias: 'pagingtreestore', | ||
currentPage: 1, | ||
config:{ | ||
totalCount: null, | ||
pageSize: null | ||
}, | ||
// Load a specific Page | ||
loadPage: function(page){ | ||
var me = this; | ||
me.currentPage = page; | ||
me.read({ | ||
page: page, | ||
start: (page - 1) * me.pageSize, | ||
limit: me.pageSize | ||
}); | ||
}, | ||
// Load next Page | ||
nextPage: function(){ | ||
this.loadPage(this.currentPage + 1); | ||
}, | ||
// Load previous Page | ||
previousPage: function(){ | ||
this.loadPage(this.currentPage - 1); | ||
}, | ||
// Overwrite function in order to set totalCount | ||
onProxyLoad: function(operation) { | ||
// This method must be overwritten in order to set totalCount | ||
var me = this, | ||
resultSet = operation.getResultSet(), | ||
node = operation.node; | ||
// If the node doesn't have a parent node, set totalCount | ||
if (resultSet && node.parentNode == null) { | ||
me.setTotalCount(resultSet.total); | ||
} | ||
// We're done here, call parent | ||
this.callParent(arguments); | ||
}, | ||
getCount : function(){ | ||
return this.getRootNode().childNodes.length; | ||
}, | ||
getRange : function(start, end){ | ||
var me = this, | ||
items = this.getRootNode().childNodes, | ||
range = [], | ||
i; | ||
if (items.length < 1) { | ||
return range; | ||
} | ||
start = start || 0; | ||
end = Math.min(typeof end == 'undefined' ? items.length - 1 : end, items.length - 1); | ||
if (start <= end) { | ||
for (i = start; i <= end; i++) { | ||
range[range.length] = items[i]; | ||
} | ||
} else { | ||
for (i = start; i >= end; i--) { | ||
range[range.length] = items[i]; | ||
} | ||
} | ||
return range; | ||
} | ||
|
||
|
||
}); |
Oops, something went wrong.