diff options
-rw-r--r-- | src/client/views/nodes/DataVizBox/components/TableBox.tsx | 138 |
1 files changed, 72 insertions, 66 deletions
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<TableBoxProps> { - @observable startID: number = 0; - @observable endID: number = 0; - _tableContainerRef: HTMLDivElement | null = null; - _tableRef = React.createRef<HTMLTableElement>(); - _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<TableBoxProps> { 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<TableBoxProps> { 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 } - ); - }}> - <table className="table" ref={this._tableRef}> + 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 } + ); + } + })}> + <table + className="table" + ref={action((r: HTMLTableElement | null) => { + if (!this.props.docView?.()?.ContentDiv?.hidden && r) { + this._tableHeight = r?.getBoundingClientRect().height ?? 0; + } + })}> + <div style={{ height: this.startID * 40 }} /> <thead> <tr> - {this.columns.map(col => - this.startID > 0 ? null : ( - <th - key={this.columns.indexOf(col)} - style={{ - color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined, - background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined, - fontWeight: 'bolder', - border: '3px solid black', - }} - onPointerDown={e => this.columnPointerDown(e, col)}> - {col} - </th> - ) - )} + {this.columns.map(col => ( + <th + key={this.columns.indexOf(col)} + style={{ + color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined, + background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined, + fontWeight: 'bolder', + border: '3px solid black', + }} + onPointerDown={e => this.columnPointerDown(e, col)}> + {col} + </th> + ))} </tr> </thead> <tbody> {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 ? ( - <tr> - <td /> {/* empty row data for out-of-view items needed to give row the default row height so that scrolling works */} - </tr> - ) : ( - <tr - key={rowId} - className={`table-row ${this.columns[0]}`} - onClick={e => 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 ( - <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}> - <div style={{ textOverflow: 'ellipsis', width: '100%', whiteSpace: 'pre', maxWidth: 150, overflow: 'hidden' }}>{record[col]}</div> - </td> - ); - })} - </tr> - ) - )} + .map(({ record, rowId }) => ( + <tr + key={rowId} + className={`table-row ${this.columns[0]}`} + onClick={e => 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 ( + <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}> + <div style={{ textOverflow: 'ellipsis', width: '100%', whiteSpace: 'pre', maxWidth: 150, overflow: 'hidden' }}>{record[col]}</div> + </td> + ); + })} + </tr> + ))} </tbody> + <div style={{ height: (this._tableDataIds.length - this.endID) * 40 }} /> </table> </div> </div> |