mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-03-13 05:15:30 +00:00
250 lines
7.1 KiB
TypeScript
250 lines
7.1 KiB
TypeScript
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;
|
|
type: string;
|
|
};
|
|
|
|
export interface IDocumentsTableComponentProps {
|
|
items: DocumentsTableComponentItem[];
|
|
onSelectedItem: (index: number) => void;
|
|
height: number;
|
|
style?: React.CSSProperties;
|
|
}
|
|
|
|
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>
|
|
);
|
|
},
|
|
}),
|
|
];
|
|
|
|
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[];
|
|
}
|
|
|
|
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
|
|
items, onSelectedItem, style, height,
|
|
}: IDocumentsTableComponentProps) => {
|
|
const { targetDocument } = useFluent();
|
|
const scrollbarWidth = useScrollbarWidth({ targetDocument });
|
|
|
|
const [activeItemIndex, setActiveItemIndex] = React.useState<number>(undefined);
|
|
|
|
const [columnSizingOptions, setColumnSizingOptions] = React.useState<TableColumnSizingOptions>({
|
|
id: {
|
|
idealWidth: 280,
|
|
minWidth: 273,
|
|
},
|
|
type: {
|
|
minWidth: 110,
|
|
defaultWidth: 120,
|
|
},
|
|
});
|
|
|
|
const onColumnResize = React.useCallback((_, { columnId, width }) => {
|
|
setColumnSizingOptions((state) => ({
|
|
...state,
|
|
[columnId]: {
|
|
...state[columnId],
|
|
idealWidth: width,
|
|
},
|
|
}));
|
|
}, []);
|
|
|
|
const [selectedRows, setSelectedRows] = React.useState<Set<TableRowId>>(
|
|
() => new Set<TableRowId>([0])
|
|
);
|
|
|
|
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>;
|
|
};
|
|
|
|
const {
|
|
getRows,
|
|
columnSizing_unstable: columnSizing,
|
|
tableRef,
|
|
selection: {
|
|
allRowsSelected,
|
|
someRowsSelected,
|
|
toggleAllRows,
|
|
toggleRow,
|
|
isRowSelected,
|
|
},
|
|
} = useTableFeatures(
|
|
{
|
|
columns,
|
|
items,
|
|
},
|
|
[
|
|
useTableColumnSizing_unstable({ columnSizingOptions, onColumnResize }),
|
|
useTableSelection({
|
|
selectionMode: "multiselect",
|
|
selectedItems: selectedRows,
|
|
onSelectionChange: (e, data) => setSelectedRows(data.selectedItems),
|
|
}),
|
|
]
|
|
);
|
|
|
|
const rows: TableRowData[] = 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) {
|
|
const newActiveItemIndex = selectedRows.values().next().value;
|
|
if (newActiveItemIndex !== activeItemIndex) {
|
|
onSelectedItem(newActiveItemIndex);
|
|
setActiveItemIndex(newActiveItemIndex);
|
|
}
|
|
}
|
|
}, [selectedRows, items]);
|
|
|
|
// Cell keyboard navigation
|
|
const keyboardNavAttr = useArrowNavigationGroup({ axis: "grid" });
|
|
|
|
const tableProps = {
|
|
"aria-label": "Filtered documents table",
|
|
role: "grid",
|
|
...columnSizing.getTableProps(),
|
|
...keyboardNavAttr,
|
|
size: "extra-small",
|
|
ref: tableRef,
|
|
...style,
|
|
};
|
|
|
|
return (
|
|
<Table noNativeElements {...tableProps}>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableSelectionCell
|
|
checked={
|
|
allRowsSelected ? true : someRowsSelected ? "mixed" : false
|
|
}
|
|
onClick={toggleAllRows}
|
|
onKeyDown={toggleAllKeydown}
|
|
checkboxIndicator={{ "aria-label": "Select all rows " }}
|
|
/>
|
|
{columns.map((column, index) => (
|
|
<Menu openOnContext key={column.columnId}>
|
|
<MenuTrigger>
|
|
<TableHeaderCell
|
|
key={column.columnId}
|
|
{...columnSizing.getTableHeaderCellProps(column.columnId)}
|
|
>
|
|
{column.renderHeaderCell()}
|
|
</TableHeaderCell>
|
|
</MenuTrigger>
|
|
<MenuPopover>
|
|
<MenuList>
|
|
<MenuItem
|
|
onClick={columnSizing.enableKeyboardMode(
|
|
column.columnId
|
|
)}
|
|
>
|
|
Keyboard Column Resizing
|
|
</MenuItem>
|
|
</MenuList>
|
|
</MenuPopover>
|
|
</Menu>
|
|
))}
|
|
{/** Scrollbar alignment for the header */}
|
|
<div role="presentation" style={{ width: scrollbarWidth }} />
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody style={{ height: "100%", flex: 1 }}>
|
|
<List
|
|
height={height !== undefined ? height - 32 : 0}
|
|
itemCount={items.length}
|
|
itemSize={45}
|
|
width="100%"
|
|
itemData={rows}
|
|
>
|
|
{RenderRow}
|
|
</List>
|
|
</TableBody>
|
|
</Table>
|
|
);
|
|
};
|