CellDragDrop.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /**
  2. * This plugin can enable a cell to cell drag and drop operation within the same grid view.
  3. *
  4. * Note that the plugin must be added to the grid view, not to the grid panel. For example,
  5. * using {@link Ext.panel.Table viewConfig}:
  6. *
  7. * viewConfig: {
  8. * plugins: {
  9. * celldragdrop: {
  10. * // Remove text from source cell and replace with value of emptyText.
  11. * applyEmptyText: true,
  12. *
  13. * //emptyText: Ext.String.htmlEncode('<<foo>>'),
  14. *
  15. * // Will only allow drops of the same type.
  16. * enforceType: true
  17. * }
  18. * }
  19. * }
  20. */
  21. Ext.define('Ext.ux.CellDragDrop', {
  22. extend: 'Ext.plugin.Abstract',
  23. alias: 'plugin.celldragdrop',
  24. uses: ['Ext.view.DragZone'],
  25. /**
  26. * @cfg {Boolean} enforceType
  27. * Set to `true` to only allow drops of the same type.
  28. *
  29. * Defaults to `false`.
  30. */
  31. enforceType: false,
  32. /**
  33. * @cfg {Boolean} applyEmptyText
  34. * If `true`, then use the value of {@link #emptyText} to replace the drag record's value after a node drop.
  35. * Note that, if dropped on a cell of a different type, it will convert the default text according to its own conversion rules.
  36. *
  37. * Defaults to `false`.
  38. */
  39. applyEmptyText: false,
  40. /**
  41. * @cfg {String} emptyText
  42. * If {@link #applyEmptyText} is `true`, then this value as the drag record's value after a node drop.
  43. *
  44. * Defaults to an empty string.
  45. */
  46. emptyText: '',
  47. /**
  48. * @cfg {String} dropBackgroundColor
  49. * The default background color for when a drop is allowed.
  50. *
  51. * Defaults to green.
  52. */
  53. dropBackgroundColor: 'green',
  54. /**
  55. * @cfg {String} noDropBackgroundColor
  56. * The default background color for when a drop is not allowed.
  57. *
  58. * Defaults to red.
  59. */
  60. noDropBackgroundColor: 'red',
  61. /**
  62. * @cfg {String} dragText
  63. * The text to show while dragging.
  64. *
  65. * Two placeholders can be used in the text:
  66. *
  67. * - `{0}` The number of selected items.
  68. * - `{1}` 's' when more than 1 items (only useful for English).
  69. * @locale
  70. */
  71. dragText: '{0} selected row{1}',
  72. /**
  73. * @cfg {String} ddGroup
  74. * A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and
  75. * DropZone used by this plugin will only interact with other drag drop objects in the same group.
  76. */
  77. ddGroup: "GridDD",
  78. /**
  79. * @cfg {Boolean} enableDrop
  80. * Set to `false` to disallow the View from accepting drop gestures.
  81. */
  82. enableDrop: true,
  83. /**
  84. * @cfg {Boolean} enableDrag
  85. * Set to `false` to disallow dragging items from the View.
  86. */
  87. enableDrag: true,
  88. /**
  89. * @cfg {Object/Boolean} containerScroll
  90. * True to register this container with the Scrollmanager for auto scrolling during drag operations.
  91. * A {@link Ext.dd.ScrollManager} configuration may also be passed.
  92. */
  93. containerScroll: false,
  94. init: function (view) {
  95. var me = this;
  96. view.on('render', me.onViewRender, me, {
  97. single: true
  98. });
  99. },
  100. destroy: function () {
  101. var me = this;
  102. me.dragZone = me.dropZone = Ext.destroy(me.dragZone, me.dropZone);
  103. me.callParent();
  104. },
  105. enable: function () {
  106. var me = this;
  107. if (me.dragZone) {
  108. me.dragZone.unlock();
  109. }
  110. if (me.dropZone) {
  111. me.dropZone.unlock();
  112. }
  113. me.callParent();
  114. },
  115. disable: function () {
  116. var me = this;
  117. if (me.dragZone) {
  118. me.dragZone.lock();
  119. }
  120. if (me.dropZone) {
  121. me.dropZone.lock();
  122. }
  123. me.callParent();
  124. },
  125. onViewRender: function (view) {
  126. var me = this,
  127. scrollEl;
  128. if (me.enableDrag) {
  129. if (me.containerScroll) {
  130. scrollEl = view.getEl();
  131. }
  132. me.dragZone = new Ext.view.DragZone({
  133. view: view,
  134. ddGroup: me.dragGroup || me.ddGroup,
  135. dragText: me.dragText,
  136. containerScroll: me.containerScroll,
  137. scrollEl: scrollEl,
  138. getDragData: function (e) {
  139. var view = this.view,
  140. item = e.getTarget(view.getItemSelector()),
  141. record = view.getRecord(item),
  142. cell = e.getTarget(view.getCellSelector()),
  143. dragEl, header;
  144. if (item) {
  145. dragEl = document.createElement('div');
  146. dragEl.className = 'x-form-text';
  147. dragEl.appendChild(document.createTextNode(cell.textContent || cell.innerText));
  148. header = view.getHeaderByCell(cell);
  149. return {
  150. event: new Ext.EventObjectImpl(e),
  151. ddel: dragEl,
  152. item: e.target,
  153. columnName: header.dataIndex,
  154. record: record
  155. };
  156. }
  157. },
  158. onInitDrag: function (x, y) {
  159. var self = this,
  160. data = self.dragData,
  161. view = self.view,
  162. selectionModel = view.getSelectionModel(),
  163. record = data.record,
  164. el = data.ddel;
  165. // Update the selection to match what would have been selected if the user had
  166. // done a full click on the target node rather than starting a drag from it.
  167. if (!selectionModel.isSelected(record)) {
  168. selectionModel.select(record, true);
  169. }
  170. Ext.fly(self.ddel).update(el.textContent || el.innerText);
  171. self.proxy.update(self.ddel);
  172. self.onStartDrag(x, y);
  173. return true;
  174. }
  175. });
  176. }
  177. if (me.enableDrop) {
  178. me.dropZone = new Ext.dd.DropZone(view.el, {
  179. view: view,
  180. ddGroup: me.dropGroup || me.ddGroup,
  181. containerScroll: true,
  182. getTargetFromEvent: function (e) {
  183. var self = this,
  184. view = self.view,
  185. cell = e.getTarget(view.cellSelector),
  186. row, header;
  187. // Ascertain whether the mousemove is within a grid cell.
  188. if (cell) {
  189. row = view.findItemByChild(cell);
  190. header = view.getHeaderByCell(cell);
  191. if (row && header) {
  192. return {
  193. node: cell,
  194. record: view.getRecord(row),
  195. columnName: header.dataIndex
  196. };
  197. }
  198. }
  199. },
  200. // On Node enter, see if it is valid for us to drop the field on that type of column.
  201. onNodeEnter: function (target, dd, e, dragData) {
  202. var self = this,
  203. destType = target.record.getField(target.columnName).type.toUpperCase(),
  204. sourceType = dragData.record.getField(dragData.columnName).type.toUpperCase();
  205. delete self.dropOK;
  206. // Return if no target node or if over the same cell as the source of the drag.
  207. if (!target || target.node === dragData.item.parentNode) {
  208. return;
  209. }
  210. // Check whether the data type of the column being dropped on accepts the
  211. // dragged field type. If so, set dropOK flag, and highlight the target node.
  212. if (me.enforceType && destType !== sourceType) {
  213. self.dropOK = false;
  214. if (me.noDropCls) {
  215. Ext.fly(target.node).addCls(me.noDropCls);
  216. } else {
  217. Ext.fly(target.node).applyStyles({
  218. backgroundColor: me.noDropBackgroundColor
  219. });
  220. }
  221. return false;
  222. }
  223. self.dropOK = true;
  224. if (me.dropCls) {
  225. Ext.fly(target.node).addCls(me.dropCls);
  226. } else {
  227. Ext.fly(target.node).applyStyles({
  228. backgroundColor: me.dropBackgroundColor
  229. });
  230. }
  231. },
  232. // Return the class name to add to the drag proxy. This provides a visual indication
  233. // of drop allowed or not allowed.
  234. onNodeOver: function (target, dd, e, dragData) {
  235. return this.dropOK ? this.dropAllowed : this.dropNotAllowed;
  236. },
  237. // Highlight the target node.
  238. onNodeOut: function (target, dd, e, dragData) {
  239. var cls = this.dropOK ? me.dropCls : me.noDropCls;
  240. if (cls) {
  241. Ext.fly(target.node).removeCls(cls);
  242. } else {
  243. Ext.fly(target.node).applyStyles({
  244. backgroundColor: ''
  245. });
  246. }
  247. },
  248. // Process the drop event if we have previously ascertained that a drop is OK.
  249. onNodeDrop: function (target, dd, e, dragData) {
  250. if (this.dropOK) {
  251. target.record.set(target.columnName, dragData.record.get(dragData.columnName));
  252. if (me.applyEmptyText) {
  253. dragData.record.set(dragData.columnName, me.emptyText);
  254. }
  255. return true;
  256. }
  257. },
  258. onCellDrop: Ext.emptyFn
  259. });
  260. }
  261. }
  262. });