3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.grid.header.DropZone
17 * @extends Ext.dd.DropZone
20 Ext.define('Ext.grid.header.DropZone', {
21 extend: 'Ext.dd.DropZone',
22 colHeaderCls: Ext.baseCSSPrefix + 'column-header',
23 proxyOffsets: [-4, -9],
25 constructor: function(headerCt){
26 this.headerCt = headerCt;
27 this.ddGroup = this.getDDGroup();
28 this.callParent([headerCt.el]);
31 getDDGroup: function() {
32 return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
35 getTargetFromEvent : function(e){
36 return e.getTarget('.' + this.colHeaderCls);
39 getTopIndicator: function() {
40 if (!this.topIndicator) {
41 this.topIndicator = Ext.DomHelper.append(Ext.getBody(), {
46 return this.topIndicator;
49 getBottomIndicator: function() {
50 if (!this.bottomIndicator) {
51 this.bottomIndicator = Ext.DomHelper.append(Ext.getBody(), {
52 cls: "col-move-bottom",
56 return this.bottomIndicator;
59 getLocation: function(e, t) {
61 region = Ext.fly(t).getRegion(),
64 if ((region.right - x) <= (region.right - region.left) / 2) {
71 header: Ext.getCmp(t.id),
76 positionIndicator: function(draggedHeader, node, e){
77 var location = this.getLocation(e, node),
78 header = location.header,
80 nextHd = draggedHeader.nextSibling('gridcolumn:not([hidden])'),
81 prevHd = draggedHeader.previousSibling('gridcolumn:not([hidden])'),
82 region, topIndicator, bottomIndicator, topAnchor, bottomAnchor,
83 topXY, bottomXY, headerCtEl, minX, maxX;
85 // Cannot drag beyond non-draggable start column
86 if (!header.draggable && header.getIndex() == 0) {
90 this.lastLocation = location;
92 if ((draggedHeader !== header) &&
93 ((pos === "before" && nextHd !== header) ||
94 (pos === "after" && prevHd !== header)) &&
95 !header.isDescendantOf(draggedHeader)) {
97 // As we move in between different DropZones that are in the same
98 // group (such as the case when in a locked grid), invalidateDrop
99 // on the other dropZones.
100 var allDropZones = Ext.dd.DragDropManager.getRelated(this),
101 ln = allDropZones.length,
105 for (; i < ln; i++) {
106 dropZone = allDropZones[i];
107 if (dropZone !== this && dropZone.invalidateDrop) {
108 dropZone.invalidateDrop();
114 topIndicator = this.getTopIndicator();
115 bottomIndicator = this.getBottomIndicator();
116 if (pos === 'before') {
123 topXY = header.el.getAnchorXY(topAnchor);
124 bottomXY = header.el.getAnchorXY(bottomAnchor);
126 // constrain the indicators to the viewable section
127 headerCtEl = this.headerCt.el;
128 minX = headerCtEl.getLeft();
129 maxX = headerCtEl.getRight();
131 topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX);
132 bottomXY[0] = Ext.Number.constrain(bottomXY[0], minX, maxX);
134 // adjust by offsets, this is to center the arrows so that they point
135 // at the split point
140 // position and show indicators
141 topIndicator.setXY(topXY);
142 bottomIndicator.setXY(bottomXY);
144 bottomIndicator.show();
145 // invalidate drop operation and hide indicators
147 this.invalidateDrop();
151 invalidateDrop: function() {
153 this.hideIndicators();
156 onNodeOver: function(node, dragZone, e, data) {
157 if (data.header.el.dom !== node) {
158 this.positionIndicator(data.header, node, e);
160 return this.valid ? this.dropAllowed : this.dropNotAllowed;
163 hideIndicators: function() {
164 this.getTopIndicator().hide();
165 this.getBottomIndicator().hide();
168 onNodeOut: function() {
169 this.hideIndicators();
172 onNodeDrop: function(node, dragZone, e, data) {
174 this.invalidateDrop();
175 var hd = data.header,
176 lastLocation = this.lastLocation,
178 fromIdx = fromCt.items.indexOf(hd), // Container.items is a MixedCollection
179 toCt = lastLocation.header.ownerCt,
180 toIdx = toCt.items.indexOf(lastLocation.header),
181 headerCt = this.headerCt,
185 if (lastLocation.pos === 'after') {
189 // If we are dragging in between two HeaderContainers that have had the lockable
190 // mixin injected we will lock/unlock headers in between sections. Note that lockable
191 // does NOT currently support grouped headers.
192 if (fromCt !== toCt && fromCt.lockableInjected && toCt.lockableInjected && toCt.lockedCt) {
193 scrollerOwner = fromCt.up('[scrollerOwner]');
194 scrollerOwner.lock(hd, toIdx);
195 } else if (fromCt !== toCt && fromCt.lockableInjected && toCt.lockableInjected && fromCt.lockedCt) {
196 scrollerOwner = fromCt.up('[scrollerOwner]');
197 scrollerOwner.unlock(hd, toIdx);
199 // If dragging rightwards, then after removal, the insertion index will be one less when moving
200 // in between the same container.
201 if ((fromCt === toCt) && (toIdx > fromCt.items.indexOf(hd))) {
205 // Remove dragged header from where it was without destroying it or relaying its Container
206 if (fromCt !== toCt) {
207 fromCt.suspendLayout = true;
208 fromCt.remove(hd, false);
209 fromCt.suspendLayout = false;
212 // Dragged the last header out of the fromCt group... The fromCt group must die
213 if (fromCt.isGroupHeader) {
214 if (!fromCt.items.getCount()) {
215 groupCt = fromCt.ownerCt;
216 groupCt.suspendLayout = true;
217 groupCt.remove(fromCt, false);
218 fromCt.el.dom.parentNode.removeChild(fromCt.el.dom);
219 groupCt.suspendLayout = false;
221 fromCt.minWidth = fromCt.getWidth() - hd.getWidth();
222 fromCt.setWidth(fromCt.minWidth);
226 // Move dragged header into its drop position
227 toCt.suspendLayout = true;
228 if (fromCt === toCt) {
229 toCt.move(fromIdx, toIdx);
231 toCt.insert(toIdx, hd);
233 toCt.suspendLayout = false;
235 // Group headers acquire the aggregate width of their child headers
236 // Therefore a child header may not flex; it must contribute a fixed width.
237 // But we restore the flex value when moving back into the main header container
238 if (toCt.isGroupHeader) {
239 hd.savedFlex = hd.flex;
241 hd.width = hd.getWidth();
242 // When there was previously a flex, we need to ensure we don't count for the
244 toCt.minWidth = toCt.getWidth() + hd.getWidth() - (hd.savedFlex ? 1 : 0);
245 toCt.setWidth(toCt.minWidth);
248 hd.flex = hd.savedFlex;
254 // Refresh columns cache in case we remove an emptied group column
255 headerCt.purgeCache();
257 headerCt.onHeaderMoved(hd, fromIdx, toIdx);
258 // Emptied group header can only be destroyed after the header and grid have been refreshed
259 if (!fromCt.items.getCount()) {