From 4660b9477b28d4e63bc8a5277890abd52f3f1981 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 7 Jun 2025 19:15:33 +1000 Subject: [PATCH] [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 --- docs/docs/settings/user.md | 1 - src/backend/InvenTree/common/setting/user.py | 6 --- .../src/pages/Index/Settings/UserSettings.tsx | 1 - src/frontend/src/tables/Column.tsx | 2 + src/frontend/src/tables/InvenTreeTable.tsx | 44 ++++++++++++------- .../src/tables/company/CompanyTable.tsx | 1 + src/frontend/src/tables/part/PartTable.tsx | 1 + 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/docs/docs/settings/user.md b/docs/docs/settings/user.md index 55ce786435..69d1026d98 100644 --- a/docs/docs/settings/user.md +++ b/docs/docs/settings/user.md @@ -25,7 +25,6 @@ The *Display Settings* screen shows general display configuration options: {{ usersetting("PART_SHOW_QUANTITY_IN_FORMS") }} {{ usersetting("DISPLAY_SCHEDULE_TAB") }} {{ usersetting("DISPLAY_STOCKTAKE_TAB") }} -{{ usersetting("TABLE_STRING_MAX_LENGTH") }} {{ usersetting("ENABLE_LAST_BREADCRUMB") }} ### Search Settings diff --git a/src/backend/InvenTree/common/setting/user.py b/src/backend/InvenTree/common/setting/user.py index bfb6e6956a..1c1f8db3c3 100644 --- a/src/backend/InvenTree/common/setting/user.py +++ b/src/backend/InvenTree/common/setting/user.py @@ -225,12 +225,6 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { 'default': True, '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': { 'name': _('Show Last Breadcrumb'), 'description': _('Show the current page in breadcrumbs'), diff --git a/src/frontend/src/pages/Index/Settings/UserSettings.tsx b/src/frontend/src/pages/Index/Settings/UserSettings.tsx index c82ad27972..5975fc461e 100644 --- a/src/frontend/src/pages/Index/Settings/UserSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/UserSettings.tsx @@ -56,7 +56,6 @@ export default function UserSettings() { 'PART_SHOW_QUANTITY_IN_FORMS', 'DISPLAY_SCHEDULE_TAB', 'DISPLAY_STOCKTAKE_TAB', - 'TABLE_STRING_MAX_LENGTH', 'ENABLE_LAST_BREADCRUMB' ]} /> diff --git a/src/frontend/src/tables/Column.tsx b/src/frontend/src/tables/Column.tsx index cdd1dcdcb3..445fbadf4d 100644 --- a/src/frontend/src/tables/Column.tsx +++ b/src/frontend/src/tables/Column.tsx @@ -16,6 +16,7 @@ import type { ApiFormFieldType } from '@lib/types/Forms'; * @param filter - A custom filter function * @param filtering - Whether the column is filterable * @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 ellipsis - Whether the column should be ellipsized * @param textAlign - The text alignment of the column @@ -36,6 +37,7 @@ export type TableColumnProps = { filter?: any; filtering?: boolean; width?: number; + resizable?: boolean; noWrap?: boolean; ellipsis?: boolean; textAlign?: 'left' | 'center' | 'right'; diff --git a/src/frontend/src/tables/InvenTreeTable.tsx b/src/frontend/src/tables/InvenTreeTable.tsx index 3cf3f42eca..ea13a6c1ca 100644 --- a/src/frontend/src/tables/InvenTreeTable.tsx +++ b/src/frontend/src/tables/InvenTreeTable.tsx @@ -9,7 +9,8 @@ import { DataTable, type DataTableCellClickHandler, type DataTableRowExpansionProps, - type DataTableSortStatus + type DataTableSortStatus, + useDataTableColumns } from 'mantine-datatable'; import type React from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react'; @@ -147,6 +148,19 @@ export function InvenTreeTable>({ const navigate = useNavigate(); 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 const filters: TableFilter[] = useMemo(() => { return ( @@ -164,7 +178,7 @@ export function InvenTreeTable>({ // Request OPTIONS data from the API, before we load the table const tableOptionQuery = useQuery({ enabled: !!url && !tableData, - queryKey: ['options', url, tableState.tableKey, props.enableColumnCaching], + queryKey: ['options', url, cacheKey, props.enableColumnCaching], retry: 3, refetchOnMount: true, gcTime: 5000, @@ -202,8 +216,6 @@ export function InvenTreeTable>({ } }); - const cacheKey = tableState.tableKey.replaceAll('-', ''); - setFieldNames(names); setTableColumnNames(cacheKey)(names); } @@ -230,8 +242,6 @@ export function InvenTreeTable>({ return; } - const cacheKey = tableState.tableKey.replaceAll('-', ''); - // First check the local cache const cachedNames = getTableColumnNames(cacheKey); @@ -242,7 +252,7 @@ export function InvenTreeTable>({ } tableOptionQuery.refetch(); - }, [url, props.params, props.enableColumnCaching]); + }, [cacheKey, url, props.params, props.enableColumnCaching]); // Build table properties based on provided props (and default props) const tableProps: InvenTreeTableProps = useMemo(() => { @@ -295,7 +305,7 @@ export function InvenTreeTable>({ return { ...col, hidden: hidden, - noWrap: true, + resizable: col.resizable ?? true, title: col.title ?? fieldNames[col.accessor] ?? `${col.accessor}` }; }); @@ -306,6 +316,7 @@ export function InvenTreeTable>({ accessor: '--actions--', title: ' ', hidden: false, + resizable: false, switchable: false, width: 50, render: (record: any, index?: number | undefined) => ( @@ -342,6 +353,12 @@ export function InvenTreeTable>({ ); } + // Final state of the table columns + const tableColumns = useDataTableColumns({ + key: cacheKey, + columns: dataColumns + }); + // Reset the pagination state when the search term changes useEffect(() => { tableState.setPage(1); @@ -759,20 +776,15 @@ export function InvenTreeTable>({ enableSelection ? onSelectedRecordsChange : undefined } rowExpansion={rowExpansion} - // rowStyle={rowStyleCallback} fetching={isFetching} noRecordsText={missingRecordsText} records={tableState.records} - columns={dataColumns} + storeColumnsKey={cacheKey} + columns={tableColumns.effectiveColumns} onCellClick={supportsCellClick ? handleCellClick : undefined} noHeader={tableProps.noHeader ?? false} defaultColumnProps={{ - noWrap: true, - textAlign: 'left', - cellsStyle: () => (theme) => ({ - // TODO @SchrodingersGat : Need a better way of handling "wide" cells, - overflow: 'hidden' - }) + textAlign: 'left' }} onCellContextMenu={ supportsContextMenu ? handleCellContextMenu : undefined diff --git a/src/frontend/src/tables/company/CompanyTable.tsx b/src/frontend/src/tables/company/CompanyTable.tsx index e66b5db66f..228358a5e0 100644 --- a/src/frontend/src/tables/company/CompanyTable.tsx +++ b/src/frontend/src/tables/company/CompanyTable.tsx @@ -43,6 +43,7 @@ export function CompanyTable({ { accessor: 'name', sortable: true, + switchable: false, render: (record: any) => { return ( diff --git a/src/frontend/src/tables/part/PartTable.tsx b/src/frontend/src/tables/part/PartTable.tsx index 66ffaf7bf2..8349a4849c 100644 --- a/src/frontend/src/tables/part/PartTable.tsx +++ b/src/frontend/src/tables/part/PartTable.tsx @@ -35,6 +35,7 @@ function partTableColumns(): TableColumn[] { title: t`Part`, sortable: true, noWrap: true, + switchable: false, render: (record: any) => PartColumn({ part: record }) }, {