@@ -55,6 +55,10 @@ angular.module('ui.select', [])
55
55
* keyIdentifier = undefined
56
56
*/
57
57
self . parse = function ( expression ) {
58
+ if ( ! expression ) {
59
+ throw uiSelectMinErr ( 'repeat' , "Expected 'repeat' expression." ) ;
60
+ }
61
+
58
62
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 * $ / ) ;
59
63
60
64
if ( ! match ) {
@@ -112,6 +116,8 @@ angular.module('ui.select', [])
112
116
ctrl . items = [ ] ;
113
117
ctrl . selected = undefined ;
114
118
ctrl . open = false ;
119
+ ctrl . focus = false ;
120
+ ctrl . focusser = undefined ; //Reference to input element used to handle focus events
115
121
ctrl . disabled = undefined ; // Initialized inside uiSelect directive link function
116
122
ctrl . resetSearchInput = undefined ; // Initialized inside uiSelect directive link function
117
123
ctrl . refreshDelay = undefined ; // Initialized inside uiSelectChoices directive link function
@@ -125,17 +131,22 @@ angular.module('ui.select', [])
125
131
function _resetSearchInput ( ) {
126
132
if ( ctrl . resetSearchInput ) {
127
133
ctrl . search = EMPTY_SEARCH ;
134
+ //reset activeIndex
135
+ if ( ctrl . selected && ctrl . items . length ) {
136
+ ctrl . activeIndex = ctrl . items . indexOf ( ctrl . selected ) ;
137
+ }
128
138
}
129
139
}
130
140
131
141
// When the user clicks on ui-select, displays the dropdown list
132
- ctrl . activate = function ( ) {
142
+ ctrl . activate = function ( initSearchValue ) {
133
143
if ( ! ctrl . disabled ) {
134
144
_resetSearchInput ( ) ;
135
145
ctrl . open = true ;
136
146
137
147
// Give it time to appear before focus
138
148
$timeout ( function ( ) {
149
+ ctrl . search = initSearchValue || ctrl . search ;
139
150
_searchInput [ 0 ] . focus ( ) ;
140
151
} ) ;
141
152
}
@@ -198,6 +209,7 @@ angular.module('ui.select', [])
198
209
if ( ctrl . open ) {
199
210
_resetSearchInput ( ) ;
200
211
ctrl . open = false ;
212
+ ctrl . focusser [ 0 ] . focus ( ) ;
201
213
}
202
214
} ;
203
215
@@ -235,7 +247,7 @@ angular.module('ui.select', [])
235
247
_searchInput . on ( 'keydown' , function ( e ) {
236
248
// Keyboard shortcuts are all about the items,
237
249
// 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 ) {
239
251
var key = e . which ;
240
252
241
253
$scope . $apply ( function ( ) {
@@ -280,8 +292,8 @@ angular.module('ui.select', [])
280
292
} ] )
281
293
282
294
. directive ( 'uiSelect' ,
283
- [ '$document' , 'uiSelectConfig' , 'uiSelectMinErr' ,
284
- function ( $document , uiSelectConfig , uiSelectMinErr ) {
295
+ [ '$document' , 'uiSelectConfig' , 'uiSelectMinErr' , '$compile' ,
296
+ function ( $document , uiSelectConfig , uiSelectMinErr , $compile ) {
285
297
286
298
return {
287
299
restrict : 'EA' ,
@@ -301,6 +313,98 @@ angular.module('ui.select', [])
301
313
var $select = ctrls [ 0 ] ;
302
314
var ngModel = ctrls [ 1 ] ;
303
315
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
+
304
408
attrs . $observe ( 'disabled' , function ( ) {
305
409
// No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string
306
410
$select . disabled = attrs . disabled !== undefined ? attrs . disabled : false ;
@@ -372,8 +476,8 @@ angular.module('ui.select', [])
372
476
} ] )
373
477
374
478
. directive ( 'uiSelectChoices' ,
375
- [ 'uiSelectConfig' , 'RepeatParser' , 'uiSelectMinErr' ,
376
- function ( uiSelectConfig , RepeatParser , uiSelectMinErr ) {
479
+ [ 'uiSelectConfig' , 'RepeatParser' , 'uiSelectMinErr' , '$compile' ,
480
+ function ( uiSelectConfig , RepeatParser , uiSelectMinErr , $compile ) {
377
481
378
482
return {
379
483
restrict : 'EA' ,
@@ -388,17 +492,27 @@ angular.module('ui.select', [])
388
492
389
493
compile : function ( tElement , tAttrs ) {
390
494
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 + ')' ) ;
391
505
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
- }
396
506
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
+ } ) ;
400
515
401
- return function link ( scope , element , attrs , $select ) {
402
516
$select . parseRepeatAttr ( attrs . repeat ) ;
403
517
404
518
scope . $watch ( '$select.search' , function ( ) {
@@ -447,18 +561,18 @@ angular.module('ui.select', [])
447
561
}
448
562
449
563
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 ;
451
565
} ;
452
566
} ) ;
453
567
454
568
angular . 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> ' ) ;
459
573
$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> ' ) ;
462
576
$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> ' ) ;
464
578
} ] ) ;
0 commit comments