Skip to content

Commit

Permalink
Merge pull request #2207 in OR/rse from ~SNGODOAN/rse:INBOX-270 to ma…
Browse files Browse the repository at this point in the history
…ster

* commit '2b6df8d348fc918932d808319652b029eeedbb98':
  INBOX-270 Order list item by date to have stable order when we modifying the list
  INBOX-270 Drag message/thread to move it to a mailbox
  INBOX-270 Add method to remove element from list
  INBOX-270 Add functions to move message/thread to a mailbox
  INBOX-274 Introduces esn.dragndrop module
  • Loading branch information
kscc25 committed May 17, 2016
2 parents b8ac9c1 + 2b6df8d commit fd9d411
Show file tree
Hide file tree
Showing 19 changed files with 1,115 additions and 15 deletions.
3 changes: 3 additions & 0 deletions frontend/css/modules/dragndrop.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.esn-drag-tooltip {
transition: top 500ms, left 500ms;
}
1 change: 1 addition & 0 deletions frontend/css/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
@import './modules/bootstrap-alerts-saving';
@import './modules/notifications';
@import './modules/search';
@import './modules/dragndrop';

/*
* Widgets
Expand Down
306 changes: 306 additions & 0 deletions frontend/js/modules/dragndrop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
'use strict';

angular.module('esn.dragndrop', ['ng.deviceDetector'])

.constant('ESN_DRAG_ANIMATION_CLASS', 'esn-drag-tooltip')
.constant('ESN_DRAG_ANIMATION_DURATION', 500)
.constant('ESN_DRAG_DISTANCE_THRESHOLD', 10)

.factory('esnDragService', function() {
var listeners = {};

function addDroppableListener(scopeId, listener) {
listeners[scopeId] = listener;
}

function removeDroppableListener(scopeId) {
delete listeners[scopeId];
}

function onDragStart(data) {
angular.forEach(listeners, function(listener) {
listener.onDragStart(data);
});
}

function onDragEnd(data) {
var dropped = false;

angular.forEach(listeners, function(listener) {
if (listener.onDragEnd(data)) {
dropped = true;
}
});

return dropped;
}

return {
addDroppableListener: addDroppableListener,
removeDroppableListener: removeDroppableListener,
onDragStart: onDragStart,
onDragEnd: onDragEnd
};
})

/**
*
* @example
* <div
* esn-draggable
* esn-drag-message="This message is dragging"
* esn-drag-data="data"
* esn-drag-class="dragging"
* esn-on-drag-start="onDragStart()"
* esn-on-drag-end="onDragEnd($dropped)"
* esn-on-drop-success="onDropSuccess()
* esn-on-drop-failure="onDropFailure()">
* </div>
*/
.directive('esnDraggable', function(
$parse,
$document,
$timeout,
deviceDetector,
esnDragService,
ESN_DRAG_ANIMATION_CLASS,
ESN_DRAG_ANIMATION_DURATION,
ESN_DRAG_DISTANCE_THRESHOLD) {

function link(scope, element, attrs) {
if (deviceDetector.isMobile()) { return; }

var tooltipElement;
var isDragging = false;
var startX, startY;
var centerX, centerY;

var esnDragData = $parse(attrs.esnDragData)(scope);
var esnDragClass = attrs.esnDragClass;
var esnOnDragStart = $parse(attrs.esnOnDragStart);
var esnOnDragEnd = $parse(attrs.esnOnDragEnd);
var esnOnDropSuccess = $parse(attrs.esnOnDropSuccess);
var esnOnDropFailure = $parse(attrs.esnOnDropFailure);

function initTooltip(content) {
tooltipElement = angular.element(
'<div class="tooltip right">' +
'<div class="tooltip-arrow"></div>' +
'<div class="tooltip-inner">' + content + '</div>' +
'</div>');

tooltipElement.css('position', 'fixed');
element.append(tooltipElement);
}

function setTooltipPosition(top, left) {
tooltipElement.css('top', (top - tooltipElement.height() / 2) + 'px');
tooltipElement.css('left', left + 'px');
}

function showTooltip() {
tooltipElement.css('opacity', 1);
}

function hideTooltip() {
tooltipElement.css('opacity', 0);
}

function addDragClass() {
esnDragClass && element.addClass(esnDragClass);
}

function removeDragClass() {
esnDragClass && element.removeClass(esnDragClass);
}

function onDragStart(event) {
addDragClass();
esnOnDragStart(scope);
esnDragService.onDragStart(esnDragData);

var offset = element.offset();

centerX = offset.left + element.width() / 2;
centerY = offset.top + element.height() / 2;

showTooltip();
}

function onDrag(event) {
setTooltipPosition(event.clientY, event.clientX);
}

function onDragEnd(event) {
removeDragClass();

var dropped = esnDragService.onDragEnd({
onDropSuccess: esnOnDropSuccess.bind(null, scope),
onDropFailure: esnOnDropFailure.bind(null, scope)
});

esnOnDragEnd(scope, { $dropped: dropped });

if (dropped) {
hideTooltip();
} else {
tooltipElement.addClass(ESN_DRAG_ANIMATION_CLASS);
setTooltipPosition(centerY, centerX);

$timeout(function() {
tooltipElement.removeClass(ESN_DRAG_ANIMATION_CLASS);
hideTooltip();
}, ESN_DRAG_ANIMATION_DURATION, false);
}

}

function onMouseMove(event) {
if (isDragging) {
onDrag(event);
} else {
if (Math.abs(event.clientX - startX) > ESN_DRAG_DISTANCE_THRESHOLD ||
Math.abs(event.clientY - startY) > ESN_DRAG_DISTANCE_THRESHOLD) {
isDragging = true;
onDragStart();
onDrag(event);
}
}
}

function onMouseUp(event) {
if (event.button !== 0) { // right-click
return;
}

if (isDragging) {
onDragEnd(event);
isDragging = false;
}

$document.off('mousemove', onMouseMove);
$document.off('mouseup', onMouseUp);
}

function onMouseDown(event) {
if (event.button !== 0) { // right-click
return;
}

event.preventDefault(); // to prevent text selection

isDragging = false;
startX = event.clientX;
startY = event.clientY;

$document.on('mousemove', onMouseMove);
$document.on('mouseup', onMouseUp);
}

initTooltip(attrs.esnDragMessage);
hideTooltip();

element.attr('draggable', false); // disable native HTML5 drag
element.on('mousedown', onMouseDown);
}

return {
restrict: 'A',
link: link
};
})

