diff --git a/README.md b/README.md index 767c603769..b84a22598f 100644 --- a/README.md +++ b/README.md @@ -105,12 +105,12 @@ interface Row { title: string; } -const columns: Column[] = [ +const columns: readonly Column[] = [ { key: 'id', name: 'ID' }, { key: 'title', name: 'Title' } ]; -const rows: Row[] = [ +const rows: readonly Row[] = [ { id: 0, title: 'Example' }, { id: 1, title: 'Demo' } ]; @@ -134,16 +134,29 @@ Set `--rdg-color-scheme: light/dark` at the `:root` to control the color theme. ###### `columns: readonly Column[]` -See [`Column`](#column). - -An array of column definitions. Each column should have a key and name. +An array of column definitions. Each column should have a key and name. See the [`Column`](#columntrow-tsummaryrow) type for all available options. -:warning: Passing a new `columns` array will trigger a re-render for the whole grid, avoid changing it as much as possible for optimal performance. +:warning: **Performance:** Passing a new `columns` array will trigger a re-render and recalculation for the entire grid. Always memoize this prop using `useMemo` or define it outside the component to avoid unnecessary re-renders. ###### `rows: readonly R[]` An array of rows, the rows data can be of any type. +:bulb: **Performance:** The grid is optimized for efficient rendering: + +- **Virtualization**: Only visible rows are rendered in the DOM +- **Individual row updates**: Row components are memoized, so updating a single row object will only re-render that specific row, not all rows +- **Array reference matters**: Changing the array reference itself (e.g., `setRows([...rows])`) triggers viewport and layout recalculations, even if the row objects are unchanged +- **Best practice**: When updating rows, create a new array but reuse unchanged row objects. For example: + + ```tsx + // ✅ Good: Only changed row is re-rendered + setRows(rows.map((row, idx) => (idx === targetIdx ? { ...row, updated: true } : row))); + + // ❌ Avoid: Creates new references for all rows, causing all visible rows to re-render + setRows(rows.map((row) => ({ ...row }))); + ``` + ###### `topSummaryRows?: Maybe` Rows pinned at the top of the grid for summary purposes. @@ -175,6 +188,8 @@ function MyGrid() { :bulb: While optional, setting this prop is recommended for optimal performance as the returned value is used to set the `key` prop on the row elements. +:warning: **Performance:** Define this function outside your component or memoize it with `useCallback` to prevent unnecessary re-renders. + ###### `onRowsChange?: Maybe<(rows: R[], data: RowsChangeData) => void>` Callback triggered when rows are changed. @@ -199,6 +214,8 @@ function MyGrid() { Height of each row in pixels. A function can be used to set different row heights. +:warning: **Performance:** When using a function, the height of all rows is calculated upfront on every render. For large datasets (1000+ rows), this can cause performance issues. Consider using a fixed height when possible, or memoize the `rowHeight` function. + ###### `headerRowHeight?: Maybe` **Default:** `35` pixels @@ -254,6 +271,14 @@ const columns: readonly Column[] = [ // other columns ]; +function rowKeyGetter(row: Row) { + return row.id; +} + +function isRowSelectionDisabled(row: Row) { + return !row.isActive; +} + function MyGrid() { const [selectedRows, setSelectedRows] = useState((): ReadonlySet => new Set()); @@ -268,14 +293,6 @@ function MyGrid() { /> ); } - -function rowKeyGetter(row: Row) { - return row.id; -} - -function isRowSelectionDisabled(row: Row) { - return !row.isActive; -} ``` ###### `sortColumns?: Maybe` @@ -360,8 +377,6 @@ function onCellMouseDown(args: CellMouseDownArgs, event: CellMouseEvent) ; ``` -See [`CellMouseArgs`](#cellmouseargs) and [`CellMouseEvent`](#cellmouseevent) - ###### `onCellClick?: Maybe<(args: CellMouseArgs, event: CellMouseEvent) => void>` Callback triggered when a cell is clicked. @@ -386,8 +401,6 @@ function onCellClick(args: CellMouseArgs, event: CellMouseEvent) { } ``` -See [`CellMouseArgs`](#cellmouseargs) and [`CellMouseEvent`](#cellmouseevent) - ###### `onCellDoubleClick?: Maybe<(args: CellMouseArgs, event: CellMouseEvent) => void>` Callback triggered when a cell is double-clicked. The default behavior is to open the editor if the cell is editable. Call `preventGridDefault` to prevent the default behavior. @@ -402,8 +415,6 @@ function onCellDoubleClick(args: CellMouseArgs, event: CellMouseEvent) { ; ``` -See [`CellMouseArgs`](#cellmouseargs) and [`CellMouseEvent`](#cellmouseevent) - ###### `onCellContextMenu?: Maybe<(args: CellMouseArgs, event: CellMouseEvent) => void>` Callback triggered when a cell is right-clicked. @@ -419,8 +430,6 @@ function onCellContextMenu(args: CellMouseArgs, event: CellMouseEvent) { ; ``` -See [`CellMouseArgs`](#cellmouseargs) and [`CellMouseEvent`](#cellmouseevent) - ###### `onCellKeyDown?: Maybe<(args: CellKeyDownArgs, event: CellKeyboardEvent) => void>` A function called when keydown event is triggered on a cell. This event can be used to customize cell navigation and editing behavior. @@ -517,8 +526,6 @@ function MyGrid() { } ``` -:warning: To prevent all rows from being unmounted on re-renders, make sure to pass a static or memoized render function to `renderRow`. - ###### `rowClass?: Maybe<(row: R, rowIdx: number) => Maybe>` Function to apply custom class names to rows. @@ -535,6 +542,8 @@ function rowClass(row: Row, rowIdx: number) { } ``` +:warning: **Performance:** Define this function outside your component or memoize it with `useCallback` to avoid re-rendering all rows on every render. + ###### `headerRowClass?: Maybe>` Custom class name for the header row. @@ -601,81 +610,313 @@ test('grid', async () => { ###### `groupIdGetter?: Maybe<(groupKey: string, parentId?: string) => string>` -#### `` +#### `` + +The default row component. Can be wrapped via the `renderers.renderRow` prop. ##### Props -See [`RenderEditCellProps`](#rendereditcellprops) +[`RenderRowProps`](#renderrowpropstrow-tsummaryrow) -#### `` +#### `` -See [`renderers`](#renderers-mayberenderersr-sr) +The default cell component. Can be wrapped via the `renderers.renderCell` prop. ##### Props -See [`RenderRowProps`](#renderrowprops) +[`CellRendererProps`](#cellrendererpropstrow-tsummaryrow) -The `ref` prop is supported. +#### `` -#### `` +A formatter component for rendering row selection checkboxes. ##### Props -###### `onSort: (ctrlClick: boolean) => void` - -###### `sortDirection: SortDirection | undefined` +###### `value: boolean` -###### `priority: number | undefined` +Whether the checkbox is checked. ###### `tabIndex: number` -###### `children: React.ReactNode` +The tab index for keyboard navigation. -#### `` +###### `disabled?: boolean | undefined` -##### Props +Whether the checkbox is disabled. -See [`RenderCellProps`](#rendercellprops) +###### `onChange: (value: boolean, isShiftClick: boolean) => void` -#### `` +Callback when the checkbox state changes. -##### Props +###### `onClick?: MouseEventHandler | undefined` -###### `value: boolean` +Optional click handler. -###### `tabIndex: number` +###### `'aria-label'?: string | undefined` -###### `disabled?: boolean | undefined` +Accessible label for the checkbox. -###### `onChange: (value: boolean, isShiftClick: boolean) => void` +###### `'aria-labelledby'?: string | undefined` -###### `onClick?: MouseEventHandler | undefined` +ID of the element that labels the checkbox. -###### `'aria-label'?: string | undefined` +### Hooks -###### `'aria-labelledby'?: string | undefined` +#### `useHeaderRowSelection()` + +Hook for managing header row selection state. Used within custom header cell renderers to implement custom "select all" functionality. + +**Returns:** + +- `isIndeterminate: boolean` - Whether some (but not all) rows are selected +- `isRowSelected: boolean` - Whether all rows are selected +- `onRowSelectionChange: (event: SelectHeaderRowEvent) => void` - Callback to change selection state + +**Example:** + +```tsx +function CustomHeaderCell() { + const { isIndeterminate, isRowSelected, onRowSelectionChange } = useHeaderRowSelection(); + + return ( + onRowSelectionChange({ checked: event.target.checked })} + /> + ); +} +``` + +#### `useRowSelection()` + +Hook for managing row selection state. Used within custom cell renderers to implement custom row selection. + +**Returns:** + +- `isRowSelectionDisabled: boolean` - Whether selection is disabled for this row +- `isRowSelected: boolean` - Whether this row is selected +- `onRowSelectionChange: (event: SelectRowEvent) => void` - Callback to change selection state + +**Example:** + +```tsx +function CustomSelectCell({ row }: RenderCellProps) { + const { isRowSelectionDisabled, isRowSelected, onRowSelectionChange } = useRowSelection(); + + return ( + + onRowSelectionChange({ + row, + checked: event.target.checked, + isShiftClick: event.nativeEvent.shiftKey + }) + } + /> + ); +} +``` + +### Render Functions + +#### `renderHeaderCell(props: RenderHeaderCellProps)` -#### `` +The default header cell renderer. Renders sortable columns with sort indicators. + +**Example:** + +```tsx +import { renderHeaderCell, type Column } from 'react-data-grid'; + +const columns: readonly Column[] = [ + { + key: 'name', + name: 'Name', + sortable: true, + renderHeaderCell + } +]; +``` + +#### `textEditor(props: RenderEditCellProps)` + +A basic text editor provided for convenience. + +**Example:** + +```tsx +import { textEditor, type Column } from 'react-data-grid'; + +const columns: readonly Column[] = [ + { + key: 'title', + name: 'Title', + renderEditCell: textEditor + } +]; +``` + +#### `renderSortIcon(props: RenderSortIconProps)` + +Renders the sort direction arrow icon. + +**Props:** + +- `sortDirection: SortDirection | undefined` - 'ASC', 'DESC', or undefined + +#### `renderSortPriority(props: RenderSortPriorityProps)` + +Renders the sort priority number for multi-column sorting. + +**Props:** + +- `priority: number | undefined` - The sort priority (1, 2, 3, etc.) + +#### `renderCheckbox(props: RenderCheckboxProps)` + +Renders a checkbox input with proper styling and accessibility. + +**Props:** + +- `checked: boolean` - Whether the checkbox is checked +- `indeterminate?: boolean` - Whether the checkbox is in indeterminate state +- `disabled?: boolean` - Whether the checkbox is disabled +- `onChange: (checked: boolean, shift: boolean) => void` - Change handler +- `tabIndex: number` - Tab index for keyboard navigation +- `aria-label?: string` - Accessible label +- `aria-labelledby?: string` - ID of labeling element + +**Example:** + +```tsx +import { DataGrid, renderCheckbox } from 'react-data-grid'; + + renderCheckbox({ ...props, 'aria-label': 'Select row' }) + }} +/>; +``` + +#### `renderToggleGroup(props: RenderGroupCellProps)` + +The default group cell renderer used by the columns used for grouping (`groupBy` prop). This renders the expand/collapse toggle. ##### Props -See [`RenderGroupCellProps`](#rendergroupcellprops) +[`RenderGroupCellProps`](#rendergroupcellpropstrow-tsummaryrow) -### Hooks +**Example:** -#### `useHeaderRowSelection(): { isIndeterminate, isRowSelected, onRowSelectionChange }` +```tsx +import { renderToggleGroup, type Column } from 'react-data-grid'; -#### `useRowSelection(): { isRowSelectionDisabled, isRowSelected, onRowSelectionChange }` +const columns: readonly Column[] = [ + { + key: 'group', + name: 'Group', + renderGroupCell: renderToggleGroup + } +]; +``` + +#### `renderValue(props: RenderCellProps)` + +The default cell renderer that renders the value of `row[column.key]`. + +**Example:** + +```tsx +import { renderValue, type Column } from 'react-data-grid'; + +const columns: readonly Column[] = [ + { + key: 'title', + name: 'Title', + renderCell: renderValue + } +]; +``` + +### Context + +#### `DataGridDefaultRenderersContext` + +Context for providing default renderers to DataGrids in your app. + +**Example:** + +```tsx +import { DataGridDefaultRenderersContext, type Renderers } from 'react-data-grid'; + +// custom implementations of renderers +const defaultGridRenderers: Renderers = { + renderCheckbox, + renderSortStatus +}; + +function AppProvider({ children }) { + return ( + + {children} + + ); +} +``` ### Other #### `SelectColumn: Column` +A pre-configured column for row selection. +Includes checkbox renderers for header, regular rows, and grouped rows. + +**Example:** + +```tsx +import { DataGrid, SelectColumn, type Column } from 'react-data-grid'; + +const columns: readonly Column[] = [SelectColumn, ...otherColumns]; + +function rowKeyGetter(row: Row) { + return row.id; +} + +function MyGrid() { + return ( + + ); +} +``` + #### `SELECT_COLUMN_KEY = 'rdg-select-column'` +The key used for the `SelectColumn`. Useful for identifying or filtering the select column. + +**Example:** + +```tsx +import { SELECT_COLUMN_KEY } from 'react-data-grid'; + +const nonSelectColumns = columns.filter((column) => column.key !== SELECT_COLUMN_KEY); +``` + ### Types -#### `Column` +#### `Column` + +Defines the configuration for a column in the grid. ##### `name: string | ReactElement` @@ -712,7 +953,7 @@ Maximum column width in pixels. ##### `cellClass?: Maybe Maybe)>` -Class name(s) for the cell +Class name(s) for cells. Can be a string or a function that returns a class name based on the row. ##### `headerCellClass?: Maybe` @@ -720,7 +961,7 @@ Class name(s) for the header cell. ##### `summaryCellClass?: Maybe Maybe)>` -Class name(s) for the summary cell. +Class name(s) for summary cells. Can be a string or a function that returns a class name based on the summary row. ##### `renderCell?: Maybe<(props: RenderCellProps) => ReactNode>` @@ -734,15 +975,40 @@ Render function to render the content of the header cell. Render function to render the content of summary cells +##### `renderGroupCell?: Maybe<(props: RenderGroupCellProps) => ReactNode>` + +Render function to render the content of group cells when using `TreeDataGrid`. + ##### `renderEditCell?: Maybe<(props: RenderEditCellProps) => ReactNode>` Render function to render the content of edit cells. When set, the column is automatically set to be editable ##### `editable?: Maybe boolean)>` -Enables cell editing. If set and no editor property specified, then a text input will be used as the cell editor. +Control whether cells can be edited with `renderEditCell`. + +##### `colSpan?: Maybe<(args: ColSpanArgs) => Maybe>` -##### `colSpan?: Maybe<(args: ColSpanArgs) => Maybe>` +Function to determine how many columns this cell should span. Returns the number of columns to span, or `undefined` for no spanning. See the `ColSpanArgs` type in the Types section below. + +**Example:** + +```tsx +import type { Column } from 'react-data-grid'; + +const columns: readonly Column[] = [ + { + key: 'title', + name: 'Title', + colSpan(args) { + if (args.type === 'ROW' && args.row.isFullWidth) { + return 5; // Span 5 columns for full-width rows + } + return undefined; + } + } +]; +``` ##### `frozen?: Maybe` @@ -782,7 +1048,7 @@ Options for cell editing. **Default**: `false` -Render the cell content in addition to the edit cell. Enable this option when the editor is rendered outside the grid, like a modal for example. +Render the cell content in addition to the edit cell content. Enable this option when the editor is rendered outside the grid, like a modal for example. ###### `commitOnOutsideClick?: Maybe` @@ -794,43 +1060,653 @@ Commit changes when clicking outside the cell. **Default**: `true` -Close the editor when the row changes externally. +Close the editor when the row value changes externally. -#### `CellMouseArgs` +#### `ColumnGroup` -##### `rowIdx: number` +Defines a group of columns that share a common header. + +```tsx +interface ColumnGroup { + readonly name: string | ReactElement; + readonly headerCellClass?: Maybe; + readonly children: readonly ColumnOrColumnGroup[]; +} +``` -Row index of the currently selected cell +**Example:** -##### `row: TRow` +```tsx +import type { ColumnOrColumnGroup } from 'react-data-grid'; + +const columns: readonly ColumnOrColumnGroup[] = [ + { + name: 'Personal Info', + children: [ + { key: 'firstName', name: 'First Name' }, + { key: 'lastName', name: 'Last Name' } + ] + } +]; +``` + +#### `ColumnOrColumnGroup` + +Union type representing either a `Column` or a `ColumnGroup`. + +#### `CalculatedColumn` + +Extends `Column` with additional computed properties used internally by the grid. This is the type passed to render functions. + +**Additional properties:** + +- `idx: number` - The column index +- `level: number` - Nesting level when using column groups +- `parent: CalculatedColumnParent | undefined` - Parent column group if nested +- Multiple Column properties have their values set to their default value + +#### `CalculatedColumnParent` + +Represents a parent column group in the calculated column structure. + +```tsx +interface CalculatedColumnParent { + readonly name: string | ReactElement; + readonly parent: CalculatedColumnParent | undefined; + readonly idx: number; + readonly colSpan: number; + readonly level: number; + readonly headerCellClass?: Maybe; +} +``` + +#### `CalculatedColumnOrColumnGroup` + +Union type representing either a `CalculatedColumnParent` or a `CalculatedColumn`. + +```tsx +type CalculatedColumnOrColumnGroup = CalculatedColumnParent | CalculatedColumn; +``` + +#### `RowHeightArgs` + +Arguments passed to the `rowHeight` function when it's a function. + +```tsx +type RowHeightArgs = { type: 'ROW'; row: TRow } | { type: 'GROUP'; row: GroupRow }; +``` + +**Example:** + +```tsx +function getRowHeight(args: RowHeightArgs): number { + if (args.type === 'GROUP') { + return 40; + } + return args.row.isLarge ? 60 : 35; +} + + +``` + +#### `RenderCellProps` + +Props passed to custom cell renderers. + +```tsx +interface RenderCellProps { + column: CalculatedColumn; + row: TRow; + rowIdx: number; + isCellEditable: boolean; + tabIndex: number; + onRowChange: (row: TRow) => void; +} +``` + +**Example:** + +```tsx +import type { RenderCellProps } from 'react-data-grid'; + +function renderCell({ row, column, onRowChange }: RenderCellProps) { + return ( +
+ {row[column.key]} + +
+ ); +} +``` + +#### `RenderHeaderCellProps` + +Props passed to custom header cell renderers. + +```tsx +interface RenderHeaderCellProps { + column: CalculatedColumn; + sortDirection: SortDirection | undefined; + priority: number | undefined; + tabIndex: number; +} +``` + +#### `RenderEditCellProps` + +Props passed to custom edit cell renderers (editors). + +```tsx +interface RenderEditCellProps { + column: CalculatedColumn; + row: TRow; + rowIdx: number; + onRowChange: (row: TRow, commitChanges?: boolean) => void; + onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void; +} +``` + +**Example:** + +```tsx +import type { RenderEditCellProps } from 'react-data-grid'; + +function CustomEditor({ row, column, onRowChange, onClose }: RenderEditCellProps) { + return ( + onRowChange({ ...row, [column.key]: event.target.value })} + onBlur={() => onClose(true)} + /> + ); +} +``` -row object of the currently selected cell +#### `RenderSummaryCellProps` + +Props passed to summary cell renderers. + +```tsx +interface RenderSummaryCellProps { + column: CalculatedColumn; + row: TSummaryRow; + tabIndex: number; +} +``` + +#### `RenderGroupCellProps` + +Props passed to group cell renderers when using `TreeDataGrid`. + +```tsx +interface RenderGroupCellProps { + groupKey: unknown; + column: CalculatedColumn; + row: GroupRow; + childRows: readonly TRow[]; + isExpanded: boolean; + tabIndex: number; + toggleGroup: () => void; +} +``` + +#### `RenderRowProps` + +Props passed to custom row renderers. + +```tsx +interface RenderRowProps { + row: TRow; + viewportColumns: readonly CalculatedColumn[]; + rowIdx: number; + selectedCellIdx: number | undefined; + isRowSelected: boolean; + isRowSelectionDisabled: boolean; + gridRowStart: number; + lastFrozenColumnIndex: number; + draggedOverCellIdx: number | undefined; + selectedCellEditor: ReactElement> | undefined; + onRowChange: (column: CalculatedColumn, rowIdx: number, newRow: TRow) => void; + rowClass: Maybe<(row: TRow, rowIdx: number) => Maybe>; + // ... and event handlers +} +``` + +#### `CellRendererProps` + +Props passed to the cell renderer when using `renderers.renderCell`. + +Extends `RenderRowProps` with cell-specific properties like `column`, `colSpan`, `isCellSelected`, etc. + +#### `Renderers` + +Custom renderer configuration for the grid. + +```tsx +interface Renderers { + renderCell?: Maybe<(key: Key, props: CellRendererProps) => ReactNode>; + renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>; + renderRow?: Maybe<(key: Key, props: RenderRowProps) => ReactNode>; + renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>; + noRowsFallback?: Maybe; +} +``` + +#### `CellMouseArgs` + +Arguments passed to cell mouse event handlers. + +```tsx +interface CellMouseArgs { + column: CalculatedColumn; + row: TRow; + rowIdx: number; + selectCell: (enableEditor?: boolean) => void; +} +``` ##### `column: CalculatedColumn` -column object of the currently selected cell +The column object of the cell. + +##### `row: TRow` + +The row object of the cell. + +##### `rowIdx: number` + +The row index of the cell. ##### `selectCell: (enableEditor?: boolean) => void` -function to manually select the cell and optionally pass `true` to start editing +Function to manually select the cell. Pass `true` to immediately start editing. + +**Example:** + +```tsx +import type { CellMouseArgs, CellMouseEvent } from 'react-data-grid'; + +function onCellClick(args: CellMouseArgs, event: CellMouseEvent) { + console.log('Clicked cell at row', args.rowIdx, 'column', args.column.key); + args.selectCell(true); // Select and start editing +} +``` #### `CellMouseEvent` -Extends `React.MouseEvent` +Extends `React.MouseEvent` with grid-specific methods. + +##### `event.preventGridDefault(): void` + +Prevents the default grid behavior for this event. + +##### `event.isGridDefaultPrevented(): boolean` + +Returns whether `preventGridDefault` was called. + +**Example:** + +```tsx +import type { CellMouseArgs, CellMouseEvent } from 'react-data-grid'; + +function onCellClick(args: CellMouseArgs, event: CellMouseEvent) { + if (args.column.key === 'actions') { + event.preventGridDefault(); // Prevent cell selection + } +} +``` + +#### `CellKeyboardEvent` + +Extends `React.KeyboardEvent` with grid-specific methods. + +##### `event.preventGridDefault(): void` + +Prevents the default grid behavior for this keyboard event. + +##### `event.isGridDefaultPrevented(): boolean` + +Returns whether `preventGridDefault` was called. + +#### `CellClipboardEvent` + +Type alias for `React.ClipboardEvent`. Used for copy and paste events. + +```tsx +type CellClipboardEvent = React.ClipboardEvent; +``` + +#### `CellKeyDownArgs` + +Arguments passed to the `onCellKeyDown` handler. The shape differs based on whether the cell is in SELECT or EDIT mode. + +**SELECT mode:** + +```tsx +interface SelectCellKeyDownArgs { + mode: 'SELECT'; + column: CalculatedColumn; + row: TRow; + rowIdx: number; + selectCell: (position: Position, options?: SelectCellOptions) => void; +} +``` + +**EDIT mode:** + +```tsx +interface EditCellKeyDownArgs { + mode: 'EDIT'; + column: CalculatedColumn; + row: TRow; + rowIdx: number; + navigate: () => void; + onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void; +} +``` + +**Example:** + +```tsx +import type { CellKeyboardEvent, CellKeyDownArgs } from 'react-data-grid'; + +function onCellKeyDown(args: CellKeyDownArgs, event: CellKeyboardEvent) { + if (args.mode === 'EDIT' && event.key === 'Escape') { + args.onClose(false); // Close without committing + event.preventGridDefault(); + } +} +``` + +#### `CellSelectArgs` + +Arguments passed to `onSelectedCellChange`. + +```tsx +interface CellSelectArgs { + rowIdx: number; + row: TRow | undefined; + column: CalculatedColumn; +} +``` + +#### `CellCopyArgs` + +Arguments passed to `onCellCopy`. + +```tsx +interface CellCopyArgs { + column: CalculatedColumn; + row: TRow; +} +``` + +#### `CellPasteArgs` + +Arguments passed to `onCellPaste`. + +```tsx +interface CellPasteArgs { + column: CalculatedColumn; + row: TRow; +} +``` -##### `event.preventGridDefault: () => void` +#### `ColSpanArgs` -##### `event.isGridDefaultPrevented: boolean` +Arguments passed to the `colSpan` function. + +```tsx +type ColSpanArgs = + | { type: 'HEADER' } + | { type: 'ROW'; row: TRow } + | { type: 'SUMMARY'; row: TSummaryRow }; +``` + +**Example:** + +```tsx +import type { Column } from 'react-data-grid'; + +const columns: readonly Column[] = [ + { + key: 'title', + name: 'Title', + colSpan(args) { + if (args.type === 'ROW' && args.row.isFullWidth) { + return 3; // Span 3 columns + } + return undefined; + } + } +]; +``` + +#### `SortColumn` + +Describes a sorted column. + +```tsx +interface SortColumn { + readonly columnKey: string; + readonly direction: SortDirection; +} +``` + +#### `SortDirection` + +```tsx +type SortDirection = 'ASC' | 'DESC'; +``` + +#### `RowsChangeData` + +Data provided to `onRowsChange` callback. + +```tsx +interface RowsChangeData { + indexes: number[]; + column: CalculatedColumn; +} +``` + +- `indexes`: Array of row indexes that changed +- `column`: The column where changes occurred + +#### `SelectRowEvent` + +Event object for row selection changes. + +```tsx +interface SelectRowEvent { + row: TRow; + checked: boolean; + isShiftClick: boolean; +} +``` + +#### `SelectHeaderRowEvent` + +Event object for header row selection changes. + +```tsx +interface SelectHeaderRowEvent { + checked: boolean; +} +``` + +#### `FillEvent` + +Event object for drag-fill operations. + +```tsx +interface FillEvent { + columnKey: string; + sourceRow: TRow; + targetRow: TRow; +} +``` + +Used with the `onFill` prop to handle cell value dragging. + +#### `GroupRow` + +Represents a grouped row in `TreeDataGrid`. + +```tsx +interface GroupRow { + readonly childRows: readonly TRow[]; + readonly id: string; + readonly parentId: unknown; + readonly groupKey: unknown; + readonly isExpanded: boolean; + readonly level: number; + readonly posInSet: number; + readonly setSize: number; + readonly startRowIndex: number; +} +``` + +#### `ColumnWidths` + +A map of column widths. + +```tsx +type ColumnWidths = ReadonlyMap; + +interface ColumnWidth { + readonly type: 'resized' | 'measured'; + readonly width: number; +} +``` + +Used with `columnWidths` and `onColumnWidthsChange` props to control column widths externally. + +#### `Position` + +Represents a cell position in the grid. + +```tsx +interface Position { + readonly idx: number; // Column index + readonly rowIdx: number; // Row index +} +``` + +#### `SelectCellOptions` + +Options for programmatically selecting a cell. + +```tsx +interface SelectCellOptions { + enableEditor?: Maybe; + shouldFocusCell?: Maybe; +} +``` + +#### `RenderCheckboxProps` + +Props for custom checkbox renderers. + +```tsx +interface RenderCheckboxProps { + checked: boolean; + indeterminate?: boolean; + disabled?: boolean; + onChange: (checked: boolean, shift: boolean) => void; + tabIndex: number; + 'aria-label'?: string; + 'aria-labelledby'?: string; +} +``` + +#### `RenderSortStatusProps` + +Props for custom sort status renderers. + +```tsx +interface RenderSortStatusProps { + sortDirection: SortDirection | undefined; + priority: number | undefined; +} +``` + +#### `RenderSortIconProps` + +Props for custom sort icon renderers. + +```tsx +interface RenderSortIconProps { + sortDirection: SortDirection | undefined; +} +``` + +#### `RenderSortPriorityProps` + +Props for custom sort priority renderers. + +```tsx +interface RenderSortPriorityProps { + priority: number | undefined; +} +``` #### `DataGridHandle` -#### `RenderEditCellProps` +Handle type assigned to a grid's `ref` for programmatic grid control. + +```tsx +interface DataGridHandle { + element: HTMLDivElement | null; + scrollToCell: (position: Partial) => void; + selectCell: (position: Position, options?: SelectCellOptions) => void; +} +``` + +**Example:** + +```tsx +import { useRef } from 'react'; +import { DataGrid, DataGridHandle } from 'react-data-grid'; + +function MyGrid() { + const gridRef = useRef(null); -#### `RenderRowProps` + function scrollToTop() { + gridRef.current?.scrollToCell({ rowIdx: 0 }); + } -#### `RenderCellProps` + return ; +} +``` -#### `RenderGroupCellProps` +#### `DefaultColumnOptions` + +Default options applied to all columns. + +```tsx +type DefaultColumnOptions = Pick< + Column, + 'minWidth' | 'maxWidth' | 'resizable' | 'sortable' | 'draggable' +>; +``` + +#### `Direction` + +Grid layout bidirectionality. + +```tsx +type Direction = 'ltr' | 'rtl'; +``` + +#### `Maybe` + +Utility type for optional values. + +```tsx +type Maybe = T | undefined | null; +``` ### Generics diff --git a/src/DataGrid.tsx b/src/DataGrid.tsx index 27d34b0ab7..b8532c13fd 100644 --- a/src/DataGrid.tsx +++ b/src/DataGrid.tsx @@ -531,20 +531,25 @@ export function DataGrid(props: DataGridPr } }, [shouldFocusCell, focusCell, selectedPosition.idx]); - useImperativeHandle(ref, () => ({ - element: gridRef.current, - scrollToCell({ idx, rowIdx }) { - const scrollToIdx = - idx !== undefined && idx > lastFrozenColumnIndex && idx < columns.length ? idx : undefined; - const scrollToRowIdx = - rowIdx !== undefined && isRowIdxWithinViewportBounds(rowIdx) ? rowIdx : undefined; - - if (scrollToIdx !== undefined || scrollToRowIdx !== undefined) { - setScrollToPosition({ idx: scrollToIdx, rowIdx: scrollToRowIdx }); - } - }, - selectCell - })); + useImperativeHandle( + ref, + (): DataGridHandle => ({ + element: gridRef.current, + scrollToCell({ idx, rowIdx }) { + const scrollToIdx = + idx !== undefined && idx > lastFrozenColumnIndex && idx < columns.length + ? idx + : undefined; + const scrollToRowIdx = + rowIdx !== undefined && isRowIdxWithinViewportBounds(rowIdx) ? rowIdx : undefined; + + if (scrollToIdx !== undefined || scrollToRowIdx !== undefined) { + setScrollToPosition({ idx: scrollToIdx, rowIdx: scrollToRowIdx }); + } + }, + selectCell + }) + ); /** * event handlers diff --git a/src/types.ts b/src/types.ts index 96d0013147..3222b60e26 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,11 +25,11 @@ export interface Column { readonly minWidth?: Maybe; /** Maximum column width in pixels */ readonly maxWidth?: Maybe; - /** Class name(s) for the cell */ + /** Class name(s) for cells */ readonly cellClass?: Maybe Maybe)>; /** Class name(s) for the header cell */ readonly headerCellClass?: Maybe; - /** Class name(s) for the summary cell */ + /** Class name(s) for summary cells */ readonly summaryCellClass?: Maybe Maybe)>; /** Render function to render the content of cells */ readonly renderCell?: Maybe<(props: RenderCellProps) => ReactNode>; @@ -59,7 +59,7 @@ export interface Column { /** Options for cell editing */ readonly editorOptions?: Maybe<{ /** - * Render the cell content in addition to the edit cell. + * Render the cell content in addition to the edit cell content. * Enable this option when the editor is rendered outside the grid, like a modal for example. * By default, the cell content is not rendered when the edit cell is open. * @default false @@ -71,7 +71,7 @@ export interface Column { */ readonly commitOnOutsideClick?: Maybe; /** - * Close the editor when the row changes externally + * Close the editor when the row value changes externally * @default true */ readonly closeOnExternalRowChange?: Maybe;