Upgrade to ExtJS 3.3.0 - Released 10/06/2010
[extjs.git] / examples / ux / ToolbarReorderer.js
1 /*!
2  * Ext JS Library 3.3.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.ux.ToolbarReorderer
9  * @extends Ext.ux.Reorderer
10  * Plugin which can be attached to any Ext.Toolbar instance. Provides ability to reorder toolbar items
11  * with drag and drop. Example:
12  * <pre>
13  * new Ext.Toolbar({
14  *     plugins: [
15  *         new Ext.ux.ToolbarReorderer({
16  *             defaultReorderable: true
17  *         })
18  *     ],
19  *     items: [
20  *       {text: 'Button 1', reorderable: false},
21  *       {text: 'Button 2'},
22  *       {text: 'Button 3'}
23  *     ]
24  * });
25  * </pre>
26  * In the example above, buttons 2 and 3 will be reorderable via drag and drop. An event named 'reordered'
27  * is added to the Toolbar, and is fired whenever a reorder has been completed.
28  */
29 Ext.ux.ToolbarReorderer = Ext.extend(Ext.ux.Reorderer, {
30     /**
31      * Initializes the plugin, decorates the toolbar with additional functionality
32      */
33     init: function(toolbar) {
34         /**
35          * This is used to store the correct x value of each button in the array. We need to use this
36          * instead of the button's reported x co-ordinate because the buttons are animated when they move -
37          * if another onDrag is fired while the button is still moving, the comparison x value will be incorrect
38          */
39         this.buttonXCache = {};
40         
41         toolbar.on({
42             scope: this,
43             add  : function(toolbar, item) {
44                 this.createIfReorderable(item);
45             }
46         });
47         
48         //super sets a reference to the toolbar in this.target
49         Ext.ux.ToolbarReorderer.superclass.init.apply(this, arguments);
50     },
51         
52     /**
53      * Sets up the given Toolbar item as a draggable
54      * @param {Mixed} button The item to make draggable (usually an Ext.Button instance)
55      */
56     createItemDD: function(button) {
57         if (button.dd != undefined) {
58             return;
59         }
60         
61         var el   = button.getEl(),
62             id   = el.id,
63             tbar = this.target,
64             me   = this;
65         
66         button.dd = new Ext.dd.DD(el, undefined, {
67             isTarget: false
68         });
69         
70         //if a button has a menu, it is disabled while dragging with this function
71         var menuDisabler = function() {
72             return false;
73         };
74         
75         Ext.apply(button.dd, {
76             b4StartDrag: function() {       
77                 this.startPosition = el.getXY();
78                 
79                 //bump up the z index of the button being dragged but keep a reference to the original
80                 this.startZIndex = el.getStyle('zIndex');
81                 el.setStyle('zIndex', 10000);
82                 
83                 button.suspendEvents();
84                 if (button.menu) {
85                     button.menu.on('beforeshow', menuDisabler, me);
86                 }
87             },
88             
89             startDrag: function() {
90                 this.constrainTo(tbar.getEl());
91                 this.setYConstraint(0, 0, 0);
92             },
93             
94             onDrag: function(e) {
95                 //calculate the button's index within the toolbar and its current midpoint
96                 var buttonX  = el.getXY()[0],
97                     deltaX   = buttonX - this.startPosition[0],
98                     items    = tbar.items.items,
99                     oldIndex = items.indexOf(button),
100                     newIndex;
101                 
102                 //find which item in the toolbar the midpoint is currently over
103                 for (var index = 0; index < items.length; index++) {
104                     var item = items[index];
105                     
106                     if (item.reorderable && item.id != button.id) {
107                         //find the midpoint of the button
108                         var box        = item.getEl().getBox(),
109                             midpoint   = (me.buttonXCache[item.id] || box.x) + (box.width / 2),
110                             movedLeft  = oldIndex > index && deltaX < 0 && buttonX < midpoint,
111                             movedRight = oldIndex < index && deltaX > 0 && (buttonX + el.getWidth()) > midpoint;
112                         
113                         if (movedLeft || movedRight) {
114                             me[movedLeft ? 'onMovedLeft' : 'onMovedRight'](button, index, oldIndex);
115                             break;
116                         }                        
117                     }
118                 }
119             },
120             
121             /**
122              * After the drag has been completed, make sure the button being dragged makes it back to
123              * the correct location and resets its z index
124              */
125             endDrag: function() {
126                 //we need to update the cache here for cases where the button was dragged but its
127                 //position in the toolbar did not change
128                 me.updateButtonXCache();
129                 
130                 el.moveTo(me.buttonXCache[button.id], el.getY(), {
131                     duration: me.animationDuration,
132                     scope   : this,
133                     callback: function() {
134                         button.resumeEvents();
135                         if (button.menu) {
136                             button.menu.un('beforeshow', menuDisabler, me);
137                         }
138                         
139                         tbar.fireEvent('reordered', button, tbar);
140                     }
141                 });
142                 
143                 el.setStyle('zIndex', this.startZIndex);
144             }
145         });
146     },
147     
148     onMovedLeft: function(item, newIndex, oldIndex) {
149         var tbar  = this.target,
150             items = tbar.items.items;
151         
152         if (newIndex != undefined && newIndex != oldIndex) {
153             //move the button currently under drag to its new location
154             tbar.remove(item, false);
155             tbar.insert(newIndex, item);
156             
157             //set the correct x location of each item in the toolbar
158             this.updateButtonXCache();
159             for (var index = 0; index < items.length; index++) {
160                 var obj  = items[index],
161                     newX = this.buttonXCache[obj.id];
162                 
163                 if (item == obj) {
164                     item.dd.startPosition[0] = newX;
165                 } else {
166                     var el = obj.getEl();
167                     
168                     el.moveTo(newX, el.getY(), {duration: this.animationDuration});
169                 }
170             }
171         }
172     },
173     
174     onMovedRight: function(item, newIndex, oldIndex) {
175         this.onMovedLeft.apply(this, arguments);
176     },
177     
178     /**
179      * @private
180      * Updates the internal cache of button X locations. 
181      */
182     updateButtonXCache: function() {
183         var tbar   = this.target,
184             items  = tbar.items,
185             totalX = tbar.getEl().getBox(true).x;
186             
187         items.each(function(item) {
188             this.buttonXCache[item.id] = totalX;
189
190             totalX += item.getEl().getWidth();
191         }, this);
192     }
193 });