mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-04-20 00:27:40 +01:00
Adding table scrolling
This commit is contained in:
parent
c6cec71fd9
commit
82c7760af2
@ -2264,33 +2264,33 @@ a:link {
|
|||||||
width: 82px;
|
width: 82px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabdocuments .scrollable {
|
// .tabdocuments .scrollable {
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
overflow-y: auto;
|
// overflow-y: auto;
|
||||||
overflow-x: hidden;
|
// overflow-x: hidden;
|
||||||
padding-left: 5px;
|
// padding-left: 5px;
|
||||||
padding-right: 5px;
|
// padding-right: 5px;
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.tabdocuments > .tabdocumentsGridElement {
|
// .tabdocuments > .tabdocumentsGridElement {
|
||||||
width: 50%;
|
// width: 50%;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.tabdocuments > .evenlySpacedHeader {
|
// .tabdocuments > .evenlySpacedHeader {
|
||||||
width: 30%;
|
// width: 30%;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.tabdocuments.scrollable:focus,
|
// .tabdocuments.scrollable:focus,
|
||||||
.tabdocuments.scrollable:active {
|
// .tabdocuments.scrollable:active {
|
||||||
outline: 1px dotted;
|
// outline: 1px dotted;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.tabdocuments .scrollable table td {
|
// .tabdocuments .scrollable table td {
|
||||||
white-space: nowrap;
|
// white-space: nowrap;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
// text-overflow: ellipsis;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.mongoDocumentEditor .monaco-editor.vs .redsquiggly {
|
.mongoDocumentEditor .monaco-editor.vs .redsquiggly {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
|
40
package-lock.json
generated
40
package-lock.json
generated
@ -51,6 +51,7 @@
|
|||||||
"@types/lodash": "4.14.171",
|
"@types/lodash": "4.14.171",
|
||||||
"@types/mkdirp": "1.0.1",
|
"@types/mkdirp": "1.0.1",
|
||||||
"@types/node-fetch": "2.5.7",
|
"@types/node-fetch": "2.5.7",
|
||||||
|
"@uiw/react-split": "5.9.3",
|
||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.4.1",
|
||||||
"canvas": "file:./canvas",
|
"canvas": "file:./canvas",
|
||||||
@ -99,6 +100,7 @@
|
|||||||
"react-redux": "7.1.3",
|
"react-redux": "7.1.3",
|
||||||
"react-splitter-layout": "4.0.0",
|
"react-splitter-layout": "4.0.0",
|
||||||
"react-string-format": "1.0.1",
|
"react-string-format": "1.0.1",
|
||||||
|
"react-window": "1.8.10",
|
||||||
"react-youtube": "9.0.1",
|
"react-youtube": "9.0.1",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"rx-jupyter": "5.5.12",
|
"rx-jupyter": "5.5.12",
|
||||||
@ -136,6 +138,7 @@
|
|||||||
"@types/react-notification-system": "0.2.39",
|
"@types/react-notification-system": "0.2.39",
|
||||||
"@types/react-redux": "7.1.7",
|
"@types/react-redux": "7.1.7",
|
||||||
"@types/react-splitter-layout": "3.0.1",
|
"@types/react-splitter-layout": "3.0.1",
|
||||||
|
"@types/react-window": "1.8.8",
|
||||||
"@types/sanitize-html": "1.27.2",
|
"@types/sanitize-html": "1.27.2",
|
||||||
"@types/sinon": "2.3.3",
|
"@types/sinon": "2.3.3",
|
||||||
"@types/styled-components": "5.1.1",
|
"@types/styled-components": "5.1.1",
|
||||||
@ -13526,6 +13529,15 @@
|
|||||||
"@types/react": "*"
|
"@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": {
|
"node_modules/@types/retry": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
||||||
@ -14003,6 +14015,18 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"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": {
|
"node_modules/@ungap/url-search-params": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@ungap/url-search-params/-/url-search-params-0.2.2.tgz",
|
"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"
|
"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": {
|
"node_modules/react-youtube": {
|
||||||
"version": "9.0.1",
|
"version": "9.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-9.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-9.0.1.tgz",
|
||||||
|
@ -96,6 +96,7 @@
|
|||||||
"react-splitter-layout": "4.0.0",
|
"react-splitter-layout": "4.0.0",
|
||||||
"react-string-format": "1.0.1",
|
"react-string-format": "1.0.1",
|
||||||
"react-youtube": "9.0.1",
|
"react-youtube": "9.0.1",
|
||||||
|
"react-window": "1.8.10",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"rx-jupyter": "5.5.12",
|
"rx-jupyter": "5.5.12",
|
||||||
"sanitize-html": "2.3.3",
|
"sanitize-html": "2.3.3",
|
||||||
@ -132,6 +133,7 @@
|
|||||||
"@types/react-notification-system": "0.2.39",
|
"@types/react-notification-system": "0.2.39",
|
||||||
"@types/react-redux": "7.1.7",
|
"@types/react-redux": "7.1.7",
|
||||||
"@types/react-splitter-layout": "3.0.1",
|
"@types/react-splitter-layout": "3.0.1",
|
||||||
|
"@types/react-window": "1.8.8",
|
||||||
"@types/sanitize-html": "1.27.2",
|
"@types/sanitize-html": "1.27.2",
|
||||||
"@types/sinon": "2.3.3",
|
"@types/sinon": "2.3.3",
|
||||||
"@types/styled-components": "5.1.1",
|
"@types/styled-components": "5.1.1",
|
||||||
|
@ -14,7 +14,7 @@ import { LocalStorageUtility, StorageKey } from 'Shared/StorageUtility';
|
|||||||
import { Action } from 'Shared/Telemetry/TelemetryConstants';
|
import { Action } from 'Shared/Telemetry/TelemetryConstants';
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { logConsoleError } from 'Utils/NotificationConsoleUtils';
|
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 { format } from "react-string-format";
|
||||||
import CloseIcon from "../../../../images/close-black.svg";
|
import CloseIcon from "../../../../images/close-black.svg";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
@ -442,6 +442,24 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
setCurrentDocument(content);
|
setCurrentDocument(content);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tableContainerRef = useRef(null);
|
||||||
|
const [tableContainerHeightPx, setTableContainerHeightPx] = useState<number>(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 <FluentProvider theme={dataExplorerLightTheme} style={{ height: "100%" }}>
|
return <FluentProvider theme={dataExplorerLightTheme} style={{ height: "100%" }}>
|
||||||
<div
|
<div
|
||||||
className="tab-pane active"
|
className="tab-pane active"
|
||||||
@ -551,10 +569,11 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
{/* <!-- Filter - End --> */}
|
{/* <!-- Filter - End --> */}
|
||||||
|
|
||||||
{/* <Split> doesn't like to be a flex child */}
|
{/* <Split> doesn't like to be a flex child */}
|
||||||
<div style={{ overflow: "hidden" }}>
|
<div style={{ overflow: "hidden", height: "100%" }}>
|
||||||
<Split>
|
<Split>
|
||||||
<div style={{ minWidth: 440, width: "20%" }}>
|
<div style={{ minWidth: 440, width: "20%", display: "flex", flexDirection: "column", height: "100%" }}
|
||||||
<DocumentsTableComponent style={{ width: "100%", height: "100%" }} items={tableItems} onSelectedItem={onSelectedDocument} />
|
ref={tableContainerRef}>
|
||||||
|
<DocumentsTableComponent style={{ width: "100%", height: "100%" }} items={tableItems} onSelectedItem={onSelectedDocument} height={tableContainerHeightPx} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ minWidth: "20%" }}><pre>{JSON.stringify(currentDocument, undefined, " ")}</pre></div>
|
<div style={{ minWidth: "20%" }}><pre>{JSON.stringify(currentDocument, undefined, " ")}</pre></div>
|
||||||
</Split>
|
</Split>
|
||||||
|
@ -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 React, { useEffect } from 'react';
|
||||||
|
import { FixedSizeList as List, ListChildComponentProps } from "react-window";
|
||||||
|
|
||||||
export type DocumentsTableComponentItem = {
|
export type DocumentsTableComponentItem = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -9,6 +10,7 @@ export type DocumentsTableComponentItem = {
|
|||||||
export interface IDocumentsTableComponentProps {
|
export interface IDocumentsTableComponentProps {
|
||||||
items: DocumentsTableComponentItem[];
|
items: DocumentsTableComponentItem[];
|
||||||
onSelectedItem: (index: number) => void;
|
onSelectedItem: (index: number) => void;
|
||||||
|
height: number;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,9 +45,52 @@ const columns: TableColumnDefinition<DocumentsTableComponentItem>[] = [
|
|||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
interface TableRowData extends RowStateBase<DocumentsTableComponentItem> {
|
||||||
|
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 <TableRow
|
||||||
|
aria-rowindex={index + 2}
|
||||||
|
style={style}
|
||||||
|
key={item.id}
|
||||||
|
// onClick={onClick}
|
||||||
|
// onKeyDown={onKeyDown}
|
||||||
|
aria-selected={selected}
|
||||||
|
appearance={appearance}
|
||||||
|
>
|
||||||
|
<TableSelectionCell
|
||||||
|
checked={selected}
|
||||||
|
checkboxIndicator={{ "aria-label": "Select row" }}
|
||||||
|
onClick={onClick}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
/>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<TableCell
|
||||||
|
key={column.columnId}
|
||||||
|
// onClick={(/* e */) => setSelectedRows(new Set<TableRowId>([index]))}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
// {...columnSizing.getTableCellProps(column.columnId)}
|
||||||
|
>
|
||||||
|
{column.renderCell(item)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>;
|
||||||
|
};
|
||||||
|
|
||||||
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
|
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
|
||||||
items, onSelectedItem, style,
|
items, onSelectedItem, style, height,
|
||||||
}: IDocumentsTableComponentProps) => {
|
}: IDocumentsTableComponentProps) => {
|
||||||
|
const { targetDocument } = useFluent();
|
||||||
|
const scrollbarWidth = useScrollbarWidth({ targetDocument });
|
||||||
|
|
||||||
const [activeItemIndex, setActiveItemIndex] = React.useState<number>(undefined);
|
const [activeItemIndex, setActiveItemIndex] = React.useState<number>(undefined);
|
||||||
|
|
||||||
const [columnSizingOptions, setColumnSizingOptions] = React.useState<TableColumnSizingOptions>({
|
const [columnSizingOptions, setColumnSizingOptions] = React.useState<TableColumnSizingOptions>({
|
||||||
@ -99,7 +144,7 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const rows = getRows((row) => {
|
const rows: TableRowData[] = getRows((row) => {
|
||||||
const selected = isRowSelected(row.rowId);
|
const selected = isRowSelected(row.rowId);
|
||||||
return {
|
return {
|
||||||
...row,
|
...row,
|
||||||
@ -150,7 +195,7 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table {...tableProps}>
|
<Table noNativeElements {...tableProps}>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableSelectionCell
|
<TableSelectionCell
|
||||||
@ -184,35 +229,20 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
|||||||
</MenuPopover>
|
</MenuPopover>
|
||||||
</Menu>
|
</Menu>
|
||||||
))}
|
))}
|
||||||
|
{/** Scrollbar alignment for the header */}
|
||||||
|
<div role="presentation" style={{ width: scrollbarWidth }} />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody style={{ height: "100%", flex: 1 }}>
|
||||||
{rows.map(({ item, selected, onClick, onKeyDown, appearance }, index: number) => (
|
<List
|
||||||
<TableRow
|
height={height - 32}
|
||||||
key={item.id}
|
itemCount={items.length}
|
||||||
// onClick={onClick}
|
itemSize={45}
|
||||||
// onKeyDown={onKeyDown}
|
width="100%"
|
||||||
aria-selected={selected}
|
itemData={rows}
|
||||||
appearance={appearance}
|
|
||||||
>
|
>
|
||||||
<TableSelectionCell
|
{RenderRow}
|
||||||
checked={selected}
|
</List>
|
||||||
checkboxIndicator={{ "aria-label": "Select row" }}
|
|
||||||
onClick={onClick}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
/>
|
|
||||||
{columns.map((column) => (
|
|
||||||
<TableCell
|
|
||||||
key={column.columnId}
|
|
||||||
onClick={(/* e */) => setSelectedRows(new Set<TableRowId>([index]))}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
{...columnSizing.getTableCellProps(column.columnId)}
|
|
||||||
>
|
|
||||||
{column.renderCell(item)}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user