mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +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:
		| @@ -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 | ||||||
|   | |||||||
| @@ -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'), | ||||||
|   | |||||||
| @@ -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' | ||||||
|             ]} |             ]} | ||||||
|           /> |           /> | ||||||
|   | |||||||
| @@ -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'; | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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'> | ||||||
|   | |||||||
| @@ -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 }) | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user