mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-05-19 14:54:40 +01:00
Extract table in a separate component
This commit is contained in:
parent
19066fc11d
commit
ed95f0e922
@ -1,5 +1,5 @@
|
|||||||
import { ItemDefinition, QueryIterator, Resource } from '@azure/cosmos';
|
import { ItemDefinition, QueryIterator, Resource } from '@azure/cosmos';
|
||||||
import { FluentProvider, Table, TableBody, TableCell, TableCellLayout, TableColumnDefinition, TableHeader, TableHeaderCell, TableRow, TableRowId, TableSelectionCell, createTableColumn, useArrowNavigationGroup, useTableFeatures, useTableSelection } from '@fluentui/react-components';
|
import { FluentProvider } from '@fluentui/react-components';
|
||||||
import Split from '@uiw/react-split';
|
import Split from '@uiw/react-split';
|
||||||
import { KeyCodes, QueryCopilotSampleContainerId, QueryCopilotSampleDatabaseId } from "Common/Constants";
|
import { KeyCodes, QueryCopilotSampleContainerId, QueryCopilotSampleDatabaseId } from "Common/Constants";
|
||||||
import { getErrorMessage, getErrorStack } from 'Common/ErrorHandlingUtils';
|
import { getErrorMessage, getErrorStack } from 'Common/ErrorHandlingUtils';
|
||||||
@ -25,11 +25,7 @@ import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcesso
|
|||||||
import * as QueryUtils from "../../../Utils/QueryUtils";
|
import * as QueryUtils from "../../../Utils/QueryUtils";
|
||||||
import DocumentId from "../../Tree/DocumentId";
|
import DocumentId from "../../Tree/DocumentId";
|
||||||
import TabsBase from "../TabsBase";
|
import TabsBase from "../TabsBase";
|
||||||
|
import { DocumentsTableComponent, DocumentsTableComponentItem } from "./DocumentsTableComponent";
|
||||||
type Item = {
|
|
||||||
id: string;
|
|
||||||
type: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class DocumentsTabV2 extends TabsBase {
|
export class DocumentsTabV2 extends TabsBase {
|
||||||
public partitionKey: DataModels.PartitionKey;
|
public partitionKey: DataModels.PartitionKey;
|
||||||
@ -98,6 +94,9 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
const [isExecutionError, setIsExecutionError] = useState<boolean>(false);
|
const [isExecutionError, setIsExecutionError] = useState<boolean>(false);
|
||||||
const [onLoadStartKey, setOnLoadStartKey] = useState<number>(props.onLoadStartKey);
|
const [onLoadStartKey, setOnLoadStartKey] = useState<number>(props.onLoadStartKey);
|
||||||
|
|
||||||
|
|
||||||
|
const [currentDocument, setCurrentDocument] = useState<unknown>(undefined);
|
||||||
|
|
||||||
// TODO remove this?
|
// TODO remove this?
|
||||||
const applyFilterButton = {
|
const applyFilterButton = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@ -423,119 +422,25 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
props.collection?.databaseId === QueryCopilotSampleDatabaseId &&
|
props.collection?.databaseId === QueryCopilotSampleDatabaseId &&
|
||||||
props.collection?.id() === QueryCopilotSampleContainerId;
|
props.collection?.id() === QueryCopilotSampleContainerId;
|
||||||
|
|
||||||
/* Below is for the table config */
|
|
||||||
const items = documentIds.map((documentId) => ({
|
// Table config here
|
||||||
|
const tableItems: DocumentsTableComponentItem[] = documentIds.map((documentId) => ({
|
||||||
id: documentId.id(),
|
id: documentId.id(),
|
||||||
// TODO: for now, merge all the pk values into a single string/column
|
// TODO: for now, merge all the pk values into a single string/column
|
||||||
type: documentId.partitionKeyProperties ? documentId.stringPartitionKeyValues.join(",") : undefined,
|
type: documentId.partitionKeyProperties ? documentId.stringPartitionKeyValues.join(",") : undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const columns: TableColumnDefinition<Item>[] = [
|
|
||||||
createTableColumn<Item>({
|
|
||||||
columnId: "id",
|
|
||||||
compare: (a, b) => {
|
|
||||||
return a.id.localeCompare(b.id);
|
|
||||||
},
|
|
||||||
renderHeaderCell: () => {
|
|
||||||
return "id";
|
|
||||||
},
|
|
||||||
renderCell: (item) => {
|
|
||||||
return (
|
|
||||||
<TableCellLayout>{item.id}</TableCellLayout>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
createTableColumn<Item>({
|
|
||||||
columnId: "type",
|
|
||||||
compare: (a, b) => {
|
|
||||||
return a.type.localeCompare(b.type);
|
|
||||||
},
|
|
||||||
renderHeaderCell: () => {
|
|
||||||
return "/type";
|
|
||||||
},
|
|
||||||
renderCell: (item) => {
|
|
||||||
return (
|
|
||||||
<TableCellLayout>{item.type}</TableCellLayout>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
const [selectedRows, setSelectedRows] = React.useState<Set<TableRowId>>(
|
|
||||||
() => new Set<TableRowId>([0])
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const onSelectedDocument = (index: number) => readSingleDocument(documentIds[index]);
|
||||||
getRows,
|
|
||||||
selection: {
|
|
||||||
allRowsSelected,
|
|
||||||
someRowsSelected,
|
|
||||||
toggleAllRows,
|
|
||||||
toggleRow,
|
|
||||||
isRowSelected,
|
|
||||||
},
|
|
||||||
} = useTableFeatures(
|
|
||||||
{
|
|
||||||
columns,
|
|
||||||
items,
|
|
||||||
},
|
|
||||||
[
|
|
||||||
useTableSelection({
|
|
||||||
selectionMode: "multiselect",
|
|
||||||
selectedItems: selectedRows,
|
|
||||||
onSelectionChange: (e, data) => setSelectedRows(data.selectedItems),
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
const rows = getRows((row) => {
|
|
||||||
const selected = isRowSelected(row.rowId);
|
|
||||||
return {
|
|
||||||
...row,
|
|
||||||
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
|
|
||||||
onKeyDown: (e: React.KeyboardEvent) => {
|
|
||||||
if (e.key === " ") {
|
|
||||||
e.preventDefault();
|
|
||||||
toggleRow(e, row.rowId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selected,
|
|
||||||
appearance: selected ? ("brand" as const) : ("none" as const),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const toggleAllKeydown = React.useCallback(
|
|
||||||
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
||||||
if (e.key === " ") {
|
|
||||||
toggleAllRows(e);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[toggleAllRows]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [currentDocument, setCurrentDocument] = useState<unknown>(undefined);
|
|
||||||
|
|
||||||
// Load document depending on selection
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedRows.size === 1 && documentIds.length > 0) {
|
|
||||||
const documentId = documentIds[selectedRows.values().next().value];
|
|
||||||
|
|
||||||
// TODO: replicate logic of selectedDocument.click();
|
// TODO: replicate logic of selectedDocument.click();
|
||||||
// TODO: Check if editor is dirty
|
// TODO: Check if editor is dirty
|
||||||
|
const readSingleDocument = (documentId: DocumentId) => (_isQueryCopilotSampleContainer
|
||||||
(_isQueryCopilotSampleContainer
|
|
||||||
? readSampleDocument(documentId)
|
? readSampleDocument(documentId)
|
||||||
: readDocument(props.collection, documentId)).then((content) => {
|
: readDocument(props.collection, documentId)).then((content) => {
|
||||||
// this.initDocumentEditor(documentId, content);
|
// this.initDocumentEditor(documentId, content);
|
||||||
setCurrentDocument(content);
|
setCurrentDocument(content);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}, [selectedRows, documentIds]);
|
|
||||||
|
|
||||||
// Cell keyboard navigation
|
|
||||||
const keyboardNavAttr = useArrowNavigationGroup({ axis: "grid" });
|
|
||||||
|
|
||||||
/* End of table config */
|
|
||||||
|
|
||||||
return <FluentProvider theme={dataExplorerLightTheme} style={{ overflow: "hidden" }}>
|
return <FluentProvider theme={dataExplorerLightTheme} style={{ overflow: "hidden" }}>
|
||||||
<div
|
<div
|
||||||
@ -645,47 +550,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
|
|
||||||
<Split style={{ height: "100%" }}>
|
<Split style={{ height: "100%" }}>
|
||||||
<div style={{ minWidth: "20%", width: "20%" }}>
|
<div style={{ minWidth: "20%", width: "20%" }}>
|
||||||
<Table aria-label="Filtered documents table" size="extra-small" {...keyboardNavAttr} role="grid">
|
<DocumentsTableComponent items={tableItems} onSelectedItem={onSelectedDocument} />
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableSelectionCell
|
|
||||||
checked={
|
|
||||||
allRowsSelected ? true : someRowsSelected ? "mixed" : false
|
|
||||||
}
|
|
||||||
onClick={toggleAllRows}
|
|
||||||
onKeyDown={toggleAllKeydown}
|
|
||||||
checkboxIndicator={{ "aria-label": "Select all rows " }}
|
|
||||||
/>
|
|
||||||
<TableHeaderCell>id</TableHeaderCell>
|
|
||||||
<TableHeaderCell>/type</TableHeaderCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{rows.map(({ item, selected, onClick, onKeyDown, appearance }, index: number) => (
|
|
||||||
<TableRow
|
|
||||||
key={item.id}
|
|
||||||
// onClick={onClick}
|
|
||||||
// onKeyDown={onKeyDown}
|
|
||||||
aria-selected={selected}
|
|
||||||
appearance={appearance}
|
|
||||||
>
|
|
||||||
<TableSelectionCell
|
|
||||||
checked={selected}
|
|
||||||
checkboxIndicator={{ "aria-label": "Select row" }}
|
|
||||||
onClick={onClick}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
/>
|
|
||||||
<TableCell onClick={e => setSelectedRows(new Set<TableRowId>([index]))} onKeyDown={onKeyDown}>
|
|
||||||
<TableCellLayout>{item.id}</TableCellLayout>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<TableCellLayout>{item.type}</TableCellLayout>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ minWidth: "20%", flex: 1 }}><pre>{JSON.stringify(currentDocument, undefined, " ")}</pre></div>
|
<div style={{ minWidth: "20%", flex: 1 }}><pre>{JSON.stringify(currentDocument, undefined, " ")}</pre></div>
|
||||||
</Split>
|
</Split>
|
||||||
|
178
src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx
Normal file
178
src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import { Table, TableBody, TableCell, TableCellLayout, TableColumnDefinition, TableHeader, TableHeaderCell, TableRow, TableRowId, TableSelectionCell, createTableColumn, useArrowNavigationGroup, useTableFeatures, useTableSelection } from '@fluentui/react-components';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
export type DocumentsTableComponentItem = {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IDocumentsTableComponentProps {
|
||||||
|
items: DocumentsTableComponentItem[];
|
||||||
|
onSelectedItem: (index: number) => void;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
|
||||||
|
items, onSelectedItem, style,
|
||||||
|
}: IDocumentsTableComponentProps) => {
|
||||||
|
const columns: TableColumnDefinition<DocumentsTableComponentItem>[] = [
|
||||||
|
createTableColumn<DocumentsTableComponentItem>({
|
||||||
|
columnId: "id",
|
||||||
|
compare: (a, b) => {
|
||||||
|
return a.id.localeCompare(b.id);
|
||||||
|
},
|
||||||
|
renderHeaderCell: () => {
|
||||||
|
return "id";
|
||||||
|
},
|
||||||
|
renderCell: (item) => {
|
||||||
|
return (
|
||||||
|
<TableCellLayout>{item.id}</TableCellLayout>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
createTableColumn<DocumentsTableComponentItem>({
|
||||||
|
columnId: "type",
|
||||||
|
compare: (a, b) => {
|
||||||
|
return a.type.localeCompare(b.type);
|
||||||
|
},
|
||||||
|
renderHeaderCell: () => {
|
||||||
|
return "/type";
|
||||||
|
},
|
||||||
|
renderCell: (item) => {
|
||||||
|
return (
|
||||||
|
<TableCellLayout>{item.type}</TableCellLayout>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
// const [columnSizingOptions] = React.useState<TableColumnSizingOptions>({
|
||||||
|
// id: {
|
||||||
|
// idealWidth: 300,
|
||||||
|
// minWidth: 150,
|
||||||
|
// },
|
||||||
|
// type: {
|
||||||
|
// minWidth: 110,
|
||||||
|
// defaultWidth: 250,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
const [selectedRows, setSelectedRows] = React.useState<Set<TableRowId>>(
|
||||||
|
() => new Set<TableRowId>([0])
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
getRows,
|
||||||
|
// columnSizing_unstable,
|
||||||
|
// tableRef,
|
||||||
|
selection: {
|
||||||
|
allRowsSelected,
|
||||||
|
someRowsSelected,
|
||||||
|
toggleAllRows,
|
||||||
|
toggleRow,
|
||||||
|
isRowSelected,
|
||||||
|
},
|
||||||
|
} = useTableFeatures(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
items,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
// useTableColumnSizing_unstable({ columnSizingOptions }),
|
||||||
|
useTableSelection({
|
||||||
|
selectionMode: "multiselect",
|
||||||
|
selectedItems: selectedRows,
|
||||||
|
onSelectionChange: (e, data) => setSelectedRows(data.selectedItems),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const rows = getRows((row) => {
|
||||||
|
const selected = isRowSelected(row.rowId);
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
onClick: (e: React.MouseEvent) => toggleRow(e, row.rowId),
|
||||||
|
onKeyDown: (e: React.KeyboardEvent) => {
|
||||||
|
if (e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleRow(e, row.rowId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selected,
|
||||||
|
appearance: selected ? ("brand" as const) : ("none" as const),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleAllKeydown = React.useCallback(
|
||||||
|
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (e.key === " ") {
|
||||||
|
toggleAllRows(e);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[toggleAllRows]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load document depending on selection
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedRows.size === 1 && items.length > 0) {
|
||||||
|
onSelectedItem(selectedRows.values().next().value);
|
||||||
|
}
|
||||||
|
}, [selectedRows, items]);
|
||||||
|
|
||||||
|
// Cell keyboard navigation
|
||||||
|
const keyboardNavAttr = useArrowNavigationGroup({ axis: "grid" });
|
||||||
|
|
||||||
|
const tableProps = {
|
||||||
|
"aria-label": "Filtered documents table",
|
||||||
|
role: "grid",
|
||||||
|
...keyboardNavAttr,
|
||||||
|
// ...columnSizing_unstable.getTableProps(),
|
||||||
|
size: "extra-small",
|
||||||
|
// ref: tableRef,
|
||||||
|
...style,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table {...tableProps}>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableSelectionCell
|
||||||
|
checked={
|
||||||
|
allRowsSelected ? true : someRowsSelected ? "mixed" : false
|
||||||
|
}
|
||||||
|
onClick={toggleAllRows}
|
||||||
|
onKeyDown={toggleAllKeydown}
|
||||||
|
checkboxIndicator={{ "aria-label": "Select all rows " }}
|
||||||
|
/>
|
||||||
|
<TableHeaderCell>id</TableHeaderCell>
|
||||||
|
<TableHeaderCell>/type</TableHeaderCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{rows.map(({ item, selected, onClick, onKeyDown, appearance }, index: number) => (
|
||||||
|
<TableRow
|
||||||
|
key={item.id}
|
||||||
|
// onClick={onClick}
|
||||||
|
// onKeyDown={onKeyDown}
|
||||||
|
aria-selected={selected}
|
||||||
|
appearance={appearance}
|
||||||
|
>
|
||||||
|
<TableSelectionCell
|
||||||
|
checked={selected}
|
||||||
|
checkboxIndicator={{ "aria-label": "Select row" }}
|
||||||
|
onClick={onClick}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
/>
|
||||||
|
<TableCell onClick={(/* e */) => setSelectedRows(new Set<TableRowId>([index]))} onKeyDown={onKeyDown}>
|
||||||
|
<TableCellLayout>{item.id}</TableCellLayout>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<TableCellLayout>{item.type}</TableCellLayout>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user