1
- angular . module ( 'ui.select' , [ 'ui.keypress' ] )
1
+ angular . module ( 'ui.select' , [ ] )
2
2
3
3
. constant ( 'uiSelectConfig' , {
4
4
defaultTheme : 'select2' ,
@@ -13,75 +13,86 @@ angular.module('ui.select', ['ui.keypress'])
13
13
return '../src/' + theme + '/select.tpl.html' ;
14
14
} ,
15
15
replace : true ,
16
- require : ' ngModel',
16
+ require : [ 'uiSelect' , ' ngModel'] ,
17
17
transclude : true ,
18
18
scope : true ,
19
- compile : function ( tElement , tAttrs , transcludeFn ) {
20
- return function ( $scope , $elm , $attrs , ngModel ) {
21
- transcludeFn ( $scope , function ( clone ) {
22
- $scope . open = false ;
19
+ controller : [ '$scope' , '$element' , '$attrs' ,
20
+ function uiSelectCtrl ( $scope , $element , $attrs ) {
21
+
22
+ var ctrl = this ;
23
23
24
- //Set transcluded elements to their correct position on template
25
- var transcluded = angular . element ( '<div/>' ) . append ( clone ) ;
26
- var transMatch = uiSelectElements . byClassName ( transcluded [ 0 ] , 'ui-select-match' ) ;
27
- var transChoices = uiSelectElements . byClassName ( transcluded [ 0 ] , 'ui-select-choices' ) ;
28
- uiSelectElements . byClassName ( $elm [ 0 ] , 'ui-select-match' ) . replaceWith ( transMatch ) ;
29
- uiSelectElements . byClassName ( $elm [ 0 ] , 'ui-select-choices' ) . replaceWith ( transChoices ) ;
30
-
31
- var input = $elm . find ( 'input' ) ;
32
- $scope . activate = function ( $event ) {
33
- if ( $event ) $event . stopPropagation ( ) ; // Prevent bubbling
34
- $scope . open = true ;
35
- //Give it time to appear before focus
36
- $timeout ( function ( ) {
37
- input [ 0 ] . focus ( ) ;
38
- } ) ;
39
- } ;
40
- $scope . $watch ( '$select.search' , function ( ) {
41
- $scope . $select . index = 0 ;
24
+ this . activate = function ( $event ) {
25
+ if ( $event ) $event . stopPropagation ( ) ; // Prevent bubbling
26
+ $scope . open = true ;
27
+ //Give it time to appear before focus
28
+ $timeout ( function ( ) {
29
+ ctrl . input [ 0 ] . focus ( ) ;
42
30
} ) ;
43
- $scope . up = function ( ) {
44
- if ( $scope . $select . index > 0 ) {
45
- $scope . $select . index -- ;
46
- $scope . ensureHighlightVisible ( ) ;
47
- }
48
- } ;
49
- $scope . down = function ( ) {
50
- items = uiSelectElements . byClassName ( $elm [ 0 ] , 'ui-select-choices-row' ) . length - 1 ;
51
- if ( $scope . $select . index < items ) {
52
- $scope . $select . index ++ ;
53
- $scope . ensureHighlightVisible ( ) ;
54
- } else {
55
- $scope . $select . index = items ;
56
- }
57
- } ;
58
- $scope . $select = function ( item ) {
59
- $scope . $select . selected = item ;
60
- ngModel . $setViewValue ( item ) ;
61
- ngModel . $render ( item ) ;
62
- $scope . close ( ) ;
63
- } ;
64
- $scope . close = function ( ) {
65
- $scope . open = false ;
66
- $scope . $select . search = "" ;
67
- } ;
68
- var dismissClickHandler = function ( evt ) {
69
- if ( angular . element ( evt . target ) . hasClass ( 'ui-select-search' ) ) {
70
- return ;
71
- }
72
- $scope . close ( ) ;
73
- $scope . $digest ( ) ;
74
- } ;
75
- $document . bind ( 'click' , dismissClickHandler ) ;
76
- } ) ;
77
- } ;
31
+ } ;
32
+
33
+ this . select = function ( item ) {
34
+ $scope . $select . selected = item ;
35
+ this . close ( ) ;
36
+ // Using a watch instead of $scope.ngModel.$setViewValue(item)
37
+ } ;
38
+
39
+ this . close = function ( ) {
40
+ $scope . open = false ;
41
+ $scope . $select . search = "" ;
42
+ } ;
43
+
44
+ this . input = $element . find ( 'input' ) ; //TODO could break if input is at other template
45
+
46
+ } ] ,
47
+ controllerAs : 'uiSelectCtrl' ,
48
+ link : function ( scope , element , attrs , controllers , transcludeFn ) {
49
+
50
+ scope . open = false ;
51
+ scope . $select = { } ; //Namespace
52
+
53
+ var uiSelectCtrl = controllers [ 0 ] ;
54
+ var ngModelCtrl = controllers [ 1 ] ;
55
+
56
+ scope . $watch ( '$select.selected' , function ( newVal , oldVal ) {
57
+ ngModelCtrl . $setViewValue ( newVal ) ;
58
+ } ) ;
59
+
60
+ $document . bind ( 'click' , function ( evt ) {
61
+ if ( angular . element ( evt . target ) . hasClass ( 'ui-select-search' ) ) {
62
+ return ;
63
+ }
64
+ uiSelectCtrl . close ( ) ; //Close if clicking outside
65
+ scope . $digest ( ) ;
66
+ } ) ;
67
+
68
+ //Move transcluded elements to their correct position on main template
69
+ transcludeFn ( scope , function ( clone ) {
70
+
71
+ var transcluded = angular . element ( '<div/>' ) . append ( clone ) ;
72
+
73
+ //Child directives could be uncompiled at this point, so we check both alternatives,
74
+ //first for compiled version (by class) or uncompiled (by tag). We place the directives
75
+ //at the insertion points that are marked with ui-select-* classes at select.tpl.html
76
+ //TODO: If we change directive restrict attribute to EA, we should do some changes here.
77
+
78
+ var transMatch = uiSelectElements . byClassName ( transcluded [ 0 ] , 'ui-select-match' ) ;
79
+ transMatch = ! transMatch . length ? transcluded . find ( 'match' ) : transMatch ;
80
+ uiSelectElements . byClassName ( element [ 0 ] , 'ui-select-match' ) . replaceWith ( transMatch ) ;
81
+
82
+ var transChoices = uiSelectElements . byClassName ( transcluded [ 0 ] , 'ui-select-choices' ) ;
83
+ transChoices = ! transChoices . length ? transcluded . find ( 'choices' ) : transChoices ;
84
+ uiSelectElements . byClassName ( element [ 0 ] , 'ui-select-choices' ) . replaceWith ( transChoices ) ;
85
+
86
+ } ) ;
87
+
78
88
}
79
89
} ;
80
90
} )
81
91
82
92
. directive ( 'choices' , function ( $sce , uiSelectConfig , uiSelectElements ) {
93
+ var HOT_KEYS = [ 9 , 13 , 27 , 38 , 40 ] ;
83
94
return {
84
- // require: '^uiSelect',
95
+ require : '^uiSelect' ,
85
96
restrict : 'E' ,
86
97
transclude : true ,
87
98
replace : true ,
@@ -90,23 +101,71 @@ angular.module('ui.select', ['ui.keypress'])
90
101
var theme = tElement [ 0 ] . parentElement . getAttribute ( 'theme' ) || uiSelectConfig . defaultTheme ;
91
102
return '../src/' + theme + '/choices.tpl.html' ;
92
103
} ,
93
- compile : function ( tElement , tAttrs , transcludeFn ) {
94
- uiSelectElements . byClassName ( tElement [ 0 ] , 'ui-select-choices-row' ) . attr ( "ng-repeat" , tAttrs . data ) ;
95
- return function ( scope , element , attrs ) {
104
+ compile : function ( tElement , tAttrs ) {
105
+
106
+ uiSelectElements . byClassName ( tElement [ 0 ] , 'ui-select-choices-row' )
107
+ . attr ( "ng-repeat" , 'item in ' + tAttrs . data )
108
+ . attr ( "ng-mouseenter" , '$select.activeIdx=$index' )
109
+ . attr ( "ng-click" , 'uiSelectCtrl.select(item)' ) ;
110
+
111
+ return function ( scope , element , attrs , uiSelectCtrl ) {
112
+
96
113
scope . trustAsHtml = function ( value ) {
97
114
return $sce . trustAsHtml ( value ) ;
98
115
} ;
116
+
99
117
var container = element . hasClass ( 'ui-select-choices-content' ) ? element [ 0 ] : uiSelectElements . byClassName ( element [ 0 ] , 'ui-select-choices-content' ) [ 0 ] ;
100
- scope . ensureHighlightVisible = function ( ) {
101
- var highlighted = uiSelectElements . byClassName ( element [ 0 ] , 'ui-select-choices-row' ) [ scope . $select . index ] ,
118
+ var ensureHighlightVisible = function ( ) {
119
+ var rows = uiSelectElements . byClassName ( element [ 0 ] , 'ui-select-choices-row' ) ;
120
+ if ( ! rows . length ) return ; //In case its empty
121
+ var highlighted = rows [ scope . $select . activeIdx ] ,
102
122
posY = highlighted . offsetTop + highlighted . clientHeight - container . scrollTop ,
103
123
maxHeight = 200 ; //TODO Need to get this value from container.max-height
104
- if ( posY > maxHeight ) {
124
+ if ( posY > maxHeight ) {
105
125
container . scrollTop += posY - maxHeight ;
106
- } else if ( posY < highlighted . clientHeight ) {
126
+ } else if ( posY < highlighted . clientHeight ) {
107
127
container . scrollTop -= highlighted . clientHeight - posY ;
108
128
}
109
129
} ;
130
+
131
+ scope . $watch ( '$select.search' , function ( ) {
132
+ scope . $select . activeIdx = 0 ;
133
+ ensureHighlightVisible ( ) ;
134
+ } ) ;
135
+
136
+ //Bind keyboard events related to choices
137
+ uiSelectCtrl . input . bind ( 'keydown' , function ( evt ) {
138
+
139
+ if ( HOT_KEYS . indexOf ( evt . which ) === - 1 ) return ; //Exit on regular key
140
+ evt . preventDefault ( ) ;
141
+
142
+ var rows = uiSelectElements . byClassName ( element [ 0 ] , 'ui-select-choices-row' ) ;
143
+
144
+ if ( evt . which === 40 ) { // down(40)
145
+ if ( scope . $select . activeIdx < rows . length ) {
146
+ scope . $select . activeIdx = ( scope . $select . activeIdx + 1 ) % rows . length || rows . length - 1 ;
147
+ ensureHighlightVisible ( ) ;
148
+ scope . $digest ( ) ;
149
+ }
150
+
151
+ } else if ( evt . which === 38 ) { // up(38)
152
+ if ( scope . $select . activeIdx > 0 ) {
153
+ scope . $select . activeIdx -- ;
154
+ ensureHighlightVisible ( ) ;
155
+ scope . $digest ( ) ;
156
+ }
157
+
158
+ } else if ( evt . which === 13 || evt . which === 9 ) { // enter(13) and tab(9)
159
+ rows [ scope . $select . activeIdx ] . click ( ) ;
160
+
161
+ } else if ( evt . which === 27 ) { // esc(27)
162
+ evt . stopPropagation ( ) ;
163
+ uiSelectCtrl . close ( ) ;
164
+ scope . $digest ( ) ;
165
+
166
+ }
167
+ } ) ;
168
+
110
169
} ;
111
170
}
112
171
} ;
@@ -122,13 +181,8 @@ angular.module('ui.select', ['ui.keypress'])
122
181
var theme = tElement [ 0 ] . parentElement . getAttribute ( 'theme' ) || uiSelectConfig . defaultTheme ;
123
182
return '../src/' + theme + '/match.tpl.html' ;
124
183
} ,
125
- compile : function ( tElement , tAttrs , transcludeFn ) {
126
- return function ( $scope , $elm , $attrs , ngModel ) {
127
- transcludeFn ( $scope , function ( clone ) {
128
- $scope . placeholder = tAttrs . placeholder || uiSelectConfig . defaultPlaceholder ;
129
- $elm . append ( clone ) ;
130
- } ) ;
131
- } ;
184
+ link : function ( scope , element , attrs ) {
185
+ scope . placeholder = attrs . placeholder || uiSelectConfig . defaultPlaceholder ;
132
186
}
133
187
} ;
134
188
} )
0 commit comments