PDA

View Full Version : Ext.ux.grid.DragSelector for 2.0



Foggy
11 Nov 2007, 8:34 AM
Hi Guys

So how i say in the premium help forum, heres my implementation of the grid DragSelector.
First, please concern that i have that hardcore js experience since few months, before i was able to do some easy form validations, but not more :)
So this is my really first, Ext.extending, expedient stuff, please consider that :D


Currently it should work with any kind of grid, here are my tests with grid3.html in the examples. Just forceFit: true is not optimal for the DragSelector because if you start on a grid row, first this row would be selected because the rowclick event is fired. So the row is marked as selected an would be deselected if the drag selector intersect with this row (hope you understand)

V 1
So here are the features:
- select all with keybord shortcut ctrl+a
- select by drag selector
- press ctrl and select with drag selector by keeping existent
- press ctrl to deselct existent rows by intersect with the drag selector

Iusses that still bother me :)
- call cancelClick if the user click anywhere in the grid, but outside of any row
- the onBodyKeyDown/UP seems to be fired not until a grid row was selected by click, alltough this event should be fired on Body keydown/up??
- im not able to scroll down while dragging the selector

So maybe one of you pro js devs could help me with this three iusses :)

Here's a live demo (http://www.office.uwd.ch/_tests/ext/examples/grid/grid3.html)
Here's the DragSelector.js not documented yet (http://www.office.uwd.ch/_tests/ext/examples/grid/GridDragSelector.js)

V 2
In this version the autoscroll should run. But there is currently many much to calculate in the onDrag function, so eminently the internet explorer would slow down...
Tough im still working on these feature and in future realeses i think i would provide some config options for the "scroll down", "ctrl is pressed" and "select all" stuff...
Anyway here is the preview of the autoscroll grid while dragging the selector:
http://www.office.uwd.ch/_tests/ext/examples/grid-dragSelector-2.0/grid3.html
And here's the current dev JS file:
http://www.office.uwd.ch/_tests/ext/examples/grid-dragSelector-2.0/GridDragSelector.js

Currently there is a bug, that i cant figure out exactly till now, but im working on ;)
If you scroll to the middle of the grid-data, began to drag the selector and scroll up a little bit, so theres a fault by calculating the drag proxy for a short while...
I have figured it out that it must be at these if/else constructions:

if ((startXY[1] - scrollTop) < xy[1]) {
var y = startXY[1] - scrollTop;
var h = Math.abs(y - xy[1]);
} else {
var y = xy[1];
var h = Math.abs(startXY[1] - xy[1]) - scrollTop;
}
Started at line 106 in the JS File...
But im confident that im find this bug ;)


V3 is out now :)
So, thats the release of my final version of the Ext.ux.grid.DragSelector.
-All Bugs are fixe, (i hope so :) )
-autoscroll works fine
-should work with any kind of grid
-Note ctrl + a => select all does not exists anymore.

DEMO V3 (http://www.office.uwd.ch/_tests/ext/examples/grid-dragSelector-3.0/grid3.html)
JS FILE V3 (http://www.office.uwd.ch/_tests/ext/examples/grid-dragSelector-3.0/GridDragSelector.js)

Ytorres
11 Nov 2007, 2:20 PM
Very nice !
good work :)

evant
12 Nov 2007, 1:15 AM
Very impressive, nice work!

galdaka
12 Nov 2007, 4:58 AM
Ohhhhhhhh! =D>

Amazing! Thanks for share!!

fangzhouxing
12 Nov 2007, 6:11 AM
It is a very good feature! Thanks for share!

Foggy
12 Nov 2007, 6:16 AM
Hi Guys

Thanks for your positive responses, im happy that anyone find this useful :)
Currently i have fixed the ctrlState iusse if you dont have clicked in the grid before start dragging the selector. And i think i have some kind of ideas to scrolldown, if the grid has a scrollPane. Theres nothing updated yet, just wont let you know that you can expect this in next few days i think ;)

Greets Foggy

Foggy
13 Nov 2007, 6:56 AM
There is a preview of the autoscroll option online yet, but im still working on ;)
Please see my first post...

Greets Foggy

galdaka
27 Nov 2007, 11:16 AM
V2 works great!!

Thanks for share your job!

Is a definite version?

Thanks in advance,

Foggy
28 Nov 2007, 7:21 AM
no its not really definite, i work on that extension already but have not much time since few weeks...
But i have some plans to optimize the speed. With large grids the fillRegions method is really slow. I plan to calculating new regions instead of regenerate all region objects in the onScroll Event...
And fixing some known bugs of course ;)
So please give me some time :)
If i see there are some guys interested i push up my work a bit ;)

Ytorres
28 Nov 2007, 10:46 PM
Hi Foggy,

I use your plugin in my app and i found a little bug.

When i use your plugin with a toolbar in a grid and i want to use a TextField in the toolbar, TextField is always desabled and we can't enabled it.

(Tested with toolbar on bottom of the grid and with version 1 of your great pugin ;) )

Do you want a live demo for this bug ?

Foggy
28 Nov 2007, 11:06 PM
Hm or could you post some sample code of the grid? Most likely i have to put in my ext examples folder and run locally, so i can figure out whats exactly the problem ;)

Ytorres
29 Nov 2007, 1:12 AM
Hm or could you post some sample code of the grid? Most likely i have to put in my ext examples folder and run locally, so i can figure out whats exactly the problem ;)

Yes, of course :)

Just add tbar to your grid, like this :



tbar : [

new Ext.form.TextField({
fieldLabel: 'plop',
name: 'plop',
width:190
})

]


In the same way, if you use PagingToolBar for the grid, the input use to jump directly to any page is also desabled.

Foggy
29 Nov 2007, 8:05 AM
Hm i cant reproduce your scenario. My textfield is always enabled and i can write text in that too..
Maybe a link to a live demo would be better, sry for that :">

Ytorres
29 Nov 2007, 8:34 AM
I profide you a demo as soon as possible ;)

Ytorres
29 Nov 2007, 10:12 AM
You can see an online demo here :

http://exjs.keliglia.com/