/**
* @example
* <div
* esn-droppable
* esn-droptarget-class="droptarget",
* esn-on-drag-enter="onDragEnter()"
* esn-on-drag-exit="onDragExit()"
* esn-on-drop="onDrop($dragData)",
* esn-is-drop-zone="isDropZone($dragData)">
* </div>
*/
.directive('esnDroppable', function(
$parse,
esnDragService,
deviceDetector) {

function link(scope, element, attrs) {
if (deviceDetector.isMobile()) { return; }

var $dragData;
var isDropCandidate = false;
var isDropZone = false;

var esnDroptargetClass = attrs.esnDroptargetClass;
var esnOnDragEnter = $parse(attrs.esnOnDragEnter);
var esnOnDragExit = $parse(attrs.esnOnDragExit);
var esnOnDrop = $parse(attrs.esnOnDrop);
var esnIsDropZone = $parse(attrs.esnIsDropZone);

function addDroptargetClass() {
esnDroptargetClass && element.addClass(esnDroptargetClass);
}

function removeDroptargetClass() {
esnDroptargetClass && element.removeClass(esnDroptargetClass);
}

function onMouseEnter(event) {
addDroptargetClass();
esnOnDragEnter(scope);
isDropCandidate = true;
}

function onMouseLeave(event) {
removeDroptargetClass();
esnOnDragExit(scope);
isDropCandidate = false;
}

function onDragStart(data) {
$dragData = data;
isDropZone = esnIsDropZone(scope, { $dragData: $dragData });

if (isDropZone) {
element.on('mouseenter', onMouseEnter);
element.on('mouseleave', onMouseLeave);
}
}

function onDragEnd(callbacks) {
if (!isDropZone) { return false; }

esnOnDragExit(scope);

element.off('mouseenter', onMouseEnter);
element.off('mouseleave', onMouseLeave);

if (isDropCandidate) {
isDropCandidate = false;
removeDroptargetClass();
esnOnDrop(scope, { $dragData: $dragData })
.then(callbacks.onDropSuccess, callbacks.onDropFailure);

return true;
}

return false;
}

esnDragService.addDroppableListener(scope.$id, {
onDragStart: onDragStart,
onDragEnd: onDragEnd
});

scope.$on('$destroy', function() {
esnDragService.removeDroppableListener(scope.$id);
});
}

return {
restrict: 'A',
link: link
};
});
11 changes: 11 additions & 0 deletions frontend/js/modules/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ angular.module('esn.provider', ['esn.aggregator', 'esn.lodash-wrapper'])
}
};

