diff --git a/src/Columns.tsx b/src/Columns.tsx index 408c8cedee..252b77db96 100644 --- a/src/Columns.tsx +++ b/src/Columns.tsx @@ -50,6 +50,7 @@ export const SelectColumn: Column = { aria-label="Select All" isCellSelected={props.isCellSelected} value={props.allRowsSelected} + isIndeterminate={props.someRowsSelected} onChange={props.onAllRowsSelectionChange} /> ); diff --git a/src/DataGrid.tsx b/src/DataGrid.tsx index a810bda3c8..5c2e42de62 100644 --- a/src/DataGrid.tsx +++ b/src/DataGrid.tsx @@ -304,7 +304,7 @@ function DataGrid( ); const allRowsSelected = useMemo((): boolean => { - // no rows to select = explicitely unchecked + // no rows to select = explicitly unchecked const { length } = rawRows; return ( length !== 0 && @@ -315,6 +315,17 @@ function DataGrid( ); }, [rawRows, selectedRows, rowKeyGetter]); + const someRowsSelected = useMemo((): boolean => { + const { length } = rawRows; + return ( + length !== 0 && + selectedRows != null && + rowKeyGetter != null && + selectedRows.size > 0 && + selectedRows.size < length + ); + }, [rawRows, selectedRows, rowKeyGetter]); + const { columns, colSpanColumns, @@ -1221,6 +1232,7 @@ function DataGrid( onColumnResize={handleColumnResizeLatest} allRowsSelected={allRowsSelected} onAllRowsSelectionChange={selectAllRowsLatest} + someRowsSelected={someRowsSelected} sortColumns={sortColumns} onSortColumnsChange={onSortColumnsChangeLatest} lastFrozenColumnIndex={lastFrozenColumnIndex} diff --git a/src/HeaderCell.tsx b/src/HeaderCell.tsx index da46e83621..231b5e033f 100644 --- a/src/HeaderCell.tsx +++ b/src/HeaderCell.tsx @@ -29,6 +29,7 @@ type SharedHeaderRowProps = Pick< | 'sortColumns' | 'onSortColumnsChange' | 'allRowsSelected' + | 'someRowsSelected' | 'onAllRowsSelectionChange' | 'selectCell' | 'onColumnResize' @@ -48,6 +49,7 @@ export default function HeaderCell({ isCellSelected, onColumnResize, allRowsSelected, + someRowsSelected, onAllRowsSelectionChange, sortColumns, onSortColumnsChange, @@ -189,6 +191,7 @@ export default function HeaderCell({ priority, onSort, allRowsSelected, + someRowsSelected, onAllRowsSelectionChange, isCellSelected })} diff --git a/src/HeaderRow.tsx b/src/HeaderRow.tsx index 8783a4f682..7090c4b7de 100644 --- a/src/HeaderRow.tsx +++ b/src/HeaderRow.tsx @@ -16,6 +16,7 @@ type SharedDataGridProps = Pick< export interface HeaderRowProps extends SharedDataGridProps { columns: readonly CalculatedColumn[]; allRowsSelected: boolean; + someRowsSelected: boolean; onAllRowsSelectionChange: (checked: boolean) => void; onColumnResize: (column: CalculatedColumn, width: number | 'max-content') => void; selectCell: (columnIdx: number) => void; @@ -50,6 +51,7 @@ const headerRowClassname = `rdg-header-row ${headerRow}`; function HeaderRow({ columns, allRowsSelected, + someRowsSelected, onAllRowsSelectionChange, onColumnResize, sortColumns, @@ -76,6 +78,7 @@ function HeaderRow({ isCellSelected={selectedCellIdx === column.idx} onColumnResize={onColumnResize} allRowsSelected={allRowsSelected} + someRowsSelected={someRowsSelected} onAllRowsSelectionChange={onAllRowsSelectionChange} onSortColumnsChange={onSortColumnsChange} sortColumns={sortColumns} diff --git a/src/formatters/SelectCellFormatter.tsx b/src/formatters/SelectCellFormatter.tsx index 0eeb5fae9f..e9df559d63 100644 --- a/src/formatters/SelectCellFormatter.tsx +++ b/src/formatters/SelectCellFormatter.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react'; import { useFocusRef } from '../hooks/useFocusRef'; import { useDefaultComponents } from '../DataGridDefaultComponentsProvider'; import type { CheckboxFormatterProps } from '../types'; @@ -6,6 +7,7 @@ type SharedInputProps = Pick void; } @@ -13,6 +15,7 @@ interface SelectCellFormatterProps extends SharedInputProps { export function SelectCellFormatter({ value, isCellSelected, + isIndeterminate = false, disabled, onChange, 'aria-label': ariaLabel, @@ -21,6 +24,13 @@ export function SelectCellFormatter({ const { ref, tabIndex } = useFocusRef(isCellSelected); const checkboxFormatter = useDefaultComponents()!.checkboxFormatter!; + useEffect(() => { + if (typeof ref.current?.indeterminate === 'boolean') { + ref.current.indeterminate = isIndeterminate; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isIndeterminate]); + return ( <> {checkboxFormatter( diff --git a/src/types.ts b/src/types.ts index 834bde66a7..4afcd2daeb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -107,6 +107,7 @@ export interface HeaderRendererProps { priority: number | undefined; onSort: (ctrlClick: boolean) => void; allRowsSelected: boolean; + someRowsSelected: boolean; onAllRowsSelectionChange: (checked: boolean) => void; isCellSelected: boolean; }