PDA

View Full Version : Ext.ux.grid.DragDropPlugin



Ronaldo
25 Jan 2009, 1:22 PM
Hi all,

I was using Drag'n drop in a grid for the first time, and noticed that the treepanel has drag'n drop events build in, I wanted these events too in the gridpanel.
(See my feature request. (http://extjs.com/forum/showthread.php?t=58167))

For now (v2.2) I created this functionality using a plugin:


Ext.ux.grid.DragDropPlugin = function(config) {
Ext.apply(this, config);
}

Ext.ux.grid.DragDropPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.addEvents('drop','dragover','dragout');
grid.on({
destroy: {scope: this, fn:this.onDestroy},
render: {scope:this, fn:this.onRender}
});
},
onDestroy: function() {
delete(this.grid);
this.target.destroy();
},
onRender: function() {
this.dropTarget = new Ext.dd.DropTarget(this.grid.getEl(), {
ddGroup : this.grid.ddGroup || 'GridDD',
grid : this.grid,
plugin: this,
notifyDrop : function(dd, e, data) {
return this.grid.fireEvent('drop', this.grid, dd, e, data);
},
notifyOver: function(dd, e, data) {
return this.grid.fireEvent('dragover', this.grid, dd, e, data);
},
notifyOut: function(dd, e, data) {
return this.grid.fireEvent('dragout', this.grid, dd, e, data);
}
});
}
};Which can be used in the grid as


MyGrid = Ext.extend(Ext.grid.GridPanel, {
...
enableDragDrop:true,
...
initComponent : function() {
this.store = ...

this.plugins = [
new Ext.ux.grid.DragDropPlugin()
];

MyGrid.superclass.initComponent.apply(this, arguments);

this.on({
drop: {scope: this, fn:this.onDrop},
dragover: {scope:this, fn:this.onDragOver},
dragout: {scope: this, fn:this.onDragOut}
});
},
onDrop: function(dd, e, data) {
...
},
onDragOver: function(dd, e, data) {
...
},
onDragOut: function(dd, e, data) {
...
},
...
});
// What I want is a drag'n drop reorder plugin using the command pattern (in addition to my other commands (http://extjs.com/forum/showthread.php?p=262951)). The outline for this command is


Ext.ux.grid.DragDropReorderPlugin = function(config) {
Ext.apply(this, config);
}

