/** * @private */Ext.define('Ext.view.DragZone', { extend: 'Ext.dd.DragZone', containerScroll: false, constructor: function(config) { var me = this, view, ownerCt, el; Ext.apply(me, config); // Create a ddGroup unless one has been configured. // User configuration of ddGroups allows users to specify which // DD instances can interact with each other. Using one // based on the id of the View would isolate it and mean it can only // interact with a DropZone on the same View also using a generated ID. if (!me.ddGroup) { me.ddGroup = 'view-dd-zone-' + me.view.id; } // Ext.dd.DragDrop instances are keyed by the ID of their encapsulating element. // So a View's DragZone cannot use the View's main element because the DropZone must use that // because the DropZone may need to scroll on hover at a scrolling boundary, and it is the View's // main element which handles scrolling. // We use the View's parent element to drag from. Ideally, we would use the internal structure, but that // is transient; DataViews recreate the internal structure dynamically as data changes. // TODO: Ext 5.0 DragDrop must allow multiple DD objects to share the same element. view = me.view; // This is for https://www.w3.org/TR/pointerevents/ platforms. // On these platforms, the pointerdown event (single touchstart) is reserved for // initiating a scroll gesture. Setting the items draggable defeats that and // enables the touchstart event to trigger a drag. // // Two finger dragging will still scroll on these platforms. view.setItemsDraggable(true); ownerCt = view.ownerCt; // We don't just grab the parent el, since the parent el may be // some el injected by the layout if (ownerCt) { el = ownerCt.getTargetEl().dom; } else { el = view.el.dom.parentNode; } me.callParent([el]); me.ddel = document.createElement('div'); me.ddel.className = Ext.baseCSSPrefix + 'grid-dd-wrap'; }, init: function(id, sGroup, config) { var me = this, eventSpec = { itemmousedown: me.onItemMouseDown, scope: me }; // If there may be ambiguity with touch/swipe to scroll and a drag gesture // trigger drag start on longpress and a *real* mousedown. if (Ext.supports.Touch) { eventSpec.itemlongpress = me.onItemLongPress; // Longpress fires contextmenu in some touch platforms, so if we are using longpress // inhibit the contextmenu on this element eventSpec.contextmenu = { element: 'el', fn: me.onViewContextMenu }; } me.initTarget(id, sGroup, config); me.view.mon(me.view, eventSpec); }, onValidDrop: function(target, e, id) { this.callParent([target, e, id]); // focus the view that the node was dropped onto so that keynav will be enabled. if (!target.el.contains(Ext.Element.getActiveElement())) { target.el.focus(); } }, onViewContextMenu: function(e) { if (e.pointerType !== 'mouse') { e.preventDefault(); } }, onItemMouseDown: function(view, record, item, index, e) { // Ignore touchstart. // For touch events, we use longpress. if (e.pointerType === 'mouse') { this.onTriggerGesture(view, record, item, index, e); } }, onItemLongPress: function(view, record, item, index, e) { // Ignore long mousedowns. // The initial mousedown started the drag. // For touch events, we use longpress. if (e.pointerType !== 'mouse') { this.onTriggerGesture(view, record, item, index, e); } }, onTriggerGesture: function(view, record, item, index, e) { var navModel; // Only respond to longpress for touch dragging. // Reject drag start if mousedown is on the actionable cell of a grid view if ((e.pointerType === 'touch' && e.type !== 'longpress') || (e.position && e.position.isEqual(e.view.actionPosition))) { return; } if (!this.isPreventDrag(e, record, item, index)) { navModel = view.getNavigationModel(); // Since handleMouseDown prevents the default behavior of the event, which // is to focus the view, we focus the view now. This ensures that the view // remains focused if the drag is cancelled, or if no drag occurs. // // A Table event will have a position property which is a CellContext if (e.position) { navModel.setPosition(e.position); } // Otherwise, just use the item index else { navModel.setPosition(index); } this.handleMouseDown(e); } }, /** * @protected * Template method called upon mousedown. May be overridden in subclasses, or configured * into an instance. * * Return `true` to prevent drag start. * @param {Ext.event.Event} e The mousedown event. * @param {Ext.data.Model} record The record mousedowned upon. * @param {HTMLElement} item The grid row mousedowned upon. * @param {Number} index The row number mousedowned upon. */ isPreventDrag: function(e, record, item, index) { return !!e.isInputFieldEvent; }, getDragData: function(e) { var view = this.view, item = e.getTarget(view.getItemSelector()); if (item) { return { copy: view.copy || (view.allowCopy && e.ctrlKey), event: e, view: view, ddel: this.ddel, item: item, records: view.getSelectionModel().getSelection(), fromPosition: Ext.fly(item).getXY() }; } }, onInitDrag: function(x, y) { var me = this, data = me.dragData, view = data.view, selectionModel = view.getSelectionModel(), record = view.getRecord(data.item); // Update the selection to match what would have been selected if the user had // done a full click on the target node rather than starting a drag from it if (!selectionModel.isSelected(record)) { selectionModel.selectWithEvent(record, me.DDMInstance.mousedownEvent); } data.records = selectionModel.getSelection(); Ext.fly(me.ddel).setHtml(me.getDragText()); me.proxy.update(me.ddel); me.onStartDrag(x, y); return true; }, getDragText: function() { var count = this.dragData.records.length; return Ext.String.format(this.dragText, count, count === 1 ? '' : 's'); }, getRepairXY : function(e, data){ return data ? data.fromPosition : false; }});