Skip to content
This repository has been archived by the owner on Nov 22, 2021. It is now read-only.

adding draggable feature to this project #823

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion scss/tags-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ tags-input {
outline: none;
@include box-shadow($tags-outline-box-shadow);
}

.tag-item-dragtag {
cursor:move;
}
.tag-list {
margin: 0;
padding: 0;
Expand Down
104 changes: 102 additions & 2 deletions src/tags-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
* promise is returned, the tag will not be removed.
* @param {expression=} [onTagRemoved=NA] Expression to evaluate upon removing an existing tag. The removed tag is available as $tag.
* @param {expression=} [onTagClicked=NA] Expression to evaluate upon clicking an existing tag. The clicked tag is available as $tag.
* @param {boolean=} [dragtag=false] Whether or not the labels can be dragged to be ordered differently
* @param {expression=} [onTagDragged=NA] Expression to evaluate upon a dropping a dragged tag. The clicked tag is available as $tag.
*/
export default function TagsInputDirective($timeout, $document, $window, $q, tagsInputConfig, tiUtil, tiConstants) {
'ngInject';
Expand Down Expand Up @@ -115,6 +117,13 @@ export default function TagsInputDirective($timeout, $document, $window, $q, tag
return tag;
});
};

self.reorder = (prevOffset, newOffset) => {
if(prevOffset<0 || newOffset<0 || prevOffset > self.items.length || newOffset > self.items.length || prevOffset==newOffset) return;
self.items.splice(newOffset, 0, self.items.splice(prevOffset, 1)[0]);
self.clearSelection();
events.trigger('tag-dragged');
};