Ext.ux.grid.DragDropReorderPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.on({
drop: {scope: this, fn:this.onDrop},
dragover: {scope:this, fn:this.onDragOver},
dragout: {scope: this, fn:this.onDragOut}
});
},
onDrop: function(dd, e, data) {

},
onDragOver: function(dd, e, data) {

},
onDragOut: function(dd, e, data) {

}
}I intend to manipulate this code (http://extjs.com/forum/showthread.php?t=21913#10) and plug it into the Ext.ux.grid.DragDropReorderPlugin in a few days, so grid reordening becomes as easy as:


MyGrid = Ext.extend(Ext.grid.GridPanel, {
...
enableDragDrop:true,
...
initComponent : function() {
this.store = ...

this.plugins = [
new Ext.ux.grid.DragDropPlugin(),
new Ext.ux.grid.DragDropReorderPlugin()
];

MyGrid.superclass.initComponent.apply(this, arguments);
},
...
});
// I hope nobody objects to manipulating the code I found. If anyone does, please let me know. Of course I'll post the code here.

Ronaldo

galdaka
25 Jan 2009, 11:45 PM
Is posible live example?

Thanks in advance,

mystix
27 Jan 2009, 6:41 PM
@ronaldo, late last year, i made a plugin out of some of the code from the post you mentioned, but i never had to use it.
it's pretty rough and minimally tested, but if it speeds up your work, feel free to rip it to shreds ;)


// Plugin to enable grid row drag and drop (requires a RowSelectionModel)
// adapted from @clarkke8's example -- http://extjs.com/forum/showthread.php?p=108306#post108306
Ext.ux.dd.GridReorderDropTarget = function(cfg) {
this.config = cfg || {};

if (cfg.listeners) {
this.listeners = cfg.listeners;
}
};
Ext.extend(Ext.ux.dd.GridReorderDropTarget, Ext.util.Observable, {
init: function(grid) {
this.addEvents(
"beforerowmove",
"afterrowmove",
"beforerowcopy",
"afterrowcopy"
);

Ext.apply(grid, {
enableDragDrop: true, // enable grid drag/drop

// if necessary, replace the grid's selection model with a new RowSelectionModel
selModel: grid.selModel instanceof Ext.grid.RowSelectionModel? grid.selModel : new Ext.grid.RowSelectionModel({
singleSelect: !!this.config.singleSelect
})
});

grid.on({
render: function(self) { // create dd.DropTarget when grid has been rendered
this.target = new Ext.dd.DropTarget(self.getEl(), {
ddGroup: self.ddGroup || 'GridDD',
grid: self,

notifyDrop: function(dd, e, data) {
// determine the drop row
var t = Ext.lib.Event.getTarget(e),
g = this.grid,
ds = g.getStore(),
sm = g.getSelectionModel(),
v = g.getView(),
rindex = v.findRowIndex(t);

if (rindex === false || rindex == data.rowIndex ||
// fire the before move/copy event on the GridView
v.fireEvent(this.copy? 'beforerowcopy' : 'beforerowmove', v, data.rowIndex, rindex, data.selections) === false
) {
return false;
}

// update the grid's store
if (!this.copy) {
for (var i = 0; i < data.selections.length; i++) {
ds.remove(ds.getById(data.selections[i].id));
}
}
ds.insert(rindex, data.selections);

// re-select the row(s)
sm.selectRecords(data.selections);

// fire the after move/copy event on the GridView
v.fireEvent(this.copy? 'afterrowcopy' : 'afterrowmove', v, data.rowIndex, rindex, data.selections);

return true; // suppress drag source's repair action
},

notifyOver: function(dd, e, data) {
var t = Ext.lib.Event.getTarget(e),
rindex = this.grid.getView().findRowIndex(t);

if (rindex == data.rowIndex) {
rindex = false;
}

return (rindex === false)? this.dropNotAllowed : this.dropAllowed;
}
});

Ext.apply(this.target, this.config);

// add RowNumberer / PagingRowNumberer detection
Ext.each(grid.getColumnModel().config, function(c) {
if (c instanceof Ext.grid.RowNumberer || c instanceof Ext.ux.grid.PagingRowNumberer) {
// refresh the gridview on row copy / move
self.getView().on(this.isCopy()? 'afterrowcopy' : 'afterrowmove', function(self) {
self.refresh();
});

return false;
}
}, this);

// register GridView's scroller with the ScrollManager (to enable scrolling)
Ext.dd.ScrollManager.register(self.getView().getEditorParent());
},

beforedestroy: function(self) {
// unregister GridView's scroller from ScrollManager
Ext.dd.ScrollManager.unregister(self.getView().getEditorParent());
},

// set the scope for all grid handlers to
// this GridReorderDropTarget plugin
scope: this
});
},

getTarget: function() {
return this.target;
},

isCopy: function() {
return !!this.getTarget().copy;
}
});

HTH :)

p.s. you might need to add some cleanup methods to the plugin() when destroying its parent grid.

Ronaldo
27 Jan 2009, 11:11 PM
Hi Mystix,

Thanks for sharing! However, I have fixed it already. And it even accepts drag'n drop from other sources like a tree ;)

Ronaldo

mystix
27 Jan 2009, 11:42 PM
Hi Mystix,

Thanks for sharing! However, I have fixed it already. And it even accepts drag'n drop from other sources like a tree ;)

Ronaldo

ooh.. goody. my turn to do some ripping >:)

Ronaldo
28 Jan 2009, 11:35 AM
Well, here you go ;)
Please note that I use the command pattern for all my grid actions, which I hope to publish soon.


