Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
feat(select): add support to select/focus by typing options
Browse files Browse the repository at this point in the history
Shoutout to @wenbolovesnz for initial inspiration.

closes #2092, closes #1740.
  • Loading branch information
rschmukler committed Mar 31, 2015
1 parent 835b745 commit f5d905a
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 6 deletions.
58 changes: 52 additions & 6 deletions src/components/select/select.js
Expand Up @@ -129,7 +129,7 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
var ngModel = ctrls[1];
var labelEl = element.find('md-select-label');
var customLabel = labelEl.text().length !== 0;
var selectContainer, selectScope;
var selectContainer, selectScope, selectMenuCtrl;
createSelect();

var originalRender = ngModel.$render;
Expand Down Expand Up @@ -171,7 +171,6 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
element.removeAttr('multiple');
}
if (selectContainer) {
var selectMenuCtrl = selectContainer.find('md-select-menu').controller('mdSelectMenu');
selectMenuCtrl.setMultiple(multiple);
originalRender = ngModel.$render;
ngModel.$render = function() {
Expand All @@ -196,17 +195,17 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
if (disabled) {
element.attr('tabindex', -1);
element.off('click', openSelect);
element.off('keydown', openOnKeypress);
element.off('keydown', handleKeypress);
} else {
element.attr('tabindex', 0);
element.on('click', openSelect);
element.on('keydown', openOnKeypress);
element.on('keydown', handleKeypress);
}
});
if (!attr.disabled && !attr.ngDisabled) {
element.attr('tabindex', 0);
element.on('click', openSelect);
element.on('keydown', openOnKeypress);
element.on('keydown', handleKeypress);
}

element.attr({
Expand Down Expand Up @@ -236,14 +235,28 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
selectEl.data('$mdSelectController', mdSelectCtrl);
selectScope = scope.$new();
selectContainer = $compile(selectContainer)(selectScope);
selectMenuCtrl = selectContainer.find('md-select-menu').controller('mdSelectMenu');
}

function openOnKeypress(e) {
function handleKeypress(e) {
var allowedCodes = [32, 13, 38, 40];
if (allowedCodes.indexOf(e.keyCode) != -1 ) {
// prevent page scrolling on interaction
e.preventDefault();
openSelect(e);
} else {
if (e.keyCode <= 90 && e.keyCode >= 31) {
e.preventDefault();
var node = selectMenuCtrl.optNodeForKeyboardSearch(e);
if (!node) return;
var optionCtrl = angular.element(node).controller('mdOption');
if (!selectMenuCtrl.isMultiple) {
selectMenuCtrl.deselect( Object.keys(selectMenuCtrl.selected)[0] );
}
selectMenuCtrl.select(optionCtrl.hashKey, optionCtrl.value);
selectMenuCtrl.refreshViewValue();
ngModel.$render();
}
}
}

Expand Down Expand Up @@ -366,6 +379,33 @@ function SelectMenuDirective($parse, $mdUtil, $mdTheming) {
}
};

var searchStr = '';
var clearSearchTimeout, optNodes, optText;
var CLEAR_SEARCH_AFTER = 300;
self.optNodeForKeyboardSearch = function(e) {
clearSearchTimeout && clearTimeout(clearSearchTimeout);
clearSearchTimeout = setTimeout(function() {
clearSearchTimeout = undefined;
searchStr = '';
optText = undefined;
optNodes = undefined;
}, CLEAR_SEARCH_AFTER);
searchStr += String.fromCharCode(e.keyCode);
var search = new RegExp('^' + searchStr, 'i');
if (!optNodes) {
optNodes = $element.find('md-option');
optText = new Array(optNodes.length);
angular.forEach(optNodes, function(el, i) {
optText[i] = el.textContent;
});
}
for (var i = 0; i < optText.length; ++i) {
if (search.test(optText[i])) {
return optNodes[i];
}
}
};


self.init = function(ngModel) {
self.ngModel = ngModel;
Expand Down Expand Up @@ -702,9 +742,15 @@ function SelectProvider($$interimElementProvider) {
switch (ev.keyCode) {
case $mdConstant.KEY_CODE.UP_ARROW: return focusPrevOption();
case $mdConstant.KEY_CODE.DOWN_ARROW: return focusNextOption();
default:
if (ev.keyCode >= 31 && ev.keyCode <= 90) {
var optNode = opts.selectEl.controller('mdSelectMenu').optNodeForKeyboardSearch(ev);
optNode && optNode.focus();
}
}
});


function focusOption(direction) {
var optionsArray = nodesToArray(optionNodes);
var index = optionsArray.indexOf(opts.focusedNode);
Expand Down
6 changes: 6 additions & 0 deletions src/components/select/select.spec.js
Expand Up @@ -635,6 +635,12 @@ describe('<md-select-menu>', function() {
var selectMenu = angular.element($document.find('md-select-menu'));
expect(selectMenu.length).toBe(1);
}));

iit('supports typing an option name', inject(function($document, $rootScope) {
var el = setupSelect('ng-model="someModel"', [1, 2, 3]);
pressKey(el, 50);
expect($rootScope.someModel).toBe(2);
}));
});

describe('md-select-menu', function() {
Expand Down

0 comments on commit f5d905a

Please sign in to comment.