2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-15 11:35:41 +00:00

[UI] Adjustable column width (#9744)

* Add prop to column def

* Enable resizable columns

* Enable cell wrapping if required

* Tweak CompanyTable

* Remove old setting

* Tweak for PartTable
This commit is contained in:
Oliver
2025-06-07 19:15:33 +10:00
committed by GitHub
parent 638cb23923
commit 4660b9477b
7 changed files with 32 additions and 24 deletions

View File

@ -25,7 +25,6 @@ The *Display Settings* screen shows general display configuration options:
{{ usersetting("PART_SHOW_QUANTITY_IN_FORMS") }} {{ usersetting("PART_SHOW_QUANTITY_IN_FORMS") }}
{{ usersetting("DISPLAY_SCHEDULE_TAB") }} {{ usersetting("DISPLAY_SCHEDULE_TAB") }}
{{ usersetting("DISPLAY_STOCKTAKE_TAB") }} {{ usersetting("DISPLAY_STOCKTAKE_TAB") }}
{{ usersetting("TABLE_STRING_MAX_LENGTH") }}
{{ usersetting("ENABLE_LAST_BREADCRUMB") }} {{ usersetting("ENABLE_LAST_BREADCRUMB") }}
### Search Settings ### Search Settings

View File

@ -225,12 +225,6 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
'default': True, 'default': True,
'validator': bool, 'validator': bool,
}, },
'TABLE_STRING_MAX_LENGTH': {
'name': _('Table String Length'),
'description': _('Maximum length limit for strings displayed in table views'),
'validator': [int, MinValueValidator(0)],
'default': 100,
},
'ENABLE_LAST_BREADCRUMB': { 'ENABLE_LAST_BREADCRUMB': {
'name': _('Show Last Breadcrumb'), 'name': _('Show Last Breadcrumb'),
'description': _('Show the current page in breadcrumbs'), 'description': _('Show the current page in breadcrumbs'),

View File

@ -56,7 +56,6 @@ export default function UserSettings() {
'PART_SHOW_QUANTITY_IN_FORMS', 'PART_SHOW_QUANTITY_IN_FORMS',
'DISPLAY_SCHEDULE_TAB', 'DISPLAY_SCHEDULE_TAB',
'DISPLAY_STOCKTAKE_TAB', 'DISPLAY_STOCKTAKE_TAB',
'TABLE_STRING_MAX_LENGTH',
'ENABLE_LAST_BREADCRUMB' 'ENABLE_LAST_BREADCRUMB'
]} ]}
/> />

View File