Ext.ux.grid.DragDropPlugin = function(config) {
Ext.apply(this, config);
}

Ext.ux.grid.DragDropPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.addEvents('beforedrop','drop','dragover','dragout');
grid.on({
destroy: {scope: this, fn:this.onDestroy},
render: {scope:this, fn:this.onRender}
});
},
onDestroy: function() {
delete(this.grid);
this.target.destroy();
},
onRender: function() {
this.dropTarget = new Ext.dd.DropTarget(this.grid.getEl(), {
ddGroup : this.grid.ddGroup || 'GridDD',
grid : this.grid,
plugin: this,
notifyDrop : function(dd, e, data) {
if(this.grid.fireEvent('beforedrop', this.grid, dd, e, data) === false)
return this.dropNotAllowed;

return this.grid.fireEvent('drop', this.grid, dd, e, data);
},
notifyOver: function(dd, e, data) {
return this.grid.fireEvent('dragover', this.grid, dd, e, data);
},
notifyOut: function(dd, e, data) {
return this.grid.fireEvent('dragout', this.grid, dd, e, data);
}
});
}
};
Ext.ux.grid.DragDropReorderPlugin = function(config) {
Ext.apply(this, config);
}

Ext.ux.grid.DragDropReorderPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.addEvents('beforeruncmd');
grid.on( {
drop : {
scope :this,
fn :this.onDrop
},
dragover : {
scope :this,
fn :this.onDragOver
},
dragout : {
scope :this,
fn :this.onDragOut
}
});
},
onDrop : function(grid, dd, e, data) {
var t = Ext.lib.Event.getTarget(e);
var rowIndex = this.grid.getView().findRowIndex(t);

var cmd = new Ext.ux.grid.ReorderCommand({
point : this.currentRowEl && this.currentRowEl.hasClass('grid-row-insert-below') ? "below" : "above",
rowIndex : rowIndex
});

if(dd.dragData && dd.dragData.grid != this.grid) {
// Hack: We're dragging data from another component onto the grid; fake a drop command
cmd.cmdName = 'dropCmd';
cmd.dragData = dd.dragData;
}

if (this.currentRowEl) {
this.currentRowEl.removeClass('grid-row-insert-below');
this.currentRowEl.removeClass('grid-row-insert-above');
}

var s = grid.getSelection();
if(grid.fireEvent('beforeruncmd', '', s, cmd) !== false) {
cmd.run(null, grid, s);
}

},
onDragOver : function(grid, dd, e, data) {
var t = Ext.lib.Event.getTarget(e);
var rindex = this.grid.getView().findRowIndex(t);

try {
var currentRow = this.grid.getView().getRow(rindex);
// Find position of row relative to page (adjusting for grid's scroll position)
var resolvedRow = new Ext.Element(currentRow).getY() - this.grid.getView().scroller.dom.scrollTop;
var rowHeight = currentRow.offsetHeight;

// Cursor relative to a row. -ve value implies cursor is above the
// row's middle and +ve value implues cursor is below the row's
// middle.
this.rowPosition = e.getPageY() - resolvedRow - (rowHeight / 2);

// Clear drag line.
if (this.currentRowEl) {
this.currentRowEl.removeClass('grid-row-insert-below');
this.currentRowEl.removeClass('grid-row-insert-above');
}

if (this.rowPosition > 0) {
// If the pointer is on the bottom half of the row.
this.currentRowEl = new Ext.Element(currentRow);
this.currentRowEl.addClass('grid-row-insert-below');
} else {
// If the pointer is on the top half of the row.
if (rindex - 1 >= 0) {
var previousRow = this.grid.getView().getRow(rindex - 1);
this.currentRowEl = new Ext.Element(previousRow);
this.currentRowEl.addClass('grid-row-insert-below');
} else {
// If the pointer is on the top half of the first row.
this.currentRowEl.addClass('grid-row-insert-above');
}
}
} catch (err) {
// console.warn(err);
rindex = false;
}
return (rindex === false) ? dd.dropNotAllowed : dd.dropAllowed;
},
onDragOut : function(grid, dd, e, data) {
// Remove drag lines when pointer leaves the gridView.
if (this.currentRowEl) {
this.currentRowEl.removeClass('grid-row-insert-above');
this.currentRowEl.removeClass('grid-row-insert-below');
}
}
}Here's how I use it


