add keyboard shortcuts to aggrow
Differential Revision: D3724598 fbshipit-source-id: e744d710ccb49f67101abcdded17085f6e7c8b23
This commit is contained in:
parent
a0b3565847
commit
28768a715f
|
@ -80,7 +80,8 @@ function Table(props){_classCallCheck(this,Table);var _this3=_possibleConstructo
|
|||
props));
|
||||
_this3.state={
|
||||
aggrow:props.aggrow,
|
||||
viewport:{top:0,height:100}};return _this3;
|
||||
viewport:{top:0,height:100},
|
||||
cursor:0};return _this3;
|
||||
|
||||
}_createClass(Table,[{key:'scroll',value:function scroll(
|
||||
|
||||
|
@ -88,9 +89,88 @@ e){
|
|||
var viewport=e.target;
|
||||
var top=Math.floor((viewport.scrollTop-viewport.clientHeight*1.0)/rowHeight);
|
||||
var height=Math.ceil(viewport.clientHeight*3.0/rowHeight);
|
||||
this.state.viewport.top=top;
|
||||
this.state.viewport.height=height;
|
||||
this.forceUpdate();
|
||||
if(top!==this.state.viewport.top||height!==this.state.viewport.height){
|
||||
this.setState({viewport:{top:top,height:height}});
|
||||
}
|
||||
}},{key:'_contractRow',value:function _contractRow(
|
||||
|
||||
row){
|
||||
var newCursor=this.state.cursor;
|
||||
if(newCursor>row.top&&newCursor<row.top+row.height){// in contracted section
|
||||
newCursor=row.top;
|
||||
}else if(newCursor>=row.top+row.height){// below contracted section
|
||||
newCursor-=row.height-1;
|
||||
}
|
||||
this.state.aggrow.contract(row);
|
||||
this.setState({cursor:newCursor});
|
||||
}},{key:'_expandRow',value:function _expandRow(
|
||||
|
||||
row){
|
||||
var newCursor=this.state.cursor;
|
||||
this.state.aggrow.expand(row);
|
||||
if(newCursor>row.top){// below expanded section
|
||||
newCursor+=row.height-1;
|
||||
}
|
||||
this.setState({cursor:newCursor});
|
||||
}},{key:'_keepCursorInViewport',value:function _keepCursorInViewport()
|
||||
|
||||
|
||||
|
||||
{
|
||||
if(this._scrollDiv){
|
||||
var cursor=this.state.cursor;
|
||||
var scrollDiv=this._scrollDiv;
|
||||
if(cursor*rowHeight<scrollDiv.scrollTop+scrollDiv.clientHeight*0.1){
|
||||
scrollDiv.scrollTop=cursor*rowHeight-scrollDiv.clientHeight*0.1;
|
||||
}else if((cursor+1)*rowHeight>scrollDiv.scrollTop+scrollDiv.clientHeight*0.9){
|
||||
scrollDiv.scrollTop=(cursor+1)*rowHeight-scrollDiv.clientHeight*0.9;
|
||||
}
|
||||
}
|
||||
}},{key:'keydown',value:function keydown(
|
||||
|
||||
e){
|
||||
var aggrow=this.state.aggrow;
|
||||
var cursor=this.state.cursor;
|
||||
var row=aggrow.getRows(cursor,1)[0];
|
||||
switch(e.keyCode){
|
||||
case 38:// up
|
||||
if(cursor>0){
|
||||
this.setState({cursor:cursor-1});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 40:// down
|
||||
if(cursor<aggrow.getHeight()-1){
|
||||
this.setState({cursor:cursor+1});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 37:// left
|
||||
if(aggrow.canContract(row)){
|
||||
this._contractRow(row);
|
||||
}else if(aggrow.getRowIndent(row)>0){
|
||||
var indent=aggrow.getRowIndent(row)-1;
|
||||
while(aggrow.getRowIndent(row)>indent){
|
||||
cursor--;
|
||||
row=aggrow.getRows(cursor,1)[0];
|
||||
}
|
||||
this.setState({cursor:cursor});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 39:// right
|
||||
if(aggrow.canExpand(row)){
|
||||
this._expandRow(row);
|
||||
}else if(cursor<aggrow.getHeight()-1){
|
||||
this.setState({cursor:cursor+1});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;}
|
||||
|
||||
}},{key:'dropAggregator',value:function dropAggregator(
|
||||
|
||||
s,d){
|
||||
|
@ -114,7 +194,7 @@ dIndex--;
|
|||
active.splice(sIndex,1);
|
||||
active.splice(dIndex,0,dragged);
|
||||
aggrow.setActiveAggregators(active);
|
||||
this.forceUpdate();
|
||||
this.setState({cursor:0});
|
||||
}else if(s.startsWith('expander:active:')){
|
||||
var _sIndex=parseInt(s.substr(16),10);
|
||||
var _dIndex=-1;
|
||||
|
@ -133,7 +213,7 @@ _dIndex--;
|
|||
_active.splice(_sIndex,1);
|
||||
_active.splice(_dIndex,0,_dragged);
|
||||
aggrow.setActiveExpanders(_active);
|
||||
this.forceUpdate();
|
||||
this.setState({cursor:0});
|
||||
}
|
||||
}},{key:'render',value:function render()
|
||||
|
||||
|
@ -218,11 +298,14 @@ borderBottom:'2px solid black'}},
|
|||
|
||||
headers),
|
||||
|
||||
React.createElement('div',{style:{
|
||||
React.createElement('div',{
|
||||
style:{
|
||||
width:'100%',
|
||||
flexGrow:'1',
|
||||
overflow:'scroll'},
|
||||
onScroll:function onScroll(e){return _this4.scroll(e);}},
|
||||
|
||||
onScroll:function onScroll(e){return _this4.scroll(e);},
|
||||
ref:function ref(div){_this4._scrollDiv=div;}},
|
||||
React.createElement('div',{style:{position:'relative'}},
|
||||
this.renderVirtualizedRows()))));
|
||||
|
||||
|
@ -259,6 +342,9 @@ var aggregates=aggrow.getActiveAggregators();
|
|||
if(row.parent!==null&&row.parent.expander%2===0){
|
||||
bg='white';
|
||||
}
|
||||
if(row.top===this.state.cursor){
|
||||
bg='lightblue';
|
||||
}
|
||||
for(var i=0;i<aggregates.length;i++){
|
||||
var aggregate=aggrow.getRowAggregate(row,i);
|
||||
columns.push(
|
||||
|
@ -288,42 +374,76 @@ flexShrink:'0'}}));
|
|||
|
||||
|
||||
if(aggrow.canExpand(row)){
|
||||
rowText+='+';
|
||||
columns.push(
|
||||
React.createElement('div',{
|
||||
style:{
|
||||
marginLeft:indent.toString()+'px',
|
||||
flexShrink:'0',
|
||||
width:'12px',
|
||||
textAlign:'center',
|
||||
border:'1px solid gray'},
|
||||
|
||||
onClick:function onClick(){return _this6._expandRow(row);}},'+'));
|
||||
|
||||
|
||||
}else if(aggrow.canContract(row)){
|
||||
rowText+='-';
|
||||
columns.push(
|
||||
React.createElement('div',{
|
||||
style:{
|
||||
marginLeft:indent.toString()+'px',
|
||||
flexShrink:'0',
|
||||
width:'12px',
|
||||
textAlign:'center',
|
||||
border:'1px solid gray'},
|
||||
|
||||
onClick:function onClick(){return _this6._contractRow(row);}},'-'));
|
||||
|
||||
|
||||
}else{
|
||||
rowText+=' ';
|
||||
columns.push(
|
||||
React.createElement('div',{
|
||||
style:{
|
||||
marginLeft:indent.toString()+'px'}}));
|
||||
|
||||
|
||||
|
||||
}
|
||||
rowText+=aggrow.getRowLabel(row);
|
||||
columns.push(
|
||||
React.createElement('div',{style:{
|
||||
marginLeft:indent.toString()+'px',
|
||||
flexShrink:'0',
|
||||
whiteSpace:'nowrap'}},
|
||||
whiteSpace:'nowrap',
|
||||
marginRight:'20px'}},
|
||||
|
||||
rowText));
|
||||
|
||||
|
||||
return(
|
||||
React.createElement('div',{style:{
|
||||
React.createElement('div',{
|
||||
key:row.top,
|
||||
style:{
|
||||
position:'absolute',
|
||||
height:(rowHeight-1).toString()+'px',
|
||||
top:(rowHeight*row.top).toString()+'px',
|
||||
display:'flex',
|
||||
flexDirection:'row',
|
||||
alignItems:'center',
|
||||
backgroundColor:bg,
|
||||
borderBottom:'1px solid gray'},
|
||||
|
||||
onClick:function onClick(){
|
||||
if(aggrow.canExpand(row)){
|
||||
aggrow.expand(row);
|
||||
_this6.forceUpdate();
|
||||
}else if(aggrow.canContract(row)){
|
||||
aggrow.contract(row);
|
||||
_this6.forceUpdate();
|
||||
}
|
||||
_this6.setState({cursor:row.top});
|
||||
}},
|
||||
columns));
|
||||
|
||||
|
||||
}},{key:'componentDidMount',value:function componentDidMount()
|
||||
|
||||
{
|
||||
this.keydown=this.keydown.bind(this);
|
||||
document.body.addEventListener('keydown',this.keydown);
|
||||
}},{key:'componentWillUnmount',value:function componentWillUnmount()
|
||||
|
||||
{
|
||||
document.body.removeEventListener('keydown',this.keydown);
|
||||
}}]);return Table;}(React.Component);// @generated
|
||||
|
|
|
@ -81,6 +81,7 @@ class Table extends React.Component { // eslint-disable-line no-unused-vars
|
|||
this.state = {
|
||||
aggrow: props.aggrow,
|
||||
viewport: { top: 0, height: 100 },
|
||||
cursor: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -88,9 +89,88 @@ class Table extends React.Component { // eslint-disable-line no-unused-vars
|
|||
const viewport = e.target;
|
||||
const top = Math.floor((viewport.scrollTop - viewport.clientHeight * 1.0) / rowHeight);
|
||||
const height = Math.ceil(viewport.clientHeight * 3.0 / rowHeight);
|
||||
this.state.viewport.top = top;
|
||||
this.state.viewport.height = height;
|
||||
this.forceUpdate();
|
||||
if (top !== this.state.viewport.top || height !== this.state.viewport.height) {
|
||||
this.setState({viewport: {top, height}});
|
||||
}
|
||||
}
|
||||
|
||||
_contractRow(row) {
|
||||
let newCursor = this.state.cursor;
|
||||
if (newCursor > row.top && newCursor < row.top + row.height) { // in contracted section
|
||||
newCursor = row.top;
|
||||
} else if (newCursor >= row.top + row.height) { // below contracted section
|
||||
newCursor -= row.height - 1;
|
||||
}
|
||||
this.state.aggrow.contract(row);
|
||||
this.setState({cursor: newCursor});
|
||||
}
|
||||
|
||||
_expandRow(row) {
|
||||
let newCursor = this.state.cursor;
|
||||
this.state.aggrow.expand(row);
|
||||
if (newCursor > row.top) { // below expanded section
|
||||
newCursor += row.height - 1;
|
||||
}
|
||||
this.setState({cursor: newCursor});
|
||||
}
|
||||
|
||||
_scrollDiv: null;
|
||||
|
||||
_keepCursorInViewport() {
|
||||
if (this._scrollDiv) {
|
||||
const cursor = this.state.cursor;
|
||||
const scrollDiv = this._scrollDiv;
|
||||
if (cursor * rowHeight < scrollDiv.scrollTop + scrollDiv.clientHeight * 0.1) {
|
||||
scrollDiv.scrollTop = cursor * rowHeight - scrollDiv.clientHeight * 0.1;
|
||||
} else if ((cursor + 1) * rowHeight > scrollDiv.scrollTop + scrollDiv.clientHeight * 0.9) {
|
||||
scrollDiv.scrollTop = (cursor + 1) * rowHeight - scrollDiv.clientHeight * 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keydown(e) {
|
||||
const aggrow = this.state.aggrow;
|
||||
let cursor = this.state.cursor;
|
||||
let row = aggrow.getRows(cursor, 1)[0];
|
||||
switch (e.keyCode) {
|
||||
case 38: // up
|
||||
if (cursor > 0) {
|
||||
this.setState({cursor: cursor - 1});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 40: // down
|
||||
if (cursor < aggrow.getHeight() - 1) {
|
||||
this.setState({cursor: cursor + 1});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 37: // left
|
||||
if (aggrow.canContract(row)) {
|
||||
this._contractRow(row);
|
||||
} else if (aggrow.getRowIndent(row) > 0) {
|
||||
const indent = aggrow.getRowIndent(row) - 1;
|
||||
while (aggrow.getRowIndent(row) > indent) {
|
||||
cursor--;
|
||||
row = aggrow.getRows(cursor, 1)[0];
|
||||
}
|
||||
this.setState({cursor: cursor});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 39: // right
|
||||
if (aggrow.canExpand(row)) {
|
||||
this._expandRow(row);
|
||||
} else if (cursor < aggrow.getHeight() - 1) {
|
||||
this.setState({cursor: cursor + 1});
|
||||
this._keepCursorInViewport();
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dropAggregator(s, d) {
|
||||
|
@ -114,7 +194,7 @@ class Table extends React.Component { // eslint-disable-line no-unused-vars
|
|||
active.splice(sIndex, 1);
|
||||
active.splice(dIndex, 0, dragged);
|
||||
aggrow.setActiveAggregators(active);
|
||||
this.forceUpdate();
|
||||
this.setState({cursor:0});
|
||||
} else if (s.startsWith('expander:active:')) {
|
||||
const sIndex = parseInt(s.substr(16), 10);
|
||||
let dIndex = -1;
|
||||
|
@ -133,7 +213,7 @@ class Table extends React.Component { // eslint-disable-line no-unused-vars
|
|||
active.splice(sIndex, 1);
|
||||
active.splice(dIndex, 0, dragged);
|
||||
aggrow.setActiveExpanders(active);
|
||||
this.forceUpdate();
|
||||
this.setState({cursor:0});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,11 +298,14 @@ class Table extends React.Component { // eslint-disable-line no-unused-vars
|
|||
}}>
|
||||
{headers}
|
||||
</div>
|
||||
<div style={{
|
||||
width: '100%',
|
||||
flexGrow: '1',
|
||||
overflow: 'scroll'
|
||||
}} onScroll={ (e) => this.scroll(e) }>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
flexGrow: '1',
|
||||
overflow: 'scroll'
|
||||
}}
|
||||
onScroll={ (e) => this.scroll(e) }
|
||||
ref={(div) => { this._scrollDiv = div; } }>
|
||||
<div style={{ position: 'relative' }}>
|
||||
{ this.renderVirtualizedRows() }
|
||||
</div>
|
||||
|
@ -259,6 +342,9 @@ class Table extends React.Component { // eslint-disable-line no-unused-vars
|
|||
if (row.parent !== null && (row.parent.expander % 2 === 0)) {
|
||||
bg = 'white';
|
||||
}
|
||||
if (row.top === this.state.cursor) {
|
||||
bg = 'lightblue';
|
||||
}
|
||||
for (let i = 0; i < aggregates.length; i++) {
|
||||
var aggregate = aggrow.getRowAggregate(row, i);
|
||||
columns.push((
|
||||
|
@ -288,43 +374,77 @@ class Table extends React.Component { // eslint-disable-line no-unused-vars
|
|||
}}></div>
|
||||
));
|
||||
if (aggrow.canExpand(row)) {
|
||||
rowText += '+';
|
||||
columns.push((
|
||||
<div
|
||||
style={{
|
||||
marginLeft: indent.toString() + 'px',
|
||||
flexShrink: '0',
|
||||
width: '12px',
|
||||
textAlign: 'center',
|
||||
border: '1px solid gray',
|
||||
}}
|
||||
onClick={ () => this._expandRow(row) }
|
||||
>+</div>
|
||||
));
|
||||
} else if (aggrow.canContract(row)) {
|
||||
rowText += '-';
|
||||
columns.push((
|
||||
<div
|
||||
style={{
|
||||
marginLeft: indent.toString() + 'px',
|
||||
flexShrink: '0',
|
||||
width: '12px',
|
||||
textAlign: 'center',
|
||||
border: '1px solid gray',
|
||||
}}
|
||||
onClick={ () => this._contractRow(row) }
|
||||
>-</div>
|
||||
));
|
||||
} else {
|
||||
rowText += ' ';
|
||||
columns.push((
|
||||
<div
|
||||
style={{
|
||||
marginLeft: indent.toString() + 'px',
|
||||
}}
|
||||
></div>
|
||||
));
|
||||
}
|
||||
rowText += aggrow.getRowLabel(row);
|
||||
columns.push((
|
||||
<div style={{
|
||||
marginLeft: indent.toString() + 'px',
|
||||
flexShrink: '0',
|
||||
whiteSpace: 'nowrap'
|
||||
whiteSpace: 'nowrap',
|
||||
marginRight: '20px'
|
||||
}}>
|
||||
{rowText}
|
||||
</div>
|
||||
));
|
||||
return (
|
||||
<div style={{
|
||||
<div
|
||||
key={row.top}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: (rowHeight - 1).toString() + 'px',
|
||||
top: (rowHeight * row.top).toString() + 'px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: bg,
|
||||
borderBottom: '1px solid gray',
|
||||
}}
|
||||
onClick={ () => {
|
||||
if (aggrow.canExpand(row)) {
|
||||
aggrow.expand(row);
|
||||
this.forceUpdate();
|
||||
} else if (aggrow.canContract(row)) {
|
||||
aggrow.contract(row);
|
||||
this.forceUpdate();
|
||||
}
|
||||
this.setState({cursor: row.top});
|
||||
}}>
|
||||
{columns}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.keydown = this.keydown.bind(this);
|
||||
document.body.addEventListener('keydown', this.keydown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.removeEventListener('keydown', this.keydown);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue