PDA

View Full Version : [2.x]Ext.ux.GridConfigWindow



jelt
22 May 2008, 7:21 AM
Hi everyone, today i come to share with you my first extension.

Live Demo available here (http://ext4sap.free.fr/GWC-grid.html)

GridConfigWindow is a little plugin to have all common grid config parameters in an unique window. (see screenshot)
It add a button to call the window (top or bottom toolbar of the grid, you choose).

In the window, you can define :
- order of the columns
- size of each column
- columns displayed
- the sort column and the sort order

Change are applied only when you push "apply" button.

This plugin use the wonderfull Ext.ux.Sortable plugin : http://www.extjs.com/forum/showthread.php?t=29033

Usage : Use it as a plugin of the gridpanel, it add a button on bottom or top toolbar if asked.




/**
* Grid Config Window plugin for Ext.grid.GridPanel
* @author Jelt
* @copyright No thanks
* @date 21. May 2008
* @version 2008-05-27 15:00:00 - Jelt
* include Ext.ux.Sortable 0.45, by Rowan
* http://www.extjs.com/forum/showthread.php?t=29033
*/

Ext.namespace('Ext.ux', 'Ext.ux.grid');
Ext.ux.grid.ConfigWindow = function(config) {
Ext.apply(this, config);
Ext.ux.grid.ConfigWindow.superclass.constructor.call(this);
};
Ext.extend(Ext.ux.grid.ConfigWindow, Ext.util.Observable, {
// options
position:'top' // position Where to display the button. Valid values are 'top', 'bottom' and ''. Use '' to not display button (window can be called by the showWindow() function)
,iconCls:'icon-magnifier' // Icon class for menu button
,windowMaximizable:true // Display window maximize icon
,sortColumn:true
,displayColumn:true
,sizeColumn:true

// Localisation
,buttonText:'Layout' // Text to display on menu button
,buttonTipText:'Configure layout of the grid' // Text to display as input tooltip
,windowTitle:'Grid Layout Configuration' //Text of the config window
,windowButtonApplyText:'Apply' //Text of the Aplpy button in window
,windowButtonCancelText:'Cancel' //Text of the Aplpy button in window
,windowHeaderFieldText:'Field Order'
,windowHeaderSizeText:'Size'
,windowHeaderDisplayText:'Display'
,windowHeaderSortText:'Sort<br>A / D'

,init:function(grid) {
if (this.buttonTipText != '') { // Be sure quicktip is enabled if required
Ext.QuickTips.init();
}
this.grid = grid;
this.GCW_id = Ext.id();
if (this.position != '') { // Draw button if required
grid.onRender = grid.onRender.createSequence(this.onRender, this);
}
}

,onRender:function() {
var grid = this.grid;
var tb = 'bottom' == this.position ? grid.bottomToolbar : grid.topToolbar;
if(0 < tb.items.getCount()) { tb.addSeparator(); }
this.layoutButton = new Ext.Button({
text: this.buttonText
, cls: 'x-btn-text-icon details'
,iconCls:this.iconCls
,tooltip:this.buttonTipText
});
tb.add(this.layoutButton);
this.layoutButton.on('click',function() { this.showWindow();},this);
}

,hideWindow:function() { // Hide config window
this.config_window.hide();
this.sortable1.disable();
}
,showWindow:function() { // Display config window
var myCols = this.grid.getColumnModel();
var myStore = this.grid.getStore();

var div_width = 303 //470;
var win_width = 343 //510;
if (this.sizeColumn){
div_width += 52;
win_width += 52;
}
if (this.displayColumn){
div_width += 62;
win_width += 62;
}
if (this.sortColumn){
div_width += 52;
win_width += 52;
}

li_template = '<div id="'+this.GCW_id+'_li_{0}" style="border:1px dotted #999999;width:'+div_width+';vertical-align:center"><table><tr class="x-grid3-row"><td width="300" id="handle_'+this.GCW_id+'_li_{0}" class="handle">{1}</td>';
if (this.sizeColumn){
li_template += '<td width="50" align="center" class="x-small-editor"><input type="text" id="'+this.GCW_id+'_sz_{0}" size=3 class="x-form-field"></td>';
}
if (this.displayColumn){
li_template += '<td width="60" align="center"><input type="checkbox" id="'+this.GCW_id+'_cb_{0}"></td>';
}
if (this.sortColumn){
li_template += '<td width="50" align="center"><input type="radio" name="sort_field_order" value="A_{0}" id="'+this.GCW_id+'_sort_{0}_A"> <input type="radio" name="sort_field_order" value="D_{0}" id="'+this.GCW_id+'_sort_{0}_D"></td>';
}
li_template += '</tr></table></div>';

// header
var config_ul_inner_array = new Array('<table class="x-grid3-header"><tr class="x-grid3-hd-row"><td width="299">'+this.windowHeaderFieldText+'</td>');
if (this.sizeColumn){
config_ul_inner_array[config_ul_inner_array.length] = '<td width="48" align="center">'+this.windowHeaderSizeText+'</td>';
}
if (this.displayColumn){
config_ul_inner_array[config_ul_inner_array.length] = '<td align="center" width="58">'+this.windowHeaderDisplayText+'</td>';
}
if (this.sortColumn){
config_ul_inner_array[config_ul_inner_array.length] = '<td align="center" width="49">'+this.windowHeaderSortText+'</td>';
}
config_ul_inner_array[config_ul_inner_array.length] = '</tr></table>';

// items
for (j=0;j<myCols.config.length;j++){
config_ul_inner_array[config_ul_inner_array.length] = String.format(li_template, myCols.config[j].dataIndex,myCols.config[j].header);
}

if (!this.config_window) {
Ext.util.CSS.createStyleSheet('.GCW_window .x-panel-body{overflow-y:auto;} .handle {cursor : move;}');
this.config_window = new Ext.Window({
title:this.windowTitle
,cls:'GCW_window'
,maximizable:this.windowMaximizable,autoScroll:true
,plain:true,closeAction:'hide'
,layout:'fit',modal:true
,resizable:true, shadow:false
,width: win_width, height: 280
,items:new Ext.Panel({ html:'<form name="'+this.GCW_id+'_config_form"><div id="'+this.GCW_id+'_div_content"></div></form>' }) // applyTo:'config_div'})
,buttonAlign:'center'

// Apply button
,buttons: [{text:this.windowButtonApplyText,handler:function(){
this.hideWindow();

// Sort
if (this.sortColumn){
for (var i=0; i<document.forms[this.GCW_id+"_config_form"].elements["sort_field_order"].length;i++) {
if (document.forms[this.GCW_id+"_config_form"].elements["sort_field_order"][i].checked) {
var field = document.forms[this.GCW_id+"_config_form"].elements["sort_field_order"][i].value;
if (field.substr(0,1)=='A'){
field = field.substr(2,32);
if ((!myStore.sortInfo)||(myStore.sortInfo.field != field)||(myStore.sortInfo.direction != 'ASC')) {
myStore.sort(field,'ASC');
}
} else {
field = field.substr(2,32);
if ((!myStore.sortInfo)||(myStore.sortInfo.field != field)||(myStore.sortInfo.direction != 'DESC')) {
myStore.sort(field,'DESC');
}
}
break;
}
}
}

// order, hidden, size
var list_sorted = this.sortable1.serialise();
var config_new = new Array();
var to_bypass = 4 + this.GCW_id.length;

for (var i=0;i<list_sorted.length;i++){
var field = list_sorted[i].id.substr(to_bypass,30);
if (myCols.config[i].dataIndex == field){
j = i;
} else {
for (var j=0;j<myCols.config.length;j++){
if (myCols.config[j].dataIndex == field){
break;
}
}
}
config_new[config_new.length] = myCols.config[j];
if (this.sizeColumn){
config_new[i].width = parseInt(Ext.getDom(this.GCW_id+'_sz_'+field).value); // set column width
}
if (this.displayColumn){
config_new[i].hidden = !Ext.getDom(this.GCW_id+'_cb_'+field).checked; // set column hidden
}
}
myCols.setConfig( config_new );

// Cancel button
}.createDelegate(this)},{ text: this.windowButtonCancelText, handler: function(){ this.hideWindow() }.createDelegate(this) }] });
this.config_window.render(document.body);
}


Ext.getDom(this.GCW_id+'_div_content').innerHTML = config_ul_inner_array.join('');

if (!this.sortable1) {
this.sortable1 = new Ext.ux.Sortable({
tagName:'div',container : this.GCW_id+'_div_content'
,handles : true });
} else {
this.sortable1.enable();
}

// values
if ((this.displayColumn)||(this.sizeColumn)){
for (j=0;j<myCols.config.length;j++){
if (this.displayColumn){
Ext.getDom(this.GCW_id+'_cb_'+myCols.config[j].dataIndex).checked = !myCols.config[j].hidden; // display fields
if (myCols.config[j].hideable==false)
Ext.getDom(this.GCW_id+'_cb_'+myCols.config[j].dataIndex).disabled = true;
}
if (this.sizeColumn){
Ext.getDom(this.GCW_id+'_sz_'+myCols.config[j].dataIndex).value=myCols.config[j].width; // size
if (myCols.config[j].resizable==false)
Ext.getDom(this.GCW_id+'_sz_'+myCols.config[j].dataIndex).disabled = true;
}
if (this.sortColumn){
if (myCols.config[j].sortable==false){
Ext.getDom(this.GCW_id+'_sort_'+myCols.config[j].dataIndex+'_A').disabled = true;
Ext.getDom(this.GCW_id+'_sort_'+myCols.config[j].dataIndex+'_D').disabled = true;
}
}
}
}
if (this.sortColumn){
if ((myStore.sortInfo)&&(myStore.sortInfo.field)&&(myStore.sortInfo.direction)){
if (myStore.sortInfo.direction == 'ASC'){ // sort
Ext.getDom(this.GCW_id+'_sort_'+myStore.sortInfo.field+'_A').checked=true;
} else {
Ext.getDom(this.GCW_id+'_sort_'+myStore.sortInfo.field+'_D').checked=true;
}
}
}
this.config_window.show();
} //EO showWindow:function
});

// include Sortable.js 0.45
Ext.namespace('Ext.ux');

Ext.ux.Sortable = function(obj){

var config = Ext.applyIf(obj,{
container : document.body,
className : null,
tagName : 'li',
handles : false,
contextMenu : false,
dragGroups : [
'default'
],
autoEnable : true,
horizontal : false
});

this.ddGroups = {};

Ext.applyIf(this,config);

this._buildQueryString();

this.addEvents (
'serialise',
'serialize',
'enable',
'disable',
'enableElement',
'disableElement'
);

this._createDragDrop();

if(this.dragGroups.length > 1){
this.addToDDGroup(this.dragGroups);
} else {
this.ddGroups[this.dragGroups]=true;
}

//automatically start the DD
if(this.autoEnable){
this.enable();
}

this.serialize = this.serialise;

}

Ext.extend(Ext.ux.Sortable,Ext.util.Observable, {
/**
* Function creates the queryString for use in all functions
* @private
*/
_buildQueryString : function(){
this.queryString = '';

if(this.tagName){
this.queryString += this.tagName.toLowerCase();
}

if(this.className){
this.queryString += '.'+this.className;
}
},
/**
* creates the DragZone and DropTarget
* @private
*/
_createDragDrop : function(){
this.dragZone = new Ext.dd.SortableDragZone(this.container, {ddGroup : this.dragGroups[0], scroll:false, containerScroll:true, queryString : this.queryString});
this.dropZone = new Ext.dd.SortableDropZone(this.container, {ddGroup : this.dragGroups[0], queryString : this.queryString, handles : this.handles, horizontal : this.horizontal});
},
/**
* Function gets the items in the list area
* @public
* @param {Boolean} flag Switch flag to fire event or not
* @returns {Array} An array ob DOM references to the nodes contained in the sortable list
*/
serialise : function(flag){
if(flag || flag == undefined){
this.fireEvent('serialise', this);
this.fireEvent('serialize', this);
}
return Ext.query(this.queryString,this.container);
},
/**
* Function enables DD on the container element
* is a long function to stop evaluation inside loops
* @public
*/
enable : function(){
this.drags = this.serialise(false);

var i = this.drags.length-1;

if(this.handles && this.contextMenu){

while(i >= 0){

Ext.dd.Registry.register(this.drags[i], {
isHandle:false,
handles : [
'handle_' + this.drags[i].id
],
ddGroups : this.ddGroups
}
);
Ext.fly('handle_' + this.drags[i].id).on('contextmenu', this.contextMenu, this, {preventDefault: true});
--i;
}
} else if (this.handles) {
while(i >= 0){
Ext.dd.Registry.register(this.drags[i], {
isHandle:false,
handles : [
'handle_' + this.drags[i].id
],
ddGroups : this.ddGroups
}
);
--i;
}
} else if(this.contextMenu){
while(i >= 0){
Ext.dd.Registry.register(
this.drags[i],
{
ddGroups : this.ddGroups
}
);
Ext.fly(this.drags[i].id).on('contextmenu', this.contextMenu, this, {preventDefault: true});
--i;
}
} else {
while(i >= 0){
Ext.dd.Registry.register(
this.drags[i],
{
ddGroups : this.ddGroups
}
);
--i;
}
}
this.dropZone.unlock();
this.dragZone.unlock();
this.fireEvent('enable', this);
},

/**
* Disable all DD and remove contextMenu listeners
* @public
*/
disable : function(){
this.drags = this.serialise(false);
var i = this.drags.length-1;
if(this.contextMenu){
while(i >= 0){
Ext.dd.Registry.unregister(this.drags[i]);
Ext.fly('handle_' + this.drags[i].id).un('contextmenu', this.contextMenu);
--i;
}
} else {
while(i >= 0){
Ext.dd.Registry.unregister(this.drags[i]);
--i;
}
}
this.dropZone.lock();
this.dragZone.lock();
this.fireEvent('disable', this);
},
/**
* Function enables a single Elements DD within the container
* @public
* @param {String} id The Id of the element you want to add to the DD list
*/
enableElement :function(id){
if(this.handles && this.contextMenu){

Ext.dd.Registry.register(id, {
isHandle:false,
handles : [
'handle_' + id
],
ddGroups : this.ddGroups
}
);
Ext.fly('handle_' + id).on('contextmenu', this.contextMenu, this, {preventDefault: true});

} else if (this.handles) {
Ext.dd.Registry.register(id, {
isHandle:false,
handles : [
'handle_' + id
],
ddGroups : this.ddGroups
}
);
} else if(this.contextMenu){
Ext.dd.Registry.register(id,{
ddGroup : this.ddGroups
});
Ext.fly('handle_' + id).un('contextmenu', this.contextMenu);
} else {
Ext.dd.Registry.register(id,{
ddGroups : this.ddGroups
});
}
this.fireEvent('enableElement', this);
},
/**
* Function disables a single Elements DD within the container
* @public
* @param {String} id The Id of the element you want to disable in the list
*/
disableElement : function(id){
Ext.dd.Registry.unregister(id);
if(this.contextMenu){
Ext.fly('handle_' + id).un('contextmenu', this.contextMenu);
}
this.fireEvent('disableElement', this);
},
/**
* Function switches DD Group from the current one
* @public
* @param {String/Array} The DD Group(s) you want to swap the list from
* @param {String/Array} The DD Group(s) you want to swap the list to
*/
swapDDGroup : function(from,to){

this.removeFromDDGroup(from);
this.addToDDGroup(to);
this.enable();
},

/**
* Function adds elements to a particular DD Group
* @public
* @param {String/Array} DD group(s) you want to add your list to
*/
addToDDGroup : function(groupName,enable){
if(typeof groupName != 'string'){
var i = groupName.length-1;
while(i>=0){
this.ddGroups[groupName[i]]=true;
this.dragZone.addToGroup(groupName[i]);
this.dropZone.addToGroup(groupName[i]);
--i;
}
} else {
this.ddGroups[groupName]=true;
this.dragZone.addToGroup(groupName);
this.dropZone.addToGroup(groupName);
}
if(typeof enable !== 'undefined' || enable){
this.enable();
}
},
/**
* Function removes a list from a particular DD Group
* @public
* @param {String/Array} DD group(s) you want to remove your list from
*/
removeFromDDGroup : function(groupName, enable){
if(typeof groupName != 'string'){
var i = groupName.length-1;
while(i>=0){
this.ddGroups[groupName[i]]=false;
this.dragZone.removeFromGroup(groupName[i]);
this.dropZone.removeFromGroup(groupName[i]);
--i;
}
} else {
this.ddGroups[groupName]=false;
this.dragZone.removeFromGroup(groupName);
this.dropZone.removeFromGroup(groupName);
}
if(typeof enable !== 'undefined' || enable){
this.enable();
}
}
});

Ext.dd.SortableDragZone = function(el, config){
Ext.dd.DragZone.superclass.constructor.call(this, el, config);
};

Ext.extend(Ext.dd.SortableDragZone, Ext.dd.DragZone, {

onInitDrag : function(x, y){
var dragged = this.dragData.ddel.cloneNode(true);
dragged.id='';

if(Ext.isIE){ //IE fix for checkbox and radio
var array_cb = Ext.fly(this.dragData.ddel).select('input[type="checkbox"]');
var array_rb = Ext.fly(this.dragData.ddel).select('input[type="radio"]');
var i = 0;
if (array_cb.elements!=0){
Ext.fly(dragged).select('input[type="checkbox"]').each(function() {
this.dom.defaultChecked = array_cb.elements[i].checked;
i++;
});
}
if (array_rb.elements!=0){
i = 0;
Ext.fly(dragged).select('input[type="radio"]').each(function() {
this.dom.defaultChecked = array_rb.elements[i].checked;
i++;
});
}
}

this.proxy.update(dragged);
this.onStartDrag(x, y);
this.dragData.ddel.style.visibility='hidden';
return true;
},

afterRepair : function(){
this.dragData.ddel.style.visibility='';
this.dragging = false;
},

getRepairXY : function(e){
//uncomment this to show animation
return Ext.Element.fly(this.dragData.ddel).getXY();
},

getNodeData : function(e){
e = Ext.EventObject.setEvent(e);
var target = e.getTarget(this.queryString);
if(target){
this.dragData.ddel = target.parentNode; // the img element
this.dragData.single = true;
return this.dragData;
}
return false;
},
onEndDrag : function(data, e){ }
});

Ext.dd.SortableDropZone = function(el, config){
Ext.dd.DropZone.superclass.constructor.call(this, el, config);
};

Ext.extend(Ext.dd.SortableDropZone, Ext.dd.DropZone, {

notifyEnter : function(source, e, data){
this.srcEl = Ext.get(data.ddel);
if(this._testDDGroup()){

if(this.srcEl !== null){
if(this.srcEl.dom.parentNode !== this.el.dom){
if(!Ext.query(this.queryString,this.el).length > 0 && this.srcEl.is(this.queryString)){
this.srcEl.appendTo(this.el);
}
}
//add DD ok class to proxy
if(this.overClass){
this.el.addClass(this.overClass);
}
return this.dropAllowed;
}
}
},

onContainerOver : function(dd, e, data){
if(this._testDDGroup()){
return this.dropAllowed;
}
},

notifyOver : function(dd, e, data){
if(this._testDDGroup()){
var x;

var n = this.getTargetFromEvent(e);

if(!n){

if(this.lastOverNode){
this.onNodeOut(this.lastOverNode, dd, e, data);
this.lastOverNode = null;
}
return this.onContainerOver(dd, e, data);
}
if(this.lastOverNode != n){
if(this.lastOverNode){
this.onNodeOut(this.lastOverNode, dd, e, data);
}
this.onNodeEnter(n, dd, e, data);
this.lastOverNode = n;
}
return this.onNodeOver(n, dd, e, data);
}
},


onNodeOver : function(n, dd, e, data){
if(this._testDDGroup()){
if(this.horizonatal) {
var x = e.getPageX();
if (x < this.lastX) {
this.goingPrevious = true;
} else if (x > this.lastX) {
this.goingPrevious = false;
}
this.lastX = x;
} else {
var y = e.getPageY();
if (y < this.lastY) {
this.goingPrevious = true;
} else if (y > this.lastY) {
this.goingPrevious = false;
}
this.lastY = y;
}
var destEl = Ext.get(n.ddel);

if((Ext.isIE)&&(this.srcEl !== null)){ //IE fix for checkbox and radio
this.srcEl.select('input[type="checkbox"]').each(function() {
this.dom.defaultChecked = this.dom.checked;
});
this.srcEl.select('input[type="radio"]').each(function() {
this.dom.defaultChecked = this.dom.checked;
});
}

if (this.goingPrevious) {
this.srcEl.insertBefore(destEl);
} else {
this.srcEl.insertAfter(destEl);
}

return this.dropAllowed;
} else {
return this.dropNotAllowed;
}
},

notifyDrop : function(dd, e, data){
if(this._testDDGroup){
if(this.srcEl !== null){
this.srcEl.setStyle('visibility','');
// refresh the drag drop manager
Ext.dd.DragDropMgr.refreshCache(this.groupName);
}
return true;
}
},
_testDDGroup : function(){
var groupTest = Ext.dd.Registry.getTarget(this.srcEl.id).ddGroups;
var result = false;

for(this.groups in groupTest){
if(groupTest[this.groups]){
result=true;
}
}
return result;
}
});


//Ext override to stop error when unloading a page
Ext.dd.DragDropMgr._remove = function(oDD) {
if(oDD){
for (var g in oDD.groups) {
if(this.ids[g]){
if (g && this.ids[g][oDD.id]) {
delete this.ids[g][oDD.id];
}
}
}
delete this.handleIds[oDD.id];
}
}
example :
Replace the array-grid.js by this one :

/*
* Ext JS Library 2.1
* Copyright(c) 2006-2008, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/

Ext.onReady(function(){

Ext.state.Manager.setProvider(new Ext.state.CookieProvider());

var myData = [
['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
];

// example of custom renderer function
function change(val){
if(val > 0){
return '<span style="color:green;">' + val + '</span>';
}else if(val < 0){
return '<span style="color:red;">' + val + '</span>';
}
return val;
}

// example of custom renderer function
function pctChange(val){
if(val > 0){
return '<span style="color:green;">' + val + '%</span>';
}else if(val < 0){
return '<span style="color:red;">' + val + '%</span>';
}
return val;
}

// create the data store
var store = new Ext.data.SimpleStore({
fields: [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
]
});
store.loadData(myData);

// create the Grid
var grid = new Ext.grid.GridPanel({
store: store,
columns: [
{id:'company',header: "Company", width: 160, sortable: true, dataIndex: 'company'},
{header: "Price", width: 75, sortable: true, renderer: 'usMoney', dataIndex: 'price'},
{header: "Change", width: 75, sortable: true, renderer: change, dataIndex: 'change'},
{header: "% Change", width: 75, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
{header: "Last Updated", width: 85, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
],
stripeRows: true,
autoExpandColumn: 'company',
height:350,
width:600,
tbar:new Ext.Toolbar(),
plugins:new Ext.ux.grid.ConfigWindow(),
title:'Array Grid'
});

grid.render('grid-example');

grid.getSelectionModel().selectFirstRow();

});

edit 26/05/2008 : Now compatible with sortable 0.45, included,multi grid problem corrected
edit 27/05/2008 : add params to disable sort/size/display columns. Manage unresizable/unsortable/unhideable grid columns.
edit 30/05/2008 : Live demo

garraS
22 May 2008, 8:54 PM
Nice! Thanks!

jelt
22 May 2008, 11:17 PM
Added Rowan patch for Sortable.

GridConfigWindow seem to be incompatible with the lattest Sortable 0.4
I'm trying to fix that and come back latter...

Animal
23 May 2008, 12:28 AM
You might be able to use this function in your extension. It allows multi-column, multi-order sorting directly on a Store (which will immediately fire an event which updates any UI widgets bound to it)

The docs are in the usual format...



Ext.override(Ext.data.Store, {
/**
* Sort by multiple fields in the specified order.
* @param {Array} An Array of field sort specifications, or, if ascending
* sort is required on all columns, an Array of field names. A field specification
* looks like:<pre><code>
{
field: 'orderNumber',
direction: 'ASC'
}
</code><pre>
*/
sortByFields: function(fields) {

// Collect sort type functions,
// Convert string field names to field+direction spec objects.
var st = [];
for (var i = 0; i < fields.length; i++) {
if (typeof fields[i] == 'string') {
fields[i] = {
field: fields[i],
direction: 'ASC'
};
}
st.push(this.fields.get(fields[i].field).sortType);
}

var fn = function(r1, r2) {
var result;
for (var i = 0; !result && i < fields.length; i++) {
var v1 = st[i](r1.data[fields[i].field]);
var v2 = st[i](r2.data[fields[i].field]);
result = (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0);
if (fields[i].direction == 'DESC') result = -result;
}
return result;
};
this.data.sort('ASC', fn);
if(this.snapshot && this.snapshot != this.data){
this.snapshot.sort('ASC', fn);
}
this.fireEvent("datachanged", this);
}
});

