us 1213 - drag multiple
parent
19da1a1b48
commit
48d9505157
|
@ -256,6 +256,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
resortUserStories: (uses, field="backlog_order") ->
|
||||
items = []
|
||||
|
||||
for item, index in uses
|
||||
item[field] = index
|
||||
if item.isModified()
|
||||
|
@ -263,8 +264,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
return items
|
||||
|
||||
moveUs: (ctx, us, newUsIndex, newSprintId) ->
|
||||
oldSprintId = us.milestone
|
||||
moveUs: (ctx, usList, newUsIndex, newSprintId) ->
|
||||
oldSprintId = usList[0].milestone
|
||||
project = usList[0].project
|
||||
|
||||
# In the same sprint or in the backlog
|
||||
if newSprintId == oldSprintId
|
||||
|
@ -277,20 +279,25 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
userstories = @scope.sprintsById[newSprintId].user_stories
|
||||
|
||||
@scope.$apply ->
|
||||
r = userstories.indexOf(us)
|
||||
userstories.splice(r, 1)
|
||||
userstories.splice(newUsIndex, 0, us)
|
||||
for us, key in usList
|
||||
r = userstories.indexOf(us)
|
||||
userstories.splice(r, 1)
|
||||
|
||||
args = [newUsIndex, 0].concat(usList)
|
||||
Array.prototype.splice.apply(userstories, args)
|
||||
|
||||
# If in backlog
|
||||
if newSprintId == null
|
||||
# Rehash userstories order field
|
||||
|
||||
items = @.resortUserStories(userstories, "backlog_order")
|
||||
data = @.prepareBulkUpdateData(items, "backlog_order")
|
||||
|
||||
# Persist in bulk all affected
|
||||
# userstories with order change
|
||||
@rs.userstories.bulkUpdateBacklogOrder(us.project, data).then =>
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
||||
for us in usList
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
# For sprint
|
||||
else
|
||||
|
@ -300,27 +307,32 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
# Persist in bulk all affected
|
||||
# userstories with order change
|
||||
@rs.userstories.bulkUpdateSprintOrder(us.project, data).then =>
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
@rs.userstories.bulkUpdateSprintOrder(project, data).then =>
|
||||
for us in usList
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
return promise
|
||||
|
||||
# From sprint to backlog
|
||||
if newSprintId == null
|
||||
us.milestone = null
|
||||
us.milestone = null for us in usList
|
||||
|
||||
@scope.$apply =>
|
||||
# Add new us to backlog userstories list
|
||||
@scope.userstories.splice(newUsIndex, 0, us)
|
||||
@scope.visibleUserstories.splice(newUsIndex, 0, us)
|
||||
# @scope.userstories.splice(newUsIndex, 0, us)
|
||||
# @scope.visibleUserstories.splice(newUsIndex, 0, us)
|
||||
args = [newUsIndex, 0].concat(usList)
|
||||
Array.prototype.splice.apply(@scope.userstories, args)
|
||||
Array.prototype.splice.apply(@scope.visibleUserstories, args)
|
||||
|
||||
# Execute the prefiltering of user stories
|
||||
@.filterVisibleUserstories()
|
||||
|
||||
# Remove the us from the sprint list.
|
||||
sprint = @scope.sprintsById[oldSprintId]
|
||||
r = sprint.user_stories.indexOf(us)
|
||||
sprint.user_stories.splice(r, 1)
|
||||
for us, key in usList
|
||||
r = sprint.user_stories.indexOf(us)
|
||||
sprint.user_stories.splice(r, 1)
|
||||
|
||||
# Persist the milestone change of userstory
|
||||
promise = @repo.save(us)
|
||||
|
@ -340,43 +352,55 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
# From backlog to sprint
|
||||
newSprint = @scope.sprintsById[newSprintId]
|
||||
if us.milestone == null
|
||||
us.milestone = newSprintId
|
||||
if oldSprintId == null
|
||||
us.milestone = newSprintId for us in usList
|
||||
|
||||
@scope.$apply =>
|
||||
args = [newUsIndex, 0].concat(usList)
|
||||
|
||||
# Add moving us to sprint user stories list
|
||||
newSprint.user_stories.splice(newUsIndex, 0, us)
|
||||
Array.prototype.splice.apply(newSprint.user_stories, args)
|
||||
|
||||
# Remove moving us from backlog userstories lists.
|
||||
r = @scope.visibleUserstories.indexOf(us)
|
||||
@scope.visibleUserstories.splice(r, 1)
|
||||
r = @scope.userstories.indexOf(us)
|
||||
@scope.userstories.splice(r, 1)
|
||||
for us, key in usList
|
||||
r = @scope.visibleUserstories.indexOf(us)
|
||||
@scope.visibleUserstories.splice(r, 1)
|
||||
|
||||
r = @scope.userstories.indexOf(us)
|
||||
@scope.userstories.splice(r, 1)
|
||||
|
||||
# From sprint to sprint
|
||||
else
|
||||
us.milestone = newSprintId
|
||||
us.milestone = newSprintId for us in usList
|
||||
|
||||
@scope.$apply =>
|
||||
args = [newUsIndex, 0].concat(usList)
|
||||
|
||||
# Add new us to backlog userstories list
|
||||
newSprint.user_stories.splice(newUsIndex, 0, us)
|
||||
Array.prototype.splice.apply(newSprint.user_stories, args)
|
||||
|
||||
# Remove the us from the sprint list.
|
||||
oldSprint = @scope.sprintsById[oldSprintId]
|
||||
r = oldSprint.user_stories.indexOf(us)
|
||||
oldSprint.user_stories.splice(r, 1)
|
||||
for us in usList
|
||||
oldSprint = @scope.sprintsById[oldSprintId]
|
||||
r = oldSprint.user_stories.indexOf(us)
|
||||
oldSprint.user_stories.splice(r, 1)
|
||||
|
||||
# Persist the milestone change of userstory
|
||||
promise = @repo.save(us)
|
||||
promises = _.map usList, (us) => @repo.save(us)
|
||||
|
||||
# Rehash userstories order field
|
||||
# and persist in bulk all changes.
|
||||
promise = promise.then =>
|
||||
promise = @q.all.apply(null, promises).then =>
|
||||
items = @.resortUserStories(newSprint.user_stories, "sprint_order")
|
||||
data = @.prepareBulkUpdateData(items, "sprint_order")
|
||||
return @rs.userstories.bulkUpdateSprintOrder(us.project, data).then =>
|
||||
|
||||
return @rs.userstories.bulkUpdateSprintOrder(project, data).then =>
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
return @rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
||||
for us in usList
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
promise.then null, ->
|
||||
console.log "FAIL" # TODO
|
||||
|
||||
|
@ -545,6 +569,7 @@ BacklogDirective = ($repo, $rootscope) ->
|
|||
|
||||
# Enable move to current sprint only when there are selected us's
|
||||
$el.on "change", ".backlog-table-body .user-stories input:checkbox", (event) ->
|
||||
target = angular.element(event.currentTarget)
|
||||
moveToCurrentSprintDom = $el.find("#move-to-current-sprint")
|
||||
selectedUsDom = $el.find(".backlog-table-body .user-stories input:checkbox:checked")
|
||||
|
||||
|
@ -553,6 +578,8 @@ BacklogDirective = ($repo, $rootscope) ->
|
|||
else
|
||||
moveToCurrentSprintDom.hide()
|
||||
|
||||
target.closest('.us-item-row').toggleClass('ui-multisortable-multiple')
|
||||
|
||||
$el.on "click", "#move-to-current-sprint", (event) =>
|
||||
# Calculating the us's to be modified
|
||||
ussDom = $el.find(".backlog-table-body .user-stories input:checkbox:checked")
|
||||
|
|
|
@ -69,23 +69,35 @@ BacklogSortableDirective = ($repo, $rs, $rootscope) ->
|
|||
cursorAt: {right: 15}
|
||||
})
|
||||
|
||||
$el.on "sortreceive", (event, ui) ->
|
||||
$el.on "multiplesortreceive", (event, ui) ->
|
||||
itemUs = ui.item.scope().us
|
||||
itemIndex = ui.item.index()
|
||||
|
||||
deleteElement(ui.item)
|
||||
$scope.$emit("sprint:us:move", itemUs, itemIndex, null)
|
||||
|
||||
$scope.$emit("sprint:us:move", [itemUs], itemIndex, null)
|
||||
ui.item.find('a').removeClass('noclick')
|
||||
|
||||
$el.on "sortstop", (event, ui) ->
|
||||
$el.on "multiplesortstop", (event, ui) ->
|
||||
# When parent not exists, do nothing
|
||||
if ui.item.parent().length == 0
|
||||
if $(ui.items[0]).parent().length == 0
|
||||
return
|
||||
|
||||
itemUs = ui.item.scope().us
|
||||
itemIndex = ui.item.index()
|
||||
$scope.$emit("sprint:us:move", itemUs, itemIndex, null)
|
||||
ui.item.find('a').removeClass('noclick')
|
||||
items = _.sortBy ui.items, (item) ->
|
||||
return $(item).index()
|
||||
|
||||
index = _.min _.map items, (item) ->
|
||||
return $(item).index()
|
||||
|
||||
us = _.map items, (item) ->
|
||||
item = $(item)
|
||||
itemUs = item.scope().us
|
||||
|
||||
item.find('a').removeClass('noclick')
|
||||
|
||||
return itemUs
|
||||
|
||||
$scope.$emit("sprint:us:move", us, index, null)
|
||||
|
||||
$el.on "sortstart", (event, ui) ->
|
||||
ui.item.find('a').addClass('noclick')
|
||||
|
@ -113,7 +125,7 @@ BacklogEmptySortableDirective = ($repo, $rs, $rootscope) ->
|
|||
itemIndex = ui.item.index()
|
||||
|
||||
deleteElement(ui.item)
|
||||
$scope.$emit("sprint:us:move", itemUs, itemIndex, null)
|
||||
$scope.$emit("sprint:us:move", [itemUs], itemIndex, null)
|
||||
ui.item.find('a').removeClass('noclick')
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
|
@ -132,24 +144,33 @@ SprintSortableDirective = ($repo, $rs, $rootscope) ->
|
|||
connectWith: ".sprint-table,.backlog-table-body,.empty-backlog"
|
||||
})
|
||||
|
||||
$el.on "sortreceive", (event, ui) ->
|
||||
itemUs = ui.item.scope().us
|
||||
itemIndex = ui.item.index()
|
||||
$el.on "multiplesortreceive", (event, ui) ->
|
||||
items = _.sortBy ui.items, (item) ->
|
||||
return $(item).index()
|
||||
|
||||
deleteElement(ui.item)
|
||||
$scope.$emit("sprint:us:move", itemUs, itemIndex, $scope.sprint.id)
|
||||
ui.item.find('a').removeClass('noclick')
|
||||
index = _.min _.map items, (item) ->
|
||||
return $(item).index()
|
||||
|
||||
$el.on "sortstop", (event, ui) ->
|
||||
us = _.map items, (item) ->
|
||||
item = $(item)
|
||||
itemUs = item.scope().us
|
||||
|
||||
deleteElement(item)
|
||||
item.find('a').removeClass('noclick')
|
||||
|
||||
return itemUs
|
||||
|
||||
$scope.$emit("sprint:us:move", us, index, $scope.sprint.id)
|
||||
|
||||
$el.on "multiplesortstop", (event, ui) ->
|
||||
# When parent not exists, do nothing
|
||||
if ui.item.parent().length == 0
|
||||
return
|
||||
|
||||
itemUs = ui.item.scope().us
|
||||
itemIndex = ui.item.index()
|
||||
|
||||
$scope.$emit("sprint:us:move", itemUs, itemIndex, $scope.sprint.id)
|
||||
ui.item.find('a').removeClass('noclick')
|
||||
$scope.$emit("sprint:us:move", [itemUs], itemIndex, $scope.sprint.id)
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
(function($) {
|
||||
var multipleSortableClass = 'ui-multisortable-multiple';
|
||||
var dragStarted = false;
|
||||
|
||||
var multiSort = {};
|
||||
|
||||
multiSort.isBelow = function(elm, compare) {
|
||||
var elmOriginalPosition = elm.data('dragmultiple:originalPosition');
|
||||
var compareOriginalPosition = compare.data('dragmultiple:originalPosition');
|
||||
|
||||
return elmOriginalPosition.top > compareOriginalPosition.top;
|
||||
};
|
||||
|
||||
multiSort.reset = function(elm) {
|
||||
$(elm)
|
||||
.removeClass("ui-sortable-helper")
|
||||
.removeAttr('style')
|
||||
.data('dragMultipleActive', false);
|
||||
};
|
||||
|
||||
multiSort.sort = function(current, positions) {
|
||||
positions.after.reverse();
|
||||
|
||||
$.each(positions.after, function () {
|
||||
multiSort.reset(this);
|
||||
current.after(this);
|
||||
});
|
||||
|
||||
$.each(positions.before, function () {
|
||||
multiSort.reset(this);
|
||||
current.before(this);
|
||||
});
|
||||
};
|
||||
|
||||
multiSort.sortPositions = function(elm, current) {
|
||||
//saved if the elements are after or before the current
|
||||
var insertAfter = [];
|
||||
var insertBefore = [];
|
||||
|
||||
$(elm).find('.' + multipleSortableClass).each(function () {
|
||||
var elm = $(this);
|
||||
|
||||
if (elm[0] === current[0] || !current.hasClass(multipleSortableClass)) return;
|
||||
|
||||
if (multiSort.isBelow(elm, current)) {
|
||||
insertAfter.push(elm);
|
||||
} else {
|
||||
insertBefore.push(elm);
|
||||
}
|
||||
});
|
||||
|
||||
return {'after': insertAfter, 'before': insertBefore};
|
||||
};
|
||||
|
||||
$.widget( "ui.sortable", $.ui.sortable, {
|
||||
_mouseStart: function() {
|
||||
dragStarted = false;
|
||||
|
||||
this._superApply( arguments );
|
||||
},
|
||||
_createHelper: function () {
|
||||
var helper = this._superApply( arguments );
|
||||
|
||||
if ($(helper).hasClass(multipleSortableClass)) {
|
||||
$(this.element).find('.' + multipleSortableClass).each(function () {
|
||||
$(this)
|
||||
.data('dragmultiple:originalPosition', $(this).position())
|
||||
.data('dragMultipleActive', true);
|
||||
});
|
||||
}
|
||||
|
||||
return helper;
|
||||
},
|
||||
_mouseStop: function (event, ui) {
|
||||
var current = this.helper;
|
||||
var elms = [];
|
||||
|
||||
if (current.hasClass(multipleSortableClass)) {
|
||||
elms = $(this.element).find('.' + multipleSortableClass);
|
||||
}
|
||||
|
||||
if (!elms.length) {
|
||||
elms = [current];
|
||||
}
|
||||
|
||||
//save the order of the elements relative to the main
|
||||
var positions = multiSort.sortPositions(this.element, current);
|
||||
|
||||
this._superApply( arguments );
|
||||
|
||||
if (this.element !== this.currentContainer.element) {
|
||||
// change to another sortable list
|
||||
multiSort.sort(current, positions);
|
||||
|
||||
$(this.currentContainer.element).trigger('multiplesortreceive', {
|
||||
'item': current,
|
||||
'items': elms
|
||||
});
|
||||
} else if (current.hasClass(multipleSortableClass)) {
|
||||
// sort in the same list
|
||||
multiSort.sort(current, positions);
|
||||
}
|
||||
|
||||
$(this.element).trigger('multiplesortstop', {
|
||||
'item': current,
|
||||
'items': elms
|
||||
});
|
||||
},
|
||||
_mouseDrag: function(key, value) {
|
||||
this._super(key, value);
|
||||
|
||||
var current = this.helper;
|
||||
|
||||
if (!current.hasClass(multipleSortableClass)) return;
|
||||
|
||||
// following the drag element
|
||||
var currentLeft = current.position().left;
|
||||
var currentTop = current.position().top;
|
||||
var currentZIndex = current.css('z-index');
|
||||
var currentPosition = current.css('position');
|
||||
|
||||
var positions = multiSort.sortPositions(this.element, current);
|
||||
|
||||
positions.before.reverse();
|
||||
|
||||
[{'positions': positions.after, type: 'after'},
|
||||
{'positions': positions.before, type: 'before'}]
|
||||
.forEach(function (item) {
|
||||
$.each(item.positions, function (index, elm) {
|
||||
var top;
|
||||
|
||||
if (item.type === 'after') {
|
||||
top = currentTop + ((index + 1) * current.outerHeight());
|
||||
} else {
|
||||
top = currentTop - ((index + 1) * current.outerHeight());
|
||||
}
|
||||
|
||||
elm
|
||||
.addClass("ui-sortable-helper")
|
||||
.css({
|
||||
width: elm.outerWidth(),
|
||||
height: elm.outerHeight(),
|
||||
position: currentPosition,
|
||||
zIndex: currentZIndex,
|
||||
top: top,
|
||||
left: currentLeft
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// it only refresh position the first time because
|
||||
// jquery-ui has saved the old positions of the draggable elements
|
||||
// and with this will remove all elements with dragMultipleActive
|
||||
if (!dragStarted) {
|
||||
dragStarted = true;
|
||||
this.refreshPositions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}(jQuery))
|
|
@ -789,7 +789,6 @@ $.Widget.prototype = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.element.trigger( event, data );
|
||||
return !( $.isFunction( callback ) &&
|
||||
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
|
||||
|
@ -2368,11 +2367,9 @@ $.ui.ddmanager = {
|
|||
if ( draggable.options.refreshPositions ) {
|
||||
$.ui.ddmanager.prepareOffsets( draggable, event );
|
||||
}
|
||||
|
||||
// Run through all droppables and check their positions based on specific tolerance options
|
||||
$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
|
||||
|
||||
if ( this.options.disabled || this.greedyChild || !this.visible ) {
|
||||
if ( this.options.disabled || this.greedyChild || !this.visible || $(this).data('dragMultipleActive')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4107,11 +4104,15 @@ return $.widget("ui.sortable", $.ui.mouse, {
|
|||
|
||||
//Rearrange
|
||||
for (i = this.items.length - 1; i >= 0; i--) {
|
||||
|
||||
//Cache variables and intersection, continue if no intersection
|
||||
item = this.items[i];
|
||||
itemElement = item.item[0];
|
||||
intersection = this._intersectsWithPointer(item);
|
||||
|
||||
if (item.item.data('dragMultipleActive')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!intersection) {
|
||||
continue;
|
||||
}
|
||||
|
@ -4166,7 +4167,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
|
|||
},
|
||||
|
||||
_mouseStop: function(event, noPropagation) {
|
||||
|
||||
if(!event) {
|
||||
return;
|
||||
}
|
||||
|
@ -4471,6 +4471,9 @@ return $.widget("ui.sortable", $.ui.mouse, {
|
|||
|
||||
for (i = this.items.length - 1; i >= 0; i--){
|
||||
item = this.items[i];
|
||||
if ($(item.item).data('dragMultipleActive')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//We ignore calculating positions of all connected containers when we're not over them
|
||||
if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
|
||||
|
@ -4536,7 +4539,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
|
|||
return element;
|
||||
},
|
||||
update: function(container, p) {
|
||||
|
||||
// 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
|
||||
// 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
|
||||
if(className && !o.forcePlaceholderSize) {
|
||||
|
@ -4661,7 +4663,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
|
|||
},
|
||||
|
||||
_createHelper: function(event) {
|
||||
|
||||
var o = this.options,
|
||||
helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
|
||||
|
||||
|
@ -4888,7 +4889,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
|
|||
},
|
||||
|
||||
_rearrange: function(event, i, a, hardRefresh) {
|
||||
|
||||
a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
|
||||
|
||||
//Various things done here to improve the performance:
|
|
@ -82,8 +82,9 @@ paths.js = [
|
|||
paths.app + "vendor/jquery-textcomplete/jquery.textcomplete.js",
|
||||
paths.app + "vendor/markitup/markitup/jquery.markitup.js",
|
||||
paths.app + "vendor/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js",
|
||||
paths.app + "js/jquery.ui.git.js",
|
||||
paths.app + "js/sha1.js",
|
||||
paths.app + "js/jquery.ui.git-custom.js",
|
||||
paths.app + "js/jquery-ui.drag-multiple-custom.js",
|
||||
paths.app + "js/sha1-custom.js",
|
||||
paths.app + "plugins/**/*.js"
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue