Skip to content

Commit

Permalink
added single/multiple selection, drag and drop operations
Browse files Browse the repository at this point in the history
  • Loading branch information
jaz303 committed Jul 21, 2008
1 parent 58493d3 commit 14e30fb
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 19 deletions.
35 changes: 22 additions & 13 deletions src/index.html
Expand Up @@ -9,24 +9,33 @@
<script type='text/javascript' src='javascripts/jquery-1.2.3.min.js'></script>
<script type='text/javascript' src='javascripts/application.js'></script>
<style type='text/css'>

#tree { }
#tree ul { }
#tree li { }
#tree .item { cursor: pointer; cursor: hand; }
#tree .selected { background-color: green; }
#tree .drop-target-valid { background-color: blue; }
#tree .drop-target-invalid { background-color: red; }
#drag-badge { position: absolute; width: 32px; height: 11px; padding: 11px 0 10px 0; background: url(red-ball.png); color: white; font-weight: bold; font: bold 11px/1 Helvetica, sans-serif; text-align: center; }
</style>
</head>
<body>
<div id='container'>

<ul id='tree'>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3
<ul>
<li>Sub item 1</li>
<li>Sub item 2</li>
<li>Sub item 3</li>
</ul>
</li>
<li>Item 4</li>
</ul>
<div id='tree'>
<ul>
<li><div class='item'>Item 1</div></li>
<li><div class='item'>Item 2</div></li>
<li><div class='item'>Item 3</div>
<ul>
<li><div class='item'>Sub item 1</div></li>
<li><div class='item'>Sub item 2</div></li>
<li><div class='item'>Sub item 3</div></li>
</ul>
</li>
<li><div class='item'>Item 4</div></li>
</ul>
</div>

</div>
</body>
Expand Down
138 changes: 132 additions & 6 deletions src/javascripts/application.js
@@ -1,25 +1,151 @@
function DragWidget(root) {
this.root = root;

$('li', this.root).click(function() {
var self = this;

});
this.root = root;
this.dragState = 'off';
this.dragStart = null;

this.attachBehaviours(root);

$('li', this.root).mousedown(function() {
this.dragBadge = $('<div id="drag-badge"></div>').hide().appendTo(document.body);

$(this.root).mousemove(function(evt) {
if (self.dragState == 'active') self.updateDragBadge(evt);
});

}

DragWidget.prototype = {
hasSelection: function() {
return $this.getSelection().length > 0;
},
getSelection: function() {
return $('li.selected', this.root);
return $('.item.selected', this.root);
},
clearSelection: function() {
$('li', this.root).removeClass('selected');
$('.item', this.root).removeClass('selected');
},
makeSelected: function(ele) {
$(ele).addClass('selected');
},
toggleSelected: function(ele) {
$(ele).toggleClass('selected');
},
attachBehaviours: function(root) {

var self = this;

$('.item', root).each(function() {
this.onselectstart = function() { return false; };
this.unselectable = 'on';
this.style.MozUserSelect = 'none';
});

$('.item', root).hover(function() {
if (self.dragState == 'active') {
if (self.nodeAcceptsDrop(self.nodeFor(this))) {
$(this).addClass('drop-target-valid');
} else {
$(this).addClass('drop-target-invalid');
}
}
}, function() {
$(this).removeClass('drop-target-valid');
$(this).removeClass('drop-target-invalid');
});

$('.item', this.root).mousedown(function(evt) {
if (!evt.metaKey) self.clearSelection();
self.toggleSelected(this);
self.dragState = 'waiting';
self.dragStart = [evt.pageX, evt.pageY];
});

$('.item', this.root).mousemove(function(evt) {
if (self.dragState == 'waiting') {
var dx = Math.abs(evt.pageX - self.dragStart[0]);
var dy = Math.abs(evt.pageY - self.dragStart[1]);
if (dx > 2 || dy > 2) {
self.makeSelected(this);
self.dragState = 'active';
}
}
});

$('.item', this.root).mouseup(function(evt) {
if (self.dragState == 'active') {
if (self.nodeAcceptsDrop(self.nodeFor(this))) {
var target = $('+ ul', this);
if (!target.length) target = $('<ul></ul>').insertAfter(this);
$.each(self.getSelection(), function(ele) {
$(this).parents('li:eq(0)').appendTo(target);
});
self.clearSelection();
}
self.dragBadge.hide();
}
self.dragState = 'off';
});

},

updateDragBadge: function(evt) {
this.dragBadge.text(this.getSelection().length)
.show()
.css({left: evt.pageX + 10 + "px",
top: evt.pageY + 10 + "px"});
},

//
// Node functions

getSelectedNodes: function() {
return this.nodesFor(this.getSelection());
},

nodesFor: function(nodes) {
var self = this;
return $.map(nodes, function(n) { return self.nodeFor(n); });
},

//
// User-overridable node functions

// takes a an inner tree elment (i.e. ".item") and converts it to
// your app's native node representation. the default behaviour is
// to return the parent <li> element.
nodeFor: function(ele) {
return ele.parentNode;
},

// returns a node's parent.
nodeParent: function(node) {
return node.parentNode;
},

// returns true iff a node is capable of containing children, false otherwise.
nodeIsContainer: function(node) {
return true;
},

// returns true iff two nodes are equal.
nodeEquals: function(left, right) {
return left == right;
},

// returns true iff a node is a valid drop target for the current selection.
nodeAcceptsDrop: function(targetNode) {
if (!this.nodeIsContainer(targetNode)) return false;
var selectedNodes = this.getSelectedNodes();
for (var i = 0; i < selectedNodes.length; i++) {
var tmp = targetNode;
while (tmp) {
if (this.nodeEquals(tmp, selectedNodes[i])) return false;
tmp = this.nodeParent(tmp);
}
}
return true;
}
}

Expand Down
Binary file added src/red-ball.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 14e30fb

Please sign in to comment.