2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-01-28 09:03:41 +00:00

[UI] Bug fix for table column reordering (#11131)

* [UI] Bug fix for table column reordering

- Ref: https://github.com/icflorescu/mantine-datatable/issues/759
- Adds hash to column cache key based on provided columns
- Prevent infinite loop for react hooks

* Remove previous cache approach

* Remove unused code
This commit is contained in:
Oliver
2026-01-14 10:44:27 +11:00
committed by GitHub
parent f2cef4f266
commit 76cfb442a4
3 changed files with 24 additions and 21 deletions

View File

@@ -24,3 +24,15 @@ export function shortenString({
return `${str.slice(0, N)} ... ${str.slice(-N)}`; return `${str.slice(0, N)} ... ${str.slice(-N)}`;
} }
/**
* Generate a short hash from a long string
*/
export function hashString(str: string): string {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return hash.toString(36);
}

View File

@@ -29,6 +29,7 @@ import { Boundary } from '../components/Boundary';
import { useApi } from '../contexts/ApiContext'; import { useApi } from '../contexts/ApiContext';
import { extractAvailableFields, mapFields } from '../functions/forms'; import { extractAvailableFields, mapFields } from '../functions/forms';
import { showApiErrorMessage } from '../functions/notifications'; import { showApiErrorMessage } from '../functions/notifications';
import { hashString } from '../functions/tables';
import { useLocalState } from '../states/LocalState'; import { useLocalState } from '../states/LocalState';
import { useUserSettingsState } from '../states/SettingsStates'; import { useUserSettingsState } from '../states/SettingsStates';
import { useStoredTableState } from '../states/StoredTableState'; import { useStoredTableState } from '../states/StoredTableState';
@@ -101,7 +102,7 @@ export function InvenTreeTable<T extends Record<string, any>>({
// Key used for caching table data // Key used for caching table data
const cacheKey = useMemo(() => { const cacheKey = useMemo(() => {
const key: string = `table-${tableState.tableKey}`; const key: string = `tbl-${tableState.tableKey}`;
// Remove anything after (and including) "mantine" // Remove anything after (and including) "mantine"
const mantineIndex = key.indexOf('-mantine'); const mantineIndex = key.indexOf('-mantine');
@@ -244,6 +245,12 @@ export function InvenTreeTable<T extends Record<string, any>>({
[tableState.setSelectedRecords] [tableState.setSelectedRecords]
); );
// A hash of the current column configuration
// This is a workaround to fix an issue with mantine-datatable where
// the columns do not update correctly when they are changed dynamically
// Ref: https://github.com/icflorescu/mantine-datatable/issues/759
const [columnHash, setColumnHash] = useState<string>('');
// Update column visibility when hiddenColumns change // Update column visibility when hiddenColumns change
const dataColumns: any = useMemo(() => { const dataColumns: any = useMemo(() => {
let cols: TableColumn[] = columns.filter((col) => col?.hidden != true); let cols: TableColumn[] = columns.filter((col) => col?.hidden != true);
@@ -296,6 +303,9 @@ export function InvenTreeTable<T extends Record<string, any>>({
}); });
} }
const columnNames: string = cols.map((col) => col.accessor).join(',');
setColumnHash(hashString(columnNames));
return cols; return cols;
}, [ }, [
columns, columns,
@@ -328,28 +338,11 @@ export function InvenTreeTable<T extends Record<string, any>>({
// Final state of the table columns // Final state of the table columns
const tableColumns = useDataTableColumns({ const tableColumns = useDataTableColumns({
key: cacheKey, key: `${cacheKey}-${columnHash}`,
columns: dataColumns, columns: dataColumns,
getInitialValueInEffect: false getInitialValueInEffect: false
}); });
// Cache the "ordering" of the columns
const dataColumnsOrder: string[] = useMemo(() => {
return dataColumns.map((col: any) => col.accessor);
}, [dataColumns]);
// Ensure that the "actions" column is always at the end of the list
// This effect is necessary as sometimes the underlying mantine-datatable columns change
useEffect(() => {
// Update the columns order only if it has changed
if (
JSON.stringify(tableColumns.columnsOrder) !=
JSON.stringify(dataColumnsOrder)
) {
tableColumns.setColumnsOrder(dataColumnsOrder);
}
}, [cacheKey, dataColumnsOrder]);
// 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);

View File

@@ -18,7 +18,6 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { ActionButton } from '@lib/components/ActionButton'; import { ActionButton } from '@lib/components/ActionButton';
import { AddItemButton } from '@lib/components/AddItemButton'; import { AddItemButton } from '@lib/components/AddItemButton';
@@ -151,7 +150,6 @@ export default function BuildOutputTable({
}: Readonly<{ build: any; refreshBuild: () => void }>) { }: Readonly<{ build: any; refreshBuild: () => void }>) {
const api = useApi(); const api = useApi();
const user = useUserState(); const user = useUserState();
const navigate = useNavigate();
const table = useTable('build-outputs'); const table = useTable('build-outputs');
const buildId: number = useMemo(() => { const buildId: number = useMemo(() => {