UNPKG

@hitachivantara/uikit-react-core

Version:

Core React components for the NEXT Design System.

443 lines (442 loc) 15 kB
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 };