self.select = index => {
if (index < 0) {
Expand Down Expand Up @@ -168,6 +177,7 @@ export default function TagsInputDirective($timeout, $document, $window, $q, tag
onTagRemoving: '&',
onTagRemoved: '&',
onTagClicked: '&',
onTagDragged: '&'
},
replace: false,
transclude: true,
Expand Down Expand Up @@ -201,7 +211,8 @@ export default function TagsInputDirective($timeout, $document, $window, $q, tag
allowLeftoverText: [Boolean, false],
addFromAutocompleteOnly: [Boolean, false],
spellcheck: [Boolean, true],
useStrings: [Boolean, false]
useStrings: [Boolean, false],
dragtag: [Boolean, false]
});

$scope.tagList = new TagList($scope.options, $scope.events,
Expand Down Expand Up @@ -280,10 +291,12 @@ export default function TagsInputDirective($timeout, $document, $window, $q, tag

scope.getTagClass = (tag, index) => {
let selected = tag === tagList.selected;
return [
var ret = [
scope.tagClass({$tag: tag, $index: index, $selected: selected}),
{ selected: selected }
];
if(options.dragtag)ret.push('tag-item-dragtag')
return ret;
};

scope.$watch('tags', value => {
Expand Down Expand Up @@ -351,11 +364,98 @@ export default function TagsInputDirective($timeout, $document, $window, $q, tag
return;
}
focusInput();
},
mousemove(event){
if(options.dragtag && scope.draggedElement){
var zoomFactor = window.innerWidth / window.outerWidth//accounting for browser zoom in/out
scope.draggedElement.elem.style.left=zoomFactor*(event.screenX - scope.draggedElement.x)+'px';
scope.draggedElement.elem.style.top=zoomFactor*(event.screenY - scope.draggedElement.y)+'px';
}
}
},
tag: {
click(tag) {
events.trigger('tag-clicked', { $tag: tag });
},
mousedown(event){
if(options.dragtag){
var target = event.target || event.srcElement || event.currentTarget;
if(target.tagName=='A')return;//if you hit the A, don't do this
while(target.tagName!='LI')target = target.parentElement //go up parents until you find the LI
target.style.position='relative';
scope.draggedElement = {'elem':target,'x':event.screenX,'y':event.screenY}

//console.log('pick up element at '+scope.draggedElement.x+" "+scope.draggedElement.y)

event.preventDefault();
event.stopImmediatePropagation();
}
},
mouseup(event){
if(options.dragtag && scope.draggedElement){
//console.log('put down element at '+event.screenX+" "+event.screenY)

var listHolder = scope.draggedElement.elem.parentElement
var allElems = listHolder.getElementsByTagName("LI")
var prevOffset = -1;
var newOffset = -1;

//find elem's offset
for(var i=0;i<allElems.length;i++){
if(allElems[i].style.position=='relative')
prevOffset = i;
}


//which coordinate do you want to consider for elem? top left? center? mouse position? .... this does the midpoint
var elem_x = scope.draggedElement.elem.offsetLeft + scope.draggedElement.elem.offsetWidth/2
var elem_y = scope.draggedElement.elem.offsetTop + scope.draggedElement.elem.offsetHeight/2

for(var i=0;i<allElems.length;i++){
if(i==prevOffset)continue;//don't consider yourself

var x_lo = allElems[i].offsetLeft
var x_hi = allElems[i].offsetLeft + allElems[i].offsetWidth
var y_lo = allElems[i].offsetTop
var y_hi = allElems[i].offsetTop + allElems[i].offsetHeight

//it's the end of the list OR the next one's upper bound is greater than current low bound
var end_of_line = i==allElems.length-1 || allElems[i+1].offsetTop > y_hi

//option 1: elem's position is inside the bounding box of this considered element
if(x_lo <= elem_x && elem_x <= x_hi && y_lo <= elem_y && elem_y <= y_hi ){
newOffset=i;
break;
}

//option 2: elem's y is within bounding box AND x is greater AND it's end of the line
if( y_lo <= elem_y && elem_y <= y_hi && elem_x > x_hi && end_of_line){
newOffset=i;
break;
}

//option 3: the last line is empty, and you drop it there, so it has to consider the last element, and elem_y is higher than the y val
if(i==allElems.length-1 && elem_y > y_hi){
newOffset=i;
break;
}

//option 4: you drop it to the left of a guy that's on the beginning of the line
if((i==0 || allElems[i-1].offsetTop < y_lo) && y_lo < elem_y && elem_y <y_hi && elem_x < x_lo){
newOffset=i;
break;
}
}

if(prevOffset!=-1 && newOffset!=-1 && newOffset!=prevOffset){
//console.log('moving:'+prevOffset+" to:"+newOffset)
tagList.reorder(prevOffset,newOffset);
}

//undo the "temp" stuff to be ready for the next one ...
scope.draggedElement.elem.style.position='static'
scope.draggedElement = null
}
}
}
};
Expand Down
6 changes: 4 additions & 2 deletions templates/tags-input.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<div class="host" tabindex="-1" ng-click="eventHandlers.host.click()" ti-transclude-append>
<div class="host" tabindex="-1" ng-click="eventHandlers.host.click()" ng-mousemove="eventHandlers.host.mousemove($event)" ti-transclude-append>
<div class="tags" ng-class="{focused: hasFocus}">
<ul class="tag-list">
<li class="tag-item"
ng-repeat="tag in tagList.items track by track(tag)"
ng-class="getTagClass(tag, $index)"
ng-click="eventHandlers.tag.click(tag)">
ng-click="eventHandlers.tag.click(tag)"
ng-mousedown="eventHandlers.tag.mousedown($event)"
ng-mouseup="eventHandlers.tag.mouseup($event)">
<ti-tag-item scope="templateScope" data="::tag"></ti-tag-item>
</li>
</ul>
Expand Down
4 changes: 2 additions & 2 deletions test/test-page.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<script type="text/javascript" src="lib/angular.js"></script>
<script type="text/javascript" src="lib/ng-stats.min.js"></script>
<script type="text/javascript" src="../build/ng-tags-input.min.js"></script>
<link rel="stylesheet" href="../build/ng-tags-input.min.css"/>
<link rel="stylesheet" href="http://mbenford.github.io/ngTagsInput/css/ng-tags-input.min.css"/>

<style>
.stats {
Expand All @@ -23,7 +23,7 @@
</head>

<body ng-controller="Ctrl">
<tags-input ng-model="tags" add-on-blur="false" ng-focus="focus()" ng-blur="blur()" add-on-paste="true" paste-split-pattern="[,;|]" spellcheck="false" display-property="name" template="tag-template">
<tags-input ng-model="tags" add-on-blur="false" ng-focus="focus()" ng-blur="blur()" add-on-paste="true" paste-split-pattern="[,;|]" spellcheck="false" display-property="name" template="tag-template" dragtag="true">
<auto-complete source="loadItems($query)" min-length="1" load-on-down-arrow="true" load-on-focus="true" load-on-empty="true" template="autocomplete-template"></auto-complete>
</tags-input>

Expand Down