mirror of
https://github.com/TracksApp/tracks.git
synced 2026-03-02 19:10:16 +01:00
This changeset adds the ability to drag and drop actions between contexts and ajaxomagically update the action's context in the backend. I've tested it in Firefox and Safari, but not on other browsers.
When you start dragging an action, the other contexts collapse to provide easier targets for dropping. After the drop, the contexts return to their previously states of collapsed/expanded. git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@275 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
parent
060f1e9a97
commit
9263249e77
6 changed files with 163 additions and 28 deletions
|
|
@ -190,6 +190,23 @@ class TodoController < ApplicationController
|
||||||
@saved = @item.save
|
@saved = @item.save
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_context
|
||||||
|
self.init
|
||||||
|
@item = check_user_return_item
|
||||||
|
context = Context.find(params['context_id']);
|
||||||
|
if @user == context.user
|
||||||
|
@original_item_context_id = @item.context_id
|
||||||
|
@item.context_id = context.id
|
||||||
|
@item.context = context
|
||||||
|
@saved = @item.save
|
||||||
|
render :action => 'update_action'
|
||||||
|
else
|
||||||
|
render :update do |page|
|
||||||
|
page.replace_html "info", content_tag("div", "Error updating the context of the dragged item. Item and context user mis-match: #{@item.user.name} and #{@context.user.name}! - refresh the page to see them.", "class" => "warning")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def deferred_update_action
|
def deferred_update_action
|
||||||
#self.init
|
#self.init
|
||||||
@tickle = check_user_return_item
|
@tickle = check_user_return_item
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
if @saved
|
if @saved
|
||||||
|
item_container_id = "item-#{@item.id}-container"
|
||||||
|
logger.info("@item.context_id = #{@item.context_id}")
|
||||||
|
logger.info("@original_item_context.id = #{@original_item_context_id}")
|
||||||
if @item.context_id == @original_item_context_id
|
if @item.context_id == @original_item_context_id
|
||||||
page.replace_html "item-#{@item.id}-container", :partial => 'todo/item'
|
page.replace_html item_container_id, :partial => 'todo/item'
|
||||||
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
|
page.visual_effect :highlight, item_container_id, :duration => 3
|
||||||
else
|
else
|
||||||
page["item-#{@item.id}-container"].remove
|
page["item-#{@item.id}-container"].remove
|
||||||
|
page.call "todoItems.expandNextActionListingByContext", "c#{@item.context_id}items", true
|
||||||
page.insert_html :bottom, "c#{@item.context_id}items", :partial => 'todo/item'
|
page.insert_html :bottom, "c#{@item.context_id}items", :partial => 'todo/item'
|
||||||
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
|
page.delay(0.5) do
|
||||||
|
page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items"
|
||||||
|
page.call "todoItems.ensureContainerHeight", "c#{@item.context_id}items"
|
||||||
|
page.call "todoItems.makeItemDraggable", item_container_id
|
||||||
|
page.visual_effect :highlight, item_container_id, :duration => 3
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
page.hide "info"
|
page.hide "info"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,11 @@ Ajax.Responders.register({
|
||||||
onComplete: function() {
|
onComplete: function() {
|
||||||
if($('busy') && Ajax.activeRequestCount==0)
|
if($('busy') && Ajax.activeRequestCount==0)
|
||||||
Element.hide('busy');
|
Element.hide('busy');
|
||||||
}
|
},
|
||||||
|
// uncomment the next three lines for easier debugging with FireBug
|
||||||
|
// onException: function(source, exception) {
|
||||||
|
// console.error(exception);
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,42 @@ var todoItems = {
|
||||||
|
|
||||||
addNextActionListingToggles: function()
|
addNextActionListingToggles: function()
|
||||||
{
|
{
|
||||||
this.contextCollapseCookieManager = new CookieManager();
|
$$('.container_toggle').each(function(toggleElem){
|
||||||
var toggleElems = document.getElementsByClassName('container_toggle');
|
|
||||||
toggleElems.each(function(toggleElem){
|
|
||||||
Event.observe(toggleElem, 'click', todoItems.toggleNextActionListing);
|
Event.observe(toggleElem, 'click', todoItems.toggleNextActionListing);
|
||||||
toggleElem.onclick = function() {return false;}; //workaround for Event.stop problem with Safari 2.0.3. See http://particletree.com/notebook/eventstop/
|
toggleElem.onclick = function() {return false;}; //workaround for Event.stop problem with Safari 2.0.3. See http://particletree.com/notebook/eventstop/
|
||||||
|
});
|
||||||
|
todoItems.setNextActionListingTogglesToCookiedState();
|
||||||
|
},
|
||||||
|
setNextActionListingTogglesToCookiedState: function()
|
||||||
|
{
|
||||||
|
contextCollapseCookieManager = new CookieManager();
|
||||||
|
$$('.container_toggle').each(function(toggleElem){
|
||||||
containerElem = todoItems.findNearestParentByClassName(toggleElem, "container");
|
containerElem = todoItems.findNearestParentByClassName(toggleElem, "container");
|
||||||
collapsedCookie = contextCollapseCookieManager.getCookie(todoItems.buildCookieName(containerElem));
|
collapsedCookie = contextCollapseCookieManager.getCookie(todoItems.buildCookieName(containerElem));
|
||||||
if (collapsedCookie)
|
itemsElem = todoItems.findItemsElem(toggleElem);
|
||||||
|
isExpanded = Element.visible(itemsElem);
|
||||||
|
if (collapsedCookie && isExpanded)
|
||||||
{
|
{
|
||||||
itemsElem = todoItems.findItemsElem(toggleElem);
|
|
||||||
todoItems.collapseNextActionListing(toggleElem, itemsElem);
|
todoItems.collapseNextActionListing(toggleElem, itemsElem);
|
||||||
|
}
|
||||||
|
else if (!collapsedCookie && !isExpanded)
|
||||||
|
{
|
||||||
|
todoItems.expandNextActionListing(toggleElem, itemsElem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
collapseAllNextActionListing: function(except)
|
||||||
|
{
|
||||||
|
$$('.container_toggle').each(function(toggleElem){
|
||||||
|
if (toggleElem != except)
|
||||||
|
itemsElem = todoItems.findItemsElem(toggleElem);
|
||||||
|
isExpanded = Element.visible(itemsElem);
|
||||||
|
if (isExpanded)
|
||||||
|
{
|
||||||
|
todoItems.collapseNextActionListing(toggleElem, itemsElem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
ensureVisibleWithEffectAppear: function(elemId)
|
ensureVisibleWithEffectAppear: function(elemId)
|
||||||
{
|
{
|
||||||
|
|
@ -58,31 +80,51 @@ var todoItems = {
|
||||||
contextCollapseCookieManager.clearCookie(todoItems.buildCookieName(containerElem))
|
contextCollapseCookieManager.clearCookie(todoItems.buildCookieName(containerElem))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
findToggleElemForContext : function(contextElem)
|
||||||
expandNextActionListing: function(toggleElem, itemsElem)
|
|
||||||
{
|
{
|
||||||
Effect.BlindDown(itemsElem, { duration: 0.4 });
|
childElems = $A($(contextElem).getElementsByTagName('a'));
|
||||||
|
return childElems.detect(function(childElem) { return childElem.className == 'container_toggle' });
|
||||||
|
},
|
||||||
|
expandNextActionListing: function(toggleElem, itemsElem, skipAnimation)
|
||||||
|
{
|
||||||
|
itemsElem = $(itemsElem)
|
||||||
|
if (skipAnimation == true) {
|
||||||
|
itemsElem.style.display = 'block';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Effect.BlindDown(itemsElem, { duration: 0.4 });
|
||||||
|
}
|
||||||
toggleElem.setAttribute('title', 'Collapse');
|
toggleElem.setAttribute('title', 'Collapse');
|
||||||
imgElem = todoItems.findFirstImgElementWithSrcContaining(toggleElem, 'expand.png');
|
imgElem = todoItems.findToggleImgElem(toggleElem);
|
||||||
imgElem.src = imgElem.src.replace('expand','collapse');
|
imgElem.src = imgElem.src.replace('expand','collapse');
|
||||||
imgElem.setAttribute('title','Collapse');
|
imgElem.setAttribute('title','Collapse');
|
||||||
},
|
},
|
||||||
|
ensureContainerHeight: function(itemsElem)
|
||||||
|
{
|
||||||
|
itemsElem = $(itemsElem);
|
||||||
|
Element.setStyle(itemsElem, {height : ''});
|
||||||
|
Element.setStyle(itemsElem, {overflow : ''});
|
||||||
|
},
|
||||||
|
expandNextActionListingByContext: function(itemsElem, skipAnimation)
|
||||||
|
{
|
||||||
|
contextElem = todoItems.findNearestParentByClassName($(itemsElem), "context");
|
||||||
|
toggleElem = todoItems.findToggleElemForContext(contextElem);
|
||||||
|
todoItems.expandNextActionListing(toggleElem, itemsElem, skipAnimation);
|
||||||
|
},
|
||||||
collapseNextActionListing: function(toggleElem, itemsElem)
|
collapseNextActionListing: function(toggleElem, itemsElem)
|
||||||
{
|
{
|
||||||
Effect.BlindUp(itemsElem, { duration: 0.4});
|
Effect.BlindUp(itemsElem, { duration: 0.4});
|
||||||
toggleElem.setAttribute('title', 'Expand');
|
toggleElem.setAttribute('title', 'Expand');
|
||||||
imgElem = todoItems.findFirstImgElementWithSrcContaining(toggleElem, 'collapse.png');
|
imgElem = todoItems.findToggleImgElem(toggleElem);
|
||||||
imgElem.src = imgElem.src.replace('collapse','expand');
|
imgElem.src = imgElem.src.replace('collapse','expand');
|
||||||
imgElem.setAttribute('title','Expand');
|
imgElem.setAttribute('title','Expand');
|
||||||
},
|
},
|
||||||
|
findToggleImgElem: function(toggleElem)
|
||||||
findFirstImgElementWithSrcContaining: function(searchRootElem, srcString)
|
|
||||||
{
|
{
|
||||||
childImgElems = $A(searchRootElem.getElementsByTagName('img'));
|
childImgElems = $A(toggleElem.getElementsByTagName('img'));
|
||||||
return childImgElems.detect(function(childImgElem) { return childImgElem.src.indexOf(srcString) != -1 });
|
return childImgElems[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
buildCookieName: function(containerElem)
|
buildCookieName: function(containerElem)
|
||||||
{
|
{
|
||||||
tracks_login = contextCollapseCookieManager.getCookie('tracks_login');
|
tracks_login = contextCollapseCookieManager.getCookie('tracks_login');
|
||||||
|
|
@ -110,6 +152,67 @@ var todoItems = {
|
||||||
return document.getElementsByClassName('toggle_target',containerElem)[0];
|
return document.getElementsByClassName('toggle_target',containerElem)[0];
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
addItemDragDrop: function()
|
||||||
|
{
|
||||||
|
$$('.item-container').each(function(containerElem){
|
||||||
|
todoItems.makeItemDraggable(containerElem);
|
||||||
|
});
|
||||||
|
$$('.context').each(function(contextElem){
|
||||||
|
todoItems.makeContextDroppable(contextElem);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
makeItemDraggable: function(itemContainerElem)
|
||||||
|
{
|
||||||
|
new Draggable($(itemContainerElem).id,
|
||||||
|
{
|
||||||
|
handle:'description',
|
||||||
|
starteffect:todoItems.startDraggingItem,
|
||||||
|
endeffect:todoItems.stopDraggingItem,
|
||||||
|
revert:true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
makeContextDroppable: function(contextElem)
|
||||||
|
{
|
||||||
|
Droppables.add($(contextElem).id,
|
||||||
|
{
|
||||||
|
accept:'item-container',
|
||||||
|
hoverclass:'item-container-drop-target',
|
||||||
|
onDrop: todoItems.itemDrop,
|
||||||
|
zindex: 1000
|
||||||
|
});
|
||||||
|
},
|
||||||
|
startDraggingItem:function(draggable)
|
||||||
|
{
|
||||||
|
parentContainer = todoItems.findNearestParentByClassName(draggable, 'container');
|
||||||
|
draggable.parentContainer = parentContainer;
|
||||||
|
toggleElem = document.getElementsByClassName('container_toggle',parentContainer)[0];
|
||||||
|
todoItems.collapseAllNextActionListing(toggleElem);
|
||||||
|
},
|
||||||
|
stopDraggingItem:function(draggable)
|
||||||
|
{
|
||||||
|
todoItems.setNextActionListingTogglesToCookiedState();
|
||||||
|
},
|
||||||
|
|
||||||
|
itemDrop:function(draggableElement, droppableElement) {
|
||||||
|
if (draggableElement.parentContainer == droppableElement) {
|
||||||
|
return; //same destination as original, nothing to be done
|
||||||
|
}
|
||||||
|
itemElementId = draggableElement.id
|
||||||
|
todoId = draggableElement.id.match(/\d+/)[0];
|
||||||
|
contextId = droppableElement.id.match(/\d+/)[0];
|
||||||
|
Draggables.drags.each(function(drag) {
|
||||||
|
if (drag.element == draggableElement) {
|
||||||
|
drag.destroy();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
new Ajax.Request('/todo/update_context', {
|
||||||
|
asynchronous:true,
|
||||||
|
evalScripts:true,
|
||||||
|
parameters:"id=" + todoId + "&context_id=" + contextId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event.observe(window, "load", todoItems.addNextActionListingToggles);
|
Event.observe(window, "load", todoItems.addNextActionListingToggles);
|
||||||
|
Event.observe(window, "load", todoItems.addItemDragDrop);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
function toggleAll(className) {
|
function toggleAll(className) {
|
||||||
var elems = document.getElementsByClassName(className);
|
document.getElementsByClassName(className).each(function(elem){
|
||||||
for (var i = 0; i < elems.length; i++) {
|
if (elem.style.display == 'block')
|
||||||
if (elems[i].style.display == 'block')
|
|
||||||
{
|
{
|
||||||
elems[i].style.display = 'none';
|
elem.style.display = 'none';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
elems[i].style.display = 'block';
|
elem.style.display = 'block';
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +129,7 @@ a.show_notes:hover {background-image: url(../images/notes_on.png); background-re
|
||||||
opacity: .75;
|
opacity: .75;
|
||||||
color: #eee;
|
color: #eee;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index:1100;
|
||||||
}
|
}
|
||||||
|
|
||||||
#date {
|
#date {
|
||||||
|
|
@ -202,7 +203,9 @@ h2 a:hover {
|
||||||
padding:2px;
|
padding:2px;
|
||||||
clear: left;
|
clear: left;
|
||||||
}
|
}
|
||||||
|
.item-container-drop-target {
|
||||||
|
border:2px inset black;
|
||||||
|
}
|
||||||
.container a.icon {
|
.container a.icon {
|
||||||
float: left;
|
float: left;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue