marceljuenemann/angular-drag-and-drop-lists

Component to handle multiple lists easily with minimal code + multi-selection

iranor1 opened this issue · 0 comments

Hello,

I found your module very useful for an in-house app I am building. I just wanted to share a component I built around it to make it simpler to make multiple lists without too much boilerplate code.

Features:

  • Display multiple drag-drop list with one line of code each
  • Select multiple elements and drag them to another list
  • Shift+click to select elements in-between (doesn't handle unselection yet)
  • Choose if elements are added at end of list or in selected spot
  • Specify elements to hide without removing them from the list

It works but it needs more polishing. If it may help someone of give you some ideas...

Example:

<drag-list list="parentsToLink" title="Parents" height="210"></drag-list>
<drag-list list="childrenToLink" title="Children" height="210"></drag-list>

In controller

$scope.parentsToLink = {items: $scope.linkedAsset.parents) , dragging: false};
$scope.childrenToLink = {items: $scope.linkedAsset.children), dragging: false};

dragList.js

app.component('dragList', {
	transclude: true,
	bindings: {
	  list: '=',
	  onUpdate: '&',
	  addAtEnd: '<',
	  filterIds: '<',
	  title: '@',
	  height: '@'
	},
	controller: function($scope) {
		var self = this;
			
		$scope.lastSelection = null;

		self.$onInit = function(){
			if (angular.isUndefined(this.addAtEnd) || this.addAtEnd=== null){
				this.addAtEnd = true; 
			}

			if (angular.isUndefined(this.height) || this.height=== null){
				this.height = 200; 
			}
		}
		
		self.$onDestroy = function(){
			if(self.list != null){
				self.list.items.forEach(function(item){
					item.selected = false;
				});
			}
		}
		
		$scope.filterElements = function(){
			return function(item) {
				return !$scope.isFiltered(item.id);
			}
		}

		$scope.selectedItem = function(item, e, list){
			item.selected = !item.selected;

			if(e.shiftKey){
				var lastSelectedIndex = list.items.indexOf($scope.lastSelection);
				var currentIndex = list.items.indexOf(item);
				
				if(currentIndex > lastSelectedIndex){
					for(var i=lastSelectedIndex; i<currentIndex; i++){
						if(!$scope.isFiltered(list.items[i].id)){
							list.items[i].selected = true;
						}
					}
				} else {
					for(var i=currentIndex; i<lastSelectedIndex; i++){
						if(!$scope.isFiltered(list.items[i].id)){
							list.items[i].selected = true;
						}
					}
				}
				
			} else {
				$scope.lastSelection = item;
			}
		}
		
		$scope.isFiltered = function(id){
			if(self.filterIds){
				return self.filterIds.indexOf(id) > -1;
			}
			return false;
		}
		
		$scope.getSelectedItemsIncluding = function(list, item) {
			item.selected = true;
			return list.items.filter(function(item) { return item.selected; });
		}
		
		$scope.onDragstart = function(list, event) {
			list.dragging = true;
		}

		$scope.onDrop = function(list, items, index) {
			angular.forEach(items, function(item) { item.selected = false; });
			
			if(self.addAtEnd){
				list.items = list.items.concat(items);
			} 
			else {
				list.items = list.items.slice(0, index)
				  .concat(items)
				  .concat(list.items.slice(index));
			}
			
			return true;
		}

		$scope.onMoved = function(list) {
			list.items = list.items.filter(function(item) { return !item.selected; });
			self.onUpdate();
		}
	},
	templateUrl: 'components/dragList.html'
});

css file

/* DragDropList */

.panel-body { padding: 5px; }
.panel-heading { padding: 5px; }

.dragDropList ul[dnd-list] {
	min-height: 30px;
	padding-left: 0px;
	height: 90%;
}

.dragDropList ul[dnd-list] .dndDraggingSource {
	display: none;
}

.dragDropList ul[dnd-list] .dndPlaceholder {
	background-color: #ddd;
	display: block;
	min-height: 5px;

}

.dragDropList ul[dnd-list] li {
	background-color: #fff;
	border: 1px solid #ddd;
	border-top-right-radius: 4px;
	border-top-left-radius: 4px;
	display: block;
	padding: 3px 3px;
	margin-bottom: -1px;
	font-size: 12px;
}

.dragDropList ul[dnd-list] li.selected {
	background-color: #dff0d8;
	color: #3c763d;
}

The file dragList.html

<div style="width: 100%" class="dragDropList">
	<div class="panel panel-info">
		<div class="panel-heading" ng-if="$ctrl.title">
			<h3 class="panel-title">{{$ctrl.title}}</h3>
		</div>
		<div class="panel-body" style="height: {{$ctrl.height}}px; overflow-y: auto">

			<ul dnd-list dnd-drop="onDrop($ctrl.list, item, index)">
				<li ng-repeat="item in $ctrl.list.items | filter: filterElements()"
					dnd-draggable="getSelectedItemsIncluding($ctrl.list, item)"
					dnd-dragstart="onDragstart($ctrl.list, event)"
					dnd-moved="onMoved($ctrl.list)"
					dnd-dragend="$ctrl.list.dragging = false"
					dnd-selected="selectedItem(item, event, $ctrl.list)"
					ng-class="{'selected': item.selected}"
					ng-hide="$ctrl.list.dragging && item.selected"
					>
					{{item.name}}
				</li>
			</ul>
		</div>
	</div>
</div>