Ext.ux.twensoc.MetaGridDefinitionFieldGrid = Ext.extend(Ext.grid.GridPanel, {
name:'MetaGridDefinitionFieldGrid',
border :false,
autoScroll :true,
viewConfig:{forceFit:true},
enableDragDrop:true,
viewConfig : {
forceFit :true
},
layout :'fit',
enableDragDrop :true,
initComponent : function() {
this.store = new Ext.data.SimpleStore( {
id :0,
fields : [ 'id', 'name']
});

this.columns = [ {name :'name', header :'Field'} ];

this.tbar = new Ext.Toolbar();
var g = Ext.ux.grid;
this.plugins = [
new g.DragDropPlugin(),
new g.DragDropReorderPlugin(),
new g.ToolbarAdapterPlugin({
cmdCfg: {baseUrl: this.baseUrl},
items:[
g.TbItemMoveUp,
g.TbItemMoveDown,
'-',
g.TbItemDeleteFromStore
]
})
];

Ext.ux.twensoc.MetaGridDefinitionFieldGrid.superclass.initComponent.apply(this, arguments);

this.on({
beforeruncmd: {scope:this, fn:this.onBeforeRunCmd}
});
},
onBeforeRunCmd: function(btn, s, cmd) {
switch(cmd.cmdName) {
case 'dropCmd':
this.addDraggedRecords(cmd.dragData.data, s);
break;
default:
break;
}
},
getMetaDataFields: function() {
var r = [];
this.store.data.each(function(fld) {
var d = fld.data;
r.push({name: d.id, type: d.type ? d.type : 'string' });
},this);
return r;
},
addDraggedRecords: function(data, s) {
var records = [], ds=this.store;
s.length=0; // clear all selections
for(var i=0;i<data.length;i++) {
if(!ds.getById(data[i].id)) {
records.push([data[i].id, data[i].name]);
s[s.length] = data[i];
}
}
if(records.length>0) {
records = ds.reader.readRecords(records);
ds.add(records['records']);
this.getSelectionModel().selectRecords(records['records']);
}
},
getSelection: function() {
var s, sm = this.getSelectionModel();
// Row selection model
if (sm.getSelected) {
s = sm.getSelected();
s = s ? [s] : [];
return s;
}
return [];
}
}); But then you need the command adapter and the commands


/**
* @class Ext.ux.grid.ToolbarAdapterPlugin
* @Author Ronald van Raaphorst
* @Version 1.0
* @License Undecided yet, will do so soon.
*/
Ext.ux.grid.ToolbarAdapterPlugin = function(config) {
Ext.apply(this, config);
}

