import * as React from 'react'; const noop = () => {}; type ID = string | number; interface BatchSelectContextProps { selectedItems: []; itemsSelectedCount: number; isItemSelected: (id: ID) => boolean; isAllItemsSelected: boolean; isPartiallySelected: boolean; selectItem: (id: ID) => void; deselectItem: (id: ID) => void; selectAll: () => void; deselectAll: () => void; toggleItem: (id: ID) => void; toggleAllItems: () => void; } export const BatchSelectContext = React.createContext({ selectedItems: null, itemsSelectedCount: null, isItemSelected: () => false, isAllItemsSelected: null, isPartiallySelected: null, selectItem: noop, deselectItem: noop, selectAll: noop, deselectAll: noop, toggleItem: noop, toggleAllItems: noop, }); interface ReducerState { idKey: ID; selectedItems: (string | number)[]; } interface ReducerAction { type: 'select' | 'deselect' | 'selectAll' | 'deselectAll'; payload?: any; } function reducer(state: ReducerState, action: ReducerAction) { switch (action.type) { case 'select': return { ...state, selectedItems: [...state.selectedItems, action.payload], }; case 'deselect': return { ...state, selectedItems: state.selectedItems.filter(item => item !== action.payload), }; case 'selectAll': return { ...state, selectedItems: action.payload.map((d: any) => d[state.idKey]), }; case 'deselectAll': return { ...state, selectedItems: [], }; default: return state; } } export function useBatchSelect( data: any, { idKey = 'id', initialSelectedItems = [] }: { idKey?: ID; initialSelectedItems?: (ID)[] } = {}, ) { const [state, dispatch] = React.useReducer(reducer, { selectedItems: initialSelectedItems, idKey, }); const selectedItems = state.selectedItems; const itemsSelectedCount = state.selectedItems.length; const isItemSelected = React.useCallback(id => state.selectedItems.indexOf(id) >= 0, [ state.selectedItems, ]); const isAllItemsSelected = data.length === state.selectedItems.length; const isPartiallySelected = selectedItems.length > 0 && !isAllItemsSelected; const selectItem = (id: ID) => dispatch({ type: 'select', payload: id }); const deselectItem = (id: ID) => dispatch({ type: 'deselect', payload: id }); const selectAll = () => dispatch({ type: 'selectAll', payload: data }); const deselectAll = () => dispatch({ type: 'deselectAll' }); const toggleItem = (id: ID) => { const type = isItemSelected(id) ? 'deselect' : 'select'; dispatch({ type, payload: id }); }; const toggleAllItems = () => { if (itemsSelectedCount) { deselectAll(); } else { selectAll(); } }; return { selectedItems, isItemSelected, isAllItemsSelected, itemsSelectedCount, isPartiallySelected, selectItem, deselectItem, selectAll, deselectAll, toggleItem, toggleAllItems, }; }