"Start" => "Grid Window" in tbar.

Thanks for your work ! ;)

Foggy
29 Nov 2007, 10:45 AM
Sorry, i see the same stuff like under http://www.keliglia.com/
I dont found any string like "grid" or "start" and there isnt a tbar ;)
Could you give me a direct link?
Greets Foggy

Ytorres
29 Nov 2007, 11:27 AM
oups :p sorry

http://extjs.keliglia.com/

Foggy
29 Nov 2007, 11:36 AM
works perfect, ill take a look ;)

Edit: ah i know this, i think this is a ext bug in grid and i think jack knows this...
There exists many threads about this out there ;)
I recommend you to set trackMouseOver on false in your grid should help...

Greets Foggy

Ytorres
29 Nov 2007, 1:45 PM
works perfect, ill take a look ;)

I recommend you to set trackMouseOver on false in your grid should help...

Greets Foggy

I have update the demo with this option. Same comportement :(

I will wait for 2.0 final to know if this is solve ^^

Foggy
29 Nov 2007, 3:22 PM
Hm, no thats really a bug of my DragSelector...
But there are iusses with form fields an trackMouseOver in grids...
Anyway i have send you a really pre released Snippet that should work in your case.
You all other guys could except this in few weeks ;)

Foggy
15 Feb 2008, 5:53 AM
V3 out now, see first post please ;)
I know you waiting for long time, please be not too angry, had a lot to do last time...

galdaka
15 Feb 2008, 8:25 AM
Excellent work!!

Thanks for your work,

DeeZ
18 Feb 2008, 7:40 AM
Great plugin !
I notice an useless "alert" in line 90:



if (grid.activeEditor && grid.activeEditor !== null) {
alert(grid.activeEditor);
return false;
}

Foggy
18 Feb 2008, 8:07 AM
Removed from demo. Thanks for the hint.

precariouspanther
18 Feb 2008, 4:47 PM
Very cool plugin - thanks Froggy!

Just one suggestion, with a large number of rows displayed on screen (as much as I love to paginate I have to provide the option to allow users to display up to 200 records on screen at a time), beginning the drag lags up as the entire row set is queried, and then whilst dragging it scans and checks for intersects against all of these rows.

To improve performance I added a flag to break out of the for-loop in the onDrag method as soon as it reaches the first row that doesn't intersect immediately after one that does. I would copy and paste my code but I've hacked it to bits to work with a custom selectionmodel and don't want to confuse anyone :S.

*Also* whilst this isn't what most other people I imagine would want to do, I've put the selection and intersection loop into the onEnd handler so that selection is done when they have finished dragging as opposed to whilst they are dragging the box. Whilst its not as nice as how desktop apps handle drag selection (in the same way you have done), it gives a much better "perceived" performance whilst dragging especially with large numbers of records on screen on slower computers.

Anywho thanks again!
-Adam

Foggy
18 Feb 2008, 11:22 PM
Yep, i know what you mean.
What i want to do against this (i was'nt able till now) is to re-calculate each row-region with my scrollTop value...
Here are my current (out commented) tries to do this:

function fillRowRegions() {
rs = [];
rsOrig = [];
var itemSelector = 'div.x-grid3-row';
var mainElement = view.el.dom;
Ext.each(Ext.query(itemSelector, mainElement), function(el) {
rsOrig[rsOrig.length] = Ext.get(el).getRegion();
rs[rs.length] = Ext.get(el).getRegion();
});
}

function syncRowRegions() {
fillRowRegions();
/*if (rs.length && rsOrig.length && rs.length === rsOrig.length) {
for(var i = 0; i < rs.length; i++){
console.log('substract ' + scrollTop);
rs[i].top = rsOrig[i].top - scrollTop;
rs[i].bottom = rsOrig[i].bottom - scrollTop;
}
}*/
}


Just fill the first region and calculate all others by getting height of any row is not a really good idea if you use a special view for example... (or did you not mean anything like this?)
Maybe i can do this by a config option for really large, normal viewed grids...
Anyway thanks for your suggestion...


Here is a screenshot of my custom grid view, wich needs to do a getRegion with all rows (and thats the bottleneck in my opintion)

ludoo
18 Feb 2008, 11:56 PM
Really wonderful job !! I was waiting since a long time a such functional plugin...

Just a few questions :
1) Is it possible to use without no conflit, this DragSelector plugin and the drag functionnality. I mean like in windows explorer : when you drag items, drag/drop is started but when you drag from outside of filenames "zone", you start a drag selection ?

2) How do you obtain a such custom grid in thumbnail view? I'm really interested because i actually use a grid coupled with a DataView but i encounters problem with selection sync.
http://extjs.com/forum/attachment.php?attachmentid=4641&d=1203406115

Thanks

LudoO

Foggy
19 Feb 2008, 1:16 AM
1) Is it possible to use without no conflit, this DragSelector plugin and the drag functionnality. I mean like in windows explorer : when you drag items, drag/drop is started but when you drag from outside of filenames "zone", you start a drag selection ?
Of course, ive done this successfully...


2) How do you obtain a such custom grid in thumbnail view? I'm really interested because i actually use a grid coupled with a DataView but i encounters problem with selection sync.
Just create a custom grid view...

ludoo
19 Feb 2008, 2:12 AM
Thanks, yes it works perfect... I forgot to activate grid drag/drop :">

precariouspanther
19 Feb 2008, 3:10 PM
Thanks for the reply froggy :)
I didn't actually mean to drop the getRegion stuff (as you said that would make non standard row heights cause problems) - sorry for not elaborating very well.

