Skip to content
This repository was archived by the owner on Oct 2, 2019. It is now read-only.

Commit e6a04b4

Browse files
author
Brian Feister
committed
Finish up demos, make tests pass, introduce distinction between tagging with labels in drop down and tagging without labels
1 parent eeea710 commit e6a04b4

File tree

8 files changed

+409
-113
lines changed

8 files changed

+409
-113
lines changed

dist/select.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*!
22
* ui-select
33
* http://github.com/angular-ui/ui-select
4-
* Version: 0.8.3 - 2014-10-20T16:06:30.609Z
4+
* Version: 0.8.3 - 2014-10-21T21:55:48.791Z
55
* License: MIT
66
*/
77

dist/select.js

Lines changed: 181 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*!
22
* ui-select
33
* http://github.com/angular-ui/ui-select
4-
* Version: 0.8.3 - 2014-10-20T16:06:30.607Z
4+
* Version: 0.8.3 - 2014-10-21T21:55:48.787Z
55
* License: MIT
66
*/
77

@@ -149,8 +149,8 @@
149149
* put as much logic in the controller (instead of the link functions) as possible so it can be easily tested.
150150
*/
151151
.controller('uiSelectCtrl',
152-
['$scope', '$element', '$timeout', 'RepeatParser', 'uiSelectMinErr',
153-
function($scope, $element, $timeout, RepeatParser, uiSelectMinErr) {
152+
['$scope', '$element', '$timeout', '$filter', 'RepeatParser', 'uiSelectMinErr',
153+
function($scope, $element, $timeout, $filter, RepeatParser, uiSelectMinErr) {
154154

155155
var ctrl = this;
156156

@@ -171,6 +171,7 @@
171171
ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function
172172
ctrl.multiple = false; // Initialized inside uiSelect directive link function
173173
ctrl.disableChoiceExpression = undefined; // Initialized inside uiSelect directive link function
174+
ctrl.$filter = $filter;
174175

175176
ctrl.isEmpty = function() {
176177
return angular.isUndefined(ctrl.selected) || ctrl.selected === null || ctrl.selected === '';
@@ -280,8 +281,10 @@
280281
if (!selectedItems.length) {
281282
setItemsFn(data);
282283
}else{
283-
var filteredItems = data.filter(function(i) {return selectedItems.indexOf(i) < 0;});
284-
setItemsFn(filteredItems);
284+
if ( data !== undefined ) {
285+
var filteredItems = data.filter(function(i) {return selectedItems.indexOf(i) < 0;});
286+
setItemsFn(filteredItems);
287+
}
285288
}
286289
ctrl.sizeSearchInput();
287290
});
@@ -317,12 +320,6 @@
317320

318321
ctrl.isActive = function(itemScope) {
319322
var itemIndex = ctrl.items.indexOf(itemScope[ctrl.itemProperty]);
320-
if ( ctrl.open && ( ctrl.items.indexOf(itemScope[ctrl.itemProperty]) === ctrl.activeIndex ) ) {
321-
var item = ctrl.items[itemIndex];
322-
if ( typeof item === 'undefined' ) {
323-
return ctrl.open && false;
324-
}
325-
}
326323
return ctrl.open && itemIndex === ctrl.activeIndex;
327324
};
328325

@@ -343,13 +340,43 @@
343340
return isDisabled;
344341
};
345342

346-
// When the user clicks on an item inside the dropdown
343+
// When the user selects an item with ENTER or clicks the dropdown
347344
ctrl.select = function(item, skipFocusser) {
348345

346+
if ( ! ctrl.items && ! ctrl.search ) return;
347+
349348
if (!item || !item._uiSelectChoiceDisabled) {
350-
if(ctrl.tagging.isActivated && !item && ctrl.search.length > 0) {
351-
// create new item on the fly
352-
item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search;
349+
if(ctrl.tagging.isActivated) {
350+
// if taggingLabel is disabled, we pull from ctrl.search val
351+
if ( ctrl.taggingLabel === false ) {
352+
if ( ctrl.activeIndex < 0 ) {
353+
item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search;
354+
if ( ! angular.equals( ctrl.items[0], item ) ) {
355+
item = item;
356+
} else {
357+
return;
358+
}
359+
} else {
360+
// keyboard nav happened first, user selected from dropdown
361+
item = ctrl.items[ctrl.activeIndex];
362+
}
363+
} else {
364+
// tagging always operates at index zero, taggingLabel === false pushes
365+
// the ctrl.search value without having it injected
366+
if ( ctrl.activeIndex === 0 ) {
367+
// ctrl.tagging pushes items to ctrl.items, so we only have empty val
368+
// for `item` if it is a detected duplicate
369+
if ( item === undefined ) return;
370+
// create new item on the fly
371+
if ( ctrl.taggingLabel && ctrl.taggingLabel.length > 0 ) {}
372+
item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : item.replace(ctrl.taggingLabel,'');
373+
}
374+
}
375+
// search ctrl.selected for dupes potentially caused by tagging and return early if found
376+
if ( ctrl.selected && ctrl.selected.filter( function (selection) { return angular.equals(selection, item); }).length > 0 ) {
377+
ctrl.close(skipFocusser);
378+
return;
379+
}
353380
}
354381

355382
var locals = {};
@@ -549,8 +576,11 @@
549576
if ( ctrl.taggingTokens.isActivated ) {
550577
for (var i = 0; i < ctrl.taggingTokens.tokens.length; i++) {
551578
if ( ctrl.taggingTokens.tokens[i] === KEY.MAP[e.keyCode] ) {
552-
ctrl.select(null, true);
553-
_searchInput.triggerHandler('tagged');
579+
// make sure there is a new value to push via tagging
580+
if ( ctrl.search.length > 0 ) {
581+
ctrl.select(null, true);
582+
_searchInput.triggerHandler('tagged');
583+
}
554584
}
555585
}
556586
}
@@ -570,6 +600,104 @@
570600

571601
});
572602

603+
_searchInput.on('keyup', function(e) {
604+
// Push a "create new" item into array if there is a search string
605+
if ( ctrl.tagging.isActivated && ctrl.search.length > 0 ) {
606+
607+
// return early with these keys
608+
if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || KEY.isVerticalMovement(e.which) ) {
609+
return;
610+
}
611+
// always reset the activeIndex to the first item when tagging
612+
ctrl.activeIndex = ctrl.taggingLabel === false ? -1 : 0;
613+
// taggingLabel === false bypasses all of this
614+
if (ctrl.taggingLabel === false) return;
615+
616+
var items = angular.copy( ctrl.items );
617+
var stashArr = angular.copy( ctrl.items );
618+
var newItem;
619+
var item;
620+
var hasTag = false;
621+
var dupeIndex = -1;
622+
var tagItems;
623+
var tagItem;
624+
625+
// case for object tagging via transform `ctrl.tagging.fct` function
626+
if ( ctrl.tagging.fct !== undefined) {
627+
tagItems = ctrl.$filter('filter')(items,{'isTag': true});
628+
if ( tagItems.length > 0 ) {
629+
tagItem = tagItems[0];
630+
}
631+
// remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous
632+
if ( items.length > 0 && tagItem ) {
633+
hasTag = true;
634+
items = items.slice(1,items.length);
635+
stashArr = stashArr.slice(1,stashArr.length);
636+
}
637+
newItem = ctrl.tagging.fct(ctrl.search);
638+
newItem.isTag = true;
639+
// verify the the tag doesn't match the value of an existing item
640+
if ( stashArr.filter( function (origItem) { return angular.equals( origItem, ctrl.tagging.fct(ctrl.search) ); } ).length > 0 ) {
641+
return;
642+
}
643+
// handle newItem string and stripping dupes in tagging string context
644+
} else {
645+
// find any tagging items already in the ctrl.items array and store them
646+
tagItems = ctrl.$filter('filter')(items,function (item) {
647+
return item.match(ctrl.taggingLabel);
648+
});
649+
if ( tagItems.length > 0 ) {
650+
tagItem = tagItems[0];
651+
}
652+
item = items[0];
653+
// remove existing tag item if found (should only ever be one tag item)
654+
if ( item !== undefined && items.length > 0 && tagItem ) {
655+
hasTag = true;
656+
items = items.slice(1,items.length);
657+
stashArr = stashArr.slice(1,stashArr.length);
658+
}
659+
newItem = ctrl.search+' '+ctrl.taggingLabel;
660+
if ( _findApproxDupe(ctrl.selected, ctrl.search) > -1 ) {
661+
return;
662+
}
663+
// verify the the tag doesn't match the value of an existing item from
664+
// the searched data set
665+
if ( stashArr.filter( function (origItem) { return origItem.toUpperCase() === ctrl.search.toUpperCase(); }).length > 0 ) {
666+
// if there is a tag from prev iteration, strip it / queue the change
667+
// and return early
668+
if ( hasTag ) {
669+
items = stashArr;
670+
$scope.$evalAsync( function () {
671+
ctrl.activeIndex = 0;
672+
ctrl.items = items;
673+
});
674+
}
675+
return;
676+
}
677+
if ( ctrl.selected.filter( function (selection) { return selection.toUpperCase() === ctrl.search.toUpperCase(); } ).length > 0 ) {
678+
// if there is a tag from prev iteration, strip it
679+
if ( hasTag ) {
680+
ctrl.items = stashArr.slice(1,stashArr.length);
681+
}
682+
return;
683+
}
684+
}
685+
if ( hasTag ) dupeIndex = _findApproxDupe(ctrl.selected, newItem);
686+
// dupe found, shave the first item
687+
if ( dupeIndex > -1 ) {
688+
items = items.slice(dupeIndex+1,items.length-1);
689+
} else {
690+
items = [];
691+
items.push(newItem);
692+
items = items.concat(stashArr);
693+
}
694+
$scope.$evalAsync( function () {
695+
ctrl.activeIndex = 0;
696+
ctrl.items = items;
697+
});
698+
}
699+
});
700+
573701
_searchInput.on('tagged', function() {
574702
$timeout(function() {
575703
_resetSearchInput();
@@ -582,6 +710,28 @@
582710
});
583711
});
584712

713+
function _findApproxDupe(haystack, needle) {
714+
var tempArr = angular.copy(haystack);
715+
var dupeIndex = -1;
716+
for (var i = 0; i <tempArr.length; i++) {
717+
// handle the simple string version of tagging
718+
if ( ctrl.tagging.fct === undefined ) {
719+
// search the array for the match
720+
if ( tempArr[i]+' '+ctrl.taggingLabel === needle ) {
721+
dupeIndex = i;
722+
}
723+
// handle the object tagging implementation
724+
} else {
725+
var mockObj = tempArr[i];
726+
mockObj.isTag = true;
727+
if ( angular.equals(mockObj, needle) ) {
728+
dupeIndex = i;
729+
}
730+
}
731+
}
732+
return dupeIndex;
733+
}
734+
585735
function _getCaretPosition(el) {
586736
if(angular.isNumber(el.selectionStart)) return el.selectionStart;
587737
// selectionStart is not supported in IE8 and we don't want hacky workarounds so we compromise
@@ -611,7 +761,7 @@
611761
}
612762

613763
$scope.$on('$destroy', function() {
614-
_searchInput.off('keydown blur');
764+
_searchInput.off('keyup keydown tagged blur');
615765
});
616766
}])
617767

@@ -813,6 +963,19 @@
813963
}
814964
});
815965

966+
attrs.$observe('taggingLabel', function() {
967+
if(attrs.tagging !== undefined && attrs.taggingLabel !== undefined)
968+
{
969+
// check eval for FALSE, in this case, we disable the labels
970+
// associated with tagging
971+
if ( attrs.taggingLabel === 'false' ) {
972+
$select.taggingLabel = false;
973+
} else {
974+
$select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)';
975+
}
976+
}
977+
});
978+
816979
attrs.$observe('taggingTokens', function() {
817980
if(attrs.tagging !== undefined && attrs.taggingTokens !== undefined)
818981
{

dist/select.min.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/select.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)