/** @private */
Ext.ux.grid.ToolbarAdapterPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.addEvents('beforeexecutecmd','afterexecutecmd','beforeruncmd');
this.createToolbar(grid);
grid.getSelectionModel().on('selectionchange', this.onSelectionChanged, this);
grid.on({show:{scope:this,fn:this.onSelectionChanged}});
grid.on({render:{scope:this,single:true,fn:this.onSelectionChanged}});
},
destroy: function() {
delete(this.grid);
},
createToolbar : function(grid) {
var tb = grid.topToolbar || grid.bottomToolbar;
if(!tb) return;
if(!tb.buttons) tb.buttons = [];

var items=this.items, p, itm;

var c = {
grid: grid,
cmdCfg: this.cmdCfg,
scope: this,
handler: this.onBtnClick
};

for(var i=0,n=items.length; i<n;i++) {
itm = items[i];
if(itm=='-' || itm=='->' || typeof itm.getCmd!='function')
p=itm;
else {
// Modifying the item; clone it first.
if(Ext.isArray(itm)) {
p = Ext.apply({}, c, itm[0]);
p = Ext.apply(p, itm[1]);
} else {
p = Ext.apply({}, c, itm);
};
}
tb.buttons.push(p);
}
if(typeof this.grid.createToolbar=='function')
this.grid.createToolbar(tb);
},
onBtnClick: function(btn) {
var s=this.grid.getSelection();
if(btn.getCmd) {
var cmd = btn.getCmd(this.cmdCfg);
if(this.grid.fireEvent('beforeruncmd', this.grid, btn, s, cmd) !== false) {
cmd.run(btn, this.grid, s);
}
}
},
onSelectionChanged: function(sm, s) {
s = this.grid.getSelection();
var cmd, fn, tbi = this.grid.topToolbar.items.items;
for(var i=0, n=tbi.length; i<n;i++) {
fn=tbi[i].isEnabledFn;
if(!fn) continue;
if(fn && typeof fn=='function') {
if(fn(this.grid, s)==true)
tbi[i].enable();
else
tbi[i].disable();
}
}
}
}
Ext.ux.grid.MoveUpCommand = Ext.extend(Ext.ux.grid.ReorderCommand, {
run : function(btn, grid, selection) {
var r = grid.store.getById(selection[0].id);
this.rowIndex = grid.store.indexOf(r);
this.point = 'above';
Ext.ux.grid.MoveUpCommand.superclass.run.call(this, btn, grid, selection);
}
});

Ext.ux.grid.MoveDownCommand = Ext.extend(Ext.ux.grid.ReorderCommand, {
run : function(btn, grid, selection) {
var r = grid.store.getById(selection[0].id);
this.rowIndex = grid.store.indexOf(r);
this.point = 'below';
Ext.ux.grid.MoveDownCommand.superclass.run.call(this, btn, grid, selection);
}
});

Ext.ux.grid.TbItemMoveUp = {
cmdName :'moveUpCmd',
iconCls :'iconMoveUp',
tooltip : {
text :'Reposition the selected item.',
title :'Move up'
},
getCmd : function(cfg) {
return new Ext.ux.grid.MoveUpCommand(cfg);
},
isEnabledFn :Ext.ux.grid.isEnabledFn
}

Ext.ux.grid.TbItemMoveDown = {
cmdName :'moveDownCmd',
iconCls :'iconMoveDown',
tooltip : {
text :'Reposition the selected item.',
title :'Move down'
},
getCmd : function(cfg) {
return new Ext.ux.grid.MoveDownCommand(cfg);
},
isEnabledFn :Ext.ux.grid.isEnabledFn
}

Ext.ux.grid.TbItemDeleteFromStore = {
cmdName :'simpleDeleteCmd',
iconCls :'iconDelete',
tooltip : {
text :'Delete the selected items.',
title :'Delete'
},
getCmd : function(cfg) {
return new Ext.ux.grid.SimpleDeleteCommand(cfg);
},
isEnabledFn :Ext.ux.grid.isEnabledFn
}

Ext.ux.grid.ReorderCommand = Ext.extend(Ext.ux.grid.Command, {
cmdName :'reorderCommand',
point :'unknown', // 'above' or 'below'
rowIndex :-1, // the target row
copy :false,
run : function(btn, grid, selection) {
var req = {
selection :selection,
cmd :this,
params : {
point :this.point,
rowIndex :this.rowIndex,
cmdName :this.cmdName
}
};

if (grid.fireEvent('beforeexecutecmd', grid, req) === false)
return;

// update the store
var ds = grid.getStore();

// The new index of the selection's first record is
// above or below the this.rowIndex
var newRowIndex;
if (this.point == 'above') {
newRowIndex = this.rowIndex == 0 ? 0 : this.rowIndex - 1;
} else {
newRowIndex = this.rowIndex < ds.data.length - 1 ? this.rowIndex + 1 : ds.data.length - 1;
}

// Prevent useless removing and inserting when the top
// or bottom is reached
// if (newRowIndex == this.rowIndex)
// return;
this.rowIndex = newRowIndex;

// Save the selected records and remove them from the
// store
var s = [], r;
for ( var i = 0; i < selection.length; i++) {
r = ds.getById(selection[i].id);
if (r) {
s.push(r);
if (!this.copy) {
ds.remove(r);
}
}
}

// Reinsert the selection
for ( var i = selection.length - 1; i >= 0; i--) {
ds.insert(this.rowIndex, s[i]);
}

// re-select the row(s)
var sm = grid.getSelectionModel();
if (sm) {
sm.selectRecords(s);
}
}
});And last but not least, the current version of the base command class for grid commands:


