Table Hook Updates (#6093)

* Update useTable hook

- Storage for "selected rows"
- Aim is to make row selection available outside table component

* Use updated hook to manage row selection

* Add more table data to useTable:

- hidden column selection
- search terms

* Remove unused import

* Remove unused function

* Cleanup
This commit is contained in:
Oliver 2023-12-15 13:53:46 +11:00 committed by GitHub
parent 72167630ac
commit 64671dce20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 40 deletions

View File

@ -1,12 +1,11 @@
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { ActionIcon, Indicator, Space, Stack, Tooltip } from '@mantine/core'; import { ActionIcon, Indicator, Space, Stack, Tooltip } from '@mantine/core';
import { Group } from '@mantine/core'; import { Group } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';
import { IconFilter, IconRefresh } from '@tabler/icons-react'; import { IconFilter, IconRefresh } from '@tabler/icons-react';
import { IconBarcode, IconPrinter } from '@tabler/icons-react'; import { IconBarcode, IconPrinter } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { DataTable, DataTableSortStatus } from 'mantine-datatable'; import { DataTable, DataTableSortStatus } from 'mantine-datatable';
import { Fragment, useEffect, useMemo, useState } from 'react'; import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
import { api } from '../../App'; import { api } from '../../App';
import { TableState } from '../../hooks/UseTable'; import { TableState } from '../../hooks/UseTable';
@ -100,12 +99,6 @@ export function InvenTreeTable<T = any>({
columns: TableColumn<T>[]; columns: TableColumn<T>[];
props: InvenTreeTableProps<T>; props: InvenTreeTableProps<T>;
}) { }) {
// Use the first part of the table key as the table name
const tableName: string = useMemo(() => {
let key = tableState?.tableKey ?? 'table';
return key.split('-')[0];
}, []);
// Build table properties based on provided props (and default props) // Build table properties based on provided props (and default props)
const tableProps: InvenTreeTableProps<T> = useMemo(() => { const tableProps: InvenTreeTableProps<T> = useMemo(() => {
return { return {
@ -119,18 +112,9 @@ export function InvenTreeTable<T = any>({
(col: TableColumn) => col.switchable ?? true (col: TableColumn) => col.switchable ?? true
); );
// A list of hidden columns, saved to local storage const onSelectedRecordsChange = useCallback((records: any[]) => {
const [hiddenColumns, setHiddenColumns] = useLocalStorage<string[]>({ tableState.setSelectedRecords(records);
key: `inventree-hidden-table-columns-${tableName}`, }, []);
defaultValue: []
});
// Data selection
const [selectedRecords, setSelectedRecords] = useState<any[]>([]);
function onSelectedRecordsChange(records: any[]) {
setSelectedRecords(records);
}
// Update column visibility when hiddenColumns change // Update column visibility when hiddenColumns change
const dataColumns: any = useMemo(() => { const dataColumns: any = useMemo(() => {
@ -138,7 +122,7 @@ export function InvenTreeTable<T = any>({
let hidden: boolean = col.hidden ?? false; let hidden: boolean = col.hidden ?? false;
if (col.switchable ?? true) { if (col.switchable ?? true) {
hidden = hiddenColumns.includes(col.accessor); hidden = tableState.hiddenColumns.includes(col.accessor);
} }
return { return {
@ -159,7 +143,7 @@ export function InvenTreeTable<T = any>({
return ( return (
<RowActions <RowActions
actions={tableProps.rowActions?.(record) ?? []} actions={tableProps.rowActions?.(record) ?? []}
disabled={selectedRecords.length > 0} disabled={tableState.selectedRecords.length > 0}
/> />
); );
} }
@ -169,10 +153,10 @@ export function InvenTreeTable<T = any>({
return cols; return cols;
}, [ }, [
columns, columns,
hiddenColumns,
tableProps.rowActions, tableProps.rowActions,
tableProps.enableSelection, tableProps.enableSelection,
selectedRecords tableState.hiddenColumns,
tableState.selectedRecords
]); ]);
// Callback when column visibility is toggled // Callback when column visibility is toggled
@ -185,7 +169,7 @@ export function InvenTreeTable<T = any>({
newColumns[colIdx].hidden = !newColumns[colIdx].hidden; newColumns[colIdx].hidden = !newColumns[colIdx].hidden;
} }
setHiddenColumns( tableState.setHiddenColumns(
newColumns.filter((col) => col.hidden).map((col) => col.accessor) newColumns.filter((col) => col.hidden).map((col) => col.accessor)
); );
} }
@ -196,13 +180,10 @@ export function InvenTreeTable<T = any>({
// Filter list visibility // Filter list visibility
const [filtersVisible, setFiltersVisible] = useState<boolean>(false); const [filtersVisible, setFiltersVisible] = useState<boolean>(false);
// Search term
const [searchTerm, setSearchTerm] = useState<string>('');
// Reset the pagination state when the search term changes // Reset the pagination state when the search term changes
useEffect(() => { useEffect(() => {
setPage(1); setPage(1);
}, [searchTerm]); }, [tableState.searchTerm]);
/* /*
* Construct query filters for the current table * Construct query filters for the current table
@ -218,8 +199,8 @@ export function InvenTreeTable<T = any>({
); );
// Add custom search term // Add custom search term
if (searchTerm) { if (tableState.searchTerm) {
queryParams.search = searchTerm; queryParams.search = tableState.searchTerm;
} }
// Pagination // Pagination
@ -340,7 +321,7 @@ export function InvenTreeTable<T = any>({
default: default:
setMissingRecordsText( setMissingRecordsText(
t`Unknown error` + ': ' + response.statusText t`Unknown error` + ': ' + response.statusText
); // TODO: Translate );
break; break;
} }
@ -352,15 +333,15 @@ export function InvenTreeTable<T = any>({
}); });
}; };
const { data, isError, isFetching, isLoading, refetch } = useQuery({ const { data, isFetching, refetch } = useQuery({
queryKey: [ queryKey: [
tableState.tableKey, page,
props.params, props.params,
sortStatus.columnAccessor, sortStatus.columnAccessor,
sortStatus.direction, sortStatus.direction,
page, tableState.tableKey,
tableState.activeFilters, tableState.activeFilters,
searchTerm tableState.searchTerm
], ],
queryFn: fetchTableData, queryFn: fetchTableData,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
@ -409,7 +390,9 @@ export function InvenTreeTable<T = any>({
<Group position="right" spacing={5}> <Group position="right" spacing={5}>
{tableProps.enableSearch && ( {tableProps.enableSearch && (
<TableSearchInput <TableSearchInput
searchCallback={(term: string) => setSearchTerm(term)} searchCallback={(term: string) =>
tableState.setSearchTerm(term)
}
/> />
)} )}
{tableProps.enableRefresh && ( {tableProps.enableRefresh && (
@ -464,7 +447,7 @@ export function InvenTreeTable<T = any>({
sortStatus={sortStatus} sortStatus={sortStatus}
onSortStatusChange={handleSortStatusChange} onSortStatusChange={handleSortStatusChange}
selectedRecords={ selectedRecords={
tableProps.enableSelection ? selectedRecords : undefined tableProps.enableSelection ? tableState.selectedRecords : undefined
} }
onSelectedRecordsChange={ onSelectedRecordsChange={
tableProps.enableSelection ? onSelectedRecordsChange : undefined tableProps.enableSelection ? onSelectedRecordsChange : undefined

View File

@ -9,13 +9,23 @@ import { TableFilter } from '../components/tables/Filter';
* tableKey: A unique key for the table. When this key changes, the table will be refreshed. * tableKey: A unique key for the table. When this key changes, the table will be refreshed.
* refreshTable: A callback function to externally refresh the table. * refreshTable: A callback function to externally refresh the table.
* activeFilters: An array of active filters (saved to local storage) * activeFilters: An array of active filters (saved to local storage)
* selectedRecords: An array of selected records (rows) in the table
* hiddenColumns: An array of hidden column names
* searchTerm: The current search term for the table
*/ */
export type TableState = { export type TableState = {
tableKey: string; tableKey: string;
refreshTable: () => void;
activeFilters: TableFilter[]; activeFilters: TableFilter[];
setActiveFilters: (filters: TableFilter[]) => void; setActiveFilters: (filters: TableFilter[]) => void;
clearActiveFilters: () => void; clearActiveFilters: () => void;
refreshTable: () => void; selectedRecords: any[];
setSelectedRecords: (records: any[]) => void;
clearSelectedRecords: () => void;
hiddenColumns: string[];
setHiddenColumns: (columns: string[]) => void;
searchTerm: string;
setSearchTerm: (term: string) => void;
}; };
/** /**
@ -49,11 +59,34 @@ export function useTable(tableName: string): TableState {
setActiveFilters([]); setActiveFilters([]);
}, []); }, []);
// Array of selected records
const [selectedRecords, setSelectedRecords] = useState<any[]>([]);
const clearSelectedRecords = useCallback(() => {
setSelectedRecords([]);
}, []);
// A list of hidden columns, saved to local storage
const [hiddenColumns, setHiddenColumns] = useLocalStorage<string[]>({
key: `inventree-hidden-table-columns-${tableName}`,
defaultValue: []
});
// Search term
const [searchTerm, setSearchTerm] = useState<string>('');
return { return {
tableKey, tableKey,
refreshTable,
activeFilters, activeFilters,
setActiveFilters, setActiveFilters,
clearActiveFilters, clearActiveFilters,
refreshTable selectedRecords,
setSelectedRecords,
clearSelectedRecords,
hiddenColumns,
setHiddenColumns,
searchTerm,
setSearchTerm
}; };
} }