I've attached a copy of my modified drag selector to give you an idea of some of the performance tweaks I made - it doesn't translate perfectly to your propper version as its currently dependent on my custom toggle selection model (which I'll also upload once its finished). The main points of interest though are:
All of the rows are no longer scanned "onStart", the original version of the script scans in all the rows when you start dragging which on very large tables cause a delay before the drag selector appears.


function syncScroll(e) {
var top = scroller.getScroll().top;
scrollTop = top - scrollTopStart;
if (isDragging) {
onDrag(e, true);
}
}
function fillAllRegions(){
mainRegion = scroller.getRegion();
bodyRegion = scroller.getRegion();
syncScroll();
}


This scanning has now been moved to the "onEnd" handler, and the loop has been tweaked to break and not scan every row as soon as it meets the end of the current selection (as the user cant create more than one drag box at a time we can assume that after the last item that intersects we don't have to scan for any more intersects)

function onEnd(e){
if(isDragging){
selModel.undoToggleRow(); //As we 'clicked' to do the drag, make sure we revert the row that was toggled before dragging...
var itemSelector = 'div.x-grid3-row';
var mainElement = view.el.dom;
var allRows=Ext.query(itemSelector, mainElement);
var el,started=false;
//Toggle all of the highlighted rows
for(var i = 0, len = allRows.length; i < len; i++){
el=allRows[i];
if(dragRegion.intersect( Ext.get(el).getRegion())){
selModel.toggleRow(i, true);
started=true;
} else if(started){
//Don't continue through entire dom if we have already reached the end of the selection zone.
break;
}
}
}
isDragging = false;
if(proxy){
proxy.setDisplayed(false);
}
}


This might be a very *bad* way of doing it given I'm creating the region objects just before they are used - but it has significantly improved performance especially as my grid dynamically changes. Potentially this performance could be further improved by storing each of these regions in a keyed array (but only after they have been initially used).

The changes I've made work really well in my own implementations but might not be suitable for your original script - but I wanted to share anyway just in case :)

Anyway thanks again for the fantastic extension - my users love it!

Foggy
21 Feb 2008, 9:47 AM
Hm, ok, now i know what you mean. Thats a very good idea. I would implement this as a config option (intersectOnDragEnd: true) in the next version...
Thanks for the code

adrianrosca
10 Apr 2008, 12:54 AM
Great work, I love it. Is the support for Ctrl+A coming back in a future version?

Foggy
10 Apr 2008, 6:38 AM
Great work, I love it. Is the support for Ctrl+A coming back in a future version?
First, thanks for your commendation :)

No Ctrl + A is not coming back. Because it wont work if you have any keydown events for your grid AND the DragSelector plugin...
So i came up with the conclusion to set up all keydown actions at one place.
And thats not the dragSelector :)
Or let me say, the dragSelector would'nt be best place for all these event listeners ;)

EzBulka
7 Jul 2008, 11:57 AM
First, kudos to you on an absolutely amazing plugin. It's usefulness is so obvious that it makes me wonder why it wasn't built into ExtJS in the first place.

I encountered a situation where I wanted to react to the drag right after it takes place, as opposed to later querying the selected records. I want to place a series of points on a map but I want it to be instantaneous.

This is what I did. I added the following line to onEnd(e) :

grid.fireEvent('dragSelected',grid.getSelectionModel().getSelections());

This fires a dragSelected event when the drag is done and passes to the function the array of selected records. Then the following code in the Grid config allows you to work with that data:


listeners:{
'dragSelected':{
fn:function(selecteds){
selecteds[x].......;
}
}
}


Not sure if this was the best way to do it but it served my purpose.

ko0kiE
3 Nov 2008, 5:38 AM
nice extension..=D>

is there anything comparable for the CellSelectionModel? would be awesome..

Foggy
3 Nov 2008, 6:53 AM
nice extension..

is there anything comparable for the CellSelectionModel? would be awesome..
Hm, nice idea, maybe i got some time to implement this feature soon.
I'll keep you posted...


First, kudos to you on an absolutely amazing plugin. It's usefulness is so obvious that it makes me wonder why it wasn't built into ExtJS in the first place.

I encountered a situation where I wanted to react to the drag right after it takes place, as opposed to later querying the selected records. I want to place a series of points on a map but I want it to be instantaneous.

This is what I did. I added the following line to onEnd(e) :

grid.fireEvent('dragSelected',grid.getSelectionModel().getSelections());

This fires a dragSelected event when the drag is done and passes to the function the array of selected records. Then the following code in the Grid config allows you to work with that data:


listeners:{
'dragSelected':{
fn:function(selecteds){
selecteds[x].......;
}
}
}


Not sure if this was the best way to do it but it served my purpose.
In my opinion it is a good solution, i will implement this event too in next version...

dbassett74
9 Jun 2009, 12:55 PM
Very nice work! Minor cosmetic issue though. When invoking autoscroll by dragging down below the bounds of the grid, the top of the selection box moves up and over the grid header. Is there anyway to confine the selector box to just the client area?

Foggy
12 Jun 2009, 11:14 PM
Is there anyway to confine the selector box to just the client area?
IMO its not possible, cause the dom structure of grid...
The selector has to go outside for the Region.intersect method...

mholyszko
16 Jun 2009, 2:18 AM
Hi Foggy,

I use your plugin in my app and i found a little bug.

When i use your plugin with a toolbar in a grid and i want to use a TextField in the toolbar, TextField is always desabled and we can't enabled it.

(Tested with toolbar on bottom of the grid and with version 1 of your great pugin ;) )

Do you want a live demo for this bug ?


Hi,
I encounter the same bug in ExtJs 2.2.1. It has nothing to do with trackMouseOver because it's the same when it's set to true or false.

The field actually is not disabled, but cannot be focused with mouse click - so it is not possible to change value in it by typing something.

Could any of you help me solve this problem, please?

ps. the input field is in bbar, which is PagingToolbar.


---
Edit: I managed to fix this by the following code:



/**
* Ext.ux.grid DragSelector Class
*
* @author Claudio Walser aka Foggy cwa[at]uwd.ch
* @copyright 2007-2008, UWD GmbH, all rights reserved.


* License: Ext.ux.grid DragSelector is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*
**/
Ext.namespace('Ext.ux.grid');