jelt
23 May 2008, 1:28 AM
I will see if i can apply to my project, which use paging and local proxy (BufferedPagingMemoryProxy). So, currently "sortByFields" function apply multi sort only on active page.

But it's a good start for multi-sort. Thanks Animal :)

jelt
23 May 2008, 5:10 AM
Code updated :

- toolbar is no more required. You can set position to 'top' or 'bottom' to let UX built a button for you, or you can set position to '' and manage this button yourself. Just call the .showWindow() function to display grid config window.

- Localisation facility : all texts are now configurable.

- Maximizable button : now optional

nareshjois
26 May 2008, 2:55 AM
Great Work
The problem I am facing with code is if we use this on two grids in a single page, this causes errors, I am not a javascript expert but i can inform that this caused because the cm of the first accessed grid is used instead of the selected one.

jelt
26 May 2008, 3:36 AM
Ouch, there is many problems using GridConfigWindow on 2 or more grids on the same page.

I haven't designed with this in mind... So i will have a look to try to manage them.

Thank you for reporting.

jelt
26 May 2008, 4:44 AM
It was problem with IDs of the generated compounds of the window. Corrected in the first post, you can test :)

nareshjois
26 May 2008, 9:48 AM
Thanks, this works correctly now ,
just a thing please remove the "console.log(field);" at line number 128,
and any ideas on how can the sort/visible/size fields optional.

