DragSelector.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /**
  2. * @class Ext.ux.DataView.DragSelector
  3. * @extends Object
  4. * @author Ed Spencer
  5. *
  6. */
  7. Ext.define('Ext.ux.DataView.DragSelector', {
  8. requires: ['Ext.dd.DragTracker', 'Ext.util.Region'],
  9. /**
  10. * Initializes the plugin by setting up the drag tracker
  11. */
  12. init: function(dataview) {
  13. /**
  14. * @property dataview
  15. * @type Ext.view.View
  16. * The DataView bound to this instance
  17. */
  18. this.dataview = dataview;
  19. dataview.mon(dataview, {
  20. beforecontainerclick: this.cancelClick,
  21. scope: this,
  22. render: {
  23. fn: this.onRender,
  24. scope: this,
  25. single: true
  26. }
  27. });
  28. },
  29. /**
  30. * @private
  31. * Called when the attached DataView is rendered. This sets up the DragTracker instance that will be used
  32. * to created a dragged selection area
  33. */
  34. onRender: function() {
  35. /**
  36. * @property tracker
  37. * @type Ext.dd.DragTracker
  38. * The DragTracker attached to this instance. Note that the 4 on* functions are called in the scope of the
  39. * DragTracker ('this' refers to the DragTracker inside those functions), so we pass a reference to the
  40. * DragSelector so that we can call this class's functions.
  41. */
  42. this.tracker = Ext.create('Ext.dd.DragTracker', {
  43. dataview: this.dataview,
  44. el: this.dataview.el,
  45. dragSelector: this,
  46. onBeforeStart: this.onBeforeStart,
  47. onStart: this.onStart,
  48. onDrag : this.onDrag,
  49. onEnd : this.onEnd
  50. });
  51. /**
  52. * @property dragRegion
  53. * @type Ext.util.Region
  54. * Represents the region currently dragged out by the user. This is used to figure out which dataview nodes are
  55. * in the selected area and to set the size of the Proxy element used to highlight the current drag area
  56. */
  57. this.dragRegion = Ext.create('Ext.util.Region');
  58. },
  59. /**
  60. * @private
  61. * Listener attached to the DragTracker's onBeforeStart event. Returns false if the drag didn't start within the
  62. * DataView's el
  63. */
  64. onBeforeStart: function(e) {
  65. return e.target == this.dataview.getEl().dom;
  66. },
  67. /**
  68. * @private
  69. * Listener attached to the DragTracker's onStart event. Cancel's the DataView's containerclick event from firing
  70. * and sets the start co-ordinates of the Proxy element. Clears any existing DataView selection
  71. * @param {Ext.EventObject} e The click event
  72. */
  73. onStart: function(e) {
  74. var dragSelector = this.dragSelector,
  75. dataview = this.dataview;
  76. // Flag which controls whether the cancelClick method vetoes the processing of the DataView's containerclick event.
  77. // On IE (where else), this needs to remain set for a millisecond after mouseup because even though the mouse has
  78. // moved, the mouseup will still trigger a click event.
  79. this.dragging = true;
  80. //here we reset and show the selection proxy element and cache the regions each item in the dataview take up
  81. dragSelector.fillRegions();
  82. dragSelector.getProxy().show();
  83. dataview.getSelectionModel().deselectAll();
  84. },
  85. /**
  86. * @private
  87. * Reusable handler that's used to cancel the container click event when dragging on the dataview. See onStart for
  88. * details
  89. */
  90. cancelClick: function() {
  91. return !this.tracker.dragging;
  92. },
  93. /**
  94. * @private
  95. * Listener attached to the DragTracker's onDrag event. Figures out how large the drag selection area should be and
  96. * updates the proxy element's size to match. Then iterates over all of the rendered items and marks them selected
  97. * if the drag region touches them
  98. * @param {Ext.EventObject} e The drag event
  99. */
  100. onDrag: function(e) {
  101. var dragSelector = this.dragSelector,
  102. selModel = dragSelector.dataview.getSelectionModel(),
  103. dragRegion = dragSelector.dragRegion,
  104. bodyRegion = dragSelector.bodyRegion,
  105. proxy = dragSelector.getProxy(),
  106. regions = dragSelector.regions,
  107. length = regions.length,
  108. startXY = this.startXY,
  109. currentXY = this.getXY(),
  110. minX = Math.min(startXY[0], currentXY[0]),
  111. minY = Math.min(startXY[1], currentXY[1]),
  112. width = Math.abs(startXY[0] - currentXY[0]),
  113. height = Math.abs(startXY[1] - currentXY[1]),
  114. region, selected, i;
  115. Ext.apply(dragRegion, {
  116. top: minY,
  117. left: minX,
  118. right: minX + width,
  119. bottom: minY + height
  120. });
  121. dragRegion.constrainTo(bodyRegion);
  122. proxy.setRegion(dragRegion);
  123. for (i = 0; i < length; i++) {
  124. region = regions[i];
  125. selected = dragRegion.intersect(region);
  126. if (selected) {
  127. selModel.select(i, true);
  128. } else {
  129. selModel.deselect(i);
  130. }
  131. }
  132. },
  133. /**
  134. * @private
  135. * Listener attached to the DragTracker's onEnd event. This is a delayed function which executes 1
  136. * millisecond after it has been called. This is because the dragging flag must remain active to cancel
  137. * the containerclick event which the mouseup event will trigger.
  138. * @param {Ext.EventObject} e The event object
  139. */
  140. onEnd: Ext.Function.createDelayed(function(e) {
  141. var dataview = this.dataview,
  142. selModel = dataview.getSelectionModel(),
  143. dragSelector = this.dragSelector;
  144. this.dragging = false;
  145. dragSelector.getProxy().hide();
  146. }, 1),
  147. /**
  148. * @private
  149. * Creates a Proxy element that will be used to highlight the drag selection region
  150. * @return {Ext.Element} The Proxy element
  151. */
  152. getProxy: function() {
  153. if (!this.proxy) {
  154. this.proxy = this.dataview.getEl().createChild({
  155. tag: 'div',
  156. cls: 'x-view-selector'
  157. });
  158. }
  159. return this.proxy;
  160. },
  161. /**
  162. * @private
  163. * Gets the region taken up by each rendered node in the DataView. We use these regions to figure out which nodes
  164. * to select based on the selector region the user has dragged out
  165. */
  166. fillRegions: function() {
  167. var dataview = this.dataview,
  168. regions = this.regions = [];
  169. dataview.all.each(function(node) {
  170. regions.push(node.getRegion());
  171. });
  172. this.bodyRegion = dataview.getEl().getRegion();
  173. }
  174. });