/** * @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 { items: T[]; keyExtractor?: (item: T) => string; } /** * Return type for bulk selection hook */ interface UseBulkSelectionReturn { selected: Set; 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( options: UseBulkSelectionOptions ): UseBulkSelectionReturn { const { items, keyExtractor = (item: T) => item.id } = options; const [selected, setSelected] = useState>(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, }; }