3 * Copyright(c) 2006-2010 Ext JS, Inc.
5 * http://www.extjs.com/license
8 * @class Ext.ux.Reorderer
10 * Generic base class for handling reordering of items. This base class must be extended to provide the
11 * actual reordering functionality - the base class just sets up events and abstract logic functions.
12 * It will fire events and set defaults, deferring the actual reordering to a doReorder implementation.
13 * See Ext.ux.TabReorderer for an example.
15 Ext.ux.Reorderer = Ext.extend(Object, {
19 * Object containing default values for plugin configuration details. These can be overridden when
20 * constructing the plugin
26 * If set to true, the rearranging of the toolbar items is animated
31 * @cfg animationDuration
33 * The duration of the animation used to move other toolbar items out of the way
35 animationDuration: 0.2,
38 * @cfg defaultReorderable
40 * True to make every toolbar draggable unless reorderable is specifically set to false.
41 * This defaults to false
43 defaultReorderable: false
47 * Creates the plugin instance, applies defaults
49 * @param {Object} config Optional config object
51 constructor: function(config) {
52 Ext.apply(this, config || {}, this.defaults);
56 * Initializes the plugin, stores a reference to the target
57 * @param {Mixed} target The target component which contains the reorderable items
59 init: function(target) {
63 * Reference to the target component which contains the reorderable items
69 var items = this.getItems();
70 for (var i=0; i < items.length; i++) {
71 this.createIfReorderable(items[i]);
76 * Reorders the items in the target component according to the given mapping object. Example:
81 * Would move the item at index 1 to index 5, and the item at index 3 to index 2
82 * @param {Object} mappings Object containing current item index as key and new index as property
84 reorder: function(mappings) {
85 var target = this.target;
87 if (target.fireEvent('before-reorder', mappings, target, this) !== false) {
88 this.doReorder(mappings);
90 target.fireEvent('reorder', mappings, target, this);
95 * Abstract function to perform the actual reordering. This MUST be overridden in a subclass
96 * @param {Object} mappings Mappings of the old item indexes to new item indexes
98 doReorder: function(paramName) {
99 throw new Error("doReorder must be implemented in the Ext.ux.Reorderer subclass");
103 * Should create and return an Ext.dd.DD for the given item. This MUST be overridden in a subclass
104 * @param {Mixed} item The item to create a DD for. This could be a TabPanel tab, a Toolbar button, etc
105 * @return {Ext.dd.DD} The DD for the given item
107 createItemDD: function(item) {
108 throw new Error("createItemDD must be implemented in the Ext.ux.Reorderer subclass");
112 * Sets up the given Toolbar item as a draggable
113 * @param {Mixed} button The item to make draggable (usually an Ext.Button instance)
115 createItemDD: function(button) {
116 var el = button.getEl(),
121 button.dd = new Ext.dd.DD(el, undefined, {
125 button.dd.constrainTo(tbar.getEl());
126 button.dd.setYConstraint(0, 0, 0);
128 Ext.apply(button.dd, {
129 b4StartDrag: function() {
130 this.startPosition = el.getXY();
132 //bump up the z index of the button being dragged but keep a reference to the original
133 this.startZIndex = el.getStyle('zIndex');
134 el.setStyle('zIndex', 10000);
136 button.suspendEvents();
139 onDrag: function(e) {
140 //calculate the button's index within the toolbar and its current midpoint
141 var buttonX = el.getXY()[0],
142 deltaX = buttonX - this.startPosition[0],
143 items = tbar.items.items,
144 oldIndex = items.indexOf(button),
147 //find which item in the toolbar the midpoint is currently over
148 for (var index = 0; index < items.length; index++) {
149 var item = items[index];
151 if (item.reorderable && item.id != button.id) {
152 //find the midpoint of the button
153 var box = item.getEl().getBox(),
154 midpoint = (me.buttonXCache[item.id] || box.x) + (box.width / 2),
155 movedLeft = oldIndex > index && deltaX < 0 && buttonX < midpoint,
156 movedRight = oldIndex < index && deltaX > 0 && (buttonX + el.getWidth()) > midpoint;
158 if (movedLeft || movedRight) {
159 me[movedLeft ? 'onMovedLeft' : 'onMovedRight'](button, index, oldIndex);
167 * After the drag has been completed, make sure the button being dragged makes it back to
168 * the correct location and resets its z index
170 endDrag: function() {
171 //we need to update the cache here for cases where the button was dragged but its
172 //position in the toolbar did not change
173 me.updateButtonXCache();
175 el.moveTo(me.buttonXCache[button.id], undefined, {
176 duration: me.animationDuration,
178 callback: function() {
179 button.resumeEvents();
181 tbar.fireEvent('reordered', button, tbar);
185 el.setStyle('zIndex', this.startZIndex);
192 * Creates a DD instance for a given item if it is reorderable
193 * @param {Mixed} item The item
195 createIfReorderable: function(item) {
196 if (this.defaultReorderable && item.reorderable == undefined) item.reorderable = true;
198 if (item.reorderable) {
200 this.createItemDD(item);
202 item.on('render', this.createItemDD.createDelegate(this, [item]), this, {single: true});
208 * Returns an array of items which will be made draggable. This defaults to the contents of this.target.items,
209 * but can be overridden - e.g. for TabPanels
210 * @return {Array} The array of items which will be made draggable
212 getItems: function() {
213 return this.target.items.items;
217 * Adds before-reorder and reorder events to the target component
219 initEvents: function() {
220 this.target.addEvents(
222 * @event before-reorder
223 * Fires before a reorder occurs. Return false to cancel
224 * @param {Object} mappings Mappings of the old item indexes to new item indexes
225 * @param {Mixed} component The target component
226 * @param {Ext.ux.TabReorderer} this The plugin instance
232 * Fires after a reorder has occured.
233 * @param {Object} mappings Mappings of the old item indexes to the new item indexes
234 * @param {Mixed} component The target component
235 * @param {Ext.ux.TabReorderer} this The plugin instance