jelt
26 May 2008, 10:07 AM
rhoooo noob inside :"> sorry for the console.log -_-

i will add parameters tomorow to set each function optional

Oh, i will add also the management of unresizable/unsortable columns

jelt
27 May 2008, 4:03 AM
Hi all,

3 new params :
,sortColumn:true
,displayColumn:true
,sizeColumn:true

Just set it to false to hide corresponding column in the grig config window.

If a column is defined as not resizable/sortable/hideable in the column model, you cannot do it more with grid config window (option is disabled)

updated first post

nareshjois
28 May 2008, 7:47 PM
Thanks jelt for the great work , this works great

jelt
30 May 2008, 12:36 AM
Live demo added :)

mattdennewitz
11 Jun 2008, 11:17 AM
could Animal's code be applied to a regular datagrid to allow for multi-column, multi-order sorting?

jelt
20 Jun 2008, 4:22 AM
Not so easy Mattdennewitz

There are some problems to solve before publishing this :

- The animal's function given work only on visible data. With paged data for example, it sort only on the current page. Not really a problem because paging = server, so, i need to manage local/remote sort, and manage the page navigation.

- The sort is correctly applied, but not managed in standard store/grid parameters nor in the column sort display.

JasonMichael
23 Jun 2008, 10:05 AM
Animal,

