X-Git-Url: http://git.roojs.org/?p=roojs1;a=blobdiff_plain;f=examples%2Fview%2Fddview.js;fp=examples%2Fview%2Fddview.js;h=0a294d766f470713d2c75d37fe2bf79cc6ac955b;hp=0000000000000000000000000000000000000000;hb=267f6bc0ecf0897515863c3c91a8eea23388e273;hpb=4806a3a6a72a6fc861a138965a6b1b5df60d0c87
diff --git a/examples/view/ddview.js b/examples/view/ddview.js
new file mode 100644
index 0000000000..0a294d766f
--- /dev/null
+++ b/examples/view/ddview.js
@@ -0,0 +1,477 @@
+Array.prototype.contains = function(element) {
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] == element) {
+ return true;
+ }
+ }
+ return false;
+};
+Roo.namespace("Roo.ux");
+
+/**
+ * @class Roo.ux.DDView
+ * A DnD enabled version of Roo.View.
+ * @param {Element/String} container The Element in which to create the View.
+ * @param {String} tpl The template string used to create the markup for each element of the View
+ * @param {Object} config The configuration properties. These include all the config options of
+ * {@link Roo.View} plus some specific to this class.
+ *
+ * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
+ * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.
+ *
+ * The following extra CSS rules are needed to provide insertion point highlighting:
+.x-view-drag-insert-above {
+ border-top:1px dotted #3366cc;
+}
+.x-view-drag-insert-below {
+ border-bottom:1px dotted #3366cc;
+}
+
+ *
+ */
+Roo.ux.DDView = function(container, tpl, config) {
+ Roo.ux.DDView.superclass.constructor.apply(this, arguments);
+ this.getEl().setStyle("outline", "0px none");
+ this.getEl().unselectable();
+ if (this.dragGroup) {
+ this.setDraggable(this.dragGroup.split(","));
+ }
+ if (this.dropGroup) {
+ this.setDroppable(this.dropGroup.split(","));
+ }
+ if (this.deletable) {
+ this.setDeletable();
+ }
+ this.isDirtyFlag = false;
+ this.addEvents({
+ "drop" : true
+ });
+};
+
+Roo.extend(Roo.ux.DDView, Roo.View, {
+/** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
+/** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
+/** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
+/** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
+
+ isFormField: true,
+
+ reset: Roo.emptyFn,
+
+ clearInvalid: Roo.form.Field.prototype.clearInvalid,
+
+ validate: function() {
+ return true;
+ },
+
+ destroy: function() {
+ this.purgeListeners();
+ this.getEl.removeAllListeners();
+ this.getEl().remove();
+ if (this.dragZone) {
+ if (this.dragZone.destroy) {
+ this.dragZone.destroy();
+ }
+ }
+ if (this.dropZone) {
+ if (this.dropZone.destroy) {
+ this.dropZone.destroy();
+ }
+ }
+ },
+
+/** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
+ getName: function() {
+ return this.name;
+ },
+
+/** Loads the View from a JSON string representing the Records to put into the Store. */
+ setValue: function(v) {
+ if (!this.store) {
+ throw "DDView.setValue(). DDView must be constructed with a valid Store";
+ }
+ var data = {};
+ data[this.store.reader.meta.root] = v ? [].concat(v) : [];
+ this.store.proxy = new Roo.data.MemoryProxy(data);
+ this.store.load();
+ },
+
+/** @return {String} a parenthesised list of the ids of the Records in the View. */
+ getValue: function() {
+ var result = '(';
+ this.store.each(function(rec) {
+ result += rec.id + ',';
+ });
+ return result.substr(0, result.length - 1) + ')';
+ },
+
+ getIds: function() {
+ var i = 0, result = new Array(this.store.getCount());
+ this.store.each(function(rec) {
+ result[i++] = rec.id;
+ });
+ return result;
+ },
+
+ isDirty: function() {
+ return this.isDirtyFlag;
+ },
+
+/**
+ * Part of the Roo.dd.DropZone interface. If no target node is found, the
+ * whole Element becomes the target, and this causes the drop gesture to append.
+ */
+ getTargetFromEvent : function(e) {
+ var target = e.getTarget();
+ while ((target !== null) && (target.parentNode != this.el.dom)) {
+ target = target.parentNode;
+ }
+ if (!target) {
+ target = this.el.dom.lastChild || this.el.dom;
+ }
+ return target;
+ },
+
+/**
+ * Create the drag data which consists of an object which has the property "ddel" as
+ * the drag proxy element.
+ */
+ getDragData : function(e) {
+ var target = this.findItemFromChild(e.getTarget());
+ if(target) {
+ this.handleSelection(e);
+ var selNodes = this.getSelectedNodes();
+ var dragData = {
+ source: this,
+ copy: this.copy || (this.allowCopy && e.ctrlKey),
+ nodes: selNodes,
+ records: []
+ };
+ var selectedIndices = this.getSelectedIndexes();
+ for (var i = 0; i < selectedIndices.length; i++) {
+ dragData.records.push(this.store.getAt(selectedIndices[i]));
+ }
+ if (selNodes.length == 1) {
+ dragData.ddel = target.cloneNode(true); // the div element
+ } else {
+ var div = document.createElement('div'); // create the multi element drag "ghost"
+ div.className = 'multi-proxy';
+ for (var i = 0, len = selNodes.length; i < len; i++) {
+ div.appendChild(selNodes[i].cloneNode(true));
+ }
+ dragData.ddel = div;
+ }
+ return dragData;
+ }
+ return false;
+ },
+
+/** Specify to which ddGroup items in this DDView may be dragged. */
+ setDraggable: function(ddGroup) {
+ if (ddGroup instanceof Array) {
+ Roo.each(ddGroup, this.setDraggable, this);
+ return;
+ }
+ if (this.dragZone) {
+ this.dragZone.addToGroup(ddGroup);
+ } else {
+ this.dragZone = new Roo.dd.DragZone(this.getEl(), {
+ containerScroll: true,
+ ddGroup: ddGroup
+ });
+// Draggability implies selection. DragZone's mousedown selects the element.
+ if (!this.multiSelect) { this.singleSelect = true; }
+
+// Wire the DragZone's handlers up to methods in *this*
+ this.dragZone.getDragData = this.getDragData.createDelegate(this);
+ }
+ },
+
+/** Specify from which ddGroup this DDView accepts drops. */
+ setDroppable: function(ddGroup) {
+ if (ddGroup instanceof Array) {
+ Roo.each(ddGroup, this.setDroppable, this);
+ return;
+ }
+ if (this.dropZone) {
+ this.dropZone.addToGroup(ddGroup);
+ } else {
+ this.dropZone = new Roo.dd.DropZone(this.getEl(), {
+ containerScroll: true,
+ ddGroup: ddGroup
+ });
+
+// Wire the DropZone's handlers up to methods in *this*
+ this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
+ this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
+ this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
+ this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
+ this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
+ }
+ },
+
+/** Decide whether to drop above or below a View node. */
+ getDropPoint : function(e, n, dd){
+ if (n == this.el.dom) { return "above"; }
+ var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
+ var c = t + (b - t) / 2;
+ var y = Roo.lib.Event.getPageY(e);
+ if(y <= c) {
+ return "above";
+ }else{
+ return "below";
+ }
+ },
+
+ onNodeEnter : function(n, dd, e, data){
+ return false;
+ },
+
+ onNodeOver : function(n, dd, e, data){
+ var pt = this.getDropPoint(e, n, dd);
+ // set the insert point style on the target node
+ var dragElClass = this.dropNotAllowed;
+ if (pt) {
+ var targetElClass;
+ if (pt == "above"){
+ dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
+ targetElClass = "x-view-drag-insert-above";
+ } else {
+ dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
+ targetElClass = "x-view-drag-insert-below";
+ }
+ if (this.lastInsertClass != targetElClass){
+ Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
+ this.lastInsertClass = targetElClass;
+ }
+ }
+ return dragElClass;
+ },
+
+ onNodeOut : function(n, dd, e, data){
+ this.removeDropIndicators(n);
+ },
+
+ onNodeDrop : function(n, dd, e, data){
+ if (this.fireEvent("drop", this, n, dd, e, data) === false) {
+ return false;
+ }
+ var pt = this.getDropPoint(e, n, dd);
+ var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
+ if (pt == "below") { insertAt++; }
+ for (var i = 0; i < data.records.length; i++) {
+ var r = data.records[i];
+ var dup = this.store.getById(r.id);
+ if (dup && (dd != this.dragZone)) {
+ Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
+ } else {
+ if (data.copy) {
+ this.store.insert(insertAt++, r.copy());
+ } else {
+ data.source.isDirtyFlag = true;
+ r.store.remove(r);
+ this.store.insert(insertAt++, r);
+ }
+ this.isDirtyFlag = true;
+ }
+ }
+ this.dragZone.cachedTarget = null;
+ return true;
+ },
+
+ removeDropIndicators : function(n){
+ if(n){
+ Roo.fly(n).removeClass([
+ "x-view-drag-insert-above",
+ "x-view-drag-insert-below"]);
+ this.lastInsertClass = "_noclass";
+ }
+ },
+
+/**
+ * Utility method. Add a delete option to the DDView's context menu.
+ * @param {String} imageUrl The URL of the "delete" icon image.
+ */
+ setDeletable: function(imageUrl) {
+ if (!this.singleSelect && !this.multiSelect) {
+ this.singleSelect = true;
+ }
+ var c = this.getContextMenu();
+ this.contextMenu.on("itemclick", function(item) {
+ switch (item.id) {
+ case "delete":
+ this.remove(this.getSelectedIndexes());
+ break;
+ }
+ }, this);
+ this.contextMenu.add({
+ icon: imageUrl || AU.resolveUrl("/images/delete.gif"),
+ id: "delete",
+ text: AU.getMessage("deleteItem")
+ });
+ },
+
+/** Return the context menu for this DDView. */
+ getContextMenu: function() {
+ if (!this.contextMenu) {
+// Create the View's context menu
+ this.contextMenu = new Roo.menu.Menu({
+ id: this.id + "-contextmenu"
+ });
+ this.el.on("contextmenu", this.showContextMenu, this);
+ }
+ return this.contextMenu;
+ },
+
+ disableContextMenu: function() {
+ if (this.contextMenu) {
+ this.el.un("contextmenu", this.showContextMenu, this);
+ }
+ },
+
+ showContextMenu: function(e, item) {
+ item = this.findItemFromChild(e.getTarget());
+ if (item) {
+ e.stopEvent();
+ this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
+ this.contextMenu.showAt(e.getXY());
+ }
+ },
+
+/**
+ * Remove {@link Roo.data.Record}s at the specified indices.
+ * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
+ */
+ remove: function(selectedIndices) {
+ selectedIndices = [].concat(selectedIndices);
+ for (var i = 0; i < selectedIndices.length; i++) {
+ var rec = this.store.getAt(selectedIndices[i]);
+ this.store.remove(rec);
+ }
+ },
+
+/**
+ * Double click fires the event, but also, if this is draggable, and there is only one other
+ * related DropZone, it transfers the selected node.
+ */
+ onDblClick : function(e){
+ var item = this.findItemFromChild(e.getTarget());
+ if(item){
+ if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
+ return false;
+ }
+ if (this.dragGroup) {
+ var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
+ while (targets.contains(this.dropZone)) {
+ targets.remove(this.dropZone);
+ }
+ if (targets.length == 1) {
+ this.dragZone.cachedTarget = null;
+ var el = Roo.get(targets[0].getEl());
+ var box = el.getBox(true);
+ targets[0].onNodeDrop(el.dom, {
+ target: el.dom,
+ xy: [box.x, box.y + box.height - 1]
+ }, null, this.getDragData(e));
+ }
+ }
+ }
+ },
+
+ handleSelection: function(e) {
+ this.dragZone.cachedTarget = null;
+ var item = this.findItemFromChild(e.getTarget());
+ if (!item) {
+ this.clearSelections(true);
+ return;
+ }
+ if (item && (this.multiSelect || this.singleSelect)){
+ if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
+ this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
+ }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
+ this.unselect(item);
+ } else {
+ this.select(item, this.multiSelect && e.ctrlKey);
+ this.lastSelection = item;
+ }
+ }
+ },
+
+ onItemClick : function(item, index, e){
+ if(this.fireEvent("beforeclick", this, index, item, e) === false){
+ return false;
+ }
+ return true;
+ },
+
+ unselect : function(nodeInfo, suppressEvent){
+ var node = this.getNode(nodeInfo);
+ if(node && this.isSelected(node)){
+ if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
+ Roo.fly(node).removeClass(this.selectedClass);
+ this.selections.remove(node);
+ if(!suppressEvent){
+ this.fireEvent("selectionchange", this, this.selections);
+ }
+ }
+ }
+ }
+});
+
+function initializePage() {
+ var collection=[
+ {'id':'40','entityImageUrl':'../shared/icons/fam/user_add.png','componentDescription':'Add User'},
+ {'id':'27','entityImageUrl':'../shared/icons/fam/user_delete.png','componentDescription':'Delete User'},
+ {'id':'28','entityImageUrl':'../shared/icons/fam/user_comment.png','componentDescription':'Comment on User'}
+ ];
+ var rec=Roo.data.Record.create([
+ {name:'id'},
+ {name:'entityImageUrl'},
+ {name:'componentDescription'}
+ ]);
+ var reader=new Roo.data.JsonReader({
+ root:'collection',
+ id:'id'
+ },rec);
+ var ds=new Roo.data.Store({
+ proxy:new Roo.data.MemoryProxy({collection:collection}),
+ reader:reader
+ });
+ var view=new Roo.ux.DDView('left-view-container','