Ext.ux.grid.DragSelector = function(cfg){
cfg = cfg || {};
var rs, rsOrig, objectsSelected = [];
var grid, view, regions, proxy, tracker, selModel, scroller;
var bodyRegion, mainRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
var dragSafe = cfg.dragSafe === true;
var ctrlState, shiftState, isDragging = false;
var scrollTopStart, scrollTop = 0;

this.init = function(cmp){
grid = cmp;
view = grid.getView();
selModel = grid.getSelectionModel();
grid.on('render', onRender);
grid.on('bodyscroll', syncScroll);
};

function syncScroll(e) {
syncRowRegions();
var top = scroller.getScroll().top;
scrollTop = top - scrollTopStart;
if (isDragging) {
onDrag(e, true);
}
}

function fillAllRegions(){
mainRegion = scroller.getRegion();
bodyRegion = scroller.getRegion();
objectsSelected = [];
var itemSelector = 'div.x-grid3-row';
var mainElement = view.el.dom;
Ext.each(Ext.query(itemSelector, mainElement), function(el) {
objectsSelected[objectsSelected.length] = selModel.isSelected(objectsSelected.length);
});
fillRowRegions();
syncScroll();
}

function fillRowRegions() {
rs = [];
rsOrig = [];
var itemSelector = 'div.x-grid3-row';
var mainElement = view.el.dom;
Ext.each(Ext.query(itemSelector, mainElement), function(el) {
rsOrig[rsOrig.length] = Ext.get(el).getRegion();
rs[rs.length] = Ext.get(el).getRegion();
});
}

function syncRowRegions() {
fillRowRegions();
}

function cancelClick(e){
ctrlState = e.ctrlKey;
shiftState = e.shiftKey;
grid.stopEditing();
var target = e.getTarget();
if (!ctrlState && !shiftState && target.className === 'x-grid3-body') {
selModel.clearSelections();
}
return true;
}

function onBeforeStart(e){
// return false if is a right mouseclick
if (e.button === 2) {
return false;
}
// return false if any grid editor is active
if (grid.activeEditor && grid.activeEditor !== null) {
return false;
}

// return false if the header was clicked
if (e.getPageY() <= view.el.getY() + 25) {
return false;
}
// scrollbar fix from digitalbucket.net :)
if (e.getPageX() > view.el.getX() + view.el.dom.clientWidth - 20) {
return false;
}
// bottom toolbar fix by mh
var bbar = grid.getBottomToolbar();
if (bbar && (e.target == bbar.el.dom || bbar.el.contains(e.target))) {
return false;
}

// call cancelClick
cancelClick(e);
return !dragSafe || e.target == view.el.dom;
}

function onStart(e){
scrollTopStart = scroller.getScroll().top;
fillAllRegions();
if(!proxy){
proxy = view.el.createChild({cls:'x-view-selector'});
} else {
proxy.setDisplayed('block');
}
isDragging = true;
}

function onDrag(e, scaleSelector){
var startXY = tracker.startXY;
var xy = tracker.getXY();
if (xy[0] < startXY[0] && !scaleSelector) {
xy[0] += 2;
}
if (scrollTop >= 0) {
if ((startXY[1]- scrollTop) <= xy[1]) {
var y = startXY[1] - scrollTop;
var h = Math.abs(y - xy[1]);
} else {
var y = xy[1];
var h = Math.abs(startXY[1] - xy[1]) - scrollTop;
}
var x = Math.min(startXY[0], xy[0]);
var w = Math.abs(startXY[0] - xy[0]);
bodyRegion.top -= scrollTop;
} else {
if ((startXY[1] - scrollTop) < xy[1]) {
var y = startXY[1] - scrollTop;
var h = Math.abs(y - xy[1]);
} else {
var y = xy[1]; // richtig in jedem fall
var h = Math.abs((startXY[1] - scrollTop) - xy[1]) ;
}

var x = Math.min(startXY[0], xy[0]);
var w = Math.abs(startXY[0] - xy[0]);

bodyRegion.bottom -= scrollTop;
}
// set this values
dragRegion.left = x;
dragRegion.top = y ;
dragRegion.right = x+w;
dragRegion.bottom = y+h;

dragRegion.constrainTo(bodyRegion);
proxy.setRegion(dragRegion);

for(var i = 0, len = rs.length; i < len; i++){
var r = rs[i], sel = dragRegion.intersect(r);
var selected = selModel.isSelected(i);
var selectedBefore = objectsSelected[i];

if (ctrlState) {
if (selectedBefore) {
if(sel && selected){
selModel.deselectRow(i);
} else if(!sel && !selected){
selModel.selectRow(i, true);
}
} else {
if(sel && !selected){
selModel.selectRow(i, true);
} else if(!sel && selected){
selModel.deselectRow(i);
}
}
} else {
if(sel && !selected){
selModel.selectRow(i, true);
} else if(!sel && selected){
selModel.deselectRow(i);
}
}

}

if (xy[1] + 10 >= mainRegion.bottom) {
// slow up for ie
if (Ext.isIE) {
setTimeout(function() {
scroller.scrollTo('top', scroller.getScroll().top + 40);
}, 100);
} else {
scroller.scrollTo('top', scroller.getScroll().top + 40);
}
}

if (xy[1] - 10 <= mainRegion.top) {
// slow up for ie
if (Ext.isIE) {
setTimeout(function() {
scroller.scrollTo('top', scroller.getScroll().top - 40);
}, 100);
} else {
scroller.scrollTo('top', scroller.getScroll().top - 40);
}
}
}

function onEnd(e){
isDragging = false;
if(proxy){
proxy.setDisplayed(false);
}
}

function onRender(view){
tracker = new Ext.dd.DragTracker({
onBeforeStart: onBeforeStart,
onStart: onStart,
onDrag: onDrag,
onEnd: onEnd
});
tracker.initEl(view.el);
scroller = Ext.get(grid.getView().scroller);
}
};

Foggy
20 Jun 2009, 8:41 AM
Hm, tought i fixed this bug...
It was because of form fields in grid header or footer...

mholyszko
23 Jun 2009, 9:05 AM
Yes, but your fix did not cover grid's bottom toolbar.

Also, what could I do to make this work with large data sets? When I have ~1000 rows shown on one page in grid, the browser hangs up completely when I try to start scrolling...

Regards,

fermo111
9 Jul 2009, 11:26 PM
I am not able to download the code or access the online demo. I have tried with two different DNS servers but I get a:


