Rewrite board drag and drop with jquery (touch devices, IE, auto-update)

This commit is contained in:
Frédéric Guillot
2014-05-17 17:35:39 -04:00
parent 09da5720e8
commit 5e4b40665f
18 changed files with 375 additions and 379 deletions

View File

@@ -29,7 +29,7 @@ body {
/* links */
a {
color: #3366CC;
border: 1px solid rgba(255, 255, 255, 0);
border: none;
}
a:focus {
@@ -123,10 +123,6 @@ label {
margin-top: 10px;
}
input[type="checkbox"] {
border: 1px solid #ccc;
}
input[type="number"],
input[type="date"],
input[type="email"],
@@ -574,17 +570,15 @@ td div.over {
}
.draggable-item {
margin-right: 5px;
margin-bottom: 10px;
}
[draggable] {
cursor: pointer;
user-select: none;
}
[draggable=true]:hover {
box-shadow: 0 0 3px #333;
.draggable-placeholder {
border: 2px dashed #000;
background: #fafafa;
height: 70px;
margin-bottom: 10px;
}
tr td.task a,
@@ -607,6 +601,8 @@ div.task-title a {
div.task {
position: relative;
margin-right: 5px;
margin-bottom: 10px;
}
.task-score {

View File

@@ -1,259 +1,102 @@
(function () {
function handleItemDragStart(e)
var checkInterval = null;
// Setup the board
function board_load_events()
{
this.style.opacity = '0.4';
dragSrcItem = this;
dragSrcColumn = this.parentNode;
e.dataTransfer.effectAllowed = 'copy';
e.dataTransfer.setData('text/plain', this.innerHTML);
}
function handleItemDragEnd(e)
{
// Restore styles
removeOver();
this.style.opacity = '1.0';
dragSrcColumn = null;
dragSrcItem = null;
}
function handleItemDragOver(e)
{
if (e.preventDefault) e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
return false;
}
function handleItemDragEnter(e)
{
if (dragSrcItem != this) {
removeOver();
this.classList.add('over');
}
}
function handleItemDrop(e)
{
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
// Drop the element if the item is not the same
if (dragSrcItem != this) {
var position = getItemPosition(this);
var item = createItem(e.dataTransfer.getData('text/plain'));
if (countColumnItems(this.parentNode) == position) {
this.parentNode.appendChild(item);
}
else {
this.parentNode.insertBefore(item, this);
$(".column").sortable({
connectWith: ".column",
placeholder: "draggable-placeholder",
stop: function(event, ui) {
board_save();
}
});
dragSrcItem.parentNode.removeChild(dragSrcItem);
var interval = parseInt($("#board").attr("data-check-interval"));
saveBoard();
}
dragSrcColumn = null;
dragSrcItem = null;
return false;
}
function handleColumnDragOver(e)
{
if (e.preventDefault) e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
return false;
}
function handleColumnDragEnter(e)
{
if (dragSrcColumn != this) {
removeOver();
this.classList.add('over');
if (interval > 0) {
checkInterval = window.setInterval(board_check, interval * 1000);
}
}
function handleColumnDrop(e)
// Stop events
function board_unload_events()
{
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
// Drop the element if the column is not the same
if (dragSrcColumn != this) {
var item = createItem(e.dataTransfer.getData('text/plain'));
this.appendChild(item);
dragSrcColumn.removeChild(dragSrcItem);
saveBoard();
}
return false;
clearInterval(checkInterval);
}
function saveBoard()
// Save and refresh the board
function board_save()
{
var data = [];
var projectId = document.getElementById("board").getAttribute("data-project-id");
var cols = document.querySelectorAll('.column');
var projectId = $("#board").attr("data-project-id");
[].forEach.call(cols, function(col) {
board_unload_events();
var task_limit = col.getAttribute("data-task-limit");
if (task_limit != "" && task_limit != "0") {
task_limit = parseInt(task_limit);
if (col.children.length > task_limit) {
col.classList.add("task-limit-warning");
}
else {
col.classList.remove("task-limit-warning");
}
var counter = document.getElementById("task-number-column-" + col.getAttribute("data-column-id"));
if (counter) counter.innerHTML = col.children.length;
}
[].forEach.call(col.children, function(item) {
$(".column").each(function() {
var columnId = $(this).attr("data-column-id");
$("#column-" + columnId + " .task").each(function(index) {
data.push({
"task_id": item.firstElementChild.getAttribute("data-task-id"),
"position": getItemPosition(item),
"column_id": col.getAttribute("data-column-id")
})
"task_id": parseInt($(this).attr("data-task-id")),
"position": index + 1,
"column_id": parseInt(columnId)
});
});
});
var xhr = new XMLHttpRequest();
xhr.open("POST", "?controller=board&action=save&project_id=" + projectId, true);
$.ajax({
url: "?controller=board&action=save&project_id=" + projectId,
data: {positions: data},
type: "POST",
success: function(data) {
$("#board").remove();
$("#main").append(data);
board_load_events();
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
}
});
}
xhr.onreadystatechange = function(response) {
// Check if a board have been changed by someone else
function board_check()
{
var projectId = $("#board").attr("data-project-id");
var timestamp = $("#board").attr("data-time");
if (this.readyState == this.DONE) {
try {
var response = JSON.parse(this.responseText);
if (response.result == false) {
window.alert('Unable to update the board');
}
else if (response.refresh == true) {
window.location = "?controller=board&action=show&project_id=" + projectId;
if (projectId != undefined && timestamp != undefined) {
$.ajax({
url: "?controller=board&action=check&project_id=" + projectId + "&timestamp=" + timestamp,
statusCode: {
200: function(data) {
$("#board").remove();
$("#main").append(data);
board_unload_events();
board_load_events();
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
}
}
catch (e) {}
}
};
xhr.send(JSON.stringify(data));
}
function getItemPosition(element)
{
var i = 0;
while ((element = element.previousSibling) != null) {
if (element.nodeName == "DIV" && element.className == "draggable-item") {
i++;
}
});
}
return i + 1;
}
function countColumnItems(element)
{
return element.children.length;
}
function createItem(html)
{
var item = document.createElement("div");
item.className = "draggable-item";
item.draggable = true;
item.innerHTML = html;
item.ondragstart = handleItemDragStart;
item.ondragend = handleItemDragEnd;
item.ondragenter = handleItemDragEnter;
item.ondragover = handleItemDragOver;
item.ondrop = handleItemDrop;
return item;
}
function removeOver()
{
// Remove column over
[].forEach.call(document.querySelectorAll('.column'), function (col) {
col.classList.remove('over');
});
// Remove item over
[].forEach.call(document.querySelectorAll('.draggable-item'), function (item) {
item.classList.remove('over');
});
}
// Drag and drop events
var dragSrcItem = null;
var dragSrcColumn = null;
var items = document.querySelectorAll('.draggable-item');
[].forEach.call(items, function(item) {
item.addEventListener('dragstart', handleItemDragStart, false);
item.addEventListener('dragend', handleItemDragEnd, false);
item.addEventListener('dragenter', handleItemDragEnter, false);
item.addEventListener('dragover', handleItemDragOver, false);
item.addEventListener('drop', handleItemDrop, false);
});
var cols = document.querySelectorAll('.column');
[].forEach.call(cols, function(col) {
col.addEventListener('dragenter', handleColumnDragEnter, false);
col.addEventListener('dragover', handleColumnDragOver, false);
col.addEventListener('drop', handleColumnDrop, false);
});
[].forEach.call(document.querySelectorAll('[data-task-id]'), function (item) {
item.addEventListener('click', function() {
window.location.href = '?controller=task&action=show&task_id=' + item.getAttribute('data-task-id');
});
});
// Filtering
// Get the selected user id
function getSelectedUserFilter()
{
var select = document.getElementById("form-user_id");
return select.options[select.selectedIndex].value;
return $("#form-user_id").val();
}
// Return true if the filter is activated
function hasDueDateFilter()
{
var dateFilter = document.getElementById("filter-due-date");
return dateFilter.classList.contains("filter-on");
return $("#filter-due-date").hasClass("filter-on");
}
// Apply user or date filter (change tasks opacity)
function applyFilter(selectedUserId, filterDueDate)
{
[].forEach.call(document.querySelectorAll('[data-task-id]'), function (item) {
$("[data-task-id]").each(function(index, item) {
var ownerId = item.getAttribute("data-owner-id");
var dueDate = item.getAttribute("data-due-date");
@@ -271,22 +114,24 @@
});
}
var userFilter = document.getElementById("form-user_id");
var dateFilter = document.getElementById("filter-due-date");
if (userFilter) {
userFilter.onchange = function() {
// Load filter events
function filter_load_events()
{
$("#form-user_id").change(function() {
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
};
}
});
if (dateFilter) {
dateFilter.onclick = function(e) {
dateFilter.classList.toggle("filter-on");
$("#filter-due-date").click(function(e) {
$(this).toggleClass("filter-on");
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
e.preventDefault();
};
});
}
// Initialization
$(function() {
board_load_events();
filter_load_events();
});
}());

4
assets/js/jquery-1.11.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11
assets/js/jquery.ui.touch-punch.min.js vendored Normal file
View File

@@ -0,0 +1,11 @@
/*!
* jQuery UI Touch Punch 0.2.3
*
* Copyright 20112014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);