--- /dev/null
+/**
+ * @class Ext.ux.DataView.Draggable
+ * @extends Object
+ * @author Ed Spencer
+ *
+<pre><code>
+Ext.create('Ext.view.View', {
+ mixins: {
+ draggable: 'Ext.ux.DataView.Draggable'
+ },
+
+ initComponent: function() {
+ this.mixins.draggable.init(this, {
+ ddConfig: {
+ ddGroup: 'someGroup'
+ }
+ });
+
+ this.callParent(arguments);
+ }
+});
+</code></pre>
+ *
+ */
+Ext.define('Ext.ux.DataView.Draggable', {
+ requires: 'Ext.dd.DragZone',
+
+ /**
+ * @cfg {String} ghostCls The CSS class added to the outermost element of the created ghost proxy
+ * (defaults to 'x-dataview-draggable-ghost')
+ */
+ ghostCls: 'x-dataview-draggable-ghost',
+
+ /**
+ * @cfg {Ext.XTemplate/Array} ghostTpl The template used in the ghost DataView
+ */
+ ghostTpl: [
+ '<tpl for=".">',
+ '{title}',
+ '</tpl>'
+ ],
+
+ /**
+ * @cfg {Object} ddConfig Config object that is applied to the internally created DragZone
+ */
+
+ /**
+ * @cfg {String} ghostConfig Config object that is used to configure the internally created DataView
+ */
+
+ init: function(dataview, config) {
+ /**
+ * @property dataview
+ * @type Ext.view.View
+ * The Ext.view.View instance that this DragZone is attached to
+ */
+ this.dataview = dataview;
+
+ dataview.on('render', this.onRender, this);
+
+ Ext.apply(this, {
+ itemSelector: dataview.itemSelector,
+ ghostConfig : {}
+ }, config || {});
+
+ Ext.applyIf(this.ghostConfig, {
+ itemSelector: 'img',
+ cls: this.ghostCls,
+ tpl: this.ghostTpl
+ });
+ },
+
+ /**
+ * @private
+ * Called when the attached DataView is rendered. Sets up the internal DragZone
+ */
+ onRender: function() {
+ var config = Ext.apply({}, this.ddConfig || {}, {
+ dvDraggable: this,
+ dataview : this.dataview,
+ getDragData: this.getDragData,
+ getTreeNode: this.getTreeNode,
+ afterRepair: this.afterRepair,
+ getRepairXY: this.getRepairXY
+ });
+
+ /**
+ * @property dragZone
+ * @type Ext.dd.DragZone
+ * The attached DragZone instane
+ */
+ this.dragZone = Ext.create('Ext.dd.DragZone', this.dataview.getEl(), config);
+ },
+
+ getDragData: function(e) {
+ var draggable = this.dvDraggable,
+ dataview = this.dataview,
+ selModel = dataview.getSelectionModel(),
+ target = e.getTarget(draggable.itemSelector),
+ selected, dragData;
+
+ if (target) {
+ if (!dataview.isSelected(target)) {
+ selModel.select(dataview.getRecord(target));
+ }
+
+ selected = dataview.getSelectedNodes();
+ dragData = {
+ copy: true,
+ nodes: selected,
+ records: selModel.getSelection(),
+ item: true
+ };
+
+ if (selected.length == 1) {
+ dragData.single = true;
+ dragData.ddel = target;
+ } else {
+ dragData.multi = true;
+ dragData.ddel = draggable.prepareGhost(selModel.getSelection()).dom;
+ }
+
+ return dragData;
+ }
+
+ return false;
+ },
+
+ getTreeNode: function() {
+ console.log('test');
+ },
+
+ afterRepair: function() {
+ this.dragging = false;
+
+ var nodes = this.dragData.nodes,
+ length = nodes.length,
+ i;
+
+ //FIXME: Ext.fly does not work here for some reason, only frames the last node
+ for (i = 0; i < length; i++) {
+ Ext.get(nodes[i]).frame('#8db2e3', 1);
+ }
+ },
+
+ /**
+ * @private
+ * Returns the x and y co-ordinates that the dragged item should be animated back to if it was dropped on an
+ * invalid drop target. If we're dragging more than one item we don't animate back and just allow afterRepair
+ * to frame each dropped item.
+ */
+ getRepairXY: function(e) {
+ if (this.dragData.multi) {
+ return false;
+ } else {
+ var repairEl = Ext.get(this.dragData.ddel),
+ repairXY = repairEl.getXY();
+
+ //take the item's margins and padding into account to make the repair animation line up perfectly
+ repairXY[0] += repairEl.getPadding('t') + repairEl.getMargin('t');
+ repairXY[1] += repairEl.getPadding('l') + repairEl.getMargin('l');
+
+ return repairXY;
+ }
+ },
+
+ /**
+ * Updates the internal ghost DataView by ensuring it is rendered and contains the correct records
+ * @param {Array} records The set of records that is currently selected in the parent DataView
+ * @return {Ext.view.View} The Ghost DataView
+ */
+ prepareGhost: function(records) {
+ var ghost = this.createGhost(records),
+ store = ghost.store;
+
+ store.removeAll();
+ store.add(records);
+
+ return ghost.getEl();
+ },
+
+ /**
+ * @private
+ * Creates the 'ghost' DataView that follows the mouse cursor during the drag operation. This div is usually a
+ * lighter-weight representation of just the nodes that are selected in the parent DataView. Delegates the creation
+ * of each selected item's element to {@link createGhostElement}
+ */
+ createGhost: function(records) {
+ if (!this.ghost) {
+ var ghostConfig = Ext.apply({}, this.ghostConfig, {
+ store: Ext.create('Ext.data.Store', {
+ model: records[0].modelName
+ })
+ });
+
+ this.ghost = Ext.create('Ext.view.View', ghostConfig);
+
+ this.ghost.render(document.createElement('div'));
+ }
+
+ return this.ghost;
+ }
+});