diff --git a/packages/material-react-table/src/hooks/useMRT_RowVirtualizer.ts b/packages/material-react-table/src/hooks/useMRT_RowVirtualizer.ts index 95883127e..ae35eee57 100644 --- a/packages/material-react-table/src/hooks/useMRT_RowVirtualizer.ts +++ b/packages/material-react-table/src/hooks/useMRT_RowVirtualizer.ts @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { type Range, useVirtualizer } from '@tanstack/react-virtual'; import { type MRT_Row, @@ -36,7 +36,19 @@ export const useMRT_RowVirtualizer = < table, }); - const rowCount = rows?.length ?? getRowModel().rows.length; + const realRows = rows ?? getRowModel().rows; + /** + * when filtering, should find the correct index in filtered rows + */ + const draggingRowIndex = useMemo( + () => + draggingRow?.id + ? realRows.findIndex((r) => r.id === draggingRow?.id) + : undefined, + [realRows, draggingRow?.id], + ); + + const rowCount = realRows.length; const normalRowHeight = density === 'compact' ? 37 : density === 'comfortable' ? 58 : 73; @@ -58,9 +70,9 @@ export const useMRT_RowVirtualizer = < overscan: 4, rangeExtractor: useCallback( (range: Range) => { - return extraIndexRangeExtractor(range, draggingRow?.index ?? 0); + return extraIndexRangeExtractor(range, draggingRowIndex); }, - [draggingRow], + [draggingRowIndex], ), ...rowVirtualizerProps, }) as unknown as MRT_RowVirtualizer; diff --git a/packages/material-react-table/src/utils/virtualization.utils.ts b/packages/material-react-table/src/utils/virtualization.utils.ts index 2b5e8a2d2..77f9975b1 100644 --- a/packages/material-react-table/src/utils/virtualization.utils.ts +++ b/packages/material-react-table/src/utils/virtualization.utils.ts @@ -1,5 +1,10 @@ import { type Range, defaultRangeExtractor } from '@tanstack/react-virtual'; +/** + * When scroll, the `draggingRow` or `draggingColumn` can be removed from document because of virtualization, + * then, the `dragEnd` event on `MRT_TableBodyRowGrabHandle` or `MRT_TableHeadCellGrabHandle` will not fire. + * We should keep the `draggingRow` or `draggingColumn` in `getVirtualItems()` to avoid this thing. + */ export const extraIndexRangeExtractor = ( range: Range, draggingIndex?: number, diff --git a/packages/material-react-table/stories/fixed-bugs/dragging-virtual-when-filtered.stories.tsx b/packages/material-react-table/stories/fixed-bugs/dragging-virtual-when-filtered.stories.tsx new file mode 100644 index 000000000..1ce72a2fb --- /dev/null +++ b/packages/material-react-table/stories/fixed-bugs/dragging-virtual-when-filtered.stories.tsx @@ -0,0 +1,68 @@ +import { faker } from '@faker-js/faker'; +import { type Meta } from '@storybook/react'; +import { useState } from 'react'; +import { + MaterialReactTable, + useMaterialReactTable, + type MRT_ColumnDef, +} from '../../src'; +const meta: Meta = { + title: 'Fixed Bugs/dragging virtual when filtered', +}; +export default meta; +const initData = [...Array(25)].map(() => ({ + age: faker.number.int(20) + 18, + email: faker.internet.email(), + firstName: faker.person.firstName(), + id: faker.string.alphanumeric(6), + lastName: faker.person.lastName(), +})); +initData.push({ + age: 18, + email: 'info@example.com', + firstName: 'Foobar', + lastName: 'Baz', + id: '1', +}); +const columns: MRT_ColumnDef<(typeof initData)[0]>[] = [ + { + accessorKey: 'id', + header: 'ID', + }, + { + accessorKey: 'firstName', + header: 'First Name', + }, + { + accessorKey: 'lastName', + header: 'Last Name', + }, + { + accessorKey: 'email', + header: 'Email Address', + }, + { + accessorKey: 'age', + header: 'Age', + }, + { + accessorKey: 'state', + header: 'State', + }, +]; +export const DraggingRowWhenFiltered = () => { + const [data, _setData] = useState(() => initData); + const t = useMaterialReactTable({ + enableRowVirtualization: true, + enableRowNumbers: true, + columns: columns, + data: data, + enableRowDragging: true, + initialState: { + density: 'compact', + columnFilters: [{ id: 'firstName', value: 'foo' }], + showColumnFilters: true, + }, + }); + return ; +};