Thanks for that multi-field sort addition. Brilliant work.

This little extension is pretty good too, Jety. An additional, cute little way to do the sorting, resizing, etc, other than using the Grid controls. Nicely programmed!

nzosig
3 Jul 2008, 10:43 PM
Hello Jelt,

I am very interested in your project Ext4SAP as I am using Ext for building frontend interfaces with a SAP WAS 6.40. By now the SAP-WAS is only used for delivering the needed data as XML strings to fill grids an forms.

I am not an ABAP-Developer, this part are taking my colleagues. But with EXT it should be possible to create the HTMLB-Elements in BSP-Applications in an ajaxian and browser-independent way.

If you are also interested in exchanging some ideas feel free to contact me under n.zosig(at)it-coburg.de

And, after all, dear community - please excuse me when I made some mistakes. This is my first posting here. I learned so much about this fascinating framework here in the forum that there was no need until today to write an active post.

Best regards from Coburg (City), Upper Franconia (District), Bavaria(State), Germany

jelt
17 Jul 2008, 2:57 AM
Hi Nzosig,

EXT4SAP is intended to provide a modern report design, embed in the sap frontend without using sap portal/java or WAS/WAD.

It must be installed by an abap developer (dev. key required)

Then, you can call it in all your reports for the rendering part. (usually, a report can be splited in 3 step : get data, process data, render data)

