@@ -55,6 +55,10 @@ angular.module('ui.select', [])
5555 * keyIdentifier = undefined
5656 */
5757 self . parse = function ( expression ) {
58+ if ( ! expression ) {
59+ throw uiSelectMinErr ( 'repeat' , "Expected 'repeat' expression." ) ;
60+ }
61+
5862 var match = expression . match ( / ^ \s * ( [ \s \S ] + ?) \s + i n \s + ( [ \s \S ] + ?) (?: \s + t r a c k \s + b y \s + ( [ \s \S ] + ?) ) ? \s * $ / ) ;
5963
6064 if ( ! match ) {
@@ -112,6 +116,8 @@ angular.module('ui.select', [])
112116 ctrl . items = [ ] ;
113117 ctrl . selected = undefined ;
114118 ctrl . open = false ;
119+ ctrl . focus = false ;
120+ ctrl . focusser = undefined ; //Reference to input element used to handle focus events
115121 ctrl . disabled = undefined ; // Initialized inside uiSelect directive link function
116122 ctrl . resetSearchInput = undefined ; // Initialized inside uiSelect directive link function
117123 ctrl . refreshDelay = undefined ; // Initialized inside uiSelectChoices directive link function
@@ -125,17 +131,22 @@ angular.module('ui.select', [])
125131 function _resetSearchInput ( ) {
126132 if ( ctrl . resetSearchInput ) {
127133 ctrl . search = EMPTY_SEARCH ;
134+ //reset activeIndex
135+ if ( ctrl . selected && ctrl . items . length ) {
136+ ctrl . activeIndex = ctrl . items . indexOf ( ctrl . selected ) ;
137+ }
128138 }
129139 }
130140
131141 // When the user clicks on ui-select, displays the dropdown list
132- ctrl . activate = function ( ) {
142+ ctrl . activate = function ( initSearchValue ) {
133143 if ( ! ctrl . disabled ) {
134144 _resetSearchInput ( ) ;
135145 ctrl . open = true ;
136146
137147 // Give it time to appear before focus
138148 $timeout ( function ( ) {
149+ ctrl . search = initSearchValue || ctrl . search ;
139150 _searchInput [ 0 ] . focus ( ) ;
140151 } ) ;
141152 }
@@ -198,6 +209,7 @@ angular.module('ui.select', [])
198209 if ( ctrl . open ) {
199210 _resetSearchInput ( ) ;
200211 ctrl . open = false ;
212+ ctrl . focusser [ 0 ] . focus ( ) ;
201213 }
202214 } ;
203215
@@ -235,7 +247,7 @@ angular.module('ui.select', [])
235247 _searchInput . on ( 'keydown' , function ( e ) {
236248 // Keyboard shortcuts are all about the items,
237249 // does not make sense (and will crash) if ctrl.items is empty
238- if ( ctrl . items . length > 0 ) {
250+ if ( ctrl . items && ctrl . items . length >= 0 ) {
239251 var key = e . which ;
240252
241253 $scope . $apply ( function ( ) {
@@ -280,8 +292,8 @@ angular.module('ui.select', [])
280292} ] )
281293
282294. directive ( 'uiSelect' ,
283- [ '$document' , 'uiSelectConfig' , 'uiSelectMinErr' ,
284- function ( $document , uiSelectConfig , uiSelectMinErr ) {
295+ [ '$document' , 'uiSelectConfig' , 'uiSelectMinErr' , '$compile' ,
296+ function ( $document , uiSelectConfig , uiSelectMinErr , $compile ) {
285297
286298 return {
287299 restrict : 'EA' ,
@@ -301,6 +313,98 @@ angular.module('ui.select', [])
301313 var $select = ctrls [ 0 ] ;
302314 var ngModel = ctrls [ 1 ] ;
303315
316+ //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954
317+ var focusser = angular . element ( "<input ng-disabled='$select.disabled' class='ui-select-focusser ui-select-offscreen' type='text' aria-haspopup='true' role='button' />" ) ;
318+ $compile ( focusser ) ( scope ) ;
319+ $select . focusser = focusser ;
320+
321+ element . append ( focusser ) ;
322+ focusser . bind ( "focus" , function ( ) {
323+ scope . $evalAsync ( function ( ) {
324+ $select . focus = true ;
325+ } ) ;
326+ } ) ;
327+ focusser . bind ( "blur" , function ( ) {
328+ scope . $evalAsync ( function ( ) {
329+ $select . focus = false ;
330+ } ) ;
331+ } ) ;
332+ focusser . bind ( "keydown" , function ( e ) {
333+
334+ if ( e . which === KEY . TAB || KEY . isControl ( e ) || KEY . isFunctionKey ( e ) || e . which === KEY . ESC ) {
335+ return ;
336+ }
337+
338+ if ( e . which == KEY . DOWN || e . which == KEY . UP || e . which == KEY . ENTER || e . which == KEY . SPACE ) {
339+ e . preventDefault ( ) ;
340+ e . stopPropagation ( ) ;
341+ $select . activate ( ) ;
342+ }
343+
344+ scope . $digest ( ) ;
345+ } ) ;
346+
347+ focusser . bind ( "keyup input" , function ( e ) {
348+
349+ if ( e . which === KEY . TAB || KEY . isControl ( e ) || KEY . isFunctionKey ( e ) || e . which === KEY . ESC || e . which == KEY . ENTER ) {
350+ return ;
351+ }
352+
353+ $select . activate ( focusser . val ( ) ) ; //User pressed some regualar key, so we pass it to the search input
354+ focusser . val ( '' ) ;
355+ scope . $digest ( ) ;
356+
357+ } ) ;
358+
359+ //TODO Refactor to reuse the KEY object from uiSelectCtrl
360+ var KEY = {
361+ TAB : 9 ,
362+ ENTER : 13 ,
363+ ESC : 27 ,
364+ SPACE : 32 ,
365+ LEFT : 37 ,
366+ UP : 38 ,
367+ RIGHT : 39 ,
368+ DOWN : 40 ,
369+ SHIFT : 16 ,
370+ CTRL : 17 ,
371+ ALT : 18 ,
372+ PAGE_UP : 33 ,
373+ PAGE_DOWN : 34 ,
374+ HOME : 36 ,
375+ END : 35 ,
376+ BACKSPACE : 8 ,
377+ DELETE : 46 ,
378+ isArrow : function ( k ) {
379+ k = k . which ? k . which : k ;
380+ switch ( k ) {
381+ case KEY . LEFT :
382+ case KEY . RIGHT :
383+ case KEY . UP :
384+ case KEY . DOWN :
385+ return true ;
386+ }
387+ return false ;
388+ } ,
389+ isControl : function ( e ) {
390+ var k = e . which ;
391+ switch ( k ) {
392+ case KEY . SHIFT :
393+ case KEY . CTRL :
394+ case KEY . ALT :
395+ return true ;
396+ }
397+
398+ if ( e . metaKey ) return true ;
399+
400+ return false ;
401+ } ,
402+ isFunctionKey : function ( k ) {
403+ k = k . which ? k . which : k ;
404+ return k >= 112 && k <= 123 ;
405+ }
406+ } ;
407+
304408 attrs . $observe ( 'disabled' , function ( ) {
305409 // No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string
306410 $select . disabled = attrs . disabled !== undefined ? attrs . disabled : false ;
@@ -372,8 +476,8 @@ angular.module('ui.select', [])
372476} ] )
373477
374478. directive ( 'uiSelectChoices' ,
375- [ 'uiSelectConfig' , 'RepeatParser' , 'uiSelectMinErr' ,
376- function ( uiSelectConfig , RepeatParser , uiSelectMinErr ) {
479+ [ 'uiSelectConfig' , 'RepeatParser' , 'uiSelectMinErr' , '$compile' ,
480+ function ( uiSelectConfig , RepeatParser , uiSelectMinErr , $compile ) {
377481
378482 return {
379483 restrict : 'EA' ,
@@ -388,17 +492,27 @@ angular.module('ui.select', [])
388492
389493 compile : function ( tElement , tAttrs ) {
390494 var repeat = RepeatParser . parse ( tAttrs . repeat ) ;
495+ return function link ( scope , element , attrs , $select , transcludeFn ) {
496+
497+ var rows = element . querySelectorAll ( '.ui-select-choices-row' ) ;
498+ if ( rows . length !== 1 ) {
499+ throw uiSelectMinErr ( 'rows' , "Expected 1 .ui-select-choices-row but got '{0}'." , rows . length ) ;
500+ }
501+
502+ rows . attr ( 'ng-repeat' , RepeatParser . getNgRepeatExpression ( repeat . lhs , '$select.items' , repeat . trackByExp ) )
503+ . attr ( 'ng-mouseenter' , '$select.activeIndex = $index' )
504+ . attr ( 'ng-click' , '$select.select(' + repeat . lhs + ')' ) ;
391505
392- var rows = tElement . querySelectorAll ( '.ui-select-choices-row' ) ;
393- if ( rows . length !== 1 ) {
394- throw uiSelectMinErr ( 'rows' , "Expected 1 .ui-select-choices-row but got '{0}'." , rows . length ) ;
395- }
396506
397- rows . attr ( 'ng-repeat' , RepeatParser . getNgRepeatExpression ( repeat . lhs , '$select.items' , repeat . trackByExp ) )
398- . attr ( 'ng-mouseenter' , '$select.activeIndex = $index' )
399- . attr ( 'ng-click' , '$select.select(' + repeat . lhs + ')' ) ;
507+ transcludeFn ( function ( clone ) {
508+ var rowsInner = element . querySelectorAll ( '.ui-select-choices-row-inner' ) ;
509+ if ( rowsInner . length !== 1 )
510+ throw uiSelectMinErr ( 'rows' , "Expected 1 .ui-select-choices-row-inner but got '{0}'." , rowsInner . length ) ;
511+
512+ rowsInner . append ( clone ) ;
513+ $compile ( element ) ( scope ) ;
514+ } ) ;
400515
401- return function link ( scope , element , attrs , $select ) {
402516 $select . parseRepeatAttr ( attrs . repeat ) ;
403517
404518 scope . $watch ( '$select.search' , function ( ) {
@@ -447,18 +561,18 @@ angular.module('ui.select', [])
447561 }
448562
449563 return function ( matchItem , query ) {
450- return query ? matchItem . replace ( new RegExp ( escapeRegexp ( query ) , 'gi' ) , '<span class="ui-select-highlight">$&</span>' ) : matchItem ;
564+ return query && matchItem ? matchItem . replace ( new RegExp ( escapeRegexp ( query ) , 'gi' ) , '<span class="ui-select-highlight">$&</span>' ) : matchItem ;
451565 } ;
452566} ) ;
453567
454568angular . module ( 'ui.select' ) . run ( [ '$templateCache' , function ( $templateCache ) {
455- $templateCache . put ( 'bootstrap/choices.tpl.html' , '<ul class="ui-select-choices ui-select-choices-content dropdown-menu" role="menu" aria-labelledby="dLabel" ng-show="$select.items.length> 0"> <li class="ui-select-choices-row" ng-class="{active: $select.activeIndex===$index}"> <a href="javascript:void(0)" ng-transclude ></a> </li> </ul> ' ) ;
456- $templateCache . put ( 'bootstrap/match.tpl.html' , '<button type="button" class="btn btn-default form-control ui-select-match" ng-hide="$select.open" ng-disabled="$select.disabled" ng-click="$select.activate()"> <span ng-hide="$select.selected !==undefined" class="text-muted">{{$select.placeholder}}</span> <span ng-show="$select.selected !==undefined" ng-transclude></span> <span class="caret"></span> </button> ' ) ;
457- $templateCache . put ( 'bootstrap/select.tpl.html' , '<div class="ui-select-bootstrap dropdown" ng-class="{open: $select.open}"> <div class="ui-select-match"></div> <input type="text" autocomplete="off" tabindex="" class="form-control ui-select-search" placeholder="{{$select.placeholder}}" ng-model="$select.search" ng-show="$select.open"> <div class="ui-select-choices"></div> </div> ' ) ;
458- $templateCache . put ( 'select2/choices.tpl.html' , '<ul class="ui-select-choices ui-select-choices-content select2-results"> <li class="ui-select-choices-row" ng-class="{\'select2-highlighted\': $select.activeIndex===$index}"> <div class="select2-result-label" ng-transclude ></div> </li> </ul> ' ) ;
569+ $templateCache . put ( 'bootstrap/choices.tpl.html' , '<ul class="ui-select-choices ui-select-choices-content dropdown-menu" role="menu" aria-labelledby="dLabel" ng-show="$select.items.length> 0"> <li class="ui-select-choices-row" ng-class="{active: $select.activeIndex===$index}"> <a class="ui-select-choices-row-inner" href="javascript:void(0)"></a> </li> </ul> ' ) ;
570+ $templateCache . put ( 'bootstrap/match.tpl.html' , '<button type="button" class="btn btn-default form-control ui-select-match" tabindex="-1" ng-hide="$select.open" ng-disabled="$select.disabled" ng-class="{\'btn-default-focus\':$select.focus}"; ng-click="$select.activate()"> <span ng-hide="$select.selected !==undefined" class="text-muted">{{$select.placeholder}}</span> <span ng-show="$select.selected !==undefined" ng-transclude></span> <span class="caret"></span> </button> ' ) ;
571+ $templateCache . put ( 'bootstrap/select.tpl.html' , '<div class="ui-select-bootstrap dropdown" ng-class="{open: $select.open}"> <div class="ui-select-match"></div> <input type="text" autocomplete="off" tabindex="-1 " class="form-control ui-select-search" placeholder="{{$select.placeholder}}" ng-model="$select.search" ng-show="$select.open"> <div class="ui-select-choices"></div> </div> ' ) ;
572+ $templateCache . put ( 'select2/choices.tpl.html' , '<ul class="ui-select-choices ui-select-choices-content select2-results"> <li class="ui-select-choices-row" ng-class="{\'select2-highlighted\': $select.activeIndex===$index}"> <div class="select2-result-label ui-select-choices-row-inner" ></div> </li> </ul> ' ) ;
459573 $templateCache . put ( 'select2/match.tpl.html' , '<a class="select2-choice ui-select-match" ng-class="{\'select2-default\': $select.selected===undefined}" ng-click="$select.activate()"> <span ng-hide="$select.selected !==undefined" class="select2-chosen">{{$select.placeholder}}</span> <span ng-show="$select.selected !==undefined" class="select2-chosen" ng-transclude></span> <span class="select2-arrow"><b></b></span> </a> ' ) ;
460- $templateCache . put ( 'select2/select.tpl.html' , '<div class="select2 select2-container" ng-class="{\'select2-container-active select2-dropdown-open\': $select.open, \'select2-container-disabled\': $select.disabled}"> <div class="ui-select-match"></div> <div class="select2-drop select2-with-searchbox select2-drop-active" ng-class="{\'select2-display-none\': !$select.open}"> <div class="select2-search"> <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="ui-select-search select2-input" ng-model="$select.search"> </div> <div class="ui-select-choices"></div> </div> </div> ' ) ;
461- $templateCache . put ( 'selectize/choices.tpl.html' , '<div ng-show="$select.open" class="ui-select-choices selectize-dropdown single"> <div class="ui-select-choices-content selectize-dropdown-content"> <div class="ui-select-choices-row" ng-class="{\'active\': $select.activeIndex===$index}"> <div class="option" data-selectable ng-transclude ></div> </div> </div> </div> ' ) ;
574+ $templateCache . put ( 'select2/select.tpl.html' , '<div class="select2 select2-container" ng-class="{\'select2-container-active select2-dropdown-open\': $select.open, \'select2-container-disabled\': $select.disabled, \'select2-container-active\': $select.focus }"> <div class="ui-select-match"></div> <div class="select2-drop select2-with-searchbox select2-drop-active" ng-class="{\'select2-display-none\': !$select.open}"> <div class="select2-search"> <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="ui-select-search select2-input" ng-model="$select.search"> </div> <div class="ui-select-choices"></div> </div> </div> ' ) ;
575+ $templateCache . put ( 'selectize/choices.tpl.html' , '<div ng-show="$select.open" class="ui-select-choices selectize-dropdown single"> <div class="ui-select-choices-content selectize-dropdown-content"> <div class="ui-select-choices-row" ng-class="{\'active\': $select.activeIndex===$index}"> <div class="option ui-select-choices-row-inner " data-selectable></div> </div> </div> </div> ' ) ;
462576 $templateCache . put ( 'selectize/match.tpl.html' , '<div ng-hide="$select.open || $select.selected===undefined" class="ui-select-match" ng-transclude></div> ' ) ;
463- $templateCache . put ( 'selectize/select.tpl.html' , '<div class="selectize-control single"> <div class="selectize-input" ng-class="{\'focus\': $select.open, \'disabled\': $select.disabled}" ng-click="$select.activate()"> <div class="ui-select-match"></div> <input type="text" autocomplete="off" tabindex="" class="ui-select-search" placeholder="{{$select.placeholder}}" ng-model="$select.search" ng-hide="$select.selected && !$select.open" ng-disabled="$select.disabled"> </div> <div class="ui-select-choices"></div> </div> ' ) ;
577+ $templateCache . put ( 'selectize/select.tpl.html' , '<div class="selectize-control single"> <div class="selectize-input" ng-class="{\'focus\': $select.open, \'disabled\': $select.disabled, \'selectize-focus\' : $select.focus }" ng-click="$select.activate()"> <div class="ui-select-match"></div> <input type="text" autocomplete="off" tabindex="-1 " class="ui-select-search" placeholder="{{$select.placeholder}}" ng-model="$select.search" ng-hide="$select.selected && !$select.open" ng-disabled="$select.disabled"> </div> <div class="ui-select-choices"></div> </div> ' ) ;
464578} ] ) ;
0 commit comments