From 82c7760af20065d07f24160edddc356dc79fb0e6 Mon Sep 17 00:00:00 2001 From: Laurent Nguyen Date: Thu, 29 Feb 2024 13:39:24 +0100 Subject: [PATCH] Adding table scrolling --- less/documentDB.less | 46 +++++----- package-lock.json | 40 ++++++++ package.json | 2 + .../Tabs/DocumentsTabV2/DocumentsTabV2.tsx | 27 +++++- .../DocumentsTableComponent.tsx | 92 ++++++++++++------- 5 files changed, 149 insertions(+), 58 deletions(-) diff --git a/less/documentDB.less b/less/documentDB.less index d8ae159db..aa970cfe4 100644 --- a/less/documentDB.less +++ b/less/documentDB.less @@ -2264,33 +2264,33 @@ a:link { width: 82px; } -.tabdocuments .scrollable { - height: 100%; - overflow-y: auto; - overflow-x: hidden; - padding-left: 5px; - padding-right: 5px; - width: 100%; -} +// .tabdocuments .scrollable { +// height: 100%; +// overflow-y: auto; +// overflow-x: hidden; +// padding-left: 5px; +// padding-right: 5px; +// width: 100%; +// } -.tabdocuments > .tabdocumentsGridElement { - width: 50%; -} +// .tabdocuments > .tabdocumentsGridElement { +// width: 50%; +// } -.tabdocuments > .evenlySpacedHeader { - width: 30%; -} +// .tabdocuments > .evenlySpacedHeader { +// width: 30%; +// } -.tabdocuments.scrollable:focus, -.tabdocuments.scrollable:active { - outline: 1px dotted; -} +// .tabdocuments.scrollable:focus, +// .tabdocuments.scrollable:active { +// outline: 1px dotted; +// } -.tabdocuments .scrollable table td { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} +// .tabdocuments .scrollable table td { +// white-space: nowrap; +// overflow: hidden; +// text-overflow: ellipsis; +// } .mongoDocumentEditor .monaco-editor.vs .redsquiggly { display: none !important; diff --git a/package-lock.json b/package-lock.json index d3a36e3f3..3fbef591d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "@types/lodash": "4.14.171", "@types/mkdirp": "1.0.1", "@types/node-fetch": "2.5.7", + "@uiw/react-split": "5.9.3", "applicationinsights": "1.8.0", "bootstrap": "3.4.1", "canvas": "file:./canvas", @@ -99,6 +100,7 @@ "react-redux": "7.1.3", "react-splitter-layout": "4.0.0", "react-string-format": "1.0.1", + "react-window": "1.8.10", "react-youtube": "9.0.1", "reflect-metadata": "0.1.13", "rx-jupyter": "5.5.12", @@ -136,6 +138,7 @@ "@types/react-notification-system": "0.2.39", "@types/react-redux": "7.1.7", "@types/react-splitter-layout": "3.0.1", + "@types/react-window": "1.8.8", "@types/sanitize-html": "1.27.2", "@types/sinon": "2.3.3", "@types/styled-components": "5.1.1", @@ -13526,6 +13529,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -14003,6 +14015,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uiw/react-split": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/@uiw/react-split/-/react-split-5.9.3.tgz", + "integrity": "sha512-HgwETU+kRhzZAp+YiE4Yu8bNJm3jxxnGgGPfkadUl8jA1wsMD3aMMskuhQF5akiUUUadiLUvAc8e1RH9Y/SKDw==", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@ungap/url-search-params": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@ungap/url-search-params/-/url-search-params-0.2.2.tgz", @@ -34753,6 +34777,22 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-window": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", + "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-youtube": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-9.0.1.tgz", diff --git a/package.json b/package.json index d3cca1f9c..7e4436622 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "react-splitter-layout": "4.0.0", "react-string-format": "1.0.1", "react-youtube": "9.0.1", + "react-window": "1.8.10", "reflect-metadata": "0.1.13", "rx-jupyter": "5.5.12", "sanitize-html": "2.3.3", @@ -132,6 +133,7 @@ "@types/react-notification-system": "0.2.39", "@types/react-redux": "7.1.7", "@types/react-splitter-layout": "3.0.1", + "@types/react-window": "1.8.8", "@types/sanitize-html": "1.27.2", "@types/sinon": "2.3.3", "@types/styled-components": "5.1.1", diff --git a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx index 1b724779d..09badf11a 100644 --- a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx +++ b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx @@ -14,7 +14,7 @@ import { LocalStorageUtility, StorageKey } from 'Shared/StorageUtility'; import { Action } from 'Shared/Telemetry/TelemetryConstants'; import { userContext } from "UserContext"; import { logConsoleError } from 'Utils/NotificationConsoleUtils'; -import React, { KeyboardEventHandler, useEffect, useMemo, useState } from "react"; +import React, { KeyboardEventHandler, useEffect, useMemo, useRef, useState } from "react"; import { format } from "react-string-format"; import CloseIcon from "../../../../images/close-black.svg"; import * as Constants from "../../../Common/Constants"; @@ -442,6 +442,24 @@ const DocumentsTabComponent: React.FunctionComponent<{ setCurrentDocument(content); }); + const tableContainerRef = useRef(null); + const [tableContainerHeightPx, setTableContainerHeightPx] = useState(undefined); + useEffect(() => { + if (!tableContainerRef.current) { + return undefined; + } + + const resizeObserver = new ResizeObserver(() => { + // Do what you want to do when the size of the element changes + setTableContainerHeightPx(tableContainerRef.current.offsetHeight); + console.log('height', tableContainerRef.current.offsetHeight); + }); + resizeObserver.observe(tableContainerRef.current); + return () => resizeObserver.disconnect(); // clean up + + + }, []); + return
*/} {/* doesn't like to be a flex child */} -
+
-
- +
+
{JSON.stringify(currentDocument, undefined, " ")}
diff --git a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx index a1001e533..53e783aa3 100644 --- a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx +++ b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx @@ -1,5 +1,6 @@ -import { Menu, MenuItem, MenuList, MenuPopover, MenuTrigger, Table, TableBody, TableCell, TableCellLayout, TableColumnDefinition, TableColumnSizingOptions, TableHeader, TableHeaderCell, TableRow, TableRowId, TableSelectionCell, createTableColumn, useArrowNavigationGroup, useTableColumnSizing_unstable, useTableFeatures, useTableSelection } from '@fluentui/react-components'; +import { Menu, MenuItem, MenuList, MenuPopover, MenuTrigger, TableRowData as RowStateBase, Table, TableBody, TableCell, TableCellLayout, TableColumnDefinition, TableColumnSizingOptions, TableHeader, TableHeaderCell, TableRow, TableRowId, TableSelectionCell, createTableColumn, useArrowNavigationGroup, useFluent, useScrollbarWidth, useTableColumnSizing_unstable, useTableFeatures, useTableSelection } from '@fluentui/react-components'; import React, { useEffect } from 'react'; +import { FixedSizeList as List, ListChildComponentProps } from "react-window"; export type DocumentsTableComponentItem = { id: string; @@ -9,6 +10,7 @@ export type DocumentsTableComponentItem = { export interface IDocumentsTableComponentProps { items: DocumentsTableComponentItem[]; onSelectedItem: (index: number) => void; + height: number; style?: React.CSSProperties; } @@ -43,9 +45,52 @@ const columns: TableColumnDefinition[] = [ }), ]; +interface TableRowData extends RowStateBase { + onClick: (e: React.MouseEvent) => void; + onKeyDown: (e: React.KeyboardEvent) => void; + selected: boolean; + appearance: "brand" | "none"; +} +interface ReactWindowRenderFnProps extends ListChildComponentProps { + data: TableRowData[]; +} + +const RenderRow = ({ index, style, data }: ReactWindowRenderFnProps) => { + const { item, selected, appearance, onClick, onKeyDown } = data[index]; + return + + {columns.map((column) => ( + setSelectedRows(new Set([index]))} + onKeyDown={onKeyDown} + // {...columnSizing.getTableCellProps(column.columnId)} + > + {column.renderCell(item)} + + ))} + ; +}; + export const DocumentsTableComponent: React.FC = ({ - items, onSelectedItem, style, + items, onSelectedItem, style, height, }: IDocumentsTableComponentProps) => { + const { targetDocument } = useFluent(); + const scrollbarWidth = useScrollbarWidth({ targetDocument }); + const [activeItemIndex, setActiveItemIndex] = React.useState(undefined); const [columnSizingOptions, setColumnSizingOptions] = React.useState({ @@ -99,7 +144,7 @@ export const DocumentsTableComponent: React.FC = ] ); - const rows = getRows((row) => { + const rows: TableRowData[] = getRows((row) => { const selected = isRowSelected(row.rowId); return { ...row, @@ -150,7 +195,7 @@ export const DocumentsTableComponent: React.FC = }; return ( - +
= ))} + {/** Scrollbar alignment for the header */} +
- - {rows.map(({ item, selected, onClick, onKeyDown, appearance }, index: number) => ( - - - {columns.map((column) => ( - setSelectedRows(new Set([index]))} - onKeyDown={onKeyDown} - {...columnSizing.getTableCellProps(column.columnId)} - > - {column.renderCell(item)} - - ))} - - ))} + + + {RenderRow} +
);