/**
* @class Ext.ux.grid.TreeCommand
* @Author Ronald van Raaphorst
* @Version 1.0
* @License LGPL
*/
Ext.ux.grid.Command = function(cfg){
Ext.apply(this,cfg);
};

/** @private */
Ext.ux.grid.Command.prototype = {
/**
* @cfg {String} baseUrl If the baseUrl property is set,
* the full url called is the concatenation of the baseUrl and the url (baseUrl + url).
* Defaults to null (not set).
*/
baseUrl: null,
/**
* @cfg {String} url The url to request when the command is executed. If the baseUrl property is set,
* the full url called is the concatenation of the baseUrl and the url (baseUrl + url).
*/
url: 'unknown url',
/**
* @cfg {String} cmdName Name of the command to send as parameter of the request.
*/
cmdName: 'unknown gridcommand',
/**
* @cfg {String} method Method used to call the url. Either 'post' or 'get'.
*/
method: 'post',
/**
* @cfg {Integer} maxMsgLen
*/
maxMsgLen: 80,
/**
* onDestroy listener that deletes every object reference in this instance.
*/
onDestroy: function() {
for(var p in this) {
if(typeof this[p] == 'object')
this[p] = null;
}
},
/**
* Executes the ajax request to the server. This method is called by most commands that inherit from
* the this object. This method sets the following request properties, set by the commands that inherit from
* this object.
* <br/><p>Options set</p><ul>
* <li><b>url</b> Url to call. This url is composed of the baseUrl concatenated with the url</li>
* <li><b>method</b> Method used to call the url (post or get).</li>
* <li><b>scope</b> The scope is set to this object instance.</li>
* <li><b>callback</b> Set to the callback method of this object.</li>
* </ul>
* <br/><p>Parameters set</p><ul>
* <li><b>cmdName</b> Sets the cmdName parameter to send as request parameter.</li>
* </ul><br/>
* @param {Object} options Basic options linked to the ajax request.
* @param {Object} params Parameters to be passed with the ajax request to the server.
*/
execute : function(options, params) {
this.lastParams = params;
var req = Ext.apply({
url: this.baseUrl ? this.baseUrl + this.url : this.url,
method: this.method,
scope: this,
callback :this.callback,
params: Ext.apply({
cmdName: this.cmdName
}, params)
}, options);
if(this.grid.fireEvent('beforeexecutecmd', this.grid, req)===false)
return;
if(this.eventName) {
if(this.grid.fireEvent('before'+this.eventName, this.grid, req)!==false)
Ext.Ajax.request(req);
} else
Ext.Ajax.request(req);
},
/**
* Placeholder for the onSuccess handler called by the callback method.
* Override this method if you need to perform specific command logic when the server request
* has returned succesfully.
* When overriding this method, be sure to call this one too as it sends the after event on behalf of the grid.
* @param {Object} req The original ajax request.
* @param {Object} dr The decoded response.
* @param {Object} response The complete ajax response.
*/
onSuccess:function(req, dr, response) {
if(typeof this.undo == 'function' && this.grid && this.grid.cmdStack && !this.addedToCmdStack) {
this.addedToCmdStack = true;
this.grid.cmdStack.add(this);
}
if(this.eventName)
this.grid.fireEvent('after'+this.eventName, this.grid, req, response);
},
/**
* Placeholder for the onFailure handler called by the callback method.
* Either the server has returned an error code (ie. 404) or the server
* responded with success=false. (In JSON: {success:'false'}).
* Override this method if you need to perform specific command logic when the server request
* has returned unsuccesfully.
* When overriding this method, be sure to call this one too as it shows the error in a Messagebox.
* @param {Object} req The original ajax request.
* @param {Object} dr The decoded response.
* @param {Object} response The complete ajax response.
*/
onFailure:function(req, dr, response) {
if(dr && dr.errors) {
this.showError('Error', dr.errors);
} else {
this.showError('Error', response.responseText, true);
}
},
/**
* Utility function to show error messages returned by the server.
* @param {String} title Title of the error message box.
* @param {Mixed} msg Single message as string, or an Array of messages to be displayed.
* @param {Boolean} fullMsgView . Boolean value defining wheter to limit the message length.
* Defaults to true.
*/
showError: function(title, msg, fullMsgView) {
var m=null, fm=Ext.util.Format.ellipsis;
if(Ext.isArray(msg)) {
m='';
for(var i=0;i<msg.length;i++) {
if(i>0) m += '<br/>';
m += fm(msg[i].msg, this.maxMsgLen);
}
}
Ext.Msg.show({
title:title || this.errorText,
msg: m ? m : !fullMsgView ? fm(msg, this.maxMsgLen) : msg,
fixCursor:true,
icon:Ext.Msg.ERROR,
buttons:Ext.Msg.OK,
minWidth: 360
});
},
/**
* Standard callback method for the ajax request.
* @param {Object} req The original ajax request.
* @param {Boolean} success Boolean value indicating whether the request was succesfull.
* @param {Object} response The complete ajax response.
*/
callback:function(req, success, response) {
if(success !== true) {
this.onFailure(req, null, response);
} else {
try {
var dr = Ext.decode(response.responseText);
if(dr.success === true)
this.onSuccess(req, dr, response);
else
this.onFailure(req, dr, response);
}
catch(ex) {
this.onFailure(req, null, response);
}
}
},
cmdToStack:function() {
if(this.grid && typeof this.grid.cmdStack=='object') {
this.grid.cmdStack.add(this);
}
},
/**
* Update node data returned from the server.
* Some commands may optionally return extra data when executing the command. The {@link Ext.ux.grid.NewCmdPlugin}
* for example expects the new id to be returned.
* @param {Object} n The node to be updated.
* @param {Object} d The data returned from the server.
*/
updateNodeData: function(n, d){
if(!n || !d) return;
n.id = d.id;
for(var p in d)
n.attributes[p] = d[p];
if(d.iconCls)
n.iconCls(d.iconCls);
if(d.leaf)
n.leaf = d.leaf;
}
}

