From 2da948c2f19c2afa6c0f92964a2622d0e9a2f144 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
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/client/views/nodes/DataVizBox/components/TableBox.tsx')

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>
-- 
cgit v1.2.3-70-g09d2

