From 2da948c2f19c2afa6c0f92964a2622d0e9a2f144 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 10 Oct 2023 14:36:07 -0400 Subject: more fixes for virtual scrolling and added keeps scroll pos on drag --- .../views/nodes/DataVizBox/components/TableBox.tsx | 138 +++++++++++---------- 1 file changed, 72 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index edf0ea4c7..b8f65cb7d 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -1,7 +1,6 @@ import { Button, Type } from 'browndash-components'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { actionFieldDecorator } from 'mobx/lib/internal'; import * as React from 'react'; import { Doc, Field, NumListCast } from '../../../../../fields/Doc'; import { List } from '../../../../../fields/List'; @@ -32,18 +31,18 @@ interface TableBoxProps { @observer export class TableBox extends React.Component { - @observable startID: number = 0; - @observable endID: number = 0; - _tableContainerRef: HTMLDivElement | null = null; - _tableRef = React.createRef(); - _inputChangedDisposer?: IReactionDisposer; + _containerRef: HTMLDivElement | null = null; + + @observable _scrollTop = -1; + @observable _tableHeight = 0; + @observable _tableContainerHeight = 0; + componentDidMount() { // if the tableData changes (ie., when records are selected by the parent (input) visulization), // then we need to remove any selected rows that are no longer part of the visualized dataset. this._inputChangedDisposer = reaction(() => this._tableData.slice(), this.filterSelectedRowsDown, { fireImmediately: true }); - - this._tableContainerRef && this.handleScroll(); + this.handleScroll(); } componentWillUnmount() { this._inputChangedDisposer?.(); @@ -79,14 +78,17 @@ export class TableBox extends React.Component { return this.props.docView?.()?.props.ScreenToLocalTransform().Scale || 1; } @computed get rowHeight() { - const tableHeight = this._tableRef.current?.getBoundingClientRect().height; - return !tableHeight ? 1 : (this.viewScale * tableHeight) / this._tableDataIds.length; + return (this.viewScale * this._tableHeight) / this._tableDataIds.length; + } + @computed get startID() { + return this.rowHeight ? Math.floor(this._scrollTop / this.rowHeight) : 0; + } + @computed get endID() { + return Math.ceil(this.startID + (this._tableContainerHeight * this.viewScale) / (this.rowHeight || 1)); } - @action handleScroll = () => { - if (this._tableContainerRef) { - this.startID = Math.floor(this._tableContainerRef.scrollTop / this.rowHeight); - this.endID = this.startID + (this._tableContainerRef.getBoundingClientRect().height * this.viewScale) / this.rowHeight; + if (!this.props.docView?.()?.ContentDiv?.hidden) { + this._scrollTop = this._containerRef?.scrollTop ?? 0; } }; @action @@ -175,66 +177,70 @@ export class TableBox extends React.Component { className={`table-container ${this.columns[0]}`} style={{ height: '100%', overflow: 'auto' }} onScroll={this.handleScroll} - ref={r => { - this._tableContainerRef = r; - r?.addEventListener( - 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) - (e: WheelEvent) => { - if (!r.scrollTop && e.deltaY <= 0) e.preventDefault(); - e.stopPropagation(); - }, - { passive: false } - ); - }}> - + ref={action((r: HTMLDivElement | null) => { + this._containerRef = r; + if (!this.props.docView?.()?.ContentDiv?.hidden && r) { + this._tableContainerHeight = r.getBoundingClientRect().height ?? 0; + r.addEventListener( + 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) + (e: WheelEvent) => { + if (!r.scrollTop && e.deltaY <= 0) e.preventDefault(); + e.stopPropagation(); + }, + { passive: false } + ); + } + })}> +
{ + if (!this.props.docView?.()?.ContentDiv?.hidden && r) { + this._tableHeight = r?.getBoundingClientRect().height ?? 0; + } + })}> +
- {this.columns.map(col => - this.startID > 0 ? null : ( - - ) - )} + {this.columns.map(col => ( + + ))} {this._tableDataIds + .filter(rowId => this.startID <= rowId && rowId <= this.endID) ?.map(rowId => ({ record: this.props.records[rowId], rowId })) - .map(({ record, rowId }) => - this.startID > rowId || rowId > this.endID ? ( - - - ) : ( - this.tableRowClick(e, rowId)} - style={{ - background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '', - width: '110%', - }}> - {this.columns.map(col => { - const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false; - return ( - - ); - })} - - ) - )} + .map(({ record, rowId }) => ( + this.tableRowClick(e, rowId)} + style={{ + background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '', + width: '110%', + }}> + {this.columns.map(col => { + const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false; + return ( + + ); + })} + + ))} +
this.columnPointerDown(e, col)}> - {col} - this.columnPointerDown(e, col)}> + {col} +
{/* empty row data for out-of-view items needed to give row the default row height so that scrolling works */} -
-
{record[col]}
-
+
{record[col]}
+
-- cgit v1.2.3-70-g09d2