EXT4SAP take the place of the ALV functions, with very similar parameters, so it's easy for your abap dev. to use it.

Currently, i have not found an easy way to do ajax request. Data are stored in a js array, and i use a local paging to improve render performance.

Jelt

Mthor
17 Jul 2008, 7:14 AM
hey jelt on this function I get an error


,onRender:function() {
var grid = this.grid;
var tb = 'bottom' == this.position ? grid.bottomToolbar : grid.topToolbar;
if(0 < tb.items.getCount()) { tb.addSeparator(); }
this.layoutButton = new Ext.Button({
text: this.buttonText
, cls: 'x-btn-text-icon details'
,iconCls:this.iconCls
,tooltip:this.buttonTipText
});
tb.add(this.layoutButton);
this.layoutButton.on('click',function() { this.showWindow();},this);
}


tb is undefined
[Break on this error] if(0 < tb.items.getCount()) { tb.addSeparator(); }

any help, thanks

jelt
21 Jul 2008, 3:50 AM
Hi Mthor,

I think you have not created a toolbar in your grid (bottom or top).

Add this option in gridconfig panel :
position:''

or create a toolbar in your grid.

In my livedemo, it's done like this with a toolbar :

var grid = new Ext.grid.GridPanel({
store: store,
columns: [ ... ],
stripeRows: true,
height:350,
width:600,
tbar:new Ext.Toolbar(),
plugins:new Ext.ux.grid.ConfigWindow(),
title:'Array Grid'
});
or like this without toolbar (be carefull, you need to call window mannually with the function showWindow() )