galdaka
28 Jan 2009, 1:12 PM
Please attach all in a zip file for upload to www.jadacosta.es (http://www.jadacosta.es) and view live demo!!!

Thanks in advance,

ttbgwt
14 Oct 2009, 5:45 AM
I have a grid using a grouping view but I would like to be able to drag a grid row and drop onto a grouping header. Any examples on how to recognize a drop event on the group header?

MichaelOstrovsky
15 Mar 2010, 8:58 AM
thanks for sharing the code. i really needed reorder functionality, and modified your plugins for that. two points i resolved are :
1. the commands stuff is really over-killing for simple dd+reorder. i cleaned it out from there.
2. the reorder calculations were incorrect. i modified it so the first record always lands where the line displayed ( as expected ).

use:


{
title:'Presets',
xtype:'grid',
viewConfig:{
forceFit: true,
autoFill:true,

},
hideHeaders:true,
enableDragDrop:true,
plugins:[
new Ext.ux.grid.DragDropPlugin(),
new Ext.ux.grid.DragDropReorderPlugin()
],
sm: new Ext.grid.RowSelectionModel()
//....}


the modified code (2 plugins in single file ) :




Ext.ux.grid.DragDropPlugin = function(config) {
Ext.apply(this, config);
}

Ext.ux.grid.DragDropPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.addEvents('beforedrop','drop','dragover','dragout');
grid.on({
destroy: {scope: this, fn:this.onDestroy},
render: {scope:this, fn:this.onRender}
});
},
onDestroy: function() {
delete(this.grid);
this.target.destroy();
},
onRender: function() {
this.dropTarget = new Ext.dd.DropTarget(this.grid.getEl(), {
ddGroup : this.grid.ddGroup || 'GridDD',
grid : this.grid,
plugin: this,
notifyDrop : function(dd, e, data) {
if(this.grid.fireEvent('beforedrop', this.grid, dd, e, data) === false)
return this.dropNotAllowed;

return this.grid.fireEvent('drop', this.grid, dd, e, data);
},
notifyOver: function(dd, e, data) {
return this.grid.fireEvent('dragover', this.grid, dd, e, data);
},
notifyOut: function(dd, e, data) {
return this.grid.fireEvent('dragout', this.grid, dd, e, data);
}
});
}
};