** server can't find www.office.uwd.ch: NXDOMAIN

Foggy
10 Jul 2009, 6:35 PM
Sorry i have some trouble with internet connection on that server.
Please be patient, should be online soon...


Yes, but your fix did not cover grid's bottom toolbar.
Take a look into that.


Also, what could I do to make this work with large data sets? When I have ~1000 rows shown on one page in grid, the browser hangs up completely when I try to start scrolling...
Yes i know, idea for that was to calculate new position of rows with the original region and scrollTop data instead of another intersect...
However, this is not working yet, i strugling with some iusses on that. So it is really slow with much records. Sorry, i recommend to use paging store till this is fixed.
One other solution is selecting the records not while drag but just once on endDrag. This will increase performance enorm...
Take a look at this post:
http://extjs.com/forum/showthread.php?p=125573#post125573

Hm, i just looked into the code of my current dragSelector.js. It seems i've done the stuff with bottmToolbar. Could you try this piece of code?



Ext.namespace('Ext.ux.grid');

Ext.ux.grid.DragSelector = function(cfg){

cfg = cfg || {};
var rs, rsOrig, objectsSelected = [];
var grid, view, regions, proxy, tracker, selModel, scroller;
var bodyRegion, mainRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
var dragSafe = cfg.dragSafe === true;
var ctrlState, shiftState, isDragging = false;
var scrollTopStart, scrollTop = 0;

this.init = function(cmp){
grid = cmp;
view = grid.getView();
selModel = grid.getSelectionModel();
grid.on('render', onRender);
grid.on('bodyscroll', syncScroll);
};

function syncScroll(e) {
// sync regions
syncRowRegions();

// get new scroll position
var top = scroller.getScroll().top;
scrollTop = top - scrollTopStart;
if (isDragging) {
onDrag(e, true);
}
}

function fillAllRegions(){
mainRegion = scroller.getRegion();
bodyRegion = scroller.getRegion();
objectsSelected = [];
var itemSelector = 'div.x-grid3-row';
var mainElement = view.el.dom;
Ext.each(Ext.query(itemSelector, mainElement), function(el) {
objectsSelected[objectsSelected.length] = selModel.isSelected(objectsSelected.length);
});
fillRowRegions();
syncScroll();
}

function fillRowRegions() {
rs = [];
rsOrig = [];
var itemSelector = 'div.x-grid3-row';
var mainElement = view.el.dom;
Ext.each(Ext.query(itemSelector, mainElement), function(el) {
rsOrig[rsOrig.length] = Ext.get(el).getRegion();
rs[rs.length] = Ext.get(el).getRegion();
});
}

function syncRowRegions() {
if (scrollTop !== 0) {
fillRowRegions();
}
/*if (rs.length && rsOrig.length && rs.length === rsOrig.length) {
for(var i = 0; i < rs.length; i++){
//console.log('substract ' + scrollTop);
rs[i].top = rsOrig[i].top - scrollTop;
rs[i].bottom = rsOrig[i].bottom - scrollTop;
}
}*/
}

function cancelClick(e){
ctrlState = e.ctrlKey;
shiftState = e.shiftKey;
grid.stopEditing();
var target = e.getTarget();
if (!ctrlState && !shiftState && (target.className === 'x-grid3-scroller' || target.className === 'x-grid3-body' )) {
selModel.clearSelections();
}
return true;
}

function onBeforeStart(e){
// return false if is a right mouseclick
if (e.button === 2) {
return false;
}
// return false if any grid editor is active
if (grid.activeEditor && grid.activeEditor !== null) {
return false;
}

// return false if the header was clicked
if (e.getPageY() <= view.el.getY() + 25) {
return false;
}

// return false if the header was clicked
if (grid.bbar && e.getPageY() > view.el.getY() + view.el.dom.clientHeight) {
return false;
}

// scrollbar fix from digitalbucket.net :)
if (e.getPageX() > view.el.getX() + view.el.dom.clientWidth - 20) {
return false;
}

// call cancelClick
cancelClick(e);
// return
return !dragSafe || e.target == view.el.dom;
}

function onStart(e){
scrollTopStart = scroller.getScroll().top;
fillAllRegions();
// init drag proxy
if(!proxy){
proxy = view.el.createChild({cls:'x-view-selector'});
} else {
proxy.setDisplayed('block');
}
isDragging = true;
}

function onDrag(e, scaleSelector){
// get drag start position
var startXY = tracker.startXY;
// current position
var xy = tracker.getXY();
// get xy, width and heigth of the drag proxy

if (xy[0] < startXY[0] && !scaleSelector) {
xy[0] += 2;
}

if (scrollTop >= 0) {

if ((startXY[1]- scrollTop) <= xy[1]) {
// feld oben
var y = startXY[1] - scrollTop;
var h = Math.abs(y - xy[1]);

} else {
var y = xy[1];
var h = Math.abs(startXY[1] - xy[1]) - scrollTop;
}

var x = Math.min(startXY[0], xy[0]);
var w = Math.abs(startXY[0] - xy[0]);



bodyRegion.top -= scrollTop;


} else {


if ((startXY[1] - scrollTop) < xy[1]) {

// feld unten
var y = startXY[1] - scrollTop;
var h = Math.abs(y - xy[1]);

} else {

// feld oben
var y = xy[1]; // richtig in jedem fall
var h = Math.abs((startXY[1] - scrollTop) - xy[1]) ;

}

var x = Math.min(startXY[0], xy[0]);
var w = Math.abs(startXY[0] - xy[0]);

bodyRegion.bottom -= scrollTop;
}
// set this values
dragRegion.left = x;
dragRegion.top = y ;
dragRegion.right = x+w;
dragRegion.bottom = y+h;

dragRegion.constrainTo(bodyRegion);
proxy.setRegion(dragRegion);

for(var i = 0, len = rs.length; i < len; i++){
var r = rs[i], sel = dragRegion.intersect(r);
var selected = selModel.isSelected(i);
var selectedBefore = objectsSelected[i];

if (ctrlState) {
if (selectedBefore) {
if(sel && selected){
selModel.deselectRow(i);
} else if(!sel && !selected){
selModel.selectRow(i, true);
}
} else {
if(sel && !selected){
selModel.selectRow(i, true);
} else if(!sel && selected){
selModel.deselectRow(i);
}
}
} else {
if(sel && !selected){
selModel.selectRow(i, true);
} else if(!sel && selected){
selModel.deselectRow(i);
}
}

}

if (xy[1] + 10 >= mainRegion.bottom) {
if (Ext.isIE) {
setTimeout(function() {
scroller.scrollTo('top', scroller.getScroll().top + 40);
}, 100);
} else {
scroller.scrollTo('top', scroller.getScroll().top + 40);
}
}

if (xy[1] - 10 <= mainRegion.top) {
if (Ext.isIE) {
setTimeout(function() {
scroller.scrollTo('top', scroller.getScroll().top - 40);
}, 100);
} else {
scroller.scrollTo('top', scroller.getScroll().top - 40);
}
}
}

function onEnd(e){
isDragging = false;
if(proxy){
proxy.setDisplayed(false);
}
}

function onRender(view){
tracker = new Ext.dd.DragTracker({
onBeforeStart: onBeforeStart,
onStart: onStart,
onDrag: onDrag,
onEnd: onEnd
});
tracker.initEl(view.el);
scroller = Ext.get(grid.getView().scroller);
}

};