var grid = new Ext.grid.GridPanel({
store: store,
columns: [ ... ],
stripeRows: true,
height:350,
width:600,
plugins: config_window = new Ext.ux.grid.ConfigWindow(
{position:''
,windowMaximizable:false })
,title:'Array Grid French'
});

Mthor
22 Jul 2008, 8:21 AM
cool thanks, I got this cool plugin to work. I do have some questions though. It seems when I refresh the page that the columns go back to the default size. is there a way to get the cookies to read this.

also when I apply the changes the first time the layout changes then the second time I try and do a layout change I get this error

this.field is null
Ext.Editor=function(B,A){this.field=B;Ex...ld=null}});Ext.reg("editor",Ext.Editor);

the error is coming from the ext-all.js

just wondering if you have run into this before.

thanks for the cool plugin

jelt
23 Jul 2008, 12:50 AM
It seems when I refresh the page that the columns go back to the default size. is there a way to get the cookies to read this.
Yes you have a solution on classic extjs grid to keep in memory the config, but i never used this. Stateid i think ?


also when I apply the changes the first time the layout changes then the second time I try and do a layout change I get this error

I need more info (code) to reproduce this problem. Use you an editable grid ? Never try the plugin with this kind of grid.

tchitani
1 Apr 2009, 7:22 PM
hi, does this great plugin work with Editor Grid? what needs to be done to fix the error:
this.field is null
Ext.Editor=function(B,A){this.field=B;Ex...ld=null}});Ext.reg("editor",Ext.Editor);
thanks

tchitani
6 Jun 2009, 1:18 AM
Hi

How to use this plugin with Checkboxselection plugin? There are many errors...
Any help?

Thanks