Datatable dropdown usability tweaks (#3980)
Adds a couple of usability tweaks to the dropdown cell type in the data table widget, to more closely mimic a native dropdown field. Pressing the up or down arrow keys when the cell is focused but with the dropdown closed will select the previous or next item automatically and set it as the cell value. This does prevent the usual table function of going to the previous or next row when focused on a dropdown cell, but I think it's a worthwhile trade-off. When the dropdown is open, the up and down arrows work the same as previously implemented. Typing out characters will initiate a search and select the first matching option automatically and set it as the cell value. For example, for the following options: Apples Oranges Bananas Typing out o and r on the keyboard will automatically select the Oranges option. Credit to @bennothommo
This commit is contained in:
parent
a11868169e
commit
14c4d1392e
|
|
@ -395,9 +395,7 @@ html.cssanimations .control-table td[data-column-type=dropdown] [data-view-conta
|
|||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
.table-control-dropdown-list li:hover,
|
||||
.table-control-dropdown-list li:focus,
|
||||
.table-control-dropdown-list li.selected {
|
||||
.table-control-dropdown-list li:focus {
|
||||
background: #34495e;
|
||||
color: white;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,10 +266,8 @@ return
|
|||
for(var i=0,len=this.options.columns.length;i<len;i++){var column=this.options.columns[i].key
|
||||
this.cellProcessors[column].onClick(ev)}
|
||||
var target=this.getEventTarget(ev,'TD')
|
||||
if(!target)
|
||||
return
|
||||
if(target.tagName!='TD')
|
||||
return
|
||||
if(!target){this.unfocusTable();return;}
|
||||
if(target.tagName!='TD'){this.unfocusTable();return;}
|
||||
this.focusCell(target,true)}
|
||||
Table.prototype.onKeydown=function(ev){if(ev.keyCode==65&&ev.altKey&&this.options.adding){if(!ev.shiftKey){this.addRecord('below')}
|
||||
else{this.addRecord('above')}
|
||||
|
|
@ -279,7 +277,7 @@ if(ev.keyCode==68&&ev.altKey&&this.options.deleting){this.deleteRecord()
|
|||
this.stopEvent(ev)
|
||||
return}
|
||||
for(var i=0,len=this.options.columns.length;i<len;i++){var column=this.options.columns[i].key
|
||||
this.cellProcessors[column].onKeyDown(ev)}
|
||||
if(this.cellProcessors[column].onKeyDown(ev)===false){return}}
|
||||
if(this.navigation.onKeydown(ev)===false){return}
|
||||
if(this.search.onKeydown(ev)===false){return}}
|
||||
Table.prototype.onFormSubmit=function(ev,data){if(data.handler==this.options.postbackHandlerName){this.unfocusTable()
|
||||
|
|
@ -789,20 +787,26 @@ throw new Error("The $.oc.table namespace is not defined. Make sure that the tab
|
|||
throw new Error("The $.oc.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");var Base=$.oc.table.processor.base,BaseProto=Base.prototype
|
||||
var DropdownProcessor=function(tableObj,columnName,columnConfiguration){this.itemListElement=null
|
||||
this.cachedOptionPromises={}
|
||||
this.searching=false
|
||||
this.searchQuery=null
|
||||
this.searchInterval=null
|
||||
this.itemClickHandler=this.onItemClick.bind(this)
|
||||
this.itemKeyDownHandler=this.onItemKeyDown.bind(this)
|
||||
this.itemMouseMoveHandler=this.onItemMouseMove.bind(this)
|
||||
Base.call(this,tableObj,columnName,columnConfiguration)}
|
||||
DropdownProcessor.prototype=Object.create(BaseProto)
|
||||
DropdownProcessor.prototype.constructor=DropdownProcessor
|
||||
DropdownProcessor.prototype.dispose=function(){this.unregisterListHandlers()
|
||||
this.itemClickHandler=null
|
||||
this.itemKeyDownHandler=null
|
||||
this.itemMouseMoveHandler=null
|
||||
this.itemListElement=null
|
||||
this.cachedOptionPromises=null
|
||||
BaseProto.dispose.call(this)}
|
||||
DropdownProcessor.prototype.unregisterListHandlers=function(){if(this.itemListElement)
|
||||
{this.itemListElement.removeEventListener('click',this.itemClickHandler)
|
||||
this.itemListElement.removeEventListener('keydown',this.itemKeyDownHandler)}}
|
||||
this.itemListElement.removeEventListener('keydown',this.itemKeyDownHandler)
|
||||
this.itemListElement.removeEventListener('mousemove',this.itemMouseMoveHandler)}}
|
||||
DropdownProcessor.prototype.renderCell=function(value,cellContentContainer){var viewContainer=this.createViewContainer(cellContentContainer,'...')
|
||||
this.fetchOptions(cellContentContainer.parentNode,function renderCellFetchOptions(options){if(options[value]!==undefined)
|
||||
viewContainer.textContent=options[value]
|
||||
|
|
@ -825,6 +829,7 @@ self=this
|
|||
this.itemListElement=document.createElement('div')
|
||||
this.itemListElement.addEventListener('click',this.itemClickHandler)
|
||||
this.itemListElement.addEventListener('keydown',this.itemKeyDownHandler)
|
||||
this.itemListElement.addEventListener('mousemove',this.itemMouseMoveHandler)
|
||||
this.itemListElement.setAttribute('class','table-control-dropdown-list')
|
||||
this.itemListElement.style.width=cellContentContainer.offsetWidth+'px'
|
||||
this.itemListElement.style.left=containerPosition.left+'px'
|
||||
|
|
@ -866,37 +871,50 @@ return cachingKey}
|
|||
DropdownProcessor.prototype.getAbsolutePosition=function(element){var top=document.body.scrollTop,left=0
|
||||
do{top+=element.offsetTop||0;top-=element.scrollTop||0;left+=element.offsetLeft||0;element=element.offsetParent;}while(element)
|
||||
return{top:top,left:left}}
|
||||
DropdownProcessor.prototype.updateCellFromSelectedItem=function(selectedItem){this.tableObj.setCellValue(this.activeCell,selectedItem.getAttribute('data-value'))
|
||||
this.setViewContainerValue(this.activeCell,selectedItem.textContent)}
|
||||
DropdownProcessor.prototype.updateCellFromFocusedItem=function(){var focusedItem=this.findFocusedItem();this.setSelectedItem(focusedItem);}
|
||||
DropdownProcessor.prototype.findSelectedItem=function(){if(this.itemListElement)
|
||||
return this.itemListElement.querySelector('ul li.selected')
|
||||
return null}
|
||||
DropdownProcessor.prototype.setSelectedItem=function(item){if(!this.itemListElement)
|
||||
return null;if(item.tagName=='LI'&&this.itemListElement.contains(item)){this.itemListElement.querySelectorAll('ul li').forEach(function(option){option.removeAttribute('class');});item.setAttribute('class','selected');}
|
||||
this.tableObj.setCellValue(this.activeCell,item.getAttribute('data-value'))
|
||||
this.setViewContainerValue(this.activeCell,item.textContent)}
|
||||
DropdownProcessor.prototype.findFocusedItem=function(){if(this.itemListElement)
|
||||
return this.itemListElement.querySelector('ul li:focus')
|
||||
return null}
|
||||
DropdownProcessor.prototype.onItemClick=function(ev){var target=this.tableObj.getEventTarget(ev)
|
||||
if(target.tagName=='LI'){this.updateCellFromSelectedItem(target)
|
||||
var selected=this.findSelectedItem()
|
||||
if(selected)
|
||||
selected.setAttribute('class','')
|
||||
target.setAttribute('class','selected')
|
||||
if(target.tagName=='LI'){target.focus();this.updateCellFromFocusedItem()
|
||||
this.hideDropdown()}}
|
||||
DropdownProcessor.prototype.onItemKeyDown=function(ev){if(!this.itemListElement)
|
||||
return
|
||||
if(ev.keyCode==40||ev.keyCode==38)
|
||||
{var selected=this.findSelectedItem(),newSelectedItem=selected.nextElementSibling
|
||||
{var focused=this.findFocusedItem(),newFocusedItem=focused.nextElementSibling
|
||||
if(ev.keyCode==38)
|
||||
newSelectedItem=selected.previousElementSibling
|
||||
if(newSelectedItem){selected.setAttribute('class','')
|
||||
newSelectedItem.setAttribute('class','selected')
|
||||
newSelectedItem.focus()}
|
||||
newFocusedItem=focused.previousElementSibling
|
||||
if(newFocusedItem){newFocusedItem.focus()}
|
||||
return}
|
||||
if(ev.keyCode==13||ev.keyCode==32){this.updateCellFromSelectedItem(this.findSelectedItem())
|
||||
if(ev.keyCode==13||ev.keyCode==32){this.updateCellFromFocusedItem()
|
||||
this.hideDropdown()
|
||||
return}
|
||||
if(ev.keyCode==9){this.updateCellFromSelectedItem(this.findSelectedItem())
|
||||
if(ev.keyCode==9){this.updateCellFromFocusedItem()
|
||||
this.tableObj.navigation.navigateNext(ev)
|
||||
this.tableObj.stopEvent(ev)}
|
||||
if(ev.keyCode==27){this.hideDropdown()}}
|
||||
DropdownProcessor.prototype.onKeyDown=function(ev){if(ev.keyCode==32)
|
||||
this.showDropdown()}
|
||||
this.tableObj.stopEvent(ev)
|
||||
return}
|
||||
if(ev.keyCode==27){this.hideDropdown()
|
||||
return}
|
||||
this.searchByTextInput(ev,true);}
|
||||
DropdownProcessor.prototype.onItemMouseMove=function(ev){if(!this.itemListElement)
|
||||
return
|
||||
var target=this.tableObj.getEventTarget(ev)
|
||||
if(target.tagName=='LI'){target.focus();}}
|
||||
DropdownProcessor.prototype.onKeyDown=function(ev){if(!this.itemListElement)
|
||||
return
|
||||
if(ev.keyCode==32&&!this.searching){this.showDropdown()}else if(ev.keyCode==40||ev.keyCode==38){var selected=this.findSelectedItem(),newSelectedItem;if(!selected){if(ev.keyCode==38){return false}
|
||||
newSelectedItem=this.itemListElement.querySelector('ul li:first-child')}else{newSelectedItem=selected.nextElementSibling
|
||||
if(ev.keyCode==38)
|
||||
newSelectedItem=selected.previousElementSibling}
|
||||
if(newSelectedItem){this.setSelectedItem(newSelectedItem);}
|
||||
return false}else{this.searchByTextInput(ev);}}
|
||||
DropdownProcessor.prototype.onRowValueChanged=function(columnName,cellElement){if(!this.columnConfiguration.dependsOn)
|
||||
return
|
||||
var dependsOnColumn=false,dependsOn=this.columnConfiguration.dependsOn
|
||||
|
|
@ -912,6 +930,12 @@ viewContainer=null})}
|
|||
DropdownProcessor.prototype.elementBelongsToProcessor=function(element){if(!this.itemListElement)
|
||||
return false
|
||||
return this.tableObj.parentContainsElement(this.itemListElement,element)}
|
||||
DropdownProcessor.prototype.searchByTextInput=function(ev,focusOnly){if(focusOnly===undefined){focusOnly=false;}
|
||||
var character=ev.key;if(character.length===1||character==='Space'){if(!this.searching){this.searching=true;this.searchQuery='';}
|
||||
this.searchQuery+=(character==='Space')?' ':character;var validItem=null;var query=this.searchQuery;this.itemListElement.querySelectorAll('ul li').forEach(function(item){if(validItem===null&&item.dataset.value&&item.dataset.value.toLowerCase().indexOf(query.toLowerCase())===0){validItem=item;}});if(validItem){if(focusOnly===true){validItem.focus();}else{this.setSelectedItem(validItem);}
|
||||
if(this.searchInterval){clearTimeout(this.searchInterval);}
|
||||
this.searchInterval=setTimeout(this.cancelTextSearch.bind(this),1000);}else{this.cancelTextSearch();}}}
|
||||
DropdownProcessor.prototype.cancelTextSearch=function(){this.searching=false;this.searchQuery=null;this.searchInterval=null;}
|
||||
$.oc.table.processor.dropdown=DropdownProcessor;}(window.jQuery);+function($){"use strict";if($.oc.table===undefined)
|
||||
throw new Error("The $.oc.table namespace is not defined. Make sure that the table.js script is loaded.");if($.oc.table.processor===undefined)
|
||||
throw new Error("The $.oc.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");var Base=$.oc.table.processor.string,BaseProto=Base.prototype
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
this.dataTableContainer = null
|
||||
|
||||
// The key of the row which is being edited at the moment.
|
||||
// This key corresponds the data source row key which
|
||||
// This key corresponds the data source row key which
|
||||
// uniquely identifies the row in the data set. When the
|
||||
// table grid notices that a cell in another row is edited it commits
|
||||
// the previously edited record to the data source.
|
||||
|
|
@ -556,8 +556,8 @@
|
|||
}
|
||||
|
||||
Table.prototype.addRecord = function(placement, noFocus) {
|
||||
// If there is no active cell, or the pagination is enabled or
|
||||
// row sorting is disabled, add the record to the bottom of
|
||||
// If there is no active cell, or the pagination is enabled or
|
||||
// row sorting is disabled, add the record to the bottom of
|
||||
// the table (last page).
|
||||
|
||||
if (!this.activeCell || this.navigation.paginationEnabled() || !this.options.rowSorting)
|
||||
|
|
@ -600,7 +600,7 @@
|
|||
])
|
||||
|
||||
this.dataSource.createRecord(recordData, placement, relativeToKey,
|
||||
this.navigation.getPageFirstRowOffset(),
|
||||
this.navigation.getPageFirstRowOffset(),
|
||||
this.options.recordsPerPage,
|
||||
function onAddRecordDataTableSuccess(records, totalCount) {
|
||||
self.buildDataTable(records, totalCount)
|
||||
|
|
@ -655,7 +655,7 @@
|
|||
else
|
||||
self.navigation.focusCellInReplacedRow(currentRowIndex, currentCellIndex)
|
||||
}
|
||||
|
||||
|
||||
self = null
|
||||
}
|
||||
)
|
||||
|
|
@ -742,11 +742,15 @@
|
|||
|
||||
var target = this.getEventTarget(ev, 'TD')
|
||||
|
||||
if (!target)
|
||||
return
|
||||
if (!target) {
|
||||
this.unfocusTable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.tagName != 'TD')
|
||||
return
|
||||
if (target.tagName != 'TD') {
|
||||
this.unfocusTable();
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusCell(target, true)
|
||||
}
|
||||
|
|
@ -777,7 +781,9 @@
|
|||
for (var i = 0, len = this.options.columns.length; i < len; i++) {
|
||||
var column = this.options.columns[i].key
|
||||
|
||||
this.cellProcessors[column].onKeyDown(ev)
|
||||
if (this.cellProcessors[column].onKeyDown(ev) === false) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (this.navigation.onKeydown(ev) === false) {
|
||||
|
|
@ -838,7 +844,7 @@
|
|||
if (this.parentContainsElement(this.el, target))
|
||||
return
|
||||
|
||||
// Request the active cell processor if the clicked
|
||||
// Request the active cell processor if the clicked
|
||||
// element belongs to any extra-table element created
|
||||
// by the processor
|
||||
|
||||
|
|
@ -887,7 +893,7 @@
|
|||
// Delete references to the control HTML elements.
|
||||
// The script doesn't remove any DOM elements themselves.
|
||||
// If it's needed it should be done by the outer script,
|
||||
// we only make sure that the table widget doesn't hold
|
||||
// we only make sure that the table widget doesn't hold
|
||||
// references to the detached DOM tree so that the garbage
|
||||
// collector can delete the elements if needed.
|
||||
this.disposeScrollbar()
|
||||
|
|
@ -901,7 +907,7 @@
|
|||
}
|
||||
|
||||
/*
|
||||
* Updates row values in the table.
|
||||
* Updates row values in the table.
|
||||
* rowIndex is an integer value containing the row index on the current page.
|
||||
* The rowValues should be a hash object containing only changed
|
||||
* columns.
|
||||
|
|
@ -999,7 +1005,7 @@
|
|||
|
||||
if (el.classList)
|
||||
return el.classList.contains(className);
|
||||
|
||||
|
||||
return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
|
||||
}
|
||||
|
||||
|
|
@ -1122,7 +1128,7 @@
|
|||
var old = $.fn.table
|
||||
|
||||
$.fn.table = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1),
|
||||
var args = Array.prototype.slice.call(arguments, 1),
|
||||
result = undefined
|
||||
|
||||
this.each(function () {
|
||||
|
|
@ -1156,4 +1162,4 @@
|
|||
$('div[data-control=table]').table()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
}(window.jQuery);
|
||||
|
|
|
|||
|
|
@ -31,10 +31,14 @@
|
|||
this.itemListElement = null
|
||||
|
||||
this.cachedOptionPromises = {}
|
||||
this.searching = false
|
||||
this.searchQuery = null
|
||||
this.searchInterval = null
|
||||
|
||||
// Event handlers
|
||||
this.itemClickHandler = this.onItemClick.bind(this)
|
||||
this.itemKeyDownHandler = this.onItemKeyDown.bind(this)
|
||||
this.itemMouseMoveHandler = this.onItemMouseMove.bind(this)
|
||||
|
||||
//
|
||||
// Parent constructor
|
||||
|
|
@ -50,6 +54,7 @@
|
|||
this.unregisterListHandlers()
|
||||
this.itemClickHandler = null
|
||||
this.itemKeyDownHandler = null
|
||||
this.itemMouseMoveHandler = null
|
||||
this.itemListElement = null
|
||||
this.cachedOptionPromises = null
|
||||
BaseProto.dispose.call(this)
|
||||
|
|
@ -64,6 +69,7 @@
|
|||
// body, not to the table.
|
||||
this.itemListElement.removeEventListener('click', this.itemClickHandler)
|
||||
this.itemListElement.removeEventListener('keydown', this.itemKeyDownHandler)
|
||||
this.itemListElement.removeEventListener('mousemove', this.itemMouseMoveHandler)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +122,7 @@
|
|||
}
|
||||
|
||||
DropdownProcessor.prototype.buildEditor = function(cellElement, cellContentContainer, isClick) {
|
||||
// Create the select control
|
||||
// Create the select control
|
||||
var currentValue = this.tableObj.getCellValue(cellElement),
|
||||
containerPosition = this.getAbsolutePosition(cellContentContainer)
|
||||
self = this
|
||||
|
|
@ -125,6 +131,7 @@
|
|||
|
||||
this.itemListElement.addEventListener('click', this.itemClickHandler)
|
||||
this.itemListElement.addEventListener('keydown', this.itemKeyDownHandler)
|
||||
this.itemListElement.addEventListener('mousemove', this.itemMouseMoveHandler)
|
||||
|
||||
this.itemListElement.setAttribute('class', 'table-control-dropdown-list')
|
||||
this.itemListElement.style.width = cellContentContainer.offsetWidth + 'px'
|
||||
|
|
@ -133,7 +140,7 @@
|
|||
|
||||
this.fetchOptions(cellElement, function renderCellFetchOptions(options) {
|
||||
var listElement = document.createElement('ul')
|
||||
|
||||
|
||||
for (var value in options) {
|
||||
var itemElement = document.createElement('li')
|
||||
itemElement.setAttribute('data-value', value)
|
||||
|
|
@ -197,7 +204,7 @@
|
|||
}
|
||||
else {
|
||||
// If options are not provided and not found in the cache,
|
||||
// request them from the server. For dependent drop-downs
|
||||
// request them from the server. For dependent drop-downs
|
||||
// the caching key contains the master column values.
|
||||
|
||||
var row = cellElement.parentNode,
|
||||
|
|
@ -263,9 +270,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.updateCellFromSelectedItem = function(selectedItem) {
|
||||
this.tableObj.setCellValue(this.activeCell, selectedItem.getAttribute('data-value'))
|
||||
this.setViewContainerValue(this.activeCell, selectedItem.textContent)
|
||||
DropdownProcessor.prototype.updateCellFromFocusedItem = function() {
|
||||
var focusedItem = this.findFocusedItem();
|
||||
this.setSelectedItem(focusedItem);
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.findSelectedItem = function() {
|
||||
|
|
@ -275,17 +282,34 @@
|
|||
return null
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.setSelectedItem = function(item) {
|
||||
if (!this.itemListElement)
|
||||
return null;
|
||||
|
||||
if (item.tagName == 'LI' && this.itemListElement.contains(item)) {
|
||||
this.itemListElement.querySelectorAll('ul li').forEach(function (option) {
|
||||
option.removeAttribute('class');
|
||||
});
|
||||
item.setAttribute('class', 'selected');
|
||||
}
|
||||
|
||||
this.tableObj.setCellValue(this.activeCell, item.getAttribute('data-value'))
|
||||
this.setViewContainerValue(this.activeCell, item.textContent)
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.findFocusedItem = function() {
|
||||
if (this.itemListElement)
|
||||
return this.itemListElement.querySelector('ul li:focus')
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.onItemClick = function(ev) {
|
||||
var target = this.tableObj.getEventTarget(ev)
|
||||
|
||||
if (target.tagName == 'LI') {
|
||||
this.updateCellFromSelectedItem(target)
|
||||
|
||||
var selected = this.findSelectedItem()
|
||||
if (selected)
|
||||
selected.setAttribute('class', '')
|
||||
|
||||
target.setAttribute('class', 'selected')
|
||||
target.focus();
|
||||
this.updateCellFromFocusedItem()
|
||||
this.hideDropdown()
|
||||
}
|
||||
}
|
||||
|
|
@ -297,16 +321,14 @@
|
|||
if (ev.keyCode == 40 || ev.keyCode == 38)
|
||||
{
|
||||
// Up or down keys - find previous/next list item and select it
|
||||
var selected = this.findSelectedItem(),
|
||||
newSelectedItem = selected.nextElementSibling
|
||||
var focused = this.findFocusedItem(),
|
||||
newFocusedItem = focused.nextElementSibling
|
||||
|
||||
if (ev.keyCode == 38)
|
||||
newSelectedItem = selected.previousElementSibling
|
||||
newFocusedItem = focused.previousElementSibling
|
||||
|
||||
if (newSelectedItem) {
|
||||
selected.setAttribute('class', '')
|
||||
newSelectedItem.setAttribute('class', 'selected')
|
||||
newSelectedItem.focus()
|
||||
if (newFocusedItem) {
|
||||
newFocusedItem.focus()
|
||||
}
|
||||
|
||||
return
|
||||
|
|
@ -314,22 +336,39 @@
|
|||
|
||||
if (ev.keyCode == 13 || ev.keyCode == 32) {
|
||||
// Return or space keys - update the selected value and hide the editor
|
||||
this.updateCellFromSelectedItem(this.findSelectedItem())
|
||||
|
||||
this.updateCellFromFocusedItem()
|
||||
this.hideDropdown()
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.keyCode == 9) {
|
||||
// Tab - update the selected value and pass control to the table navigation
|
||||
this.updateCellFromSelectedItem(this.findSelectedItem())
|
||||
this.updateCellFromFocusedItem()
|
||||
this.tableObj.navigation.navigateNext(ev)
|
||||
this.tableObj.stopEvent(ev)
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.keyCode == 27) {
|
||||
// Esc - hide the drop-down
|
||||
this.hideDropdown()
|
||||
return
|
||||
}
|
||||
|
||||
this.searchByTextInput(ev, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler for mouse movements over options in the dropdown menu
|
||||
*/
|
||||
DropdownProcessor.prototype.onItemMouseMove = function(ev) {
|
||||
if (!this.itemListElement)
|
||||
return
|
||||
|
||||
var target = this.tableObj.getEventTarget(ev)
|
||||
|
||||
if (target.tagName == 'LI') {
|
||||
target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -338,8 +377,36 @@
|
|||
* for all processors.
|
||||
*/
|
||||
DropdownProcessor.prototype.onKeyDown = function(ev) {
|
||||
if (ev.keyCode == 32)
|
||||
if (!this.itemListElement)
|
||||
return
|
||||
|
||||
if (ev.keyCode == 32 && !this.searching) { // Spacebar
|
||||
this.showDropdown()
|
||||
} else if (ev.keyCode == 40 || ev.keyCode == 38) { // Up and down arrow keys
|
||||
var selected = this.findSelectedItem(),
|
||||
newSelectedItem;
|
||||
|
||||
if (!selected) {
|
||||
if (ev.keyCode == 38) {
|
||||
// Only show an initial item when the down array key is pressed
|
||||
return false
|
||||
}
|
||||
newSelectedItem = this.itemListElement.querySelector('ul li:first-child')
|
||||
} else {
|
||||
newSelectedItem = selected.nextElementSibling
|
||||
|
||||
if (ev.keyCode == 38)
|
||||
newSelectedItem = selected.previousElementSibling
|
||||
}
|
||||
|
||||
if (newSelectedItem) {
|
||||
this.setSelectedItem(newSelectedItem);
|
||||
}
|
||||
|
||||
return false // Stop propogation of event
|
||||
} else {
|
||||
this.searchByTextInput(ev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -386,8 +453,8 @@
|
|||
}
|
||||
|
||||
/*
|
||||
* Determines whether the specified element is some element created by the
|
||||
* processor.
|
||||
* Determines whether the specified element is some element created by the
|
||||
* processor.
|
||||
*/
|
||||
DropdownProcessor.prototype.elementBelongsToProcessor = function(element) {
|
||||
if (!this.itemListElement)
|
||||
|
|
@ -396,5 +463,59 @@
|
|||
return this.tableObj.parentContainsElement(this.itemListElement, element)
|
||||
}
|
||||
|
||||
/*
|
||||
* Provides auto-complete like functionality for typing in a query and selecting
|
||||
* a matching list option
|
||||
*/
|
||||
DropdownProcessor.prototype.searchByTextInput = function(ev, focusOnly) {
|
||||
if (focusOnly === undefined) {
|
||||
focusOnly = false;
|
||||
}
|
||||
var character = ev.key;
|
||||
|
||||
if (character.length === 1 || character === 'Space') {
|
||||
if (!this.searching) {
|
||||
this.searching = true;
|
||||
this.searchQuery = '';
|
||||
}
|
||||
|
||||
this.searchQuery += (character === 'Space') ? ' ' : character;
|
||||
|
||||
// Search for a valid option in dropdown
|
||||
var validItem = null;
|
||||
var query = this.searchQuery;
|
||||
|
||||
this.itemListElement.querySelectorAll('ul li').forEach(function(item) {
|
||||
if (validItem === null && item.dataset.value && item.dataset.value.toLowerCase().indexOf(query.toLowerCase()) === 0) {
|
||||
validItem = item;
|
||||
}
|
||||
});
|
||||
|
||||
if (validItem) {
|
||||
// If a valid item is found, select item and allow for fine-tuning the search query
|
||||
if (focusOnly === true) {
|
||||
validItem.focus();
|
||||
} else {
|
||||
this.setSelectedItem(validItem);
|
||||
}
|
||||
|
||||
if (this.searchInterval) {
|
||||
clearTimeout(this.searchInterval);
|
||||
}
|
||||
|
||||
this.searchInterval = setTimeout(this.cancelTextSearch.bind(this), 1000);
|
||||
} else {
|
||||
this.cancelTextSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.cancelTextSearch = function() {
|
||||
this.searching = false;
|
||||
this.searchQuery = null;
|
||||
this.searchInterval = null;
|
||||
}
|
||||
|
||||
|
||||
$.oc.table.processor.dropdown = DropdownProcessor;
|
||||
}(window.jQuery);
|
||||
}(window.jQuery);
|
||||
|
|
|
|||
|
|
@ -476,11 +476,9 @@ html.cssanimations {
|
|||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.selected {
|
||||
&:focus {
|
||||
background: @color-focus;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue