facebookarchive / fixed-data-table

A React table component designed to allow presenting thousands of rows of data.
http://facebook.github.io/fixed-data-table/
Other
4.3k stars 553 forks source link

Table not scrollable on Mobile #365

Open CoderBlaine opened 8 years ago

CoderBlaine commented 8 years ago

I've created a simple table and it displays fine on pc however when viewing it on mobile it does not scroll.

However the examples seem to scroll any idea what would cause this?

CoderBlaine commented 8 years ago

Here is an example that will not scroll in mobile.

http://pastebin.com/TjgV2ebv

vinayaknagpal commented 8 years ago

I ran into this a few weeks ago. Turns out touch events need to be explicitly captured and handled to get scrolling on mobile devices.

The site examples offer a good starting point, check out this commit

ssolders commented 8 years ago

I'm having this issue as well, has anyone come up with a good solution? Can you give any further directions @vinayaknagpal ?

CoderBlaine commented 8 years ago

Hi,

Scrolling for mobile is not automatically built in. So you need to go through the steps of adding the mobile scroll wrapper as per the commit example posted by vinayaknagal above.

Hopefully they build this feature in to a future version as I imagine almost everyone wants support for mobile.

ssolders commented 8 years ago

Hi, Yea I realize that, I guess I was looking for some more extensive examples. I've managed to get it "working", but having the issue that when I stop scrolling in the table it resets to its initial position (resets to left: 0, top: 0).

It's reset using the _handleScroll function, not due to the component "reseting" and setting the initial state.

I've updated TouchWrapper/TouchableArea to use ES6 class + export so I can use import instead of require, that's the only updates to those files.

Here are my relevant files: https://plnkr.co/edit/S7doxrrMb0mjnl4aYATA

Any ideas?

Edit: I worked out the reset problem by not calling this.props.scroller.doTouchEnd(e.timeStamp) in the handleTouchEnd in TouchableArea.js along with only setting a new top + left value in state if they are > 0.

  handleTouchEnd(e) {
    if (!this.props.scroller || !this.props.touchable) {
      return;
    }
    //Without this the scroller was reset to top:0 left: 0 on touchEnd.
    e.preventDefault();
    return
    // this.props.scroller.doTouchEnd(e.timeStamp);
  } 
    _handleScroll(left, top) {
        //Don't allow to scroll sub 0
        this.setState({
            left: ( left > 0  ? left : 0),
            top: ( top > 0  ? top : 0)
        });
    }
alexandergunnarson commented 8 years ago

I've tried the Zynga Scroller as per the TouchWrapper/TouchableArea example, with the helpful hints provided by @ssolders and @vinayaknagpal . However, because Zynga Scroller simply sets props.scrollLeft and props.scrollTop, which according to #202 has no effect after mount. Due to #202, I've been forced to use an older version of FixedDataTable (the July 22nd version of the branch featured in #216 's pull request), which is inconvenient. I'm not even sure whether it works yet, but I'll post back once I try.

I second @CoderBlaine in advocating that Facebook "build[s] this feature in to a future version as I imagine almost everyone wants support for mobile."

UPDATE: The programmatic scrolling works on desktop! However, the FixedDataTable version in #216 is sadly sufficiently behind the current master branch as to have a different render/data model: it lacks the Cell component and needs props.rowGetter to work. Still trying to work out the kinks in that. But at least it scrolls!

UPDATE 2: The scrolling is very slow... I think I'm going to make @u9520107's changes to the current version and see what happens. I wish Facebook would enable mobile scrolling by default. Oh well.

UPDATE 3: I made the changes and scrolling turned out to be painfully slow. Guess FixedDataTable performance has had that great of an improvement in the past few months. I moved to react-canvas and now it scrolls smooth as butter.

dcoales commented 8 years ago

I'm not familiar with Zynga, TouchWrapper etc but I used the following code. I'm new to this so there is probably an easier way but this seems to work. One thing I noticed was that I had to turn off the scroll bars temporarily when on the desktop for the scrollTop and scrollLeft functions to work after the first render. I've got them permanently turned off on mobile.

`
render(){ var Table = FixedDataTable.Table; return ( <div style={style} ref={ref=>{this.container=ref}} onTouchStart={e=>this.onTouchStart(e)} onTouchEnd={e=>this.onTouchEnd(e)} onTouchMove={e=>this.__onTouchMove(e)}

<Table ref={ref=>this.grid=ref} {...tableProps} rowClassNameGetter={rowClassNameGetter} rowStyleGetter={index => this.rowStyleGetter(index)} width={width} height={height} onRowDoubleClick={onRowDoubleClick} onRowClick={onRowClick} onColumnResizeEndCallback={this.onColumnResizeEndCallback} isColumnResizing={false} onRowMouseEnter={(e, index) => this.onRowMouseEnter(e, index)} onRowMouseLeave={(e, index) => this.onRowMouseLeave(e, index)} onScrollEnd={(x,y)=>this.__onScrollEnd(x,y)} scrollTop={this.state.scrollTop} scrollLeft={this.state.scrollLeft} overflowX={this.state.overflowX} overflowY={this.state.overflowY}

{columns}

); }

__onScrollEnd(x,y){
    // remember the scroll position at the end of mouse or wheel scrolling
    if (!this.touching){
        this.state.scrollLeft = x;
        this.state.scrollTop = y;
    }
}

__onTouchStart(e){
    if (!this.mobile)this.setState({overflowX:'hidden',overflowY:'hidden'});
    this.touching = true;
    this.touchStart = this.__getTouchCoordinates(e);
}
__onTouchMove(e){
    e.preventDefault();
   this.__scrollGrid(e)
}
__onTouchEnd(e){
    this.touching = false;
    if (!this.mobile)this.setState({overflowX:'auto',overflowY:'auto'});
}

__scrollGrid(e){
    var touchEnd = this.__getTouchCoordinates(e);
    var scrollX = touchEnd.x - this.touchStart.x ;
    var scrollY = touchEnd.y - this.touchStart.y;

    var deltaX = scrollX < 0 ? scrollX * -1 : scrollX;
    var deltaY = scrollY < 0 ? scrollY * -1: scrollY;

    var scrollLeft, scrollTop;
    if (deltaX > deltaY){
        scrollLeft = Math.max(0,this.state.scrollLeft - scrollX);
        scrollTop = this.state.scrollTop;
    } else {
        scrollLeft = this.state.scrollLeft;
        scrollTop = Math.max(this.state.scrollTop - scrollY);
    }

    this.setState({scrollLeft: scrollLeft, scrollTop: scrollTop});
    this.touchStart = touchEnd;
}

__getTouchCoordinates(e){
    var touches = e.nativeEvent.changedTouches;
    if (touches && touches.length > 0){
        return {x:touches.item(0).clientX, y: touches.item(0).clientY};
    }
    return false;
}

`

Any suggestions for improvements gratefully received :-)