Foggy
14 Jul 2009, 8:48 PM
Should be online again...

MD
16 Jul 2009, 8:31 AM
Seems your server is down again. Just tried the link for the v1/v2/v3 demos, all 404 :(

Foggy
16 Jul 2009, 9:19 AM
Argh, Server is up, just www subdomain isn't properly setup'ed on my dns.
I'll fix that, for now it works just w/o www, just http://office.uwd.ch/_tests/ext/examples/grid/grid3.html

Sorry for that mistake and thanks for the hint.

Greets

Edit: DNS is edited, just take a while for propagate it...

d@vid
17 Jul 2009, 3:35 PM
Hi,

I just tested your plugin and it didn't work by me.

Here is my code:


//----------------
//Grilles et composants
//----------------

HoraireDataStore = new Ext.data.GroupingStore({
id: 'HoraireDataStore',
proxy: new Ext.data.HttpProxy({
url: 'datastore/datastore_horaire.php',
method: 'POST'
}),
baseParams:{task: "LISTING"}, // this parameter is passed for any HTTP request
reader: new Ext.data.JsonReader({
root: 'results',
totalProperty: 'total',
id: 'id_Horaire'
},[
{name: 'IDHoraire', type: 'int', mapping: 'id_horaire'},
{name: 'Mandat', type: 'string', mapping: 'lieu'},
{name: 'Type', type: 'string', mapping: 'type'},
{name: 'DateDebut', type: 'date', mapping: 'date_service_debut'},
{name: 'DateFin', type: 'date', mapping: 'date_service_fin'},
{name: 'HoraireDebut', type: 'string', mapping: 'horaire_debut'},
{name: 'HoraireFin', type: 'string', mapping: 'horaire_fin'},
{name: 'AgentRequis', type: 'string', mapping: 'agent_requis'},
{name: 'Annoncer', type: 'string', mapping: 'annoncer'},

]),
sortInfo:{field: 'DateDebut', direction: "ASC"},
groupField:'DateDebut'

});

HoraireColumnModel = new Ext.grid.ColumnModel(
[{
header: '#',
dataIndex: 'IDHoraire',
readOnly: true,
hidden: true
},{
header: 'Mandat',
dataIndex: 'Mandat',
readOnly: true,
width:200


},{
header: 'Type',
dataIndex: 'Type',
readOnly: true,
width:150


},{
header: 'Date D&eacute;but',
readOnly: true,
dataIndex: 'DateDebut',
readOnly: true,
renderer: Ext.util.Format.dateRenderer('d/m/y'),
width:100


},{
header: 'Date Fin',
dataIndex: 'DateFin',
renderer: Ext.util.Format.dateRenderer('d/m/y'),
readOnly: true,
width:100

},{
header: 'Horaire D&eacute;but',
dataIndex: 'HoraireDebut',
readOnly: true,
width:100

},{

header: 'Horaire Fin',
dataIndex: 'HoraireFin',
readOnly: true,
width:100


},{

header: "Requis",
dataIndex: 'AgentRequis',
readOnly: true,
width:50


}
,{
header: 'Inscription',
dataIndex: 'Annoncer',
readOnly: true,
width:160


}]
);
HoraireColumnModel.defaultSortable= true;

HoraireView = new Ext.grid.GroupingView({
forceFit:true,
// startCollapsed : true,
// custom grouping text template to display the number of items per group
groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
}),



HoraireListingEditorGrid = new Ext.grid.EditorGridPanel({
id: 'HoraireListingEditorGrid',
store: HoraireDataStore,
cm: HoraireColumnModel,
view:HoraireView,
height: 700,
maxHeigth: 700,
width: 950,
enableColLock:true,
enableColumnResize :false,
enableColumnHide :false,
clicksToEdit:1,
selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
tbar: [{
text: 'New',
tooltip: 'nouvel horaire',
iconCls:'add',//this is defined in our styles.css
handler : function() {
if(CanSee(privilege) )ShowForm('include/html/horaire/ form_horaire_autoload.inc.php',FormWindowHoraire);
else cannotSee();

}},'-',{
text: 'Confirmation',
tooltip: 'Confirmer',
iconCls:'ok',//this is defined in our styles.css
handler : function(){if(HoraireListingEditorGrid.selModel.getCount() ==1){ ConfirmerEmploye(HoraireListingEditorGrid.selModel.getSelected());}
else {Ext.MessageBox.alert('Erreur','Selectionner UNE Ligne');}
}

},'-',{
text: 's\'annoncer',
tooltip: 's\'annoncer',
iconCls:'right',//this is defined in our styles.css
handler : annoncer

},'-',{

text: 'Email',
tooltip: 'Envoyer un email aux employ&eacute;s',
iconCls:'mail',//this is defined in our styles.css
handler : function(){HoraireListingEditorGrid.getEl().mask();}
//confirmMail



},'-',{
text:'Expand/Toggle',
iconCls:'trendUp',
handler:function(){HoraireView.toggleAllGroups();}
},'-',selectMois




],
plugins: [new Ext.ux.grid.DragSelector()],
plugins: [new Ext.ux.grid.Search({
searchText: 'Rechercher',
mode: 'local',
minChars:1,
iconCls:'search',
width : 200,
minCharsTipText:'Veuillez taper minimum 1 characteres',
autoFocus:true,
menuStyle:'checkbox',
disableIndexes:['IDHoraire','DateDebut','DateFin','HoraireDebut','HoraireFin'],
position:'top'


})]

});




HoraireListingEditorGrid.addListener('rowcontextmenu', onHoraireListingEditorGridContextMenu);


HoraireListingContextMenu = new Ext.menu.Menu({
id: 'HoraireListingEditorGridContextMenu',
items: [
{ text: 'Modifier cet horaire',handler:modifyHoraire },
{ text: 'Supprimer cet horaire ',handler:ConfirmDeleteHoraire },
{ text: 'Confirmer des employ&eacute;s',handler:function(){ConfirmerEmploye(HoraireListingEditorGrid.selModel.getSelected());}


}



]
});


I tried to debug but i didn't see any errors.

Sorry for my bad english, thanks for help.

Foggy
19 Jul 2009, 2:10 AM
plugins: [new Ext.ux.grid.DragSelector()],
plugins: [new Ext.ux.grid.Search({
searchText: 'Rechercher',
mode: 'local',
minChars:1,
iconCls:'search',
width : 200,
minCharsTipText:'Veuillez taper minimum 1 characteres',
autoFocus:true,
menuStyle:'checkbox',
disableIndexes:['IDHoraire','DateDebut','DateFin','HoraireDebut','HoraireFin'],
position:'top'


})]
That can't work, set plugins just once and give an array with all plugins...

MD
19 Jul 2009, 3:22 PM
Foggy, thanks for sorting out the server -- was glad to finally see the plugin in action! Very nicely done! =D> I already have a few spots where I'd like to implement it. ;)

Foggy
19 Jul 2009, 11:40 PM
@MD
Thanks, i'm glad you like it ;)

mholyszko
24 Jul 2009, 9:14 AM
Yes i know, idea for that was to calculate new position of rows with the original region and scrollTop data instead of another intersect...
However, this is not working yet, i strugling with some iusses on that. So it is really slow with much records. Sorry, i recommend to use paging store till this is fixed.
One other solution is selecting the records not while drag but just once on endDrag. This will increase performance enorm...
Take a look at this post:
http://extjs.com/forum/showthread.php?p=125573#post125573

Hm, i just looked into the code of my current dragSelector.js. It seems i've done the stuff with bottmToolbar. Could you try this piece of code?


Tried your code and yes, it behaves now good when it comes to bottom toolbar, clicking on scrollbar etc.

But handling a large data set is still very slow (~1000 records hangs firefox). I tried to implement the solution from the post you put a link to, but it was too hard for me to understand the solution and build a working scripts from bits of codes from another post of the same guy.

I checked also how does it behave on ExtJS 3.0, with or without BufferView (http://extjs.com/deploy/dev/examples/ux/BufferView.js) but results are the same.

Anyway, thanks for your efforts. I'm looking forward to hearing from you that you were able to overcome this problem :)

Regards,
mh

NepalPro
3 Sep 2009, 9:40 AM
This control is great.;)

Minor enhancement for IE6 almost negligible :

This resolves a minor bug in IE6 (works as expected in mozilla) as follows:-

The start of the drag selector always begin with some specific height and when dragging and moving the mouse up and down the drag start region, the proxy seems to scroll a bit up in IE6.

Have the following style class for the proxy.

if(!proxy){
//proxy = view.el.createChild({cls:'x-view-selector'});//original style class
proxy = view.el.createChild({cls:'x-proxy-selector'});//new style class
}else{

use the following style :

.x-proxy-selector{
position:absolute;left:0;top:0;width:0;background:#ff0000; border:1px dotted #39b; opacity:.5;-moz-opacity:.5;filter: alpha(opacity=50);zoom:1;

line-height:0px;
font-size:0px;
}

xLP
4 Sep 2009, 11:30 AM
As someone else did before, I wonder why it's not part of ExtJS lib! Maybe they maintain somewhere a page of killer extensions which lists this one?
Great job Froggy, thanks a lot...

There's a little thing I didn't like: when you have enough items to have a scrollbar, and start selecting, then list scrolls down, the selection rectangle is drawn on the headers.

Here's a quick fix, in onStart(), replace

proxy = view.el.createChild({cls:'x-view-selector'});
with

proxy = view.scroller.createChild({cls:'x-view-selector'});

Am I the only one to have experienced that? Or myabe it's an expected behaviour?

Greetings

xLP

xLP
14 Sep 2009, 8:33 AM
If you go to the demo page, then resize columns of one grid so that you get a horizontal scrollbar, then start drag-selecting, and go to the bottom of the list... it'll scroll and scroll down "for ever".

Here's a "fix", Foggy, I'd love you to have a look at it ;)

First, add a new variable... "
var scrollMax = 0;"

Then, in onStart(e) add at the top:


var sr = scroller.getRegion();
var br = view.mainBody.getRegion();
scrollMax = (br.bottom - br.top) - (sr.bottom - sr.top);


Finally, in onDrag, after


if (xy[1] + 10 >= mainRegion.bottom) {

add


if(scroller.getScroll().top <= scrollMax)


(Note it's possible to optimize at least a little by replacing calls to scroller.getScroll() with a single call saved into one variable)

Greetins

xLP

xLP
15 Sep 2009, 2:53 AM
I have a new patch...

Remove my previous patch in onStart, declare new variables scrollMaxX and scrollMaxY and remove scrollMax.

In onBeforeStart, remove the patch from digitalbucket.net.

Then, on top, add:



var sr = scroller.getRegion();
var br = view.mainBody.getRegion();
scrollMaxY = (br.bottom - br.top) - (sr.bottom - sr.top);
scrollMaxX = (br.right - br.left) - (sr.right - sr.left);

if (scrollMaxX > 0 && scrollMaxY < 0) {
scrollMaxY += view.scrollOffset;
}

if (scrollMaxY > 0 && scrollMaxX < 0) {
scrollMaxX += view.scrollOffset;
}

if (scrollMaxX > 0 && e.getPageY() > sr.bottom - view.scrollOffset) {
return false;
}
if (scrollMaxY > 0 && e.getPageX() > sr.right - view.scrollOffset) {
return false;
}


This will prevent selection while scrolling with horizontal scrollbar. This should also provide better precision and flexibility than digitalbucket.net patch.

As usual, Foggy, any comment? I'd love if you'd integrate that in your source ;)

Foggy
16 Sep 2009, 7:08 AM
Hi xLP

Thanks alot for your effort, i'll integrate this patches as soon as possible. They looks good for me...

xLP
16 Sep 2009, 7:48 AM
Hi Foggy,

If you like I may post online somewhere a file with all my patches integrated so you can have a look ;)

LP

xLP
16 Sep 2009, 11:30 PM
Quick patch of the day ;)
Add


