I had to do this and Victors sample is definitely missing functions (e.g. createItem) and things with the laterlist look like they have changed since.
I have wrapped up the code into blocks that can just be pasted into layerListview.
The css will give you the map viewer style dotted line.
Notes
- Because wab doesn't nest the expanded items (the layer swatch) it only supports dragging above an item.
- Feature layers can't be moved, this is a limitation of 3x.
- there well may be edge layer types I haven't tested.
- tested and working with 2.20
LayerListView.js
_setUpDnD: function () {
this.dndList = new Source(this.layerListTable.parentNode, {
skipForm: true,
singular: true
});
dojo.connect(this.dndList, "onDropInternal", this, "onDrop");
this.dndContainer = new Container(this.layerListTable);
// listen for drag and check we can drop
this.onMouseMoveHandler = this.own(dojo.connect(this.dndList, "onMouseMove", lang.hitch(this, "handleDragMoveDnd")));
this.onMouseMoveHandler = this.own(dojo.connect(this.dndList, "onDndStart", lang.hitch(this, "handleDragStart")));
aspect.around(this, "addLayerNode", function (orig) {
return function(){
var args = orig.apply(this, arguments);
// ignore any layer which isn't a parent
if(arguments[0].parentLayerInfo){
return args;
}
// we need to add a node and a class as the dnd requires it
var id = args.layerTrNode.getAttribute("layerTrNodeId");
var layerInfo = this.operLayerInfos.getLayerInfoById(id);
// you can't move feature layers!
var lyr = layerInfo.originOperLayer.layerObject;
if (lyr.type && lyr.type.toLowerCase() === "feature layer") {
return args;
}
args.layerTrNode.setAttribute("id", args.layerTrNode.getAttribute("layerTrNodeId"));
// we also need the d&d class
domClass.add(args.layerTrNode, "dojoDndItem");
this.dndList.setItem(args.layerTrNode.id, args.layerTrNode)
return args;
}
}, true)
},
handleDragMoveDnd: function (a) {
Manager.manager().canDropFlag = true;
if (this.dndList.isDragging) {
// var layer2ContentTrNode = query("tr[layerContentTrNodeId='" + this.dndList.current.id + "']", this.layerListTable)[0];
query(".dndDashedLine", this.layerListTable).forEach(function (n) {
domClass.remove(n, "dndDashedLine");
})
if (this.dndList.current) {
if (this.dndList.currentDragInfo && this.dndList.currentDragInfo != this.dndList.current.id) {
// if its the next node don't let it drop it before/after depending on direction
// turn the map into an array
var layerInfoArray = this.operLayerInfos.getLayerInfoArray();
var ids = []
array.forEach(layerInfoArray, function (currentLayerInfo, index) {
if (this.dndList.map[currentLayerInfo.id]) {
ids.push(currentLayerInfo.id);
}
}, this);
var currentIdx = ids.indexOf(this.dndList.currentDragInfo);
var nextIdx = ids.indexOf(this.dndList.current.id);
if (domClass.contains(this.dndList.current, "dojoDndItemAfter")) {
// do not let users drop before. This is the issue with the jimu list which doesn't
// have the content (eg the layer swatch) is not a child of the layer item. we let this
// go if its the very bottom item
domClass.add(this.dndList.current, "dndDashedLine");
if(!nextIdx == ids.length - 1){
Manager.manager().canDropFlag = false;
domClass.remove(this.dndList.current, "dndDashedLine");
}
}
if (domClass.contains(this.dndList.current, "dojoDndItemBefore")) {
domClass.add(this.dndList.current, "dndDashedLine");
if (nextIdx - currentIdx === 1) {
Manager.manager().canDropFlag = false;
domClass.remove(this.dndList.current, "dndDashedLine");
}
}
} else if (this.dndList.currentDragInfo === this.dndList.current.id) {
Manager.manager().canDropFlag = false; // don't let it drop on top of itself
}else{
Manager.manager().canDropFlag = false;
}
}
else {
// this will happen when user mouses over a feature layer
Manager.manager().canDropFlag = false;
}
dojo.toggleClass(Manager.manager().avatar.node, "dojoDndAvatarCanDrop", Manager.manager().canDropFlag);
}
},
handleDragStart: function (a, c, e) {
if (this.dndList.current) {
this.dndList.currentDragInfo = this.dndList.current.id;;
}else{
this.dndList.currentDragInfo = null;
}
},
onDrop: function (nodes) {
this.dndList.getAllNodes().forEach(function (n) {
domClass.remove(n, "dndDashedLine");
})
// new position
var allNodes = this.dndList.getAllNodes();
var newPos = -1;
for (var i = 0; i < allNodes.length; i++) {
if (allNodes[i].id === nodes[0].id) {
newPos = i;
break;
}
}
// old position
var layerInfo = this.operLayerInfos.getLayerInfoById(nodes[0].id);
var oldPos = -1;
var layerInfoArray = this.operLayerInfos.getLayerInfoArray();
array.forEach(layerInfoArray, function (currentLayerInfo, index) {
if (layerInfo.id === currentLayerInfo.id) {
oldPos = index;
}
}, this);
// if we have less d&d nodes its because there are feature layers
if (layerInfoArray.length !== allNodes.length) {
newPos += (layerInfoArray.length - allNodes.length);
}
var steps = -1;
if (oldPos < newPos) {
steps = newPos - oldPos;
steps = this.operLayerInfos.moveDownLayer(layerInfo, steps)
} else {
steps = oldPos - newPos;
steps = this.operLayerInfos.moveUpLayer(layerInfo, steps)
}
this.emit('dnd-drop', nodes);
// just for good look
this.refresh();
}
style.css
.jimu-widget-layerList .dojoDndItemBefore.dndDashedLine{
border-width: 0 !important;
border-top: #009cff !important;
border-style: dashed !important;
border-top-width: 2px !important;
}
.jimu-widget-layerList .dojoDndItemAfter.dndDashedLine{
border-width: 0 !important;
border-bottom: #009cff !important;
border-style: dashed !important;
border-bottom-width: 2px !important;
}
.jimu-widget-layerList .dojoDndItem{
border-width: 0 !important;
}
.jimu-widget-layerList .layer-list-table {
border-collapse: collapse !important;
}