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

feat(focusable): allow control to be get focus. Support for tab navigati... #92

Merged
merged 2 commits into from
Jul 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/bootstrap/match.tpl.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<button type="button" class="btn btn-default form-control ui-select-match"
<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>
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/select.tpl.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="ui-select-bootstrap dropdown" ng-class="{open: $select.open}">
<div class="ui-select-match"></div>
<input type="text" autocomplete="off" tabindex=""
<input type="text" autocomplete="off" tabindex="-1"
class="form-control ui-select-search"
placeholder="{{$select.placeholder}}"
ng-model="$select.search"
Expand Down
30 changes: 30 additions & 0 deletions src/select.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,30 @@
font-weight: bold;
}

.ui-select-offscreen {
clip: rect(0 0 0 0) !important;
width: 1px !important;
height: 1px !important;
border: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
position: absolute !important;
outline: 0 !important;
left: 0px !important;
top: 0px !important;
}

/* Select2 theme */


/* Selectize theme */

/* Helper class to show styles when focus */
.selectize-input.selectize-focus{
border-color: #007FBB !important;
}

/* Fix input width for Selectize theme */
.selectize-control > .selectize-input > input {
width: 100%;
Expand All @@ -22,6 +40,18 @@

/* Bootstrap theme */

/* Helper class to show styles when focus */
.btn-default-focus {
color: #333;
background-color: #EBEBEB;
border-color: #ADADAD;
text-decoration: none;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
}


/* Fix Bootstrap dropdown position when inside a input-group */
.input-group > .ui-select-bootstrap.dropdown {
/* Instead of relative */
Expand Down
102 changes: 99 additions & 3 deletions src/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ angular.module('ui.select', [])
ctrl.items = [];
ctrl.selected = undefined;
ctrl.open = false;
ctrl.focus = false;
ctrl.focusser = undefined; //Reference to input element used to handle focus events
ctrl.disabled = undefined; // Initialized inside uiSelect directive link function
ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function
ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function
Expand All @@ -137,13 +139,14 @@ angular.module('ui.select', [])
}

// When the user clicks on ui-select, displays the dropdown list
ctrl.activate = function() {
ctrl.activate = function(initSearchValue) {
if (!ctrl.disabled) {
_resetSearchInput();
ctrl.open = true;

// Give it time to appear before focus
$timeout(function() {
ctrl.search = initSearchValue || ctrl.search;
_searchInput[0].focus();
});
}
Expand Down Expand Up @@ -206,6 +209,7 @@ angular.module('ui.select', [])
if (ctrl.open) {
_resetSearchInput();
ctrl.open = false;
ctrl.focusser[0].focus();
}
};

Expand Down Expand Up @@ -288,8 +292,8 @@ angular.module('ui.select', [])
}])

.directive('uiSelect',
['$document', 'uiSelectConfig', 'uiSelectMinErr',
function($document, uiSelectConfig, uiSelectMinErr) {
['$document', 'uiSelectConfig', 'uiSelectMinErr', '$compile',
function($document, uiSelectConfig, uiSelectMinErr, $compile) {

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

//Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954
var focusser = angular.element("<input ng-disabled='$select.disabled' class='ui-select-focusser ui-select-offscreen' type='text' aria-haspopup='true' role='button' />");
$compile(focusser)(scope);
$select.focusser = focusser;

element.append(focusser);
focusser.bind("focus", function(){
scope.$evalAsync(function(){
$select.focus = true;
});
});
focusser.bind("blur", function(){
scope.$evalAsync(function(){
$select.focus = false;
});
});
focusser.bind("keydown", function(e){

if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
return;
}

if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){
e.preventDefault();
e.stopPropagation();
$select.activate();
}

scope.$digest();
});

focusser.bind("keyup input", function(e){

if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER) {
return;
}

$select.activate(focusser.val()); //User pressed some regualar key, so we pass it to the search input
focusser.val('');
scope.$digest();

});

//TODO Refactor to reuse the KEY object from uiSelectCtrl
var KEY = {
TAB: 9,
ENTER: 13,
ESC: 27,
SPACE: 32,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
SHIFT: 16,
CTRL: 17,
ALT: 18,
PAGE_UP: 33,
PAGE_DOWN: 34,
HOME: 36,
END: 35,
BACKSPACE: 8,
DELETE: 46,
isArrow: function (k) {
k = k.which ? k.which : k;
switch (k) {
case KEY.LEFT:
case KEY.RIGHT:
case KEY.UP:
case KEY.DOWN:
return true;
}
return false;
},
isControl: function (e) {
var k = e.which;
switch (k) {
case KEY.SHIFT:
case KEY.CTRL:
case KEY.ALT:
return true;
}

if (e.metaKey) return true;

return false;
},
isFunctionKey: function (k) {
k = k.which ? k.which : k;
return k >= 112 && k <= 123;
}
};

attrs.$observe('disabled', function() {
// No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string
$select.disabled = attrs.disabled !== undefined ? attrs.disabled : false;
Expand Down
3 changes: 2 additions & 1 deletion src/select2/select.tpl.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div class="select2 select2-container"
ng-class="{'select2-container-active select2-dropdown-open': $select.open,
'select2-container-disabled': $select.disabled}">
'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}">
Expand Down
4 changes: 2 additions & 2 deletions src/selectize/select.tpl.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div class="selectize-control single">
<div class="selectize-input"
ng-class="{'focus': $select.open, 'disabled': $select.disabled}"
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=""
<input type="text" autocomplete="off" tabindex="-1"
class="ui-select-search"
placeholder="{{$select.placeholder}}"
ng-model="$select.search"
Expand Down