fix(data-table): preserve prior rows when multi-selecting
onRowSelectionChange computed the next selection from the render-time `rowSelection` closure, which could be stale by the time a second row was toggled. The updater then produced a selection containing only the latest row, and the persistedMap sync deleted the earlier one — so checking a second part unchecked the first, making multi-select behave as single-select. Track the latest selection in a ref and derive the updater from it, so each toggle builds on the current selection. Fixes adding multiple parts/services/ expense items in invoices, estimates, and job cards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
29d90b5e71
commit
75e44f5b26
@ -39,8 +39,13 @@ export function DataTable<TData>({
|
|||||||
// Persisted map of id → original row data across all pages
|
// Persisted map of id → original row data across all pages
|
||||||
const persistedMap = useRef<Map<string, TData>>(new Map())
|
const persistedMap = useRef<Map<string, TData>>(new Map())
|
||||||
|
|
||||||
// Current-page selection state that TanStack Table controls
|
// Current-page selection state that TanStack Table controls.
|
||||||
|
// A ref mirrors the latest value so onRowSelectionChange never derives the
|
||||||
|
// next selection from a stale closure (which would drop previously-checked
|
||||||
|
// rows and make multi-select behave like single-select).
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||||
|
const rowSelectionRef = useRef<RowSelectionState>({})
|
||||||
|
rowSelectionRef.current = rowSelection
|
||||||
|
|
||||||
// When the page/data changes, restore selection state for the new page from the map
|
// When the page/data changes, restore selection state for the new page from the map
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -50,6 +55,7 @@ export function DataTable<TData>({
|
|||||||
const id = String((row as Record<string, unknown>)[rowKeyStr])
|
const id = String((row as Record<string, unknown>)[rowKeyStr])
|
||||||
if (persistedMap.current.has(id)) restored[id] = true
|
if (persistedMap.current.has(id)) restored[id] = true
|
||||||
})
|
})
|
||||||
|
rowSelectionRef.current = restored
|
||||||
setRowSelection(restored)
|
setRowSelection(restored)
|
||||||
}, [data]) // eslint-disable-line react-hooks/exhaustive-deps
|
}, [data]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
@ -128,7 +134,10 @@ export function DataTable<TData>({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onRowSelectionChange: (updater) => {
|
onRowSelectionChange: (updater) => {
|
||||||
const next = typeof updater === "function" ? updater(rowSelection) : updater
|
// Derive from the ref (always current), not the render-time closure,
|
||||||
|
// so toggling a second row preserves earlier selections.
|
||||||
|
const next = typeof updater === "function" ? updater(rowSelectionRef.current) : updater
|
||||||
|
rowSelectionRef.current = next
|
||||||
setRowSelection(next)
|
setRowSelection(next)
|
||||||
if (selection) {
|
if (selection) {
|
||||||
// Sync current page into the persisted map
|
// Sync current page into the persisted map
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user