Restore persistence in selection when paging (#5349)

This commit is contained in:
WithoutPants 2024-10-07 11:20:20 +11:00 committed by GitHub
parent 7199d2b5ac
commit 35b74be585
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -191,39 +191,55 @@ export function useListKeyboardShortcuts(props: {
} }
export function useListSelect<T extends { id: string }>(items: T[]) { export function useListSelect<T extends { id: string }>(items: T[]) {
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set()); const [itemsSelected, setItemsSelected] = useState<T[]>([]);
const [lastClickedId, setLastClickedId] = useState<string>(); const [lastClickedId, setLastClickedId] = useState<string>();
const prevItems = usePrevious(items); const selectedIds = useMemo(() => {
useEffect(() => {
if (prevItems === items) {
return;
}
// filter out any selectedIds that are no longer in the list
const newSelectedIds = new Set<string>(); const newSelectedIds = new Set<string>();
itemsSelected.forEach((item) => {
selectedIds.forEach((id) => { newSelectedIds.add(item.id);
if (items.some((item) => item.id === id)) {
newSelectedIds.add(id);
}
}); });
setSelectedIds(newSelectedIds); return newSelectedIds;
}, [prevItems, items, selectedIds]); }, [itemsSelected]);
// const prevItems = usePrevious(items);
// #5341 - HACK/TODO: this is a regression of previous behaviour. I don't like the idea
// of keeping selected items that are no longer in the list, since its not
// clear to the user that the item is still selected, but there is now an expectation of
// this behaviour.
// useEffect(() => {
// if (prevItems === items) {
// return;
// }
// // filter out any selectedIds that are no longer in the list
// const newSelectedIds = new Set<string>();
// selectedIds.forEach((id) => {
// if (items.some((item) => item.id === id)) {
// newSelectedIds.add(id);
// }
// });
// setSelectedIds(newSelectedIds);
// }, [prevItems, items, selectedIds]);
function singleSelect(id: string, selected: boolean) { function singleSelect(id: string, selected: boolean) {
setLastClickedId(id); setLastClickedId(id);
const newSelectedIds = new Set(selectedIds); setItemsSelected((prevSelected) => {
if (selected) { if (selected) {
newSelectedIds.add(id); const item = items.find((i) => i.id === id);
} else { if (item) {
newSelectedIds.delete(id); return [...prevSelected, item];
} }
return prevSelected;
setSelectedIds(newSelectedIds); } else {
return prevSelected.filter((item) => item.id !== id);
}
});
} }
function selectRange(startIndex: number, endIndex: number) { function selectRange(startIndex: number, endIndex: number) {
@ -236,13 +252,9 @@ export function useListSelect<T extends { id: string }>(items: T[]) {
} }
const subset = items.slice(start, end + 1); const subset = items.slice(start, end + 1);
const newSelectedIds = new Set<string>();
subset.forEach((item) => { const newSelected = itemsSelected.concat(subset);
newSelectedIds.add(item.id); setItemsSelected(newSelected);
});
setSelectedIds(newSelectedIds);
} }
function multiSelect(id: string) { function multiSelect(id: string) {
@ -271,32 +283,19 @@ export function useListSelect<T extends { id: string }>(items: T[]) {
} }
function onSelectAll() { function onSelectAll() {
const newSelectedIds = new Set<string>(); // #5341 - HACK/TODO: maintaining legacy behaviour of replacing selected items with
items.forEach((item) => { // all items on the current page. To be consistent with the existing behaviour, it
newSelectedIds.add(item.id); // should probably _add_ all items on the current page to the selected items.
}); setItemsSelected([...items]);
setSelectedIds(newSelectedIds);
setLastClickedId(undefined); setLastClickedId(undefined);
} }
function onSelectNone() { function onSelectNone() {
const newSelectedIds = new Set<string>(); setItemsSelected([]);
setSelectedIds(newSelectedIds);
setLastClickedId(undefined); setLastClickedId(undefined);
} }
const getSelected = useMemo(() => { const getSelected = useCallback(() => itemsSelected, [itemsSelected]);
let cached: T[] | undefined;
return () => {
if (cached) {
return cached;
}
cached = items.filter((value) => selectedIds.has(value.id));
return cached;
};
}, [items, selectedIds]);
return { return {
selectedIds, selectedIds,