import { useCallback, useState } from 'react';
import { useDeepCompareEffect } from 'use-deep-compare';
import useSelectionManager from '../useSelectionManager';
import {
checkCurrentPageSelected,
checkboxState,
compileTitle,
selectOrUnselect,
} from './helpers';
import useCallbacksCallback from '@/Frameworks/AsyncTableTools/hooks/useTableState/hooks/useCallbacksCallback';
/**
* @typedef {object} useBulkSelectReturn
*
* @property {Function} [markRowSelected] "Transformer" function to be passed to the rowsBuilder
* @property {object} [toolbarProps] Object containing PrimaryToolbar props
* @property {object} [tableProps] Object containing Patternfly (v4) Table props
*
*/
/**
* Provides properties for a Pattternfly (based) Table and Toolbar component to implement bulk selection
*
* @param {object} [options] AsyncTableTools options
* @param {number} [options.total] Number to show as total count
* @param {Function} [options.onSelect] function to call when a selection is made
* @param {Array} [options.selected] Array of itemIds that should be currently selected. If it changes, the current selection will change
* @param {Array} [options.preselected] Array of itemIds that should be selected when initialising
* @param {Function} [options.itemIdsInTable] Function to call to retrieve IDs when "Select All" is chosen
* @param {Array} [options.itemIdsOnPage] Array of item ids visible on the page
* @param {string} [options.identifier] Property of the items that should be used as ID to select them
*
* @returns {useBulkSelectReturn} Functions and props to use for setting up bulk selection
*
* @category AsyncTableTools
* @subcategory Hooks
*
*/
const useBulkSelect = ({
total = 0,
onSelect,
selected,
preselected,
itemIdsInTable,
itemIdsOnPage,
identifier = 'itemId',
}) => {
const [loading, setLoading] = useState(false);
const enableBulkSelect = !!onSelect;
const {
selection: selectedIds = [],
set,
select,
deselect,
clear,
reset,
} = useSelectionManager(preselected, {}, onSelect);
const selectedIdsTotal = (selectedIds || []).length;
const paginatedTotal = itemIdsOnPage?.length || total;
const allSelected = selectedIdsTotal === total;
const noneSelected = selectedIdsTotal === 0;
const currentPageSelected = checkCurrentPageSelected(
itemIdsOnPage,
selectedIds,
);
// TODO this is not totally wrong, but when the tree view is active there is currently no total, which causes the selection to be disabled there.
// The bug may not even be fixed here, but in the tables that use selection and the tree view. They will need to provide an appropriate total still
const isDisabled = total === 0;
const checked = checkboxState(selectedIdsTotal, total);
const title = compileTitle(selectedIdsTotal, loading);
const isItemSelected = useCallback(
(itemId) => selectedIds.includes(itemId),
[selectedIds],
);
const selectOne = useCallback(
(_, _selected, _key, { item }) => {
return isItemSelected(item.itemId)
? deselect(item[identifier])
: select(item[identifier]);
},
[isItemSelected, select, deselect, identifier],
);
const selectPage = useCallback(
() =>
!currentPageSelected ? select(itemIdsOnPage) : deselect(itemIdsOnPage),
[select, deselect, itemIdsOnPage, currentPageSelected],
);
const resetSelection = useCallback(() => {
reset();
}, [reset]);
useCallbacksCallback('resetSelection', resetSelection);
const selectAll = async () => {
setLoading(true);
if (allSelected) {
clear();
} else {
set(await itemIdsInTable());
}
setLoading(false);
};
const markRowSelected = useCallback(
(item, rowsForItem, _runningIndex, isTreeTable) => {
const firstRow = rowsForItem[0];
const remainingRows = rowsForItem.slice(1);
return [
{
...firstRow,
...(!isTreeTable
? { selected: selectedIds.includes(item.itemId) }
: {}),
props: {
...firstRow.props,
...(isTreeTable && !item.isTreeBranch
? { isChecked: selectedIds.includes(item.itemId) }
: {}),
},
},
...remainingRows,
];
},
[selectedIds],
);
useDeepCompareEffect(() => {
selected && set(selected);
}, [set, selected]);
return {
tableView: {
enableBulkSelect,
markRowSelected,
isItemSelected,
select,
deselect,
},
...(enableBulkSelect
? {
tableProps: {
onSelect: total > 0 ? selectOne : undefined,
canSelectAll: false,
},
toolbarProps: {
bulkSelect: {
toggleProps: { children: [title] },
isDisabled,
items: [
{
title: 'Select none',
onClick: clear,
props: {
isDisabled: noneSelected,
},
},
...(itemIdsOnPage
? [
{
title: `${selectOrUnselect(
currentPageSelected,
)} page (${paginatedTotal} items)`,
onClick: selectPage,
},
]
: []),
...(itemIdsInTable
? [
{
title: `${selectOrUnselect(
allSelected,
)} all (${total} items)`,
onClick: selectAll,
},
]
: []),
],
checked,
onSelect: !isDisabled ? selectPage : undefined,
},
},
}
: {}),
};
};
export default useBulkSelect;
Source