From 5d34d7945789db9c9e84f76a18acf4c2e86eb12d Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 24 Oct 2013 14:00:36 +0200 Subject: [PATCH] Assign tags by drag & dropping them onto tasks (#2389); some fixes concerning saving and resorting tasks --- plugins/tasklist/skins/larry/tasklist.css | 16 ++- plugins/tasklist/tasklist.js | 129 +++++++++++++++++----- 2 files changed, 113 insertions(+), 32 deletions(-) diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css index fadb62b7..9efc18b8 100644 --- a/plugins/tasklist/skins/larry/tasklist.css +++ b/plugins/tasklist/skins/larry/tasklist.css @@ -189,13 +189,10 @@ body.attachmentwin #topnav .topright { #tagslist li.inactive { color: #89b3be; + padding-right: 0.6em; /* display: none; */ } -#tagslist li.inactive .count { - display: none; -} - #tagslist li .count { position: relative; top: -1px; @@ -213,6 +210,11 @@ body.attachmentwin #topnav .topright { border-radius: 8px; } +.tag-draghelper .tag .count, +#tagslist li.inactive .count { + display: none; +} + #tasklists li { margin: 0; height: 20px; @@ -504,6 +506,7 @@ body.attachmentwin #topnav .topright { text-align: right; } +.tag-draghelper .tag, .taskhead .tags .tag { font-size: 85%; background: #d9ecf4; @@ -513,6 +516,11 @@ body.attachmentwin #topnav .topright { margin-right: 3px; } +.tag-draghelper li.tag { + list-style: none; + font-size: 100%; +} + .taskhead .date { position: absolute; top: 4px; diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js index d3180118..68ea1174 100644 --- a/plugins/tasklist/tasklist.js +++ b/plugins/tasklist/tasklist.js @@ -64,6 +64,8 @@ function rcube_tasklist_ui(settings) var search_request; var search_query; var completeness_slider; + var task_draghelper; + var tag_draghelper; var me = this; // general datepicker settings @@ -337,7 +339,8 @@ function rcube_tasklist_ui(settings) $(input).datepicker('widget').find('button.ui-datepicker-close') .html(rcmail.gettext('nodate','tasklist')) .attr('onclick', '') - .click(function(e){ + .unbind('click') + .bind('click', function(e){ $(input).datepicker('setDate', null).datepicker('hide'); }); }, 1); @@ -570,8 +573,19 @@ function rcube_tasklist_ui(settings) // append new tags to tag cloud $.each(newtags, function(i, tag){ - $('
  • ').attr('rel', tag).data('value', tag).html(Q(tag) + '').appendTo(rcmail.gui_objects.tagslist); - }); + $('
  • ').attr('rel', tag).data('value', tag) + .html(Q(tag) + '') + .appendTo(rcmail.gui_objects.tagslist) + .draggable({ + addClasses: false, + revert: 'invalid', + revertDuration: 300, + helper: tag_draggable_helper, + start: tag_draggable_start, + appendTo: 'body', + cursor: 'pointer', + }); + }); // re-sort tags list $(rcmail.gui_objects.tagslist).children('li').sortElements(function(a,b){ @@ -595,6 +609,59 @@ function rcube_tasklist_ui(settings) }); } + /* Helper functions for drag & drop functionality of tags */ + + function tag_draggable_helper() + { + if (!tag_draghelper) + tag_draghelper = $('
    '); + else + tag_draghelper.html(''); + + $(this).clone().addClass('tag').appendTo(tag_draghelper); + return tag_draghelper; + } + + function tag_draggable_start(event, ui) + { + $('.taskhead').droppable({ + hoverClass: 'droptarget', + accept: tag_droppable_accept, + drop: tag_draggable_dropped, + addClasses: false + }); + } + + function tag_droppable_accept(draggable) + { + if (rcmail.busy) + return false; + + var tag = draggable.data('value'), + drop_id = $(this).data('id'), + drop_rec = listdata[drop_id]; + + // target already has this tag assigned + if (!drop_rec || (drop_rec.tags && $.inArray(tag, drop_rec.tags) >= 0)) { + return false; + } + + return true; + } + + function tag_draggable_dropped(event, ui) + { + var drop_id = $(this).data('id'), + tag = ui.draggable.data('value'), + rec = listdata[drop_id]; + + if (rec && rec.id) { + if (!rec.tags) rec.tags = []; + rec.tags.push(tag); + save_task(rec, 'edit'); + } + } + /** * */ @@ -721,10 +788,10 @@ function rcube_tasklist_ui(settings) revert: 'invalid', addClasses: false, cursorAt: { left:-10, top:12 }, - helper: draggable_helper, + helper: task_draggable_helper, appendTo: 'body', - start: draggable_start, - stop: draggable_stop, + start: task_draggable_start, + stop: task_draggable_stop, revertDuration: 300 }); @@ -769,7 +836,7 @@ function rcube_tasklist_ui(settings) */ function resort_task(rec, li, animated) { - var dir = 0, index, slice, next_li, next_id, next_rec; + var dir = 0, index, slice, cmp, next_li, next_id, next_rec, insert_after, past_myself; // animated moving var insert_animated = function(li, before, after) { @@ -795,33 +862,36 @@ function rcube_tasklist_ui(settings) } // find the right place to insert the task item - li.siblings().each(function(i, elem){ + li.parent().children('.taskitem').each(function(i, elem){ next_li = $(elem); next_id = next_li.attr('rel'); next_rec = listdata[next_id]; if (next_id == rec.id) { - next_li = null; + past_myself = true; return 1; // continue } - if (next_rec && task_cmp(rec, next_rec) > 0) { + cmp = next_rec ? task_cmp(rec, next_rec) : 0; + + if (cmp > 0 || (cmp == 0 && !past_myself)) { + insert_after = next_li; return 1; // continue; } - else if (next_rec && next_li && task_cmp(rec, next_rec) < 0) { + else if (next_li && cmp < 0) { if (animated) insert_animated(li, next_li); else li.insertBefore(next_li); - next_li = null; - return false; + index = $.inArray(next_id, listindex); + return false; // break } }); - index = $.inArray(next_id, listindex); + if (insert_after) { + if (animated) insert_animated(li, null, insert_after); + else li.insertAfter(insert_after); - if (next_li) { - if (animated) insert_animated(li, null, next_li); - else li.insertAfter(next_li); - index++; + next_id = insert_after.attr('rel'); + index = $.inArray(next_id, listindex); } // insert into list index @@ -865,20 +935,20 @@ function rcube_tasklist_ui(settings) /* Helper functions for drag & drop functionality */ - function draggable_helper() + function task_draggable_helper() { - if (!draghelper) - draghelper = $('
    '); + if (!task_draghelper) + task_draghelper = $('
    '); - return draghelper; + return task_draghelper; } - function draggable_start(event, ui) + function task_draggable_start(event, ui) { $('.taskhead, #rootdroppable, #'+rcmail.gui_objects.folderlist.id+' li').droppable({ hoverClass: 'droptarget', - accept: droppable_accept, - drop: draggable_dropped, + accept: task_droppable_accept, + drop: task_draggable_dropped, addClasses: false }); @@ -886,13 +956,13 @@ function rcube_tasklist_ui(settings) $('#rootdroppable').show(); } - function draggable_stop(event, ui) + function task_draggable_stop(event, ui) { $(this).parent().removeClass('dragging'); $('#rootdroppable').hide(); } - function droppable_accept(draggable) + function task_droppable_accept(draggable) { if (rcmail.busy) return false; @@ -924,7 +994,7 @@ function rcube_tasklist_ui(settings) return true; } - function draggable_dropped(event, ui) + function task_draggable_dropped(event, ui) { var drop_id = $(this).data('id'), task_id = ui.draggable.data('id'), @@ -1220,6 +1290,9 @@ function rcube_tasklist_ui(settings) if (!me.selected_task.list && list.id) me.selected_task.list = list.id; + if (!me.selected_task.tags.length) + me.selected_task.tags = ''; + if (save_task(me.selected_task, action)) $dialog.dialog('close'); };