if (grid.el.isMasked()) {
e.stopEvent();
return false;
}

on top of onBeforeDrag, to prevent people from drawing a selection while the grid is loading...

Foggy
17 Sep 2009, 6:08 AM
If you like I may post online somewhere
That would be awesome ;)


on top of onBeforeDrag, to prevent people from drawing a selection while the grid is loading
And again, thanks alot :)

xLP
17 Sep 2009, 6:30 AM
http://pastebin.ca/1569661

It's valid for one month ;)

And well, I thank YOU for making this very nice stuff! I'm brand new to ExtJS, not really experienced in Javascript also, and would need days to make such a thing!

Additionnally, it helped me understand how ExtJS works...

Regarding changes in the pastebin'ed file, I marked the all the following way:
<LP /> indicated one line changed by me (most often, added)
<LP> ... </LP> indicates a block changed by me...

Cheers

Foggy
21 Sep 2009, 1:45 AM
Your patches are integrated, thanks for providing full js file.
I've just updated the copyright ;)

/**
* Ext.ux.grid DragSelector Class
*
* @author Claudio Walser aka Foggy cwa[at]uwd.ch
* @copyright 2007-2008, UWD GmbH, all rights reserved.
* @thanks Many thanks to xLP for his bugfixes. View profile at http://www.extjs.com/forum/member.php?u=77893

* License: Ext.ux.grid DragSelector is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*
**/

