marceljuenemann / angular-drag-and-drop-lists

Angular directives for sorting nested lists using the HTML5 Drag & Drop API
MIT License
2.16k stars 713 forks source link

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

Open iranor1 opened 6 years ago

iranor1 commented 6 years ago

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:

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>