ByDateElementGroupingTool.prototype.removeElement = function removeElement(element) {
angular.forEach(this.allElements, function(group) {
var index = group.elements.indexOf(element);

if (index > -1) {
group.elements.splice(index, 1);
}
});
};

ByDateElementGroupingTool.prototype._isToday = function _isSameDay(currentMoment, targetMoment) {
return currentMoment.clone().startOf('day').isBefore(targetMoment);
};
Expand Down Expand Up @@ -226,6 +236,7 @@ angular.module('esn.provider', ['esn.aggregator', 'esn.lodash-wrapper'])
return function(scope, loadNextItems, elementGroupingTool) {
var groups = elementGroupingTool;

scope.groups = groups;
scope.groupedElements = groups.getGroupedElements();

return infiniteScrollHelperBuilder(scope, loadNextItems, function(newElements) {
Expand Down
1 change: 1 addition & 0 deletions frontend/views/esn/index.jade
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ html
script(src='js/modules/config.js')
script(src='/components/material-admin/js/modules/form.js')
script(src='js/modules/highlight.js')
script(src='js/modules/dragndrop.js')

body.ng-cloak(ng-app='esnApp' ng-controller="sessionInitESNController" resize-scrollbar ng-nicescroll nice-option="{cursorcolor: 'rgba(0,0,0,0.4)', cursorborder: 'none', cursorwidth:'6px', autohidemode: false, zindex: 12}")
#wrap(ng-include='session.template')
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@inboxAsideIconFontSize: 2em;
@inboxAsideMailboxNameHeight: 30px;
@dividerColor: darken(@darkenBgColor, 10%);
@inboxAsideDragOverBgColor: #FFCC80;

.inbox-aside {

Expand Down Expand Up @@ -157,6 +158,10 @@
background-color: @darkenBgColor;
}
}

.clickable.droptarget {
background-color: @inboxAsideDragOverBgColor;
}
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
@inboxDraggingItemBgColor: #FFF9C4;

infinite-list.list-emails {
.lv-body .lv-item {
padding: 0;

.clickable {
padding: 7px 10px;

&.dragging {
background-color: @inboxDraggingItemBgColor;
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion modules/linagora.esn.unifiedinbox/frontend/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ angular.module('linagora.esn.unifiedinbox', [
'esn.url',
'esn.background',
'esn.aggregator',
'esn.provider'
'esn.provider',
'esn.dragndrop'
])

.config(function($stateProvider, dynamicDirectiveServiceProvider) {
Expand Down
Loading

0 comments on commit fd9d411

Please sign in to comment.