SimpliField/angular-sf-dragndrop

View on GitHub
src/index.js

Summary

Maintainability
F
4 days
Test Coverage
const moduleName = 'simplifield.dragndrop';

export default angular
  .module(moduleName, [])
  .factory('sfDragNDropService', DragNDropService)
  .directive('sfDrop', ['$parse', 'sfDragNDropService', DropDirective])
  .directive('sfDrag', ['$parse', 'sfDragNDropService', DragDirective])
  .name;

function DragNDropService() {
  const SESSION_KEYS = [
    'item',
    'target',
    'previous',
    'itemIndex',
    'targetIndex',
    'previousIndex',
  ];
  const sfDragNDropService = {
    session: {},
    reset: function reset() {
      Object.keys(sfDragNDropService.session).forEach(function(key) {
        if('type' === key) {
          sfDragNDropService.session.type = '';
        } else if (SESSION_KEYS.includes(key)) {
          sfDragNDropService.session[key] = null;
        } else {
          delete sfDragNDropService.session[key];
        }
      });
    }
  };

  sfDragNDropService.reset();

  return sfDragNDropService;
}

function DropDirective($parse, sfDragNDropService) {
  return {
    restrict: 'A',
    link: function($scope, element, attrs) {
      // Keep a ref to the dragged element
         const item = $parse(attrs.sfDrop);
      // Setting callbacks
      const onDropCallback = $parse(attrs.sfOnDrop);
      const onDragEnterCallback = $parse(attrs.sfOnDragEnter);
      const onDragLeaveCallback = $parse(attrs.sfOnDragLeave);
      const onDragOverCallback = $parse(attrs.sfOnDragOver);
      // Bind drag events
      element.bind('dragenter', function(evt) {
        if(sfDragNDropService.session.type !== (attrs.sfDragType || 'all')) {
          return;
        }
        evt.preventDefault();
        sfDragNDropService.session.target = item($scope);
        sfDragNDropService.session.targetIndex = onDragEnterCallback($scope, {
          $type: sfDragNDropService.session.type,
          $item: sfDragNDropService.session.item,
          $itemIndex: sfDragNDropService.session.itemIndex,
          $target: sfDragNDropService.session.target,
          $targetIndex: sfDragNDropService.session.targetIndex,
          $session: sfDragNDropService.session
        });
        $scope.$apply();
      });
      element.bind('dragleave', function(evt) {
        if(sfDragNDropService.session.type !== (attrs.sfDragType || 'all')) {
          return;
        }
        evt.preventDefault();
        sfDragNDropService.session.previous = item($scope);
        sfDragNDropService.session.previousIndex = sfDragNDropService.session.targetIndex;
        onDragLeaveCallback($scope, {
          $type: sfDragNDropService.session.type,
          $item: sfDragNDropService.session.item,
          $itemIndex: sfDragNDropService.session.itemIndex,
          $previous: sfDragNDropService.session.previous,
          $previousIndex: sfDragNDropService.session.previousIndex,
          $session: sfDragNDropService.session
        });

        $scope.$apply();
      });
      element.bind('dragover', function(evt) {
        if(sfDragNDropService.session.type !== (attrs.sfDragType || 'all')) {
          return;
        }
        evt.preventDefault();
        sfDragNDropService.session.target = item($scope);
        onDragOverCallback($scope, {
          $type: sfDragNDropService.session.type,
          $item: sfDragNDropService.session.item,
          $itemIndex: sfDragNDropService.session.itemIndex,
          $target: sfDragNDropService.session.target,
          $targetIndex: sfDragNDropService.session.targetIndex,
          $previous: sfDragNDropService.session.target,
          $previousIndex: sfDragNDropService.session.targetIndex,
          $session: sfDragNDropService.session
        });
        $scope.$apply();
      });
      element.bind('drop', function(evt) {
        if(sfDragNDropService.session.type !== (attrs.sfDragType || 'all')) {
          return;
        }
        evt.preventDefault();
        sfDragNDropService.session.target = item($scope);
        onDropCallback($scope, {
          $type: sfDragNDropService.session.type,
          $item: sfDragNDropService.session.item,
          $itemIndex: sfDragNDropService.session.itemIndex,
          $target: sfDragNDropService.session.target,
          $targetIndex: sfDragNDropService.session.targetIndex,
          $previous: sfDragNDropService.session.previous,
          $previousIndex: sfDragNDropService.session.previousIndex,
          $session: sfDragNDropService.session
        });
        $scope.$apply();
      });
    }
  };

}

function DragDirective($parse, sfDragNDropService) {
  return {
    restrict: 'A',
    link: function($scope, element, attrs) {
      // Keep a ref to the dragged model value
         const item = $parse(attrs.sfDrag);

      // Try to get dragged datas
         const getDragData = $parse(attrs.sfDragData);

      // Setting callbacks
      const dragGenerator = attrs.sfDragGenerator ?
        $parse(attrs.sfDragGenerator) :
        null;

      // Setting callbacks
      const onDragCallback = $parse(attrs.sfOnDrag);
      const onDragEndCallback = $parse(attrs.sfOnDragEnd);

      // Make the element draggable
      if(attrs.sfDraggable) {
        $scope.$watch(
          function() {
            return $parse(attrs.sfDraggable)($scope, {
              $type: sfDragNDropService.session.type,
              $item: sfDragNDropService.session.item,
              $itemIndex: sfDragNDropService.session.itemIndex,
              $session: sfDragNDropService.session
            });
          },
          function(newVal, oldVal) {
            attrs.$set('draggable', (!!newVal).toString());
          }
        );
      } else {
        attrs.$set('draggable', 'true');
      }

      // Bind drag events
      element.bind('dragstart', function(evt) {
        var draggedItem = item($scope);
        function computeDragItem() {
           var itemIndex = onDragCallback($scope, {
            $item: draggedItem,
            $session: sfDragNDropService.session
          });
          if(-1 !== itemIndex) {
            sfDragNDropService.session.itemIndex = itemIndex;
            sfDragNDropService.session.item = draggedItem;
            sfDragNDropService.session.type = attrs.sfDragType || 'all';
            sfDragNDropService.session.mime = attrs.sfDragMime || 'text/plain';
            sfDragNDropService.session.data = getDragData($scope) || '' + itemIndex;
            evt.stopPropagation();
            (evt.dataTransfer || evt.originalEvent.dataTransfer)
              .setData(sfDragNDropService.session.mime, sfDragNDropService.session.data);
            (evt.dataTransfer || evt.originalEvent.dataTransfer)
              .effectAllowed = attrs.sfDragEffect || 'move';
          }
        }
        if(dragGenerator) {
          draggedItem = dragGenerator($scope, {
            $type: attrs.sfDragType || 'all',
            $item: draggedItem
          });
        }
        if(draggedItem.then) {
          draggedItem.then(function(value) {
            draggedItem = value;
            computeDragItem();
          });
        } else {
          computeDragItem();
        }
        $scope.$apply();
      });

      element.bind('dragend', function(evt) {
        onDragEndCallback($scope, {
          $type: sfDragNDropService.session.type,
          $item: sfDragNDropService.session.item,
          $itemIndex: sfDragNDropService.session.itemIndex,
          $previous: sfDragNDropService.session.previous,
          $previousIndex: sfDragNDropService.session.previousIndex,
          $session: sfDragNDropService.session
        });
        sfDragNDropService.reset();
        $scope.$apply();
      });
    }
  };
}