/** * A specialized {@link Ext.util.KeyNav} implementation for navigating a {@link Ext.view.BoundList} using * the keyboard. The up, down, pageup, pagedown, home, and end keys move the active highlight * through the list. The enter key invokes the selection model's select action using the highlighted item. */Ext.define('Ext.view.BoundListKeyNav', { extend: 'Ext.view.NavigationModel', alias: 'view.navigation.boundlist', /** * @cfg {Ext.view.BoundList} boundList (required) * The {@link Ext.view.BoundList} instance for which key navigation will be managed. */ initKeyNav: function(view) { var me = this, field = me.view.pickerField; // BoundLists must be able to function standalone with no bound field if (!view.pickerField) { return; } if (!field.rendered) { field.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true}); return; } me.keyNav = new Ext.util.KeyNav({ target: field.inputEl, forceKeyDown: true, up: me.onKeyUp, down: me.onKeyDown, right: me.onKeyRight, left: me.onKeyLeft, pageDown: me.onKeyPageDown, pageUp: me.onKeyPageUp, home: me.onKeyHome, end: me.onKeyEnd, tab: me.onKeyTab, space: me.onKeySpace, enter: me.onKeyEnter, scope: me }); }, onItemMouseDown: function(view, record, item, index, event) { this.callParent([view, record, item, index, event]); // Stop the mousedown from blurring the input field event.preventDefault(); }, onKeyUp: function() { var me = this, boundList = me.view, allItems = boundList.all, oldItem = boundList.highlightedItem, oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1, newItemIdx = oldItemIdx > 0 ? oldItemIdx - 1 : allItems.getCount() - 1; //wraps around me.setPosition(newItemIdx); }, onKeyDown: function() { var me = this, boundList = me.view, allItems = boundList.all, oldItem = boundList.highlightedItem, oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1, newItemIdx = oldItemIdx < allItems.getCount() - 1 ? oldItemIdx + 1 : 0; //wraps around me.setPosition(newItemIdx); }, onKeyLeft: Ext.emptyFn, onKeyRight: Ext.emptyFn, onKeyTab: function(e) { var view = this.view, field = view.pickerField; if (view.isVisible()) { if (field.selectOnTab) { this.selectHighlighted(e); } field.collapse(); } // Tab key event is allowed to propagate to field return true; }, onKeyEnter: function(e) { var selModel = this.view.getSelectionModel(), field = this.view.pickerField, count = selModel.getCount(); this.selectHighlighted(e); // Handle the case where the highlighted item is already selected // In this case, the change event won't fire, so just collapse if (!field.multiSelect && count === selModel.getCount()) { field.collapse(); } }, onKeySpace: function() { this.callParent(arguments); // Allow to propagate to field return true; }, /** * Highlights the item at the given index. * @param {Number} index */ focusItem: function(item) { var me = this, boundList = me.view; if (typeof item === 'number') { item = boundList.all.item(item); } if (item) { item = item.dom; boundList.highlightItem(item); boundList.getOverflowEl().scrollChildIntoView(item, false); } }, /** * Triggers selection of the currently highlighted item according to the behavior of * the configured SelectionModel. */ selectHighlighted: function(e) { var boundList = this.view, selModel = boundList.getSelectionModel(), highlightedRec; highlightedRec = boundList.getNavigationModel().getRecord(); if (highlightedRec) { // Select if not already selected. // If already selected, selecting with no CTRL flag will deselect the record. if (e.getKey() === e.ENTER || !selModel.isSelected(highlightedRec)) { selModel.selectWithEvent(highlightedRec, e); } } } });