2 * @class Ext.view.DropZone
3 * @extends Ext.dd.DropZone
6 Ext.define('Ext.view.DropZone', {
7 extend: 'Ext.dd.DropZone',
9 indicatorHtml: '<div class="x-grid-drop-indicator-left"></div><div class="x-grid-drop-indicator-right"></div>',
10 indicatorCls: 'x-grid-drop-indicator',
12 constructor: function(config) {
14 Ext.apply(me, config);
16 // Create a ddGroup unless one has been configured.
17 // User configuration of ddGroups allows users to specify which
18 // DD instances can interact with each other. Using one
19 // based on the id of the View would isolate it and mean it can only
20 // interact with a DragZone on the same View also using a generated ID.
22 me.ddGroup = 'view-dd-zone-' + me.view.id;
25 // The DropZone's encapsulating element is the View's main element. It must be this because drop gestures
26 // may require scrolling on hover near a scrolling boundary. In Ext 4.x two DD instances may not use the
27 // same element, so a DragZone on this same View must use the View's parent element as its element.
28 me.callParent([me.view.el]);
31 // Fire an event through the client DataView. Lock this DropZone during the event processing so that
32 // its data does not become corrupted by processing mouse events.
33 fireViewEvent: function() {
35 var result = this.view.fireEvent.apply(this.view, arguments);
40 getTargetFromEvent : function(e) {
41 var node = e.getTarget(this.view.getItemSelector()),
42 mouseY, nodeList, testNode, i, len, box;
44 // Not over a row node: The content may be narrower than the View's encapsulating element, so return the closest.
45 // If we fall through because the mouse is below the nodes (or there are no nodes), we'll get an onContainerOver call.
47 mouseY = e.getPageY();
48 for (i = 0, nodeList = this.view.getNodes(), len = nodeList.length; i < len; i++) {
49 testNode = nodeList[i];
50 box = Ext.fly(testNode).getBox();
51 if (mouseY <= box.bottom) {
59 getIndicator: function() {
63 me.indicator = Ext.createWidget('component', {
64 html: me.indicatorHtml,
74 getPosition: function(e, node) {
76 region = Ext.fly(node).getRegion(),
79 if ((region.bottom - y) >= (region.bottom - region.top) / 2) {
88 * @private Determines whether the record at the specified offset from the passed record
89 * is in the drag payload.
93 * @returns {Boolean} True if the targeted record is in the drag payload
95 containsRecordAtOffset: function(records, record, offset) {
100 recordIndex = view.indexOf(record),
101 nodeBefore = view.getNode(recordIndex + offset),
102 recordBefore = nodeBefore ? view.getRecord(nodeBefore) : null;
104 return recordBefore && Ext.Array.contains(records, recordBefore);
107 positionIndicator: function(node, data, e) {
110 pos = me.getPosition(e, node),
111 overRecord = view.getRecord(node),
112 draggingRecords = data.records,
113 indicator, indicatorY;
115 if (!Ext.Array.contains(draggingRecords, overRecord) && (
116 pos == 'before' && !me.containsRecordAtOffset(draggingRecords, overRecord, -1) ||
117 pos == 'after' && !me.containsRecordAtOffset(draggingRecords, overRecord, 1)
121 if (me.overRecord != overRecord || me.currentPosition != pos) {
123 indicatorY = Ext.fly(node).getY() - view.el.getY() - 1;
124 if (pos == 'after') {
125 indicatorY += Ext.fly(node).getHeight();
127 me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, indicatorY);
129 // Cache the overRecord and the 'before' or 'after' indicator.
130 me.overRecord = overRecord;
131 me.currentPosition = pos;
138 invalidateDrop: function() {
141 this.getIndicator().hide();
145 // The mouse is over a View node
146 onNodeOver: function(node, dragZone, e, data) {
147 if (!Ext.Array.contains(data.records, this.view.getRecord(node))) {
148 this.positionIndicator(node, data, e);
150 return this.valid ? this.dropAllowed : this.dropNotAllowed;
153 // Moved out of the DropZone without dropping.
154 // Remove drop position indicator
155 notifyOut: function(node, dragZone, e, data) {
156 this.callParent(arguments);
157 delete this.overRecord;
158 delete this.currentPosition;
159 if (this.indicator) {
160 this.indicator.hide();
164 // The mouse is past the end of all nodes (or there are no nodes)
165 onContainerOver : function(dd, e, data) {
167 c = v.store.getCount();
169 // There are records, so position after the last one
171 this.positionIndicator(v.getNode(c - 1), data, e);
174 // No records, position the indicator at the top
176 delete this.overRecord;
177 delete this.currentPosition;
178 this.getIndicator().setWidth(Ext.fly(v.el).getWidth()).showAt(0, 0);
181 return this.dropAllowed;
184 onContainerDrop : function(dd, e, data) {
185 return this.onNodeDrop(dd, null, e, data);
188 onNodeDrop: function(node, dragZone, e, data) {
192 // Create a closure to perform the operation which the event handler may use.
193 // Users may now return <code>0</code> from the beforedrop handler, and perform any kind
194 // of asynchronous processing such as an Ext.Msg.confirm, or an Ajax request,
195 // and complete the drop gesture at some point in the future by calling this function.
196 processDrop = function () {
198 me.handleNodeDrop(data, me.overRecord, me.currentPosition);
200 me.fireViewEvent('drop', node, data, me.overRecord, me.currentPosition);
205 performOperation = me.fireViewEvent('beforedrop', node, data, me.overRecord, me.currentPosition, processDrop);
206 if (performOperation === 0) {
208 } else if (performOperation !== false) {
209 // If the processDrop function was called in the event handler, do not do it again.