Files
motovaultpro/frontend/src/features/admin/hooks/useBulkSelection.ts
2025-11-06 16:29:11 -06:00

115 lines
2.7 KiB
TypeScript

/**
* @ai-summary Hook for managing bulk selection state across paginated data
* @ai-context Supports individual toggle, select all, and reset operations
*/
import { useState, useCallback, useMemo } from 'react';
/**
* Options for bulk selection hook
*/
interface UseBulkSelectionOptions<T> {
items: T[];
keyExtractor?: (item: T) => string;
}
/**
* Return type for bulk selection hook
*/
interface UseBulkSelectionReturn<T> {
selected: Set<string>;
toggleItem: (id: string) => void;
toggleAll: (items: T[]) => void;
isSelected: (id: string) => boolean;
reset: () => void;
count: number;
selectedItems: T[];
}
/**
* Custom hook for managing bulk selection state
* Supports selection across pagination boundaries
*
* @example
* ```typescript
* const { selected, toggleItem, toggleAll, reset, count } = useBulkSelection({ items: data });
* ```
*/
export function useBulkSelection<T extends { id: string }>(
options: UseBulkSelectionOptions<T>
): UseBulkSelectionReturn<T> {
const { items, keyExtractor = (item: T) => item.id } = options;
const [selected, setSelected] = useState<Set<string>>(new Set());
/**
* Toggle individual item selection
*/
const toggleItem = useCallback((id: string) => {
setSelected((prev) => {
const next = new Set(prev);
if (next.has(id)) {
next.delete(id);
} else {
next.add(id);
}
return next;
});
}, []);
/**
* Toggle all items - if all are selected, deselect all; otherwise select all
* This supports "Select All" across pagination
*/
const toggleAll = useCallback((itemsToToggle: T[]) => {
setSelected((prev) => {
const itemIds = itemsToToggle.map(keyExtractor);
const allSelected = itemIds.every((id) => prev.has(id));
if (allSelected) {
// Deselect all items
const next = new Set(prev);
itemIds.forEach((id) => next.delete(id));
return next;
} else {
// Select all items
const next = new Set(prev);
itemIds.forEach((id) => next.add(id));
return next;
}
});
}, [keyExtractor]);
/**
* Check if item is selected
*/
const isSelected = useCallback(
(id: string) => selected.has(id),
[selected]
);
/**
* Clear all selections
*/
const reset = useCallback(() => {
setSelected(new Set());
}, []);
/**
* Get array of selected items from current items list
*/
const selectedItems = useMemo(() => {
return items.filter((item) => selected.has(keyExtractor(item)));
}, [items, selected, keyExtractor]);
return {
selected,
toggleItem,
toggleAll,
isSelected,
reset,
count: selected.size,
selectedItems,
};
}