Skip to content

Commit 6b76dda

Browse files
committed
Merge pull request angular-ui#92 from angular-ui/feat-focusable
feat(focusable): allow control to be get focus. Support for tab navigati...
2 parents 9cc7b5f + 0b2cbf9 commit 6b76dda

File tree

6 files changed

+136
-8
lines changed

6 files changed

+136
-8
lines changed

src/bootstrap/match.tpl.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
<button type="button" class="btn btn-default form-control ui-select-match"
1+
<button type="button" class="btn btn-default form-control ui-select-match" tabindex="-1"
22
ng-hide="$select.open"
33
ng-disabled="$select.disabled"
4+
ng-class="{'btn-default-focus':$select.focus}";
45
ng-click="$select.activate()">
56
<span ng-hide="$select.selected !== undefined" class="text-muted">{{$select.placeholder}}</span>
67
<span ng-show="$select.selected !== undefined" ng-transclude></span>

src/bootstrap/select.tpl.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="ui-select-bootstrap dropdown" ng-class="{open: $select.open}">
22
<div class="ui-select-match"></div>
3-
<input type="text" autocomplete="off" tabindex=""
3+
<input type="text" autocomplete="off" tabindex="-1"
44
class="form-control ui-select-search"
55
placeholder="{{$select.placeholder}}"
66
ng-model="$select.search"

src/select.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,30 @@
33
font-weight: bold;
44
}
55

6+
.ui-select-offscreen {
7+
clip: rect(0 0 0 0) !important;
8+
width: 1px !important;
9+
height: 1px !important;
10+
border: 0 !important;
11+
margin: 0 !important;
12+
padding: 0 !important;
13+
overflow: hidden !important;
14+
position: absolute !important;
15+
outline: 0 !important;
16+
left: 0px !important;
17+
top: 0px !important;
18+
}
619

720
/* Select2 theme */
821

922

1023
/* Selectize theme */
1124

25+
/* Helper class to show styles when focus */
26+
.selectize-input.selectize-focus{
27+
border-color: #007FBB !important;
28+
}
29+
1230
/* Fix input width for Selectize theme */
1331
.selectize-control > .selectize-input > input {
1432
width: 100%;
@@ -22,6 +40,18 @@
2240

2341
/* Bootstrap theme */
2442

43+
/* Helper class to show styles when focus */
44+
.btn-default-focus {
45+
color: #333;
46+
background-color: #EBEBEB;
47+
border-color: #ADADAD;
48+
text-decoration: none;
49+
outline: 5px auto -webkit-focus-ring-color;
50+
outline-offset: -2px;
51+
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
52+
}
53+
54+
2555
/* Fix Bootstrap dropdown position when inside a input-group */
2656
.input-group > .ui-select-bootstrap.dropdown {
2757
/* Instead of relative */

src/select.js

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ angular.module('ui.select', [])
116116
ctrl.items = [];
117117
ctrl.selected = undefined;
118118
ctrl.open = false;
119+
ctrl.focus = false;
120+
ctrl.focusser = undefined; //Reference to input element used to handle focus events
119121
ctrl.disabled = undefined; // Initialized inside uiSelect directive link function
120122
ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function
121123
ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function
@@ -137,13 +139,14 @@ angular.module('ui.select', [])
137139
}
138140

139141
// When the user clicks on ui-select, displays the dropdown list
140-
ctrl.activate = function() {
142+
ctrl.activate = function(initSearchValue) {
141143
if (!ctrl.disabled) {
142144
_resetSearchInput();
143145
ctrl.open = true;
144146

145147
// Give it time to appear before focus
146148
$timeout(function() {
149+
ctrl.search = initSearchValue || ctrl.search;
147150
_searchInput[0].focus();
148151
});
149152
}
@@ -206,6 +209,7 @@ angular.module('ui.select', [])
206209
if (ctrl.open) {
207210
_resetSearchInput();
208211
ctrl.open = false;
212+
ctrl.focusser[0].focus();
209213
}
210214
};
211215

@@ -288,8 +292,8 @@ angular.module('ui.select', [])
288292
}])
289293

290294
.directive('uiSelect',
291-
['$document', 'uiSelectConfig', 'uiSelectMinErr',
292-
function($document, uiSelectConfig, uiSelectMinErr) {
295+
['$document', 'uiSelectConfig', 'uiSelectMinErr', '$compile',
296+
function($document, uiSelectConfig, uiSelectMinErr, $compile) {
293297

294298
return {
295299
restrict: 'EA',
@@ -309,6 +313,98 @@ angular.module('ui.select', [])
309313
var $select = ctrls[0];
310314
var ngModel = ctrls[1];
311315

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+
312408
attrs.$observe('disabled', function() {
313409
// No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string
314410
$select.disabled = attrs.disabled !== undefined ? attrs.disabled : false;

src/select2/select.tpl.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<div class="select2 select2-container"
22
ng-class="{'select2-container-active select2-dropdown-open': $select.open,
3-
'select2-container-disabled': $select.disabled}">
3+
'select2-container-disabled': $select.disabled,
4+
'select2-container-active': $select.focus }">
45
<div class="ui-select-match"></div>
56
<div class="select2-drop select2-with-searchbox select2-drop-active"
67
ng-class="{'select2-display-none': !$select.open}">

src/selectize/select.tpl.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<div class="selectize-control single">
22
<div class="selectize-input"
3-
ng-class="{'focus': $select.open, 'disabled': $select.disabled}"
3+
ng-class="{'focus': $select.open, 'disabled': $select.disabled, 'selectize-focus' : $select.focus}"
44
ng-click="$select.activate()">
55
<div class="ui-select-match"></div>
6-
<input type="text" autocomplete="off" tabindex=""
6+
<input type="text" autocomplete="off" tabindex="-1"
77
class="ui-select-search"
88
placeholder="{{$select.placeholder}}"
99
ng-model="$select.search"

0 commit comments

Comments
 (0)