Ext.ux.grid.DragDropReorderPlugin = function(config) {
Ext.apply(this, config);
}

Ext.ux.grid.DragDropReorderPlugin.prototype = {

init : function(grid) {
this.grid = grid;
grid.on( {
scope:this,
drop : this.onDrop,
dragover : this.onDragOver,
dragout : this.onDragOut
});
},

onDrop : function(grid, dd, e, data) {

var ds = grid.getStore();
var sm = grid.getSelectionModel();
var v= grid.getView();

var tEl = Ext.lib.Event.getTarget(e);
var targetIndex = v.findRowIndex(tEl);

var sRec= sm.getSelected();
var sourceIndex= ds.indexOf(sRec);

newIndex = targetIndex;

if (sourceIndex != targetIndex){
if (targetIndex < sourceIndex)
newIndex++;
var point = this.currentRowEl && this.currentRowEl.hasClass('grid-row-insert-below') ? "below" : "above";
if (point === 'above')
newIndex--;
}

newIndex.constrain(0,ds.data.length - 1);

targetIndex = newIndex;

var selections=sm.getSelections();

var temp = [], r;
for ( var i = 0; i < selections.length; i++) {
if (r = ds.getById(selections[i].id)) {
temp.push(r);
ds.remove(r);
}
}

for ( var i = selections.length - 1; i >= 0; i--) {
ds.insert(targetIndex, temp[i]);
}

sm && sm.selectRecords(temp);

if (this.currentRowEl) {
this.currentRowEl.removeClass('grid-row-insert-below');
this.currentRowEl.removeClass('grid-row-insert-above');
}

},

onDragOver : function(grid, dd, e, data) {
var t = Ext.lib.Event.getTarget(e);
var rindex = this.grid.getView().findRowIndex(t);

try {
var currentRow = this.grid.getView().getRow(rindex);
// Find position of row relative to page (adjusting for grid's scroll position)
var resolvedRow = new Ext.Element(currentRow).getY() - this.grid.getView().scroller.dom.scrollTop;
var rowHeight = currentRow.offsetHeight;

// Cursor relative to a row. -ve value implies cursor is above the
// row's middle and +ve value implues cursor is below the row's
// middle.
this.rowPosition = e.getPageY() - resolvedRow - (rowHeight / 2);

// Clear drag line.
if (this.currentRowEl) {
this.currentRowEl.removeClass('grid-row-insert-below');
this.currentRowEl.removeClass('grid-row-insert-above');
}

this.currentRowEl = new Ext.Element(currentRow);
this.currentRowEl.addClass('grid-row-insert-'+((this.rowPosition>0)?'below':'above'));

} catch (err) {
// console.warn(err);
rindex = false;
}
return (rindex === false) ? dd.dropNotAllowed : dd.dropAllowed;
},
onDragOut : function(grid, dd, e, data) {
// Remove drag lines when pointer leaves the gridView.
if (this.currentRowEl) {
this.currentRowEl.removeClass('grid-row-insert-above');
this.currentRowEl.removeClass('grid-row-insert-below');
}
}
}