crashedsnow
22 Nov 2009, 1:34 AM
Hey there,

Any plans to support Google Chrome with your extension? I realize Chrome is not on the "official" list of supported browsers for ExtJS, but so far everything is working for me except your (wonderful) drag selector.

Specifically, if the grid has a horizontal scrollbar (browser scrollbar), and you click on the scroll bar the select box is created (unexpectedly). The problem is that once it's created in this way, you can't get rid of it. It remains after releasing the mouse button.

I also get some strange resizing of the browser scrollbars.

I have created a quick screencast to demonstrate. Not sure how long this link will last though...

http://bit.ly/6FW80M

You'll see I can click and drag as expected, but if I hit the bottom horizontal scrollbar it goes all bonkers.

Thanks for your help.

P.S:
Chrome: 4.0.223.16
Windows 7 64 Bit

xLP
22 Nov 2009, 8:55 AM
Hey there,

Any plans to support Google Chrome with your extension? I realize Chrome is not on the "official" list of supported browsers for ExtJS, but so far everything is working for me except your (wonderful) drag selector.

Specifically, if the grid has a horizontal scrollbar (browser scrollbar), and you click on the scroll bar the select box is created (unexpectedly). The problem is that once it's created in this way, you can't get rid of it. It remains after releasing the mouse button.

I also get some strange resizing of the browser scrollbars.

I have created a quick screencast to demonstrate. Not sure how long this link will last though...

http://bit.ly/6FW80M

You'll see I can click and drag as expected, but if I hit the bottom horizontal scrollbar it goes all bonkers.

Thanks for your help.

P.S:
Chrome: 4.0.223.16
Windows 7 64 Bit

Hi,

I'm not the author of this great extension, however you can look at previous pages, I submitted a patch to fix a few bugs and improve a bit the scrollbar handling. This may give you a rather precise idea at where to look at to fix your problem.
Especially have a look at "onBeforeStart"



if (scrollMaxX > 0 && e.getPageY() > sr.bottom - view.scrollOffset) {
return false;
}

Those are the lines responsible for cancelling drag if you clicked in the near of the horizontal scrollbar. If you manage to fix it would be nice to tell it to Foggy (the author).

And maybe he will have a better answer for you ;)

crashedsnow
22 Nov 2009, 2:52 PM
Thanks xLP, I'll have a go at hacking through the code :)

Appreciate you taking the time to respond.

ktreagu
2 Apr 2010, 6:55 AM
Is there a current version of this out there somewhere?

darklow
30 Sep 2010, 12:14 AM
Does anyone know how to adjust this plugin to use together with CellSelectionModel to select not just rows, but multiple cells in overlapping area?
Thanks.

ktreagu
30 Sep 2010, 6:19 AM
I would love the answer to that question myself. I looked into it a while back and it seemed like it could be done but the drag selector would have to be enhanced. Let me know if you find a way to do it.

darklow
30 Sep 2010, 6:42 AM
I created quite dirty version of EditorGrid CellSelection together with DragSelector v3

Also i found out DragSelector original selection method by calculating (on each onDrag event) each grid cell intersection with selected area region is very slow solution for large amount data grids, so i wrote an alternative version of DragSelector, which uses X and Y coordinate calculation method, which is like 100x faster for large data amounts, however it has one small bug i described and you can see in example

Here i set up two examples:
http://dev.first.lv/darklow/extjs/ext/examples/grid/grid-celldragselect.html

If anyone have time and more power in his hands to fix DragSelector or create more beatiful code and implementation please let us know. Thanks