@hitachivantara/uikit-react-core
Version:
Core React components for the NEXT Design System.
443 lines (442 loc) • 15 kB
JavaScript
import { jsx } from "react/jsx-runtime";
import { useMemo, useCallback } from "react";
import { actions, ensurePluginOrder, useGetLatest, useMountedLayoutEffect, makePropGetter } from "react-table";
import { useLabels } from "../../hooks/useLabels.js";
import { HvCheckBox } from "../../CheckBox/CheckBox.js";
actions.resetSelectedRows = "resetSelectedRows";
actions.toggleAllRowsSelected = "toggleAllRowsSelected";
actions.toggleRowSelected = "toggleRowSelected";
actions.toggleAllPageRowsSelected = "toggleAllPageRowsSelected";
actions.resetLockedSelectionRows = "resetLockedSelectionRows";
actions.toggleRowLockedSelection = "toggleRowLockedSelection";
const DEFAULT_LABELS = {
selectRowCheckBoxAriaLabel: "Select this row"
};
const hideHeaderVariants = ["checkbox", "actions"];
const CellWithCheckBox = ({ row, labels: labelsProp }) => {
const labels = useLabels(DEFAULT_LABELS, labelsProp);
const { onChange, checked, disabled, indeterminate } = row.getToggleRowSelectedProps();
return /* @__PURE__ */ jsx(
HvCheckBox,
{
onChange,
checked,
disabled,
indeterminate,
"aria-label": labels.selectRowCheckBoxAriaLabel
}
);
};
const visibleColumnsHook = (columns) => {
const selectionColumn = {
id: "_hv_selection",
variant: "checkbox",
width: 32,
// this will only work when using useHvTableSticky
// but ensures it stays left of any sticky column
sticky: "left",
Cell: CellWithCheckBox
};
return [selectionColumn, ...columns];
};
const getHeaderPropsHook = (props, { column }) => {
const nextProps = {};
if (hideHeaderVariants.includes(column.variant)) {
nextProps["aria-hidden"] = true;
}
return [props, nextProps];
};
const getRowPropsHook = (props, { row }) => {
const nextProps = {
selected: row.isSelected
};
return [props, nextProps];
};
const defaultGetToggleRowSelectedProps = (props, meta) => {
const { instance, row } = meta;
const { manualRowSelectedKey = "isSelected" } = instance;
let checked = false;
if (row.original?.[manualRowSelectedKey]) {
checked = true;
} else {
checked = row.isSelected;
}
return [
props,
{
onChange: (e, check) => {
row.toggleRowSelected(check ?? e?.target?.checked);
},
disabled: row.isSelectionLocked,
checked,
indeterminate: row.isSomeSelected
}
];
};
const defaultGetToggleAllRowsSelectedProps = (props, { instance }) => [
props,
{
onChange: (e) => {
instance.toggleAllRowsSelected(e.target.checked);
},
checked: instance.isAllRowsSelected,
indeterminate: Boolean(
!instance.isAllRowsSelected && Object.keys(instance.state.selectedRowIds).length
)
}
];
const defaultGetToggleAllPageRowsSelectedProps = (props, { instance }) => [
props,
{
onChange(e) {
instance.toggleAllPageRowsSelected(e.target.checked);
},
checked: instance.isAllPageRowsSelected,
indeterminate: Boolean(
!instance.isAllPageRowsSelected && instance.page.some(({ id }) => instance.state.selectedRowIds[id])
)
}
];
function reducer(state, action, previousState, instance) {
if (action.type === actions.init) {
return {
selectedRowIds: {},
lockedSelectionRowIds: {},
...state
};
}
if (action.type === actions.resetSelectedRows) {
return {
...state,
selectedRowIds: instance.initialState.selectedRowIds || {}
};
}
if (action.type === actions.toggleAllRowsSelected) {
const { value: setSelected } = action;
const {
isAllRowsSelected,
rowsById,
initialRowsById,
nonGroupedRowsById = rowsById,
applyToggleAllRowsSelectedToPrefilteredRows
} = instance;
const rowsToSelect = applyToggleAllRowsSelectedToPrefilteredRows ? initialRowsById : nonGroupedRowsById;
const selectAll = typeof setSelected !== "undefined" ? setSelected : !isAllRowsSelected;
const selectedRowIds = { ...state.selectedRowIds };
if (selectAll) {
Object.keys(rowsToSelect).forEach((rowId) => {
const isSelectionLocked = state.lockedSelectionRowIds[rowId];
if (!isSelectionLocked) {
selectedRowIds[rowId] = true;
}
});
} else {
Object.keys(rowsToSelect).forEach((rowId) => {
const isSelectionLocked = state.lockedSelectionRowIds[rowId];
if (!isSelectionLocked) {
delete selectedRowIds[rowId];
}
});
}
return {
...state,
selectedRowIds
};
}
if (action.type === actions.toggleRowSelected) {
const { id, value: setSelected } = action;
const isSelectionLocked = state.lockedSelectionRowIds[id];
if (isSelectionLocked) {
return state;
}
const { rowsById, selectSubRows = true, getSubRows } = instance;
const isSelected = state.selectedRowIds[id];
const shouldExist = typeof setSelected !== "undefined" ? setSelected : !isSelected;
if (isSelected === shouldExist) {
return state;
}
const newSelectedRowIds = { ...state.selectedRowIds };
const handleRowById = (rowId) => {
const row = rowsById[rowId];
if (!row.isGrouped) {
if (shouldExist) {
newSelectedRowIds[rowId] = true;
} else {
delete newSelectedRowIds[rowId];
}
}
if (selectSubRows && getSubRows(row)) {
getSubRows(row).forEach((subrow) => {
handleRowById(subrow.id);
});
}
};
handleRowById(id);
return {
...state,
selectedRowIds: newSelectedRowIds
};
}
if (action.type === actions.toggleAllPageRowsSelected) {
const { value: setSelected } = action;
const {
page,
rowsById,
selectSubRows = true,
isAllPageRowsSelected,
getSubRows
} = instance;
const selectAll = typeof setSelected !== "undefined" ? setSelected : !isAllPageRowsSelected;
const newSelectedRowIds = { ...state.selectedRowIds };
const handleRowById = (rowId) => {
const row = rowsById[rowId];
const isSelectionLocked = state.lockedSelectionRowIds[rowId];
if (!isSelectionLocked && !row.isGrouped) {
if (selectAll) {
newSelectedRowIds[rowId] = true;
} else {
delete newSelectedRowIds[rowId];
}
}
if (selectSubRows && getSubRows(row)) {
getSubRows(row).forEach((subrow) => {
handleRowById(subrow.id);
});
}
};
page.forEach((row) => handleRowById(row.id));
return {
...state,
selectedRowIds: newSelectedRowIds
};
}
if (action.type === actions.resetLockedSelectionRows) {
return {
...state,
lockedSelectionRowIds: instance.initialState.lockedSelectionRowIds || {}
};
}
if (action.type === actions.toggleRowLockedSelection) {
const { id, value: setLockedSelection } = action;
const { rowsById, selectSubRows = true, getSubRows } = instance;
const isLockedSelection = state.lockedSelectionRowIds[id];
const shouldExist = typeof setLockedSelection !== "undefined" ? setLockedSelection : !isLockedSelection;
if (isLockedSelection === shouldExist) {
return state;
}
const newLockedSelectionRowIds = { ...state.lockedSelectionRowIds };
const handleRowById = (rowId) => {
const row = rowsById[rowId];
if (!row.isGrouped) {
if (shouldExist) {
newLockedSelectionRowIds[rowId] = true;
} else {
delete newLockedSelectionRowIds[rowId];
}
}
if (selectSubRows && getSubRows(row)) {
getSubRows(row).forEach((subrow) => {
handleRowById(subrow.id);
});
}
};
handleRowById(id);
return {
...state,
lockedSelectionRowIds: newLockedSelectionRowIds
};
}
return state;
}
function getRowIsSelected(row, selectedRowIds, getSubRows) {
if (selectedRowIds[row.id]) {
return true;
}
const subRows = getSubRows(row);
if (subRows?.length) {
let allChildrenSelected = true;
let someSelected = false;
subRows.forEach((subRow) => {
if (someSelected && !allChildrenSelected) {
return;
}
if (getRowIsSelected(subRow, selectedRowIds, getSubRows)) {
someSelected = true;
} else {
allChildrenSelected = false;
}
});
if (allChildrenSelected) {
return true;
}
if (someSelected) {
return null;
}
}
return false;
}
function useInstance(instance) {
const {
data,
rows,
getHooks,
plugins,
rowsById,
initialRowsById,
nonGroupedRowsById = rowsById,
autoResetSelectedRows = true,
autoResetLockedSelectionRows = true,
state: { selectedRowIds, lockedSelectionRowIds },
selectSubRows = true,
dispatch,
page,
getSubRows,
applyToggleAllRowsSelectedToPrefilteredRows
} = instance;
ensurePluginOrder(
plugins,
["useFilters", "useGroupBy", "useSortBy", "useExpanded", "usePagination"],
"useHvRowSelection"
);
const rowsToSelect = applyToggleAllRowsSelectedToPrefilteredRows ? initialRowsById : nonGroupedRowsById;
const selectedFlatRows = useMemo(() => {
const selectedRows = [];
rows.forEach((row) => {
const isSelected = selectSubRows ? getRowIsSelected(row, selectedRowIds, getSubRows) : !!selectedRowIds[row.id];
row.isSelected = !!isSelected;
row.isSomeSelected = isSelected === null;
if (isSelected) {
selectedRows.push(row);
}
});
return selectedRows;
}, [rows, selectSubRows, selectedRowIds, getSubRows]);
const existsLockedRows = !!Object.keys(lockedSelectionRowIds).length;
const isNoRowsSelected = !(Object.keys(rowsToSelect).length && selectedFlatRows.length);
let isNoPageRowsSelected;
let isAllRowsSelected;
let isAllPageRowsSelected;
let isAllSelectableRowsUnselected;
let isAllSelectablePageRowsUnselected;
let isAllSelectableRowsSelected;
let isAllSelectablePageRowsSelected;
if (isNoRowsSelected) {
isAllRowsSelected = false;
isAllPageRowsSelected = false;
isAllSelectableRowsSelected = existsLockedRows && !Object.keys(rowsToSelect).some((id) => !lockedSelectionRowIds[id]);
isAllSelectablePageRowsSelected = isAllSelectableRowsSelected;
isAllSelectableRowsUnselected = isAllSelectableRowsSelected;
isAllSelectablePageRowsUnselected = isAllSelectableRowsSelected;
isNoPageRowsSelected = true;
} else {
isAllRowsSelected = !Object.keys(rowsToSelect).some(
(id) => !selectedRowIds[id]
);
if (isAllRowsSelected) {
isAllSelectableRowsSelected = true;
isAllSelectablePageRowsSelected = true;
isAllSelectableRowsUnselected = existsLockedRows && Object.keys(rowsToSelect).filter((id) => !lockedSelectionRowIds[id]).length === 0;
isAllSelectablePageRowsUnselected = isAllSelectableRowsUnselected;
isNoPageRowsSelected = false;
isAllPageRowsSelected = true;
} else {
isAllSelectableRowsSelected = existsLockedRows && !Object.keys(rowsToSelect).filter((id) => !lockedSelectionRowIds[id]).some((id) => !selectedRowIds[id]);
isAllSelectableRowsUnselected = !existsLockedRows || !Object.keys(rowsToSelect).filter((id) => !lockedSelectionRowIds[id]).some((id) => selectedRowIds[id]);
isAllPageRowsSelected = !(page?.length && page.some(({ id }) => !selectedRowIds[id]));
if (isAllPageRowsSelected) {
isAllSelectablePageRowsSelected = true;
isAllSelectablePageRowsUnselected = existsLockedRows && page && page.length && page.filter(({ id }) => !lockedSelectionRowIds[id]).length === 0;
isNoPageRowsSelected = false;
} else {
isAllSelectablePageRowsSelected = existsLockedRows && !(page?.length && page.filter(({ id }) => !lockedSelectionRowIds[id]).some(({ id }) => !selectedRowIds[id]));
isAllSelectablePageRowsUnselected = !existsLockedRows || !(page?.length && page.filter(({ id }) => !lockedSelectionRowIds[id]).some(({ id }) => selectedRowIds[id]));
isNoPageRowsSelected = !(page?.length && page.some(({ id }) => selectedRowIds[id]));
}
}
}
const getAutoResetSelectedRows = useGetLatest(autoResetSelectedRows);
const getAutoResetLockedSelectionRows = useGetLatest(
autoResetLockedSelectionRows
);
useMountedLayoutEffect(() => {
if (getAutoResetSelectedRows()) {
dispatch({ type: actions.resetSelectedRows });
}
if (getAutoResetLockedSelectionRows()) {
dispatch({ type: actions.resetLockedSelectionRows });
}
}, [dispatch, data]);
const toggleAllRowsSelected = useCallback(
(value) => dispatch({ type: actions.toggleAllRowsSelected, value }),
[dispatch]
);
const toggleAllPageRowsSelected = useCallback(
(value) => dispatch({ type: actions.toggleAllPageRowsSelected, value }),
[dispatch]
);
const toggleRowSelected = useCallback(
(id, value) => dispatch({ type: actions.toggleRowSelected, id, value }),
[dispatch]
);
const getInstance = useGetLatest(instance);
const getToggleAllRowsSelectedProps = makePropGetter(
getHooks().getToggleAllRowsSelectedProps,
{
instance: getInstance()
}
);
const getToggleAllPageRowsSelectedProps = makePropGetter(
getHooks().getToggleAllPageRowsSelectedProps,
{ instance: getInstance() }
);
const toggleRowLockedSelection = useCallback(
(id, value) => dispatch({ type: actions.toggleRowLockedSelection, id, value }),
[dispatch]
);
Object.assign(instance, {
selectedFlatRows,
isNoRowsSelected,
isNoPageRowsSelected,
isAllRowsSelected,
isAllPageRowsSelected,
isAllSelectableRowsSelected,
isAllSelectablePageRowsSelected,
isAllSelectableRowsUnselected,
isAllSelectablePageRowsUnselected,
toggleRowSelected,
toggleAllRowsSelected,
getToggleAllRowsSelectedProps,
getToggleAllPageRowsSelectedProps,
toggleAllPageRowsSelected,
toggleRowLockedSelection
});
}
function prepareRow(row, { instance }) {
row.toggleRowSelected = (set) => instance.toggleRowSelected(row.id, set);
row.getToggleRowSelectedProps = makePropGetter(
instance.getHooks().getToggleRowSelectedProps,
{
instance,
row
}
);
row.toggleRowLockedSelection = (set) => instance.toggleRowLockedSelection(row.id, set);
row.isSelectionLocked = instance.state?.lockedSelectionRowIds?.[row.id] || false;
}
const useHvRowSelection = (hooks) => {
hooks.visibleColumns.push(visibleColumnsHook);
hooks.getRowProps.push(getRowPropsHook);
hooks.getHeaderProps.push(getHeaderPropsHook);
hooks.getToggleRowSelectedProps = [defaultGetToggleRowSelectedProps];
hooks.getToggleAllRowsSelectedProps = [defaultGetToggleAllRowsSelectedProps];
hooks.getToggleAllPageRowsSelectedProps = [
defaultGetToggleAllPageRowsSelectedProps
];
hooks.stateReducers.push(reducer);
hooks.useInstance.push(useInstance);
hooks.prepareRow.push(prepareRow);
};
useHvRowSelection.pluginName = "useHvRowSelection";
export {
useHvRowSelection
};