@ -16,6 +16,7 @@ import type { ApiFormFieldType } from '@lib/types/Forms';
* @param filter - A custom filter function * @param filter - A custom filter function
* @param filtering - Whether the column is filterable * @param filtering - Whether the column is filterable
* @param width - The width of the column * @param width - The width of the column
* @param resizable - Whether the column is resizable (defaults to true)
* @param noWrap - Whether the column should wrap * @param noWrap - Whether the column should wrap
* @param ellipsis - Whether the column should be ellipsized * @param ellipsis - Whether the column should be ellipsized
* @param textAlign - The text alignment of the column * @param textAlign - The text alignment of the column
@ -36,6 +37,7 @@ export type TableColumnProps<T = any> = {
filter?: any; filter?: any;
filtering?: boolean; filtering?: boolean;
width?: number; width?: number;
resizable?: boolean;
noWrap?: boolean; noWrap?: boolean;
ellipsis?: boolean; ellipsis?: boolean;
textAlign?: 'left' | 'center' | 'right'; textAlign?: 'left' | 'center' | 'right';

View File

@ -9,7 +9,8 @@ import {
DataTable, DataTable,
type DataTableCellClickHandler, type DataTableCellClickHandler,
type DataTableRowExpansionProps, type DataTableRowExpansionProps,
type DataTableSortStatus type DataTableSortStatus,
useDataTableColumns
} from 'mantine-datatable'; } from 'mantine-datatable';
import type React from 'react'; import type React from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
@ -147,6 +148,19 @@ export function InvenTreeTable<T extends Record<string, any>>({
const navigate = useNavigate(); const navigate = useNavigate();
const { showContextMenu } = useContextMenu(); const { showContextMenu } = useContextMenu();
// Key used for caching table data
const cacheKey = useMemo(() => {
const key: string = `table-${tableState.tableKey}`;
// Remove anything after (and including) "mantine"
const mantineIndex = key.indexOf('-mantine');
if (mantineIndex >= 0) {
return key.substring(0, mantineIndex);
} else {
return key;
}
}, [tableState.tableKey]);
// Construct table filters - note that we can introspect filter labels from column names // Construct table filters - note that we can introspect filter labels from column names
const filters: TableFilter[] = useMemo(() => { const filters: TableFilter[] = useMemo(() => {
return ( return (
@ -164,7 +178,7 @@ export function InvenTreeTable<T extends Record<string, any>>({
// Request OPTIONS data from the API, before we load the table // Request OPTIONS data from the API, before we load the table
const tableOptionQuery = useQuery({ const tableOptionQuery = useQuery({
enabled: !!url && !tableData, enabled: !!url && !tableData,
queryKey: ['options', url, tableState.tableKey, props.enableColumnCaching], queryKey: ['options', url, cacheKey, props.enableColumnCaching],
retry: 3, retry: 3,
refetchOnMount: true, refetchOnMount: true,
gcTime: 5000, gcTime: 5000,
@ -202,8 +216,6 @@ export function InvenTreeTable<T extends Record<string, any>>({
} }
}); });
const cacheKey = tableState.tableKey.replaceAll('-', '');
setFieldNames(names); setFieldNames(names);
setTableColumnNames(cacheKey)(names); setTableColumnNames(cacheKey)(names);
} }
@ -230,8 +242,6 @@ export function InvenTreeTable<T extends Record<string, any>>({
return; return;
} }
const cacheKey = tableState.tableKey.replaceAll('-', '');
// First check the local cache // First check the local cache
const cachedNames = getTableColumnNames(cacheKey); const cachedNames = getTableColumnNames(cacheKey);
@ -242,7 +252,7 @@ export function InvenTreeTable<T extends Record<string, any>>({
} }
tableOptionQuery.refetch(); tableOptionQuery.refetch();
}, [url, props.params, props.enableColumnCaching]); }, [cacheKey, url, props.params, props.enableColumnCaching]);
// 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(() => {
@ -295,7 +305,7 @@ export function InvenTreeTable<T extends Record<string, any>>({
return { return {
...col, ...col,
hidden: hidden, hidden: hidden,
noWrap: true, resizable: col.resizable ?? true,
title: col.title ?? fieldNames[col.accessor] ?? `${col.accessor}` title: col.title ?? fieldNames[col.accessor] ?? `${col.accessor}`
}; };
}); });
@ -306,6 +316,7 @@ export function InvenTreeTable<T extends Record<string, any>>({
accessor: '--actions--', accessor: '--actions--',
title: ' ', title: ' ',
hidden: false, hidden: false,
resizable: false,
switchable: false, switchable: false,
width: 50, width: 50,
render: (record: any, index?: number | undefined) => ( render: (record: any, index?: number | undefined) => (
@ -342,6 +353,12 @@ export function InvenTreeTable<T extends Record<string, any>>({
); );
} }
// Final state of the table columns
const tableColumns = useDataTableColumns({
key: cacheKey,
columns: dataColumns
});
// Reset the pagination state when the search term changes // Reset the pagination state when the search term changes
useEffect(() => { useEffect(() => {
tableState.setPage(1); tableState.setPage(1);
@ -759,20 +776,15 @@ export function InvenTreeTable<T extends Record<string, any>>({
enableSelection ? onSelectedRecordsChange : undefined enableSelection ? onSelectedRecordsChange : undefined
} }
rowExpansion={rowExpansion} rowExpansion={rowExpansion}
// rowStyle={rowStyleCallback}
fetching={isFetching} fetching={isFetching}
noRecordsText={missingRecordsText} noRecordsText={missingRecordsText}
records={tableState.records} records={tableState.records}
columns={dataColumns} storeColumnsKey={cacheKey}
columns={tableColumns.effectiveColumns}
onCellClick={supportsCellClick ? handleCellClick : undefined} onCellClick={supportsCellClick ? handleCellClick : undefined}
noHeader={tableProps.noHeader ?? false} noHeader={tableProps.noHeader ?? false}
defaultColumnProps={{ defaultColumnProps={{
noWrap: true, textAlign: 'left'
textAlign: 'left',
cellsStyle: () => (theme) => ({
// TODO @SchrodingersGat : Need a better way of handling "wide" cells,
overflow: 'hidden'
})
}} }}
onCellContextMenu={ onCellContextMenu={
supportsContextMenu ? handleCellContextMenu : undefined supportsContextMenu ? handleCellContextMenu : undefined

View File

@ -43,6 +43,7 @@ export function CompanyTable({
{ {
accessor: 'name', accessor: 'name',
sortable: true, sortable: true,
switchable: false,
render: (record: any) => { render: (record: any) => {
return ( return (
<Group gap='xs' wrap='nowrap'> <Group gap='xs' wrap='nowrap'>

View File

@ -35,6 +35,7 @@ function partTableColumns(): TableColumn[] {
title: t`Part`, title: t`Part`,
sortable: true, sortable: true,
noWrap: true, noWrap: true,
switchable: false,
render: (record: any) => PartColumn({ part: record }) render: (record: any) => PartColumn({ part: record })
}, },
{ {