Allow navigation of query history using the arrow keys. Fixes #2590

This commit is contained in:
Matthew Kleiman
2017-07-20 20:47:40 +01:00
committed by Dave Page
parent 64f3a559ab
commit 21bfcd83f4
5 changed files with 371 additions and 137 deletions

View File

@@ -7,7 +7,10 @@
//
//////////////////////////////////////////////////////////////
/* eslint-disable react/no-find-dom-node */
import React from 'react';
import ReactDOM from 'react-dom';
import SplitPane from 'react-split-pane';
import QueryHistoryEntry from './query_history_entry';
import QueryHistoryDetail from './query_history_detail';
@@ -20,6 +23,9 @@ const queryDetailDivStyle = {
display: 'flex',
};
const ARROWUP = 38;
const ARROWDOWN = 40;
export default class QueryHistory extends React.Component {
constructor(props) {
@@ -29,6 +35,9 @@ export default class QueryHistory extends React.Component {
history: [],
selectedEntry: 0,
};
this.onKeyDownHandler = this.onKeyDownHandler.bind(this);
this.navigateUpAndDown = this.navigateUpAndDown.bind(this);
}
componentWillMount() {
@@ -46,6 +55,17 @@ export default class QueryHistory extends React.Component {
this.resetCurrentHistoryDetail(this.state.history);
}
refocus() {
if (this.state.history.length > 0) {
this.retrieveSelectedQuery().parentElement.focus();
}
}
retrieveSelectedQuery() {
return ReactDOM.findDOMNode(this)
.getElementsByClassName('selected')[0];
}
getCurrentHistoryDetail() {
return this.state.currentHistoryDetail;
}
@@ -80,16 +100,88 @@ export default class QueryHistory extends React.Component {
this.setCurrentHistoryDetail(index, this.state.history);
}
isInvisible(element) {
return this.isAbovePaneTop(element) || this.isBelowPaneBottom(element);
}
isBelowPaneBottom(element) {
const paneElement = ReactDOM.findDOMNode(this).getElementsByClassName('Pane1')[0];
return element.getBoundingClientRect().bottom > paneElement.getBoundingClientRect().bottom;
}
isAbovePaneTop(element) {
const paneElement = ReactDOM.findDOMNode(this).getElementsByClassName('Pane1')[0];
return element.getBoundingClientRect().top < paneElement.getBoundingClientRect().top;
}
navigateUpAndDown(event) {
let arrowKeys = [ARROWUP, ARROWDOWN];
let key = event.keyCode || event.which;
if (arrowKeys.indexOf(key) > -1) {
event.preventDefault();
this.onKeyDownHandler(event);
return false;
}
return true;
}
onKeyDownHandler(event) {
if (this.isArrowDown(event)) {
if (!this.isLastEntry()) {
let nextEntry = this.state.selectedEntry + 1;
this.setCurrentHistoryDetail(nextEntry, this.state.history);
if (this.isInvisible(this.getEntryFromList(nextEntry))) {
this.getEntryFromList(nextEntry).scrollIntoView(false);
}
}
} else if (this.isArrowUp(event)) {
if (!this.isFirstEntry()) {
let previousEntry = this.state.selectedEntry - 1;
this.setCurrentHistoryDetail(previousEntry, this.state.history);
if (this.isInvisible(this.getEntryFromList(previousEntry))) {
this.getEntryFromList(previousEntry).scrollIntoView(true);
}
}
}
}
getEntryFromList(entryIndex) {
return ReactDOM.findDOMNode(this).getElementsByClassName('entry')[entryIndex];
}
isFirstEntry() {
return this.state.selectedEntry === 0;
}
isLastEntry() {
return this.state.selectedEntry === this.state.history.length - 1;
}
isArrowUp(event) {
return (event.keyCode || event.which) === ARROWUP;
}
isArrowDown(event) {
return (event.keyCode || event.which) === ARROWDOWN;
}
render() {
return (
<SplitPane defaultSize="50%" split="vertical" pane1Style={queryEntryListDivStyle}
pane2Style={queryDetailDivStyle}>
<div id='query_list' className="query-history">
<div id='query_list'
className="query-history">
<ul>
{this.retrieveOrderedHistory()
.map((entry, index) =>
<li key={index} className='list-item' onClick={this.onClickHandler.bind(this, index)}>
<QueryHistoryEntry historyEntry={entry} isSelected={index == this.state.selectedEntry}/>
<li key={index} className='list-item' tabIndex='0'
onClick={this.onClickHandler.bind(this, index)}
onKeyDown={this.navigateUpAndDown}>
<QueryHistoryEntry
historyEntry={entry}
isSelected={index == this.state.selectedEntry}/>
</li>)
.value()
}