(js) Add drag'n'drop to month's view

This commit is contained in:
Francis Lachapelle
2015-11-20 13:31:04 -05:00
parent 6a3a249bd6
commit 90e79e7605
10 changed files with 196 additions and 149 deletions
@@ -48,7 +48,8 @@
' opacity: 0.8;',
' }',
/* Foreground color */
' .fg-folder{{ cssCtrl.ngModel.id }} {',
' .fg-folder{{ cssCtrl.ngModel.id }},',
' .sg-event.fg-folder{{ cssCtrl.ngModel.id }} md-icon {',
' color: {{ cssCtrl.ngModel.color }} !important;',
' }',
/* Border color */
@@ -29,10 +29,11 @@
},
replace: true,
template: [
'<div class="sg-event sg-draggable-calendar-block"',
'<div class="sg-event"',
// Add a class while dragging
' ng-class="{\'sg-event--dragging\': block.dragging}">',
' <div class="eventInside" ng-click="clickBlock({clickEvent: $event, clickComponent: block.component})">',
' <div class="eventInside"',
' ng-click="clickBlock({clickEvent: $event, clickComponent: block.component})">',
// Categories color stripes
' <div class="sg-category" ng-repeat="category in block.component.categories"',
' ng-class="\'bg-category\' + category"',
@@ -86,7 +87,8 @@
iElement.addClass('lasts' + scope.block.length);
// Set background color
iElement.addClass('bg-folder' + scope.block.component.pid);
if (scope.block.component)
iElement.addClass('bg-folder' + scope.block.component.pid);
}
}
@@ -4,49 +4,22 @@
'use strict';
/*
* sgCalendarDayBlockGhost - An event ghost block to be displayed while dragging an event block. Each day of the
* calendar's view is associated to a ghost block.
* sgCalendarBlock - Applied to an event ghost block to be displayed while dragging an event block. Each day of the
* calendar's view must have a ghost block.
* @memberof SOGo.SchedulerUI
* @restrict element
* @restrict attribute
*
* @example:
<sg-calendar-day-block-ghost/>
<sg-calendar-day-block
sg-calendar-ghost
sg-block="list.component.$ghost">/
*/
sgCalendarDayBlockGhost.$inject = ['$rootScope', '$timeout', 'CalendarSettings', 'Calendar', 'Component'];
function sgCalendarDayBlockGhost($rootScope, $timeout, CalendarSettings, Calendar, Component) {
sgCalendarGhost.$inject = ['$rootScope', '$timeout', 'CalendarSettings', 'Calendar', 'Component'];
function sgCalendarGhost($rootScope, $timeout, CalendarSettings, Calendar, Component) {
return {
restrict: 'E',
restrict: 'A',
require: ['^sgCalendarDay', '^sgCalendarScrollView'],
replace: true,
template: [
'<div class="sg-event sg-event--ghost md-whiteframe-3dp ng-hide">',
' <div class="eventInside">',
// Categories color stripes
' <div class="sg-category" ng-repeat="category in block.component.categories"',
' ng-class="\'bg-category\' + category"',
' ng-style="{ right: ($index * 3) + \'px\' }"></div>',
' <div class="text">{{ block.component.summary }}',
' <span class="icons">',
// Component is reccurent
' <md-icon ng-if="block.component.occurrenceId" class="material-icons icon-repeat"></md-icon>',
// Component has an alarm
' <md-icon ng-if="block.component.c_nextalarm" class="material-icons icon-alarm"></md-icon>',
// Component is confidential
' <md-icon ng-if="block.component.c_classification == 1" class="material-icons icon-visibility-off"></md-icon>',
// Component is private
' <md-icon ng-if="block.component.c_classification == 2" class="material-icons icon-vpn-key"></md-icon>',
' </span>',
// Location
' <div class="secondary" ng-if="block.component.c_location">',
' <md-icon>place</md-icon> {{block.component.c_location}}',
' </div>',
' </div>',
' </div>',
' <div class="ghostStartHour" ng-if="startHour">{{ startHour }}</div>',
' <div class="ghostEndHour" ng-if="endHour">{{ endHour }}</div>',
'</div>'
].join(''),
link: link
};
@@ -57,6 +30,8 @@
calendarDayCtrl = ctrls[0];
scrollViewCtrl = ctrls[1];
iElement.addClass('sg-event--ghost md-whiteframe-3dp ng-hide');
// Listen on drag gestures
var deregisterDragStart = $rootScope.$on('calendar:dragstart', initGhost);
var deregisterDrag = $rootScope.$on('calendar:drag', updateGhost);
@@ -88,7 +63,7 @@
function updateGhost() {
// From SOGoEventDragGhostController._updateGhosts
var showGhost, isAllDay, originalDay, currentDay, wasOtherBlock,
var showGhost, isRelative, originalDay, currentDay, wasOtherBlock,
start, duration, durationLeft, maxDuration, enableTransition;
showGhost = false;
@@ -99,7 +74,7 @@
if (Calendar.$view && Calendar.$view.type == scrollViewCtrl.type) {
// The view of the dragging block is the scrolling view of this ghost block
isAllDay = scope.block.component.c_isallday;
isRelative = scrollViewCtrl.type === 'multiday-allday' || scrollViewCtrl.type === 'monthly';
originalDay = scope.block.pointerHandler.originalEventCoordinates.dayNumber;
currentDay = scope.block.pointerHandler.currentEventCoordinates.dayNumber;
start = scope.block.pointerHandler.currentEventCoordinates.start;
@@ -119,14 +94,19 @@
if (currentDay > -1 && currentDay == calendarDayCtrl.dayNumber) {
// This ghost block (day) is the first of the dragging event
showGhost = true;
if (!isAllDay) {
if (!isRelative) {
// Show start hour and set the vertical position
scope.startHour = getStartTime(start);
wasOtherBlock = parseInt(iElement.css('top')) === 0;
if (wasOtherBlock)
iElement.addClass('sg-event--notransition');
iElement.css('top', (start * Calendar.$view.quarterHeight) + 'px');
iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px');
// Set the height
if (Calendar.$view.quarterHeight) {
iElement.css('top', (start * Calendar.$view.quarterHeight) + 'px');
iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px');
}
else
iElement.css('top', Calendar.$view.topOffset + 'px');
if (wasOtherBlock)
$timeout(enableTransition);
}
@@ -146,13 +126,14 @@
if (currentDay > -1 && currentDay == calendarDayCtrl.dayNumber) {
// The dragging event overlaps this current ghost's day
showGhost = true;
if (!isAllDay) {
if (!isRelative) {
wasOtherBlock = parseInt(iElement.css('top')) !== 0;
if (wasOtherBlock)
iElement.addClass('sg-event--notransition');
iElement.css('top', Calendar.$view.topOffset + 'px');
// Set the height
iElement.css('top', '0px');
iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px');
if (Calendar.$view.quarterHeight)
iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px');
if (wasOtherBlock)
$timeout(enableTransition);
}
@@ -168,7 +149,7 @@
}
if (!durationLeft) {
// Reached last ghost block
if (isAllDay) {
if (isRelative) {
iElement.addClass('sg-event--ghost--last');
}
else {
@@ -211,5 +192,5 @@
angular
.module('SOGo.SchedulerUI')
.directive('sgCalendarDayBlockGhost', sgCalendarDayBlockGhost);
.directive('sgCalendarGhost', sgCalendarGhost);
})();
@@ -27,6 +27,7 @@
},
template: [
'<sg-calendar-month-event',
' class="sg-draggable-calendar-block"',
' ng-repeat="block in blocks[day]"',
' sg-block="block"',
' sg-click="clickBlock({event: clickEvent, component: clickComponent})"/>'
@@ -24,8 +24,11 @@
},
replace: true,
template: [
'<div class="sg-event sg-draggable" ng-click="clickBlock({clickEvent: $event, clickComponent: block.component})">',
' <span class="secondary" ng-if="!block.component.c_isallday">{{ block.starthour }} - </span>',
'<div class="sg-event"',
// Add a class while dragging
' ng-class="{\'sg-event--dragging\': block.dragging}"',
' ng-click="clickBlock({clickEvent: $event, clickComponent: block.component})">',
' <span class="secondary" ng-if="!block.component.c_isallday">{{ block.starthour }}</span>',
' {{ block.component.summary }}',
' <span class="icons">',
// Component is reccurent
@@ -37,15 +40,14 @@
// Component is private
' <md-icon ng-if="block.component.c_classification == 2" class="material-icons icon-vpn-key"></md-icon>',
' </span>',
' <div class="leftDragGrip"></div>',
' <div class="rightDragGrip"></div>',
'</div>'
].join(''),
link: link
};
function link(scope, iElement, attrs) {
iElement.addClass('bg-folder' + scope.block.component.pid);
if (scope.block.component)
iElement.addClass('bg-folder' + scope.block.component.pid);
}
}
@@ -68,29 +68,33 @@
}
function getQuarterHeight() {
var hour0, hour23, height;
var hour0, hour23, height = null;
hour0 = document.getElementById('hour0');
hour23 = document.getElementById('hour23');
height = ((hour23.offsetTop - hour0.offsetTop) / (23 * 4));
if (hour0 && hour23)
height = ((hour23.offsetTop - hour0.offsetTop) / (23 * 4));
return height;
}
function getDayWidth(viewLeft) {
var width, offset, nodes, domRect;
function getDayDimensions(viewLeft) {
var width, height, leftOffset, topOffset, nodes, domRect, tileHeader;
width = 0;
offset = 0;
height = width = leftOffset = topOffset = 0;
nodes = scrollView.getElementsByClassName('day0');
if (nodes.length > 0) {
domRect = nodes[0].getBoundingClientRect();
height = domRect.height;
width = domRect.width;
offset = domRect.left - viewLeft;
leftOffset = domRect.left - viewLeft;
tileHeader = nodes[0].getElementsByClassName('sg-calendar-tile-header');
if (tileHeader.length > 0)
topOffset = tileHeader[0].clientHeight;
}
return [width, offset];
return { height: height, width: width, offset: { left: leftOffset, top: topOffset } };
}
function getMaxColumns() {
@@ -104,20 +108,22 @@
}
// View has been resized;
// Compute the view's origins (x, y), a day's width (dayWidth) and the left margin (daysOffset).
// Compute the view's origins (x, y), a day's dimensions and left margin.
function updateCoordinates() {
var domRect, dayWidth;
var domRect, dayDimensions;
domRect = scrollView.getBoundingClientRect();
dayWidth = getDayWidth(domRect.left);
dayDimensions = getDayDimensions(domRect.left);
angular.extend(view, {
coordinates: {
x: domRect.left,
y: domRect.top
},
dayWidth: dayWidth[0],
daysOffset: dayWidth[1]
dayHeight: dayDimensions.height,
dayWidth: dayDimensions.width,
daysOffset: dayDimensions.offset.left,
topOffset: dayDimensions.offset.top
});
}
@@ -138,31 +144,31 @@
scrollStep = view.scrollStep;
pointerHandler = Component.$ghost.pointerHandler;
if (pointerHandler) {
pointerCoordinates = pointerHandler.getContainerBasedCoordinates(view);
pointerCoordinates = pointerHandler.getContainerBasedCoordinates(view);
if (pointerCoordinates) {
// Pointer is inside view; Adjust scrollbar if necessary
Calendar.$view = view;
now = new Date().getTime();
if (!lastScroll || now > lastScroll + 100) {
lastScroll = now;
scrollY = pointerCoordinates.y - scrollStep;
if (scrollY < 0) {
minY = -scrollView.scrollTop;
if (scrollY < minY)
scrollY = minY;
scrollView.scrollTop += scrollY;
}
else {
scrollY = pointerCoordinates.y + scrollStep;
delta = scrollY - scrollView.clientHeight;
if (delta > 0) {
scrollView.scrollTop += delta;
if (pointerCoordinates) {
// Pointer is inside view; Adjust scrollbar if necessary
Calendar.$view = view;
now = new Date().getTime();
if (!lastScroll || now > lastScroll + 100) {
lastScroll = now;
scrollY = pointerCoordinates.y - scrollStep;
if (scrollY < 0) {
minY = -scrollView.scrollTop;
if (scrollY < minY)
scrollY = minY;
scrollView.scrollTop += scrollY;
}
else {
scrollY = pointerCoordinates.y + scrollStep;
delta = scrollY - scrollView.clientHeight;
if (delta > 0) {
scrollView.scrollTop += delta;
}
}
}
}
}
}
}
}
};
@@ -47,7 +47,8 @@
dragGrip = angular.element('<div class="dragGrip"></div>');
dragGrip.addClass('bdr-folder' + component.pid);
if (component.c_isallday) {
if (component.c_isallday ||
element[0].parentNode.tagName === 'SG-CALENDAR-MONTH-DAY') {
if (isFirstBlock) {
leftGrip = angular.element('<div class="dragGrip-left"></div>').append(dragGrip);
element.append(leftGrip);
@@ -73,8 +74,7 @@
var block, dragMode, eventType, startDate, newData, newComponent, pointerHandler;
dragMode = 'move-event';
eventType = 'multiday';
if (scope.block && scope.block.component) {
// Move or resize existing component
if (ev.target.className == 'dragGrip-top' ||
@@ -136,7 +136,9 @@
b.dragging = true;
});
if (block.component.c_isallday)
if (element[0].parentNode.tagName === 'SG-CALENDAR-MONTH-DAY')
eventType = 'monthly';
else if (block.component.c_isallday)
eventType = 'multiday-allday';
// Update pointer handler
@@ -145,6 +147,7 @@
pointerHandler.initFromBlock(block);
// Update Component.$ghost
Component.$ghost.starthour = block.starthour;
Component.$ghost.component = block.component;
$log.debug('emit calendar:dragstart');
@@ -220,7 +223,8 @@
/**
* SOGoEventDragEventCoordinates
*/
function SOGoEventDragEventCoordinates() {
function SOGoEventDragEventCoordinates(eventType) {
this.setEventType(eventType);
}
SOGoEventDragEventCoordinates.prototype = {
@@ -235,13 +239,19 @@
},
initFromBlock: function(block) {
// Get the start (first quarter) from the event's first block
this.start = block.component.blocks[0].start;
if (this.eventType === 'monthly')
this.start = 0;
else
// Get the start (first quarter) from the event's first block
this.start = block.component.blocks[0].start;
// Compute overall length
this.duration = _.sum(block.component.blocks, function(b) {
return b.length;
});
if (this.eventType === 'monthly')
this.duration = block.component.blocks.length * 96;
else
this.duration = _.sum(block.component.blocks, function(b) {
return b.length;
});
// Get the dayNumber from the event's first block
this.dayNumber = block.component.blocks[0].dayNumber;
@@ -299,7 +309,7 @@
originalCoordinates: null,
currentCoordinates: null,
// Pointer relative xy coordinates within view
// Pointer relative xy coordinates within view (row-column)
originalViewCoordinates: null,
currentViewCoordinates: null,
@@ -314,8 +324,8 @@
getEventViewCoordinates: null,
initFromBlock: function SEDPH_initFromBlock(block) {
this.currentEventCoordinates = new SOGoEventDragEventCoordinates();
this.originalEventCoordinates = new SOGoEventDragEventCoordinates();
this.currentEventCoordinates = new SOGoEventDragEventCoordinates(this.eventType);
this.originalEventCoordinates = new SOGoEventDragEventCoordinates(this.eventType);
this.originalEventCoordinates.initFromBlock(block);
},
@@ -375,7 +385,7 @@
var deltaQuarters = delta.x * CalendarSettings.EventDragDayLength + delta.y;
$log.debug('quarters delta ' + deltaQuarters);
if (!this.originalEventCoordinates.start) {
if (angular.isUndefined(this.originalEventCoordinates.start)) {
this.originalEventCoordinates.dayNumber = this.originalViewCoordinates.x;
this.originalEventCoordinates.start = this.originalViewCoordinates.y;
}
@@ -498,35 +508,36 @@
return coordinates;
},
// getEventMonthlyViewCoordinates: function SEDPH_gEMonthlyViewC() {
// /* x = day; y = quarter */
// var coordinates;
getEventMonthlyViewCoordinates: function SEDPH_gEMonthlyViewC(view, pointerCoordinates) {
/* x = day; y = quarter */
var coordinates;
// var pxCoordinates = this.getContainerBasedCoordinates();
// if (pxCoordinates) {
// coordinates = new SOGoCoordinates();
// var utilities = SOGoEventDragUtilities();
// var daysOffset = utilities.getDaysOffset();
// var daysTopOffset = daysOffset; /* change later */
// var dayHeight = utilities.getDayHeight();
// var daysY = Math.floor((pxCoordinates.y - daysTopOffset) / dayHeight);
// if (daysY < 0)
// daysY = 0;
// var dayWidth = utilities.getDayWidth();
var pxCoordinates = this.getContainerBasedCoordinates(view, pointerCoordinates);
if (pxCoordinates) {
coordinates = new SOGoCoordinates();
// coordinates.x = Math.floor((pxCoordinates.x - daysOffset) / dayWidth);
// if (coordinates.x < 0)
// coordinates.x = 0;
// else if (coordinates.x > 6)
// coordinates.x = 6;
// coordinates.x += 7 * daysY;
// coordinates.y = 0;
// } else {
// coordinates = null;
// }
var daysTopOffset = 0;
var dayWidth = view.dayWidth;
var daysOffset = view.daysOffset;
var dayHeight = view.dayHeight;
var daysY = Math.floor((pxCoordinates.y - daysTopOffset) / dayHeight);
if (daysY < 0)
daysY = 0;
// return coordinates;
// },
coordinates.x = Math.floor((pxCoordinates.x - daysOffset) / dayWidth);
if (coordinates.x < 0)
coordinates.x = 0;
else if (coordinates.x > 6)
coordinates.x = 6;
coordinates.x += 7 * daysY;
coordinates.y = 0;
}
else {
coordinates = null;
}
return coordinates;
},
getDistance: function SEDPH_getDistance() {
return this.currentCoordinates.getDistance(this.originalCoordinates);