md-chip-draggable.directive.js 6.75 KB
/*
 * Copyright © 2016-2019 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import './md-chip-draggable.scss';

var globalDraggingChipsWrapId = null;

export default angular.module('thingsboard.directives.mdChipDraggable', [])
    .directive('tbChipDraggable', function () {
    return {
      restrict: 'A',
      scope: {},
      bindToController: true,
      controllerAs: 'vm',
      controller: ['$document', '$scope', '$element', '$timeout',
        function ($document, $scope, $element, $timeout) {
          var handle = $element[0];
          var draggingClassName = 'dragging';
          var droppingClassName = 'dropping';
          var droppingBeforeClassName = 'dropping-before';
          var droppingAfterClassName = 'dropping-after';
          var dragging = false;
          var preventDrag = false;
          var dropPosition;
          var dropTimeout;
          var counter = 0;

          var move = function (from, to) {
            this.splice(to, 0, this.splice(from, 1)[0]);
          };

          $element = angular.element($element[0].closest('md-chip'));

          $element.attr('draggable', true);

          $element.on('mousedown', function (event) {
            if (event.target !== handle) {
              preventDrag = true;
            }
          });

          $document.on('mouseup', function () {
            preventDrag = false;
          });

          $element.on('dragstart', function (event) {
            if (preventDrag) {
              event.preventDefault();

            } else {
              dragging = true;

              globalDraggingChipsWrapId = angular.element($element[0].closest('md-chips-wrap')).attr('id');

              $element.addClass(draggingClassName);

              var dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer;

              dataTransfer.effectAllowed = 'copyMove';
              dataTransfer.dropEffect = 'move';

              dataTransfer.setData('text/plain', $scope.$parent.$mdChipsCtrl.items.indexOf($scope.$parent.$chip));
            }
          });

          $element.on('dragend', function () {
            dragging = false;
            globalDraggingChipsWrapId = null;

            $element.removeClass(draggingClassName);
          });

          var dragOverHandler = function (event) {
            if (dragging) {
              return;
            }

            var targetChipsWrapId = angular.element($element[0].closest('md-chips-wrap')).attr('id');

            event.preventDefault();

            if (globalDraggingChipsWrapId !== targetChipsWrapId) {
                return;
            }

            var bounds = $element[0].getBoundingClientRect();

            var props = {
              width: bounds.right - bounds.left,
              height: bounds.bottom - bounds.top,
              x: (event.originalEvent || event).clientX - bounds.left,
              y: (event.originalEvent || event).clientY - bounds.top,
            };

            var horizontalOffset = props.x;
            var horizontalMidPoint = props.width / 2;

            var verticalOffset = props.y;
            var verticalMidPoint = props.height / 2;

            $element.addClass(droppingClassName);

            $element.removeClass(droppingAfterClassName);
            $element.removeClass(droppingBeforeClassName);

            if (horizontalOffset >= horizontalMidPoint || verticalOffset >= verticalMidPoint) {
                dropPosition = 'after';
                $element.addClass(droppingAfterClassName);
            } else {
                dropPosition = 'before';
                $element.addClass(droppingBeforeClassName);
            }

          };

          var dropHandler = function (event) {
            counter = 0;
            event.preventDefault();

            var targetChipsWrapId = angular.element($element[0].closest('md-chips-wrap')).attr('id');
            if (globalDraggingChipsWrapId !== targetChipsWrapId) {
                return;
            }

            var droppedItemIndex = parseInt((event.dataTransfer || event.originalEvent.dataTransfer).getData('text/plain'), 10);
            var currentIndex = $scope.$parent.$mdChipsCtrl.items.indexOf($scope.$parent.$chip);
            var newIndex = null;

            if (dropPosition === 'before') {
              if (droppedItemIndex < currentIndex) {
                newIndex = currentIndex - 1;
              } else {
                newIndex = currentIndex;
              }
            } else {
              if (droppedItemIndex < currentIndex) {
                newIndex = currentIndex;
              } else {
                newIndex = currentIndex + 1;
              }
            }

            // prevent event firing multiple times in firefox
            $timeout.cancel(dropTimeout);
            dropTimeout = $timeout(function () {
              dropPosition = null;

              move.apply($scope.$parent.$mdChipsCtrl.items, [droppedItemIndex, newIndex]);

              $scope.$apply(function () {
                $scope.$emit('mdChipDraggable:change', {
                  collection: $scope.$parent.$mdChipsCtrl.items,
                  item: $scope.$parent.$mdChipsCtrl.items[droppedItemIndex],
                  from: droppedItemIndex,
                  to: newIndex,
                });
              });

              $element.removeClass(droppingClassName);
              $element.removeClass(droppingAfterClassName);
              $element.removeClass(droppingBeforeClassName);

              $element.off('drop', dropHandler);
            }, 1000 / 16);
          };

          $element.on('dragenter', function () {
              counter++;
            if (dragging) {
              return;
            }

            $element.off('dragover', dragOverHandler);
            $element.off('drop', dropHandler);

            $element.on('dragover', dragOverHandler);
            $element.on('drop', dropHandler);
          });

          $element.on('dragleave', function () {
              counter--;
            if (counter <=0) {
                counter = 0;
                $element.removeClass(droppingClassName);
                $element.removeClass(droppingAfterClassName);
                $element.removeClass(droppingBeforeClassName);
            }
          });

        }],
    };
  })
  .name;

/* eslint-enable angular/angularelement */