diff --git a/README.md b/README.md index dbc97a2..c1eadae 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,23 @@ $scope.shinyThings = function (item) { }; ``` +### btf-eliminate +Makes it so that the item is eliminated if it is not dropped inside of another dragon. +Add the `btf-eliminate` attribute to an element to get the behavior. + +Example: +```html +

These get copied

+
+ {{item.name}} +
+

These get moved or eliminated

+
+ {{item.name}} +
+``` + + ## Example See [`example.html`](http://htmlpreview.github.io/?https://github.com/btford/angular-dragon-drop/blob/master/example.html). diff --git a/dragon-drop.js b/dragon-drop.js index 3222953..283ad69 100644 --- a/dragon-drop.js +++ b/dragon-drop.js @@ -1,13 +1,15 @@ +'use strict'; + /* * angular-dragon-drop v0.3.1 * (c) 2013 Brian Ford http://briantford.com * License: MIT */ -'use strict'; + angular.module('btford.dragon-drop', []). - directive('btfDragon', function ($document, $compile, $rootScope) { + directive('btfDragon', ['$document', '$compile', '$rootScope', function ($document, $compile, $rootScope) { /* ^ ^ |\ \ / /| @@ -15,10 +17,10 @@ angular.module('btford.dragon-drop', []). / /\ \ \ _ \/ _ / / \ / / /\ \ {*}\/{*} / / \ \ | | | \ \( (00) ) / // |\ \ - | | | |\ \(V""V)\ / / | || \| - | | | | \ |^--^| \ / / || || || + | | | |\ \(V""V)\ / / | || \| + | | | | \ |^--^| \ / / || || || / / / | |( WWWW__ \/ /| || || || - | | | | | | \______\ / / || || || + | | | | | | \______\ / / || || || | | | / | | )|______\ ) | / | || || / / / / / /______/ /| \ \ || || / / / / / /\_____/ |/ /__\ \ \ \ \ @@ -32,7 +34,7 @@ angular.module('btford.dragon-drop', []). \ \________\_ _\ ____/ __/ /\_____ __/ / )\_, _____/ / ___/ \uuuu/ ___/___) \______/ - VVV V VVV V + VVV V VVV V */ // this ASCII dragon is really important, do not remove @@ -40,9 +42,24 @@ angular.module('btford.dragon-drop', []). dragKey, dragOrigin, dragDuplicate = false, + dragEliminate = false, + mouseReleased = true, floaty, offsetX, - offsetY; + offsetY, + fixed; + + var isFixed = function(element) { + var parents = element.parent(), i, len = parents.length; + for (i = 0; i < len; i++) { + if (parents[i].hasAttribute('btf-dragon-fixed')) { + return true; + } else if (parents[i].hasAttribute('btf-dragon')) { + return false; + } + } + return false; + }; var drag = function (ev) { var x = ev.clientX - offsetX, @@ -62,14 +79,32 @@ angular.module('btford.dragon-drop', []). } }; - var add = function (collection, item, key) { + var add = function (collection, item, key, position) { if (collection instanceof Array) { - collection.push(item); + var pos; + if (position === 0 || position) { + pos = position; + } else { + pos = collection.length; + } + collection.splice(pos, 0, item); } else { collection[key] = item; } }; + var findContainer = function(elem){ + var children = elem.find('*'); + + for (var i = 0; i < children.length; i++){ + if (children[i].hasAttribute('btf-dragon-container')) { + return angular.element(children[i]); + } + } + + return null; + }; + var documentBody = angular.element($document[0].body); var disableSelect = function () { @@ -94,7 +129,9 @@ angular.module('btford.dragon-drop', []). var killFloaty = function () { if (floaty) { + $rootScope.$broadcast('drag-end'); $document.unbind('mousemove', drag); + floaty.scope().$destroy(); floaty.remove(); floaty = null; } @@ -103,7 +140,8 @@ angular.module('btford.dragon-drop', []). var getElementOffset = function (elt) { var box = elt.getBoundingClientRect(); - var body = $document[0].body; + //var body = $document[0].body; + var body = $document[0].documentElement; var xPosition = box.left + body.scrollLeft; var yPosition = box.top + body.scrollTop; @@ -127,6 +165,8 @@ angular.module('btford.dragon-drop', []). }; $document.bind('mouseup', function (ev) { + mouseReleased = true; + if (!dragValue) { return; } @@ -143,6 +183,43 @@ angular.module('btford.dragon-drop', []). dropArea = dropArea.parent(); } + if (dropArea.attr('btf-dragon-sortable') !== undefined) { + + var min = dropArea[0].getBoundingClientRect().top; + var max = dropArea[0].getBoundingClientRect().bottom; + var positions = []; + var position; + + positions.push(min); + + var i, j, leni, lenj; + for (i = 0, leni = dropArea[0].children.length; i < leni; i++) { + var totalHeight = 0; + var smallestTop = Number.POSITIVE_INFINITY; + for (j = 0, lenj = dropArea[0].children[i].getClientRects().length; j < lenj; j++) { + if (smallestTop > dropArea[0].children[i].getClientRects()[j].top) { + smallestTop = dropArea[0].children[i].getClientRects()[j].top; + } + totalHeight += dropArea[0].children[i].getClientRects()[j].height; + } + if (dropArea[0].children[i].attributes['btf-dragon-position'] !== undefined) { + positions.push(smallestTop + (totalHeight / 2)); + } + + } + + positions.push(max); + + i = 0; + while (i < positions.length) { + if (positions[i] <= ev.clientY) { + position = i; + } + i++; + } + + } + if (dropArea.length > 0) { var expression = dropArea.attr('btf-dragon'); var targetScope = dropArea.scope(); @@ -150,9 +227,9 @@ angular.module('btford.dragon-drop', []). var targetList = targetScope.$eval(match[2]); targetScope.$apply(function () { - add(targetList, dragValue, dragKey); + add(targetList, dragValue, dragKey, position); }); - } else if (!dragDuplicate) { + } else if (!dragDuplicate && !dragEliminate) { // no dropArea here // put item back to origin $rootScope.$apply(function () { @@ -173,8 +250,8 @@ angular.module('btford.dragon-drop', []). var expression = attr.btfDragon; var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*$/); if (!match) { - throw Error("Expected btfDragon in form of '_item_ in _collection_' but got '" + - expression + "'."); + throw new Error('Expected btfDragon in form of "_item_ in _collection_" but got "' + + expression + '"."'); } var lhs = match[1]; var rhs = match[2]; @@ -184,8 +261,18 @@ angular.module('btford.dragon-drop', []). var valueIdentifier = match[3] || match[1]; var keyIdentifier = match[2]; + var duplicate = container.attr('btf-double-dragon') !== undefined; + // pull out the template to re-use. // Improvised ng-transclude. + if (container.attr('btf-dragon-base') !== undefined){ + container = findContainer(container); + + if (!container){ + throw new Error('Expected btf-dragon-base to be used with a companion btf-dragon-conatiner'); + } + } + var template = container.html(); // wrap text nodes @@ -201,22 +288,29 @@ angular.module('btford.dragon-drop', []). var child = template.clone(); child.attr('ng-repeat', expression); + if (container.attr('btf-dragon-sortable') !== undefined) { + child.attr('btf-dragon-position', '{{$index}}'); + } + container.html(''); container.append(child); - var duplicate = container.attr('btf-double-dragon') !== undefined; + var eliminate = container.attr('btf-dragon-eliminate') !== undefined; + return function (scope, elt, attr) { var accepts = scope.$eval(attr.btfDragonAccepts); if (accepts !== undefined && typeof accepts !== 'function') { - throw Error('Expected btfDragonAccepts to be a function.'); + throw new Error('Expected btfDragonAccepts to be a function.'); } var spawnFloaty = function () { + $rootScope.$broadcast('drag-start'); scope.$apply(function () { floaty = template.clone(); + floaty.css('position', 'fixed'); floaty.css('margin', '0px'); @@ -235,10 +329,38 @@ angular.module('btford.dragon-drop', []). }; elt.bind('mousedown', function (ev) { - if (dragValue) { + + //If a person uses middle or right mouse button, don't do anything + if ([1, 2].indexOf(ev.button) > -1) { + return; + } + + var tag = $document[0].elementFromPoint(ev.clientX,ev.clientY).tagName; + if (tag === 'SELECT' || tag === 'INPUT' || tag === 'BUTTON') { + return; + } else { + + mouseReleased = false; + + if (isFixed(angular.element(ev.target))) { + fixed = true; + } else { + fixed = false; + } + + } + + }); + + elt.bind('mousemove', function(ev) { + if(dragValue||mouseReleased){ + return; + } + + if(isFixed(angular.element(ev.target)) || fixed){ return; } - + // find the right parent var originElement = angular.element(ev.target); var originScope = originElement.scope(); @@ -268,14 +390,17 @@ angular.module('btford.dragon-drop', []). }); } dragDuplicate = duplicate; + dragEliminate = eliminate; + offsetX = (ev.pageX - offset.left); offsetY = (ev.pageY - offset.top); spawnFloaty(); drag(ev); + }); }; } }; - }); + }]);