diff --git a/README.md b/README.md index 2940542996..33a9dbbd93 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ Control column widths using the [`width`](#width-maybenumber--string), [`minWidt ### Custom Renderers -Replace default components with custom implementations using the [`renderers`](#renderers-mayberenderersr-sr) prop. Columns can also have custom renderers using the [`renderCell`](#rendercell-maybeprops-rendercellpropstrow-tsummaryrow--reactnode), [`renderHeaderCell`](#renderheadercell-maybeprops-renderheadercellpropstrow-tsummaryrow--reactnode), [`renderSummaryCell`](#rendersummarycell-maybeprops-rendersummarycellpropstsummaryrow-trow--reactnode), [`renderGroupCell`](#rendergroupcell-maybeprops-rendergroupcellpropstrow-tsummaryrow--reactnode), and [`renderEditCell`](#rendereditcell-maybeprops-rendereditcellpropstrow-tsummaryrow--reactnode) properties. +Replace default components with custom implementations using the [`renderers`](#renderers-mayberenderersr-sr) prop. Columns can also have custom renderers using the [`renderCell`](#rendercell-maybeprops-rendercellcontentpropstrow-tsummaryrow--reactnode), [`renderHeaderCell`](#renderheadercell-maybeprops-renderheadercellcontentpropstrow-tsummaryrow--reactnode), [`renderSummaryCell`](#rendersummarycell-maybeprops-rendersummarycellcontentpropstsummaryrow-trow--reactnode), [`renderGroupCell`](#rendergroupcell-maybeprops-rendergroupcellcontentpropstrow-tsummaryrow--reactnode), and [`renderEditCell`](#rendereditcell-maybeprops-rendereditcellcontentpropstrow-tsummaryrow--reactnode) properties. ## API Reference @@ -869,6 +869,8 @@ function rowGrouper(rows: readonly Row[], columnKey: string): Record` **Required.** A set of group IDs that are currently expanded. Group IDs are generated by `groupIdGetter`. @@ -898,6 +900,8 @@ function MyGrid() { Function to generate unique IDs for group rows. If not provided, a default implementation is used that concatenates parent and group keys with `__`. +:warning: **Performance:** Define this function outside your component or memoize it with `useCallback` to prevent unnecessary re-renders. + ###### `rowHeight?: Maybe) => number)>` **Note:** Unlike `DataGrid`, the `rowHeight` function receives [`RowHeightArgs`](#rowheightargstrow) which includes a `type` property to distinguish between regular rows and group rows: @@ -927,11 +931,11 @@ The default cell component. Can be wrapped via the `renderers.renderCell` prop. ##### Props -[`CellRendererProps`](#cellrendererpropstrow-tsummaryrow) +[`RenderCellProps`](#rendercellpropstrow-tsummaryrow) -#### `` +#### `` -A formatter component for rendering row selection checkboxes. +A component for rendering row selection checkboxes. ##### Props @@ -1018,7 +1022,7 @@ Hook for managing row selection state. Used within custom cell renderers to impl **Example:** ```tsx -function CustomSelectCell({ row }: RenderCellProps) { +function CustomSelectCell({ row }: RenderCellContentProps) { const { isRowSelectionDisabled, isRowSelected, onRowSelectionChange } = useRowSelection(); return ( @@ -1040,7 +1044,7 @@ function CustomSelectCell({ row }: RenderCellProps) { ### Render Functions -#### `renderHeaderCell(props: RenderHeaderCellProps)` +#### `renderHeaderCell(props: RenderHeaderCellContentProps)` The default header cell renderer. Renders sortable columns with sort indicators. @@ -1059,7 +1063,7 @@ const columns: readonly Column[] = [ ]; ``` -#### `renderTextEditor(props: RenderEditCellProps)` +#### `renderTextEditor(props: RenderEditCellContentProps)` A basic text editor provided for convenience. @@ -1119,13 +1123,13 @@ import { DataGrid, renderCheckbox } from 'react-data-grid'; />; ``` -#### `renderToggleGroup(props: RenderGroupCellProps)` +#### `renderToggleGroup(props: RenderGroupCellContentProps)` The default group cell renderer used by the columns used for grouping (`groupBy` prop). This renders the expand/collapse toggle. ##### Props -[`RenderGroupCellProps`](#rendergroupcellpropstrow-tsummaryrow) +[`RenderGroupCellContentProps`](#rendergroupcellcontentpropstrow-tsummaryrow) **Example:** @@ -1141,7 +1145,7 @@ const columns: readonly Column[] = [ ]; ``` -#### `renderValue(props: RenderCellProps)` +#### `renderValue(props: RenderCellContentProps)` The default cell renderer that renders the value of `row[column.key]`. @@ -1347,23 +1351,23 @@ Class name(s) for the header 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>` +##### `renderCell?: Maybe<(props: RenderCellContentProps) => ReactNode>` Render function to render the content of cells. -##### `renderHeaderCell?: Maybe<(props: RenderHeaderCellProps) => ReactNode>` +##### `renderHeaderCell?: Maybe<(props: RenderHeaderCellContentProps) => ReactNode>` Render function to render the content of the header cell. -##### `renderSummaryCell?: Maybe<(props: RenderSummaryCellProps) => ReactNode>` +##### `renderSummaryCell?: Maybe<(props: RenderSummaryCellContentProps) => ReactNode>` Render function to render the content of summary cells -##### `renderGroupCell?: Maybe<(props: RenderGroupCellProps) => ReactNode>` +##### `renderGroupCell?: Maybe<(props: RenderGroupCellContentProps) => ReactNode>` Render function to render the content of group cells when using `TreeDataGrid`. -##### `renderEditCell?: Maybe<(props: RenderEditCellProps) => ReactNode>` +##### `renderEditCell?: Maybe<(props: RenderEditCellContentProps) => ReactNode>` Render function to render the content of edit cells. When set, the column is automatically set to be editable @@ -1533,12 +1537,12 @@ function getRowHeight(args: RowHeightArgs): number { ``` -#### `RenderCellProps` +#### `RenderCellContentProps` Props passed to custom cell renderers. ```tsx -interface RenderCellProps { +interface RenderCellContentProps { column: CalculatedColumn; row: TRow; rowIdx: number; @@ -1551,9 +1555,9 @@ interface RenderCellProps { **Example:** ```tsx -import type { RenderCellProps } from 'react-data-grid'; +import type { RenderCellContentProps } from 'react-data-grid'; -function renderCell({ row, column, onRowChange }: RenderCellProps) { +function renderCell({ row, column, onRowChange }: RenderCellContentProps) { return (
{row[column.key]} @@ -1563,12 +1567,12 @@ function renderCell({ row, column, onRowChange }: RenderCellProps) { } ``` -#### `RenderHeaderCellProps` +#### `RenderHeaderCellContentProps` Props passed to custom header cell renderers. ```tsx -interface RenderHeaderCellProps { +interface RenderHeaderCellContentProps { column: CalculatedColumn; sortDirection: SortDirection | undefined; priority: number | undefined; @@ -1576,12 +1580,12 @@ interface RenderHeaderCellProps { } ``` -#### `RenderEditCellProps` +#### `RenderEditCellContentProps` Props passed to custom edit cell renderers (editors). ```tsx -interface RenderEditCellProps { +interface RenderEditCellContentProps { column: CalculatedColumn; row: TRow; rowIdx: number; @@ -1593,9 +1597,9 @@ interface RenderEditCellProps { **Example:** ```tsx -import type { RenderEditCellProps } from 'react-data-grid'; +import type { RenderEditCellContentProps } from 'react-data-grid'; -function CustomEditor({ row, column, onRowChange, onClose }: RenderEditCellProps) { +function renderEditor({ row, column, onRowChange, onClose }: RenderEditCellContentProps) { return ( ` +#### `RenderSummaryCellContentProps` Props passed to summary cell renderers. ```tsx -interface RenderSummaryCellProps { +interface RenderSummaryCellContentProps { column: CalculatedColumn; row: TSummaryRow; tabIndex: number; } ``` -#### `RenderGroupCellProps` +#### `RenderGroupCellContentProps` Props passed to group cell renderers when using `TreeDataGrid`. ```tsx -interface RenderGroupCellProps { +interface RenderGroupCellContentProps { groupKey: unknown; column: CalculatedColumn; row: GroupRow; @@ -1650,14 +1654,14 @@ interface RenderRowProps { gridRowStart: number; lastFrozenColumnIndex: number; draggedOverCellIdx: number | undefined; - selectedCellEditor: ReactElement> | undefined; + selectedCellEditor: ReactElement> | undefined; onRowChange: (column: CalculatedColumn, rowIdx: number, newRow: TRow) => void; rowClass: Maybe<(row: TRow, rowIdx: number) => Maybe>; // ... and event handlers } ``` -#### `CellRendererProps` +#### `RenderCellProps` Props passed to the cell renderer when using `renderers.renderCell`. @@ -1669,7 +1673,7 @@ Custom renderer configuration for the grid. ```tsx interface Renderers { - renderCell?: Maybe<(key: Key, props: CellRendererProps) => ReactNode>; + renderCell?: Maybe<(key: Key, props: RenderCellProps) => ReactNode>; renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>; renderRow?: Maybe<(key: Key, props: RenderRowProps) => ReactNode>; renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>; @@ -1778,9 +1782,9 @@ Arguments passed to the `onCellKeyDown` handler. The shape differs based on whet **SELECT mode:** ```tsx -interface SelectCellKeyDownArgs { +interface SelectCellKeyDownArgs { mode: 'SELECT'; - column: CalculatedColumn; + column: CalculatedColumn | undefined; row: TRow; rowIdx: number; selectCell: (position: Position, options?: SelectCellOptions) => void; @@ -1790,7 +1794,7 @@ interface SelectCellKeyDownArgs { **EDIT mode:** ```tsx -interface EditCellKeyDownArgs { +interface EditCellKeyDownArgs { mode: 'EDIT'; column: CalculatedColumn; row: TRow; diff --git a/src/Cell.tsx b/src/Cell.tsx index 253d4bbd1a..02db3852a0 100644 --- a/src/Cell.tsx +++ b/src/Cell.tsx @@ -3,7 +3,7 @@ import { css } from 'ecij'; import { useRovingTabIndex } from './hooks'; import { createCellEvent, getCellClassname, getCellStyle, isCellEditableUtil } from './utils'; -import type { CellMouseEventHandler, CellRendererProps } from './types'; +import type { CellMouseEventHandler, RenderCellProps } from './types'; const cellDraggedOver = css` @layer rdg.Cell { @@ -33,7 +33,7 @@ function Cell({ selectCell, style, ...props -}: CellRendererProps) { +}: RenderCellProps) { const { tabIndex, childTabIndex, onFocus } = useRovingTabIndex(isCellSelected); const { cellClass } = column; @@ -126,10 +126,10 @@ function Cell({ ); } -const CellComponent = memo(Cell) as (props: CellRendererProps) => React.JSX.Element; +const MemoCell = memo(Cell) as (props: RenderCellProps) => React.JSX.Element; -export default CellComponent; +export { MemoCell as Cell }; -export function defaultRenderCell(key: React.Key, props: CellRendererProps) { - return ; +export function defaultRenderCell(key: React.Key, props: RenderCellProps) { + return ; } diff --git a/src/Columns.tsx b/src/Columns.tsx index 163b3280d7..7eedb3f7aa 100644 --- a/src/Columns.tsx +++ b/src/Columns.tsx @@ -1,14 +1,19 @@ import { useHeaderRowSelection, useRowSelection } from './hooks'; -import type { Column, RenderCellProps, RenderGroupCellProps, RenderHeaderCellProps } from './types'; -import { SelectCellFormatter } from './cellRenderers'; +import type { + Column, + RenderCellContentProps, + RenderGroupCellContentProps, + RenderHeaderCellContentProps +} from './types'; +import { SelectCheckbox } from './cellRenderers'; export const SELECT_COLUMN_KEY = 'rdg-select-column'; -function HeaderRenderer({ tabIndex }: RenderHeaderCellProps) { +function SelectAllCell({ tabIndex }: RenderHeaderCellContentProps) { const { isIndeterminate, isRowSelected, onRowSelectionChange } = useHeaderRowSelection(); return ( - ) { ); } -function SelectFormatter({ row, tabIndex }: RenderCellProps) { +function RowSelectCell({ row, tabIndex }: RenderCellContentProps) { const { isRowSelectionDisabled, isRowSelected, onRowSelectionChange } = useRowSelection(); return ( - ) { ); } -function SelectGroupFormatter({ row, tabIndex }: RenderGroupCellProps) { +function GroupSelectCell({ row, tabIndex }: RenderGroupCellContentProps) { const { isRowSelected, onRowSelectionChange } = useRowSelection(); return ( - = { sortable: false, frozen: true, renderHeaderCell(props) { - return ; + return ; }, renderCell(props) { - return ; + return ; }, renderGroupCell(props) { - return ; + return ; } }; diff --git a/src/DataGrid.tsx b/src/DataGrid.tsx index 71ee3dc72a..33c3035dfe 100644 --- a/src/DataGrid.tsx +++ b/src/DataGrid.tsx @@ -467,7 +467,7 @@ export function DataGrid(props: DataGridPr */ const handleColumnResizeLatest = useLatestFunc(handleColumnResize); const handleColumnResizeEndLatest = useLatestFunc(handleColumnResizeEnd); - const onColumnsReorderLastest = useLatestFunc(onColumnsReorder); + const onColumnsReorderLatest = useLatestFunc(onColumnsReorder); const onSortColumnsChangeLatest = useLatestFunc(onSortColumnsChange); const onCellMouseDownLatest = useLatestFunc(onCellMouseDown); const onCellClickLatest = useLatestFunc(onCellClick); @@ -475,7 +475,7 @@ export function DataGrid(props: DataGridPr const onCellContextMenuLatest = useLatestFunc(onCellContextMenu); const selectHeaderRowLatest = useLatestFunc(selectHeaderRow); const selectRowLatest = useLatestFunc(selectRow); - const handleFormatterRowChangeLatest = useLatestFunc(updateRow); + const updateRowLatest = useLatestFunc(updateRow); const selectCellLatest = useLatestFunc(selectCell); const selectHeaderCellLatest = useLatestFunc(selectHeaderCell); @@ -960,7 +960,8 @@ export function DataGrid(props: DataGridPr const isLastRow = rowIdx === maxRowIdx; const columnWidth = getColumnWidth(column); const colSpan = column.colSpan?.({ type: 'ROW', row: rows[rowIdx] }) ?? 1; - const { insetInlineStart, ...style } = getCellStyle(column, colSpan); + const style = getCellStyle(column, colSpan); + const { insetInlineStart } = style; const marginEnd = 'calc(var(--rdg-drag-handle-size) * -0.5 + 1px)'; const isLastColumn = column.idx + colSpan - 1 === maxColIdx; const dragHandleStyle: React.CSSProperties = { @@ -1125,7 +1126,7 @@ export function DataGrid(props: DataGridPr selectedCellIdx: selectedRowIdx === rowIdx ? selectedIdx : undefined, draggedOverCellIdx: getDraggedOverCellIdx(rowIdx), lastFrozenColumnIndex, - onRowChange: handleFormatterRowChangeLatest, + onRowChange: updateRowLatest, selectCell: selectCellLatest, selectedCellEditor: getCellEditor(rowIdx), isTreeGrid @@ -1219,7 +1220,7 @@ export function DataGrid(props: DataGridPr columns={getRowViewportColumns(mainHeaderRowIdx)} onColumnResize={handleColumnResizeLatest} onColumnResizeEnd={handleColumnResizeEndLatest} - onColumnsReorder={onColumnsReorderLastest} + onColumnsReorder={onColumnsReorderLatest} sortColumns={sortColumns} onSortColumnsChange={onSortColumnsChangeLatest} lastFrozenColumnIndex={lastFrozenColumnIndex} diff --git a/src/EditCell.tsx b/src/EditCell.tsx index 35b5638007..bdfdcc51a2 100644 --- a/src/EditCell.tsx +++ b/src/EditCell.tsx @@ -2,14 +2,7 @@ import { useEffectEvent, useLayoutEffect, useRef } from 'react'; import { css } from 'ecij'; import { createCellEvent, getCellClassname, getCellStyle, onEditorNavigation } from './utils'; -import type { - CellKeyboardEvent, - CellRendererProps, - EditCellKeyDownArgs, - Maybe, - Omit, - RenderEditCellProps -} from './types'; +import type { EditCellProps } from './types'; declare global { const scheduler: Scheduler | undefined; @@ -50,19 +43,6 @@ const cellEditing = css` } `; -type SharedCellRendererProps = Pick, 'colSpan'>; - -interface EditCellProps - extends - Omit, 'onRowChange' | 'onClose'>, - SharedCellRendererProps { - rowIdx: number; - onRowChange: (row: R, commitChanges: boolean, shouldFocusCell: boolean) => void; - closeEditor: (shouldFocusCell: boolean) => void; - navigate: (event: React.KeyboardEvent) => void; - onKeyDown: Maybe<(args: EditCellKeyDownArgs, event: CellKeyboardEvent) => void>; -} - export default function EditCell({ column, colSpan, @@ -70,8 +50,8 @@ export default function EditCell({ rowIdx, onRowChange, closeEditor, - onKeyDown, - navigate + navigate, + onKeyDown }: EditCellProps) { const captureEventRef = useRef(undefined); const abortControllerRef = useRef(undefined); diff --git a/src/GroupRow.tsx b/src/GroupedRow.tsx similarity index 92% rename from src/GroupRow.tsx rename to src/GroupedRow.tsx index 7c2c453c6b..ae5c675c0f 100644 --- a/src/GroupRow.tsx +++ b/src/GroupedRow.tsx @@ -24,7 +24,7 @@ const groupRow = css` const groupRowClassname = `rdg-group-row ${groupRow}`; -interface GroupRowRendererProps extends BaseRenderRowProps { +interface GroupedRowProps extends BaseRenderRowProps { row: GroupRow; groupBy: readonly string[]; toggleGroup: (expandedGroupId: unknown) => void; @@ -43,7 +43,7 @@ function GroupedRow({ toggleGroup, isRowSelectionDisabled: _isRowSelectionDisabled, ...props -}: GroupRowRendererProps) { +}: GroupedRowProps) { const isPositionOnRow = selectedCellIdx === -1; // Select is always the first column const idx = viewportColumns[0].key === SELECT_COLUMN_KEY ? row.level + 1 : row.level; @@ -97,6 +97,4 @@ function GroupedRow({ ); } -export default memo(GroupedRow) as ( - props: GroupRowRendererProps -) => React.JSX.Element; +export default memo(GroupedRow) as (props: GroupedRowProps) => React.JSX.Element; diff --git a/src/Row.tsx b/src/Row.tsx index d38ea2bc5d..4c27d0a10e 100644 --- a/src/Row.tsx +++ b/src/Row.tsx @@ -102,10 +102,10 @@ function Row({ ); } -const RowComponent = memo(Row) as (props: RenderRowProps) => React.JSX.Element; +const MemoRow = memo(Row) as (props: RenderRowProps) => React.JSX.Element; -export default RowComponent; +export { MemoRow as Row }; export function defaultRenderRow(key: React.Key, props: RenderRowProps) { - return ; + return ; } diff --git a/src/SummaryCell.tsx b/src/SummaryCell.tsx index d44738ed03..4e7010e900 100644 --- a/src/SummaryCell.tsx +++ b/src/SummaryCell.tsx @@ -2,14 +2,14 @@ import { memo } from 'react'; import { useRovingTabIndex } from './hooks'; import { getCellClassname, getCellStyle } from './utils'; -import type { CellRendererProps } from './types'; +import type { RenderCellProps } from './types'; -type SharedCellRendererProps = Pick< - CellRendererProps, +type SharedRenderCellProps = Pick< + RenderCellProps, 'rowIdx' | 'column' | 'colSpan' | 'isCellSelected' | 'selectCell' >; -interface SummaryCellProps extends SharedCellRendererProps { +interface SummaryCellProps extends SharedRenderCellProps { row: SR; } diff --git a/src/TreeDataGrid.tsx b/src/TreeDataGrid.tsx index bafb2e0f10..5a7a6f9712 100644 --- a/src/TreeDataGrid.tsx +++ b/src/TreeDataGrid.tsx @@ -22,7 +22,7 @@ import { SELECT_COLUMN_KEY } from './Columns'; import { DataGrid } from './DataGrid'; import type { DataGridProps } from './DataGrid'; import { useDefaultRenderers } from './DataGridDefaultRenderersContext'; -import GroupedRow from './GroupRow'; +import GroupedRow from './GroupedRow'; import { defaultRenderRow } from './Row'; export interface TreeDataGridProps extends Omit< @@ -115,27 +115,29 @@ export function TreeDataGrid({ const [groupedRows, rowsCount] = useMemo(() => { if (groupBy.length === 0) return [undefined, rawRows.length]; - const groupRows = ( + function groupRows( rows: readonly R[], - [groupByKey, ...remainingGroupByKeys]: readonly string[], + groupByIndex: number, startRowIndex: number - ): [Readonly>, number] => { + ): [Readonly>, number] { let groupRowsCount = 0; + const groupByKey = groupBy[groupByIndex]; + const isLastGroupBy = groupByIndex === groupBy.length - 1; const groups: GroupByDictionary = {}; + for (const [key, childRows] of Object.entries(rowGrouper(rows, groupByKey))) { // Recursively group each parent group - const [childGroups, childRowsCount] = - remainingGroupByKeys.length === 0 - ? [childRows, childRows.length] - : groupRows(childRows, remainingGroupByKeys, startRowIndex + groupRowsCount + 1); // 1 for parent row + const [childGroups, childRowsCount] = isLastGroupBy + ? [childRows, childRows.length] + : groupRows(childRows, groupByIndex + 1, startRowIndex + groupRowsCount + 1); // 1 for parent row groups[key] = { childRows, childGroups, startRowIndex: startRowIndex + groupRowsCount }; groupRowsCount += childRowsCount + 1; // 1 for parent row } return [groups, groupRowsCount]; - }; + } - return groupRows(rawRows, groupBy, 0); + return groupRows(rawRows, 0, 0); }, [groupBy, rowGrouper, rawRows]); const [rows, isGroupRow] = useMemo((): [ diff --git a/src/cellRenderers/SelectCellFormatter.tsx b/src/cellRenderers/SelectCheckbox.tsx similarity index 83% rename from src/cellRenderers/SelectCellFormatter.tsx rename to src/cellRenderers/SelectCheckbox.tsx index e006386fd7..145a6ebe06 100644 --- a/src/cellRenderers/SelectCellFormatter.tsx +++ b/src/cellRenderers/SelectCheckbox.tsx @@ -6,11 +6,11 @@ type SharedInputProps = Pick< 'disabled' | 'tabIndex' | 'aria-label' | 'aria-labelledby' | 'indeterminate' | 'onChange' >; -interface SelectCellFormatterProps extends SharedInputProps { +interface SelectCheckboxProps extends SharedInputProps { value: boolean; } -export function SelectCellFormatter({ +export function SelectCheckbox({ value, tabIndex, indeterminate, @@ -18,7 +18,7 @@ export function SelectCellFormatter({ onChange, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy -}: SelectCellFormatterProps) { +}: SelectCheckboxProps) { const renderCheckbox = useDefaultRenderers()!.renderCheckbox!; return renderCheckbox({ diff --git a/src/cellRenderers/index.ts b/src/cellRenderers/index.ts index 0eb0747f28..d126dc2f65 100644 --- a/src/cellRenderers/index.ts +++ b/src/cellRenderers/index.ts @@ -1,4 +1,4 @@ export * from './renderCheckbox'; export * from './renderToggleGroup'; export * from './renderValue'; -export * from './SelectCellFormatter'; +export * from './SelectCheckbox'; diff --git a/src/cellRenderers/renderToggleGroup.tsx b/src/cellRenderers/renderToggleGroup.tsx index ba7884dd7e..aacf370344 100644 --- a/src/cellRenderers/renderToggleGroup.tsx +++ b/src/cellRenderers/renderToggleGroup.tsx @@ -1,6 +1,6 @@ import { css } from 'ecij'; -import type { RenderGroupCellProps } from '../types'; +import type { RenderGroupCellContentProps } from '../types'; const groupCellContent = css` @layer rdg.GroupCellContent { @@ -26,7 +26,7 @@ const caret = css` const caretClassname = `rdg-caret ${caret}`; -export function renderToggleGroup(props: RenderGroupCellProps) { +export function renderToggleGroup(props: RenderGroupCellContentProps) { return ; } @@ -35,7 +35,7 @@ export function ToggleGroup({ isExpanded, tabIndex, toggleGroup -}: RenderGroupCellProps) { +}: RenderGroupCellContentProps) { function handleKeyDown({ key }: React.KeyboardEvent) { if (key === 'Enter') { toggleGroup(); diff --git a/src/cellRenderers/renderValue.tsx b/src/cellRenderers/renderValue.tsx index 8ea0bb06da..fa28d992ed 100644 --- a/src/cellRenderers/renderValue.tsx +++ b/src/cellRenderers/renderValue.tsx @@ -1,6 +1,6 @@ -import type { RenderCellProps } from '../types'; +import type { RenderCellContentProps } from '../types'; -export function renderValue(props: RenderCellProps) { +export function renderValue(props: RenderCellContentProps) { try { return props.row[props.column.key as keyof R] as React.ReactNode; } catch { diff --git a/src/editors/renderTextEditor.tsx b/src/editors/renderTextEditor.tsx index cba7b4a115..a53e9a4eca 100644 --- a/src/editors/renderTextEditor.tsx +++ b/src/editors/renderTextEditor.tsx @@ -1,6 +1,6 @@ import { css } from 'ecij'; -import type { RenderEditCellProps } from '../types'; +import type { RenderEditCellContentProps } from '../types'; const textEditorInternalClassname = css` @layer rdg.TextEditor { @@ -43,7 +43,7 @@ export function renderTextEditor({ column, onRowChange, onClose -}: RenderEditCellProps) { +}: RenderEditCellContentProps) { return ( = { -readonly [P in keyof T]: T[P] extends readonly (infer V)[] ? Mutable[] : T[P]; diff --git a/src/hooks/useLatestFunc.ts b/src/hooks/useLatestFunc.ts index 634694f3bb..c78c78dd05 100644 --- a/src/hooks/useLatestFunc.ts +++ b/src/hooks/useLatestFunc.ts @@ -3,18 +3,25 @@ import { useCallback, useLayoutEffect, useRef } from 'react'; import type { Maybe } from '../types'; // https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function useLatestFunc any>>(fn: T): T { + +export function useLatestFunc( + fn: (...args: Args) => void +): (...args: Args) => void; + +export function useLatestFunc( + fn: Maybe<(...args: Args) => void> +): Maybe<(...args: Args) => void>; + +export function useLatestFunc(fn: Maybe<(...args: Args) => void>) { const ref = useRef(fn); useLayoutEffect(() => { ref.current = fn; }); - const callbackFn = useCallback((...args: Parameters>) => { + const callbackFn = useCallback((...args: Args): void => { ref.current!(...args); }, []); - // @ts-expect-error return fn ? callbackFn : fn; } diff --git a/src/index.ts b/src/index.ts index ce5f9900fa..e47cb16bcd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,12 +8,12 @@ export { } from './DataGrid'; export { TreeDataGrid, type TreeDataGridProps } from './TreeDataGrid'; export { DataGridDefaultRenderersContext } from './DataGridDefaultRenderersContext'; -export { default as Row } from './Row'; -export { default as Cell } from './Cell'; +export { Row } from './Row'; +export { Cell } from './Cell'; export * from './Columns'; export * from './cellRenderers'; export { renderTextEditor } from './editors/renderTextEditor'; -export { default as renderHeaderCell } from './renderHeaderCell'; +export { renderHeaderCell } from './renderHeaderCell'; export { renderSortIcon, renderSortPriority } from './sortStatus'; export { useRowSelection, useHeaderRowSelection } from './hooks'; export type { @@ -26,7 +26,6 @@ export type { CellMouseArgs, CellMouseEvent, CellPasteArgs, - CellRendererProps, CellSelectArgs, ColSpanArgs, Column, @@ -36,17 +35,18 @@ export type { ColumnWidths, Direction, FillEvent, + RenderCellContentProps, RenderCellProps, RenderCheckboxProps, - RenderEditCellProps, + RenderEditCellContentProps, Renderers, - RenderGroupCellProps, - RenderHeaderCellProps, + RenderGroupCellContentProps, + RenderHeaderCellContentProps, RenderRowProps, RenderSortIconProps, RenderSortPriorityProps, RenderSortStatusProps, - RenderSummaryCellProps, + RenderSummaryCellContentProps, RowHeightArgs, RowsChangeData, SelectCellOptions, diff --git a/src/renderHeaderCell.tsx b/src/renderHeaderCell.tsx index eb14ee48f4..f3248bfd06 100644 --- a/src/renderHeaderCell.tsx +++ b/src/renderHeaderCell.tsx @@ -1,6 +1,6 @@ import { css } from 'ecij'; -import type { RenderHeaderCellProps } from './types'; +import type { RenderHeaderCellContentProps } from './types'; import { useDefaultRenderers } from './DataGridDefaultRenderersContext'; const headerSortCellClassname = css` @@ -19,39 +19,31 @@ const headerSortName = css` const headerSortNameClassname = `rdg-header-sort-name ${headerSortName}`; -export default function renderHeaderCell({ +export function renderHeaderCell({ column, sortDirection, priority -}: RenderHeaderCellProps) { +}: RenderHeaderCellContentProps) { if (!column.sortable) return column.name; - return ( - - {column.name} - - ); + return ; } -type SharedHeaderCellProps = Pick< - RenderHeaderCellProps, - 'sortDirection' | 'priority' +type SortableHeaderCellProps = Pick< + RenderHeaderCellContentProps, + 'column' | 'sortDirection' | 'priority' >; -interface SortableHeaderCellProps extends SharedHeaderCellProps { - children: React.ReactNode; -} - function SortableHeaderCell({ + column, sortDirection, - priority, - children + priority }: SortableHeaderCellProps) { const renderSortStatus = useDefaultRenderers()!.renderSortStatus!; return ( - {children} + {column.name} {renderSortStatus({ sortDirection, priority })} ); diff --git a/src/types.ts b/src/types.ts index 3eb61268aa..36e7f8bd39 100644 --- a/src/types.ts +++ b/src/types.ts @@ -32,17 +32,23 @@ export interface Column { /** Class name(s) for summary cells */ readonly summaryCellClass?: Maybe Maybe)>; /** Render function to render the content of cells */ - readonly renderCell?: Maybe<(props: RenderCellProps) => ReactNode>; + readonly renderCell?: Maybe<(props: RenderCellContentProps) => ReactNode>; /** Render function to render the content of the header cell */ - readonly renderHeaderCell?: Maybe<(props: RenderHeaderCellProps) => ReactNode>; + readonly renderHeaderCell?: Maybe< + (props: RenderHeaderCellContentProps) => ReactNode + >; /** Render function to render the content of summary cells */ readonly renderSummaryCell?: Maybe< - (props: RenderSummaryCellProps) => ReactNode + (props: RenderSummaryCellContentProps) => ReactNode >; /** Render function to render the content of group cells */ - readonly renderGroupCell?: Maybe<(props: RenderGroupCellProps) => ReactNode>; + readonly renderGroupCell?: Maybe< + (props: RenderGroupCellContentProps) => ReactNode + >; /** Render function to render the content of edit cells. When set, the column is automatically set to be editable */ - readonly renderEditCell?: Maybe<(props: RenderEditCellProps) => ReactNode>; + readonly renderEditCell?: Maybe< + (props: RenderEditCellContentProps) => ReactNode + >; /** Enables cell editing. If set and no editor property specified, then a textinput will be used as the cell editor */ readonly editable?: Maybe boolean)>; readonly colSpan?: Maybe<(args: ColSpanArgs) => Maybe>; @@ -89,8 +95,8 @@ export interface CalculatedColumn extends Column) => ReactNode; - readonly renderHeaderCell: (props: RenderHeaderCellProps) => ReactNode; + readonly renderCell: (props: RenderCellContentProps) => ReactNode; + readonly renderHeaderCell: (props: RenderHeaderCellContentProps) => ReactNode; } export interface ColumnGroup { @@ -120,7 +126,7 @@ export interface Position { readonly rowIdx: number; } -export interface RenderCellProps { +export interface RenderCellContentProps { column: CalculatedColumn; row: TRow; rowIdx: number; @@ -129,13 +135,13 @@ export interface RenderCellProps { onRowChange: (row: TRow) => void; } -export interface RenderSummaryCellProps { +export interface RenderSummaryCellContentProps { column: CalculatedColumn; row: TSummaryRow; tabIndex: number; } -export interface RenderGroupCellProps { +export interface RenderGroupCellContentProps { groupKey: unknown; column: CalculatedColumn; row: GroupRow; @@ -145,7 +151,7 @@ export interface RenderGroupCellProps { toggleGroup: () => void; } -export interface RenderEditCellProps { +export interface RenderEditCellContentProps { column: CalculatedColumn; row: TRow; rowIdx: number; @@ -153,14 +159,14 @@ export interface RenderEditCellProps { onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void; } -export interface RenderHeaderCellProps { +export interface RenderHeaderCellContentProps { column: CalculatedColumn; sortDirection: SortDirection | undefined; priority: number | undefined; tabIndex: number; } -interface BaseCellRendererProps +interface BaseRenderCellProps extends Omit, 'children'>, Pick< @@ -171,10 +177,7 @@ interface BaseCellRendererProps selectCell: (position: Position, options?: SelectCellOptions) => void; } -export interface CellRendererProps extends BaseCellRendererProps< - TRow, - TSummaryRow -> { +export interface RenderCellProps extends BaseRenderCellProps { column: CalculatedColumn; row: TRow; colSpan: number | undefined; @@ -183,6 +186,16 @@ export interface CellRendererProps extends BaseCellRendererPr onRowChange: (column: CalculatedColumn, newRow: TRow) => void; } +export interface EditCellProps extends Pick< + RenderCellProps, + 'column' | 'colSpan' | 'row' | 'rowIdx' +> { + onRowChange: (row: R, commitChanges: boolean, shouldFocusCell: boolean) => void; + closeEditor: (shouldFocusCell: boolean) => void; + navigate: (event: React.KeyboardEvent) => void; + onKeyDown: Maybe<(args: EditCellKeyDownArgs, event: CellKeyboardEvent) => void>; +} + export type CellEvent> = E & { preventGridDefault: () => void; isGridDefaultPrevented: () => boolean; @@ -232,7 +245,7 @@ export type CellMouseEventHandler = Maybe< (args: CellMouseArgs, NoInfer>, event: CellMouseEvent) => void >; -export interface BaseRenderRowProps extends BaseCellRendererProps< +export interface BaseRenderRowProps extends BaseRenderCellProps< TRow, TSummaryRow > { @@ -251,7 +264,7 @@ export interface RenderRowProps extends BaseRenderR row: TRow; lastFrozenColumnIndex: number; draggedOverCellIdx: number | undefined; - selectedCellEditor: ReactElement> | undefined; + selectedCellEditor: ReactElement> | undefined; onRowChange: (column: CalculatedColumn, rowIdx: number, newRow: TRow) => void; rowClass: Maybe<(row: TRow, rowIdx: number) => Maybe>; isTreeGrid: boolean; @@ -334,7 +347,7 @@ export interface RenderCheckboxProps extends Pick< } export interface Renderers { - renderCell?: Maybe<(key: Key, props: CellRendererProps) => ReactNode>; + renderCell?: Maybe<(key: Key, props: RenderCellProps) => ReactNode>; renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>; renderRow?: Maybe<(key: Key, props: RenderRowProps) => ReactNode>; renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>; diff --git a/src/utils/eventUtils.ts b/src/utils/eventUtils.ts index d8d47a4892..5806b5af2a 100644 --- a/src/utils/eventUtils.ts +++ b/src/utils/eventUtils.ts @@ -4,17 +4,17 @@ export function createCellEvent>( event: E ): CellEvent { let defaultPrevented = false; - const cellEvent = { - ...event, - preventGridDefault() { - defaultPrevented = true; + + return Object.create(event, { + preventGridDefault: { + value() { + defaultPrevented = true; + } }, - isGridDefaultPrevented() { - return defaultPrevented; + isGridDefaultPrevented: { + value() { + return defaultPrevented; + } } - }; - - Object.setPrototypeOf(cellEvent, Object.getPrototypeOf(event)); - - return cellEvent; + }); } diff --git a/test/browser/TreeDataGrid.test.tsx b/test/browser/TreeDataGrid.test.tsx index 3689661e96..77fc7d91b5 100644 --- a/test/browser/TreeDataGrid.test.tsx +++ b/test/browser/TreeDataGrid.test.tsx @@ -97,9 +97,7 @@ function TestGrid({ }) { const [rows, setRows] = useState(initialRows); const [selectedRows, setSelectedRows] = useState((): ReadonlySet => new Set()); - const [expandedGroupIds, setExpandedGroupIds] = useState( - (): ReadonlySet => new Set([]) - ); + const [expandedGroupIds, setExpandedGroupIds] = useState((): ReadonlySet => new Set([])); return ( { ); }); -test('update row using cell renderer', async () => { +test('update row using cell content renderer', async () => { await setup(['year']); await userEvent.click(page.getCell({ name: '2021' })); await userEvent.click(page.getCell({ name: 'USA' })); diff --git a/test/browser/column/renderCell.test.tsx b/test/browser/column/renderCell.test.tsx index d0bdaf7ff1..e111b90824 100644 --- a/test/browser/column/renderCell.test.tsx +++ b/test/browser/column/renderCell.test.tsx @@ -3,7 +3,7 @@ import { page, userEvent } from 'vitest/browser'; import { DataGrid } from '../../../src'; import type { Column } from '../../../src'; -import defaultRenderHeaderCell from '../../../src/renderHeaderCell'; +import { renderHeaderCell } from '../../../src/renderHeaderCell'; import { getCellsAtRowIndex, safeTab, setup } from '../utils'; const cells = page.getCell(); @@ -33,7 +33,7 @@ describe('renderValue', () => { }); }); -describe('Custom cell renderer', () => { +describe('Custom cell content renderer', () => { const columns: readonly Column[] = [ { key: 'id', @@ -49,7 +49,7 @@ describe('Custom cell renderer', () => { const rows: readonly Row[] = [{ id: 101 }]; - it('should replace the default cell renderer', async () => { + it('should replace the default cell content renderer', async () => { await setup({ columns, rows }); await expect.element(cells.nth(0)).toHaveTextContent('#101'); await expect.element(cells.nth(1)).toHaveTextContent('No name'); @@ -108,7 +108,7 @@ describe('Custom cell renderer', () => { sortable: false, draggable: false, width: 'auto', - renderHeaderCell: defaultRenderHeaderCell + renderHeaderCell }, indexes: [0] }); @@ -167,7 +167,7 @@ test('Focus child if it sets tabIndex', async () => { test('Cell should not steal focus when the focus is outside the grid and cell is recreated', async () => { const columns: readonly Column[] = [{ key: 'id', name: 'ID' }]; - function FormatterTest() { + function Test() { const [rows, setRows] = useState((): readonly Row[] => [{ id: 1 }]); function onClick() { @@ -189,7 +189,7 @@ test('Cell should not steal focus when the focus is outside the grid and cell is ); } - await page.render(); + await page.render(); const cell = getCellsAtRowIndex(0).nth(0); await userEvent.click(cell); diff --git a/test/browser/keyboardNavigation.test.tsx b/test/browser/keyboardNavigation.test.tsx index 6d435374aa..fa63a3f34c 100644 --- a/test/browser/keyboardNavigation.test.tsx +++ b/test/browser/keyboardNavigation.test.tsx @@ -173,13 +173,13 @@ test('grid enter/exit', async () => { await expect.element(afterButton).toHaveFocus(); }); -test('navigation with focusable cell renderer', async () => { +test('navigation with focusable cell content', async () => { await setup({ columns, rows: Array.from({ length: 1 }), bottomSummaryRows }, true); await tabIntoGrid(); await userEvent.keyboard('{arrowdown}'); await validateCellPosition(0, 1); - // cell should not set tabIndex to 0 if it contains a focusable cell renderer + // cell should not set tabIndex to 0 if it contains a focusable cell content await expect.element(selectedCell).toHaveAttribute('tabIndex', '-1'); const checkbox = selectedCell.getByRole('checkbox'); await expect.element(checkbox).toHaveFocus(); @@ -187,7 +187,7 @@ test('navigation with focusable cell renderer', async () => { await safeTab(); await validateCellPosition(1, 1); - // cell should set tabIndex to 0 if it does not have focusable cell renderer + // cell should set tabIndex to 0 if it does not have focusable cell content await expect.element(selectedCell).toHaveAttribute('tabIndex', '0'); }); diff --git a/test/browser/renderers.test.tsx b/test/browser/renderers.test.tsx index 54059ee126..0b9a38ed7c 100644 --- a/test/browser/renderers.test.tsx +++ b/test/browser/renderers.test.tsx @@ -10,7 +10,7 @@ import { SelectColumn } from '../../src'; import type { - CellRendererProps, + RenderCellProps, Column, DataGridProps, RenderRowProps, @@ -41,11 +41,11 @@ const columns: readonly Column[] = [ } ]; -function renderGlobalCell(key: React.Key, props: CellRendererProps) { +function renderGlobalCell(key: React.Key, props: RenderCellProps) { return ; } -function renderLocalCell(key: React.Key, props: CellRendererProps) { +function renderLocalCell(key: React.Key, props: RenderCellProps) { return ; } diff --git a/website/components/CellExpanderFormatter.tsx b/website/components/CellExpander.tsx similarity index 80% rename from website/components/CellExpanderFormatter.tsx rename to website/components/CellExpander.tsx index 7d27dddfcf..7ef2521b67 100644 --- a/website/components/CellExpanderFormatter.tsx +++ b/website/components/CellExpander.tsx @@ -7,17 +7,13 @@ const cellExpandClassname = css` cursor: pointer; `; -interface CellExpanderFormatterProps { +interface CellExpanderProps { tabIndex: number; expanded: boolean; onCellExpand: () => void; } -export function CellExpanderFormatter({ - tabIndex, - expanded, - onCellExpand -}: CellExpanderFormatterProps) { +export function CellExpander({ tabIndex, expanded, onCellExpand }: CellExpanderProps) { function handleKeyDown(e: React.KeyboardEvent) { if (e.key === ' ' || e.key === 'Enter') { // prevent scrolling diff --git a/website/components/DraggableRowRenderer.tsx b/website/components/DraggableRow.tsx similarity index 93% rename from website/components/DraggableRowRenderer.tsx rename to website/components/DraggableRow.tsx index fe0a58908e..5e93924430 100644 --- a/website/components/DraggableRowRenderer.tsx +++ b/website/components/DraggableRow.tsx @@ -12,16 +12,16 @@ const rowOverClassname = css` background-color: #ececec; `; -interface DraggableRowRenderProps extends RenderRowProps { +interface DraggableRowProps extends RenderRowProps { onRowReorder: (sourceIndex: number, targetIndex: number) => void; } -export function DraggableRowRenderer({ +export function DraggableRow({ rowIdx, className, onRowReorder, ...props -}: DraggableRowRenderProps) { +}: DraggableRowProps) { const [isDragging, setIsDragging] = useState(false); const [isOver, setIsOver] = useState(false); diff --git a/website/components/index.ts b/website/components/index.ts index d8e454b6f5..2683718ec0 100644 --- a/website/components/index.ts +++ b/website/components/index.ts @@ -1,3 +1,3 @@ -export * from './CellExpanderFormatter'; +export * from './CellExpander'; export * from './ChildRowDeleteButton'; -export * from './DraggableRowRenderer'; +export * from './DraggableRow'; diff --git a/website/renderers/renderCoordinates.ts b/website/renderers/renderCoordinates.ts index 3761a3a710..ecaf8abc42 100644 --- a/website/renderers/renderCoordinates.ts +++ b/website/renderers/renderCoordinates.ts @@ -1,6 +1,6 @@ -import type { RenderCellProps } from '../../src'; +import type { RenderCellContentProps } from '../../src'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function renderCoordinates(props: RenderCellProps) { +export function renderCoordinates(props: RenderCellContentProps) { return `${props.column.key}×${props.row}`; } diff --git a/website/routes/CommonFeatures.tsx b/website/routes/CommonFeatures.tsx index b24c187f42..39b3039830 100644 --- a/website/routes/CommonFeatures.tsx +++ b/website/routes/CommonFeatures.tsx @@ -7,7 +7,7 @@ import { css } from 'ecij'; import { DataGrid, renderTextEditor, - SelectCellFormatter, + SelectCheckbox, SelectColumn, type Column, type DataGridHandle, @@ -48,11 +48,11 @@ const dialogContainerClassname = css` } `; -const dateFormatter = new Intl.DateTimeFormat(navigator.language); +const dateFormatter = new Intl.DateTimeFormat(navigator.language).format; const currencyFormatter = new Intl.NumberFormat(navigator.language, { style: 'currency', currency: 'eur' -}); +}).format; interface SummaryRow { id: string; @@ -192,21 +192,21 @@ function getColumns( key: 'startTimestamp', name: 'Start date', renderCell(props) { - return dateFormatter.format(props.row.startTimestamp); + return dateFormatter(props.row.startTimestamp); } }, { key: 'endTimestamp', name: 'Deadline', renderCell(props) { - return dateFormatter.format(props.row.endTimestamp); + return dateFormatter(props.row.endTimestamp); } }, { key: 'budget', name: 'Budget', renderCell(props) { - return currencyFormatter.format(props.row.budget); + return currencyFormatter(props.row.budget); } }, { @@ -227,7 +227,7 @@ function getColumns( name: 'Available', renderCell({ row, onRowChange, tabIndex }) { return ( - { onRowChange({ ...row, available: !row.available }); diff --git a/website/routes/CustomizableRenderers.tsx b/website/routes/CustomizableRenderers.tsx index 0707751ef8..e14b8bcdf8 100644 --- a/website/routes/CustomizableRenderers.tsx +++ b/website/routes/CustomizableRenderers.tsx @@ -4,7 +4,7 @@ import { css } from 'ecij'; import { Row as BaseRow, Cell, DataGrid, renderTextEditor, SelectColumn } from '../../src'; import type { - CellRendererProps, + RenderCellProps, Column, RenderCheckboxProps, RenderRowProps, @@ -159,7 +159,7 @@ function renderSortStatus({ sortDirection, priority }: RenderSortStatusProps) { const cellStyle: React.CSSProperties = { color: 'red' }; -function renderCell(key: React.Key, props: CellRendererProps) { +function renderCell(key: React.Key, props: RenderCellProps) { const style = props.column.key === 'priority' && props.row.priority === 'Critical' ? cellStyle : undefined; diff --git a/website/routes/HeaderFilters.tsx b/website/routes/HeaderFilters.tsx index 4c8c72483d..77f922eb8e 100644 --- a/website/routes/HeaderFilters.tsx +++ b/website/routes/HeaderFilters.tsx @@ -3,7 +3,7 @@ import { faker } from '@faker-js/faker'; import { createFileRoute } from '@tanstack/react-router'; import { css } from 'ecij'; -import { DataGrid, type Column, type RenderHeaderCellProps } from '../../src'; +import { DataGrid, type Column, type RenderHeaderCellContentProps } from '../../src'; import type { Omit } from '../../src/types'; import { useDirection } from '../directionContext'; @@ -97,7 +97,7 @@ function HeaderFilters() { const developerOptions = useMemo( () => - Array.from(new Set(rows.map((r) => r.developer))).map((d) => ({ + Array.from(new Set(rows.map((r) => r.developer)), (d) => ({ label: d, value: d })), @@ -116,7 +116,7 @@ function HeaderFilters() { name: 'Title', headerCellClass: filterColumnClassName, renderHeaderCell: (p) => ( - {...p}> + {...p}> {({ filters, ...rest }) => ( )} - + ) }, { @@ -139,7 +139,7 @@ function HeaderFilters() { name: 'Priority', headerCellClass: filterColumnClassName, renderHeaderCell: (p) => ( - {...p}> + {...p}> {({ filters, ...rest }) => ( )} - + ) }, { @@ -168,7 +168,7 @@ function HeaderFilters() { name: 'Issue Type', headerCellClass: filterColumnClassName, renderHeaderCell: (p) => ( - {...p}> + {...p}> {({ filters, ...rest }) => ( )} - + ) }, { @@ -197,7 +197,7 @@ function HeaderFilters() { name: 'Developer', headerCellClass: filterColumnClassName, renderHeaderCell: (p) => ( - {...p}> + {...p}> {({ filters, ...rest }) => ( )} - + ) }, { @@ -221,7 +221,7 @@ function HeaderFilters() { name: '% Complete', headerCellClass: filterColumnClassName, renderHeaderCell: (p) => ( - {...p}> + {...p}> {({ filters, ...rest }) => ( )} - + ) } ]; @@ -311,11 +311,11 @@ function HeaderFilters() { ); } -function FilterRenderer({ +function FilterCell({ tabIndex, column, children -}: RenderHeaderCellProps & { +}: RenderHeaderCellContentProps & { children: (args: { tabIndex: number; filters: Filter }) => React.ReactElement; }) { const filters = use(FilterContext)!; diff --git a/website/routes/InfiniteScrolling.tsx b/website/routes/InfiniteScrolling.tsx index ecea0424ae..4c4317611f 100644 --- a/website/routes/InfiniteScrolling.tsx +++ b/website/routes/InfiniteScrolling.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useTransition } from 'react'; import { faker } from '@faker-js/faker'; import { createFileRoute } from '@tanstack/react-router'; import { css } from 'ecij'; @@ -96,17 +96,15 @@ function loadMoreRows(newRowsCount: number, length: number): Promise { function InfiniteScrolling() { const direction = useDirection(); const [rows, setRows] = useState(() => createRows(50)); - const [isLoading, setIsLoading] = useState(false); + const [isPending, startTransition] = useTransition(); - async function handleScroll(event: React.UIEvent) { - if (isLoading || !isAtBottom(event)) return; + function handleScroll(event: React.UIEvent) { + if (isPending || !isAtBottom(event)) return; - setIsLoading(true); - - const newRows = await loadMoreRows(50, rows.length); - - setRows([...rows, ...newRows]); - setIsLoading(false); + startTransition(async () => { + const newRows = await loadMoreRows(50, rows.length); + setRows([...rows, ...newRows]); + }); } return ( @@ -122,7 +120,7 @@ function InfiniteScrolling() { className="fill-grid" direction={direction} /> - {isLoading &&
Loading more rows...
} + {isPending &&
Loading more rows...
} ); } diff --git a/website/routes/MasterDetail.tsx b/website/routes/MasterDetail.tsx index 0a81104457..030a0f498c 100644 --- a/website/routes/MasterDetail.tsx +++ b/website/routes/MasterDetail.tsx @@ -4,7 +4,7 @@ import { createFileRoute } from '@tanstack/react-router'; import { css } from 'ecij'; import { DataGrid, type Column, type Direction, type RowsChangeData } from '../../src'; -import { CellExpanderFormatter } from '../components'; +import { CellExpander } from '../components'; import { useDirection } from '../directionContext'; export const Route = createFileRoute('/MasterDetail')({ @@ -94,7 +94,7 @@ function MasterDetail() { } return ( - { diff --git a/website/routes/NoRows.tsx b/website/routes/NoRows.tsx index 93ca3ebbf8..f458b718ab 100644 --- a/website/routes/NoRows.tsx +++ b/website/routes/NoRows.tsx @@ -13,7 +13,7 @@ const gridClassname = css` block-size: 300px; `; -function EmptyRowsRenderer() { +function NoRowsFallback() { return (
Nothing to show{' '} @@ -52,7 +52,7 @@ function NoRows() { aria-label="No Rows Example" columns={columns} rows={rows} - renderers={{ noRowsFallback: }} + renderers={{ noRowsFallback: }} selectedRows={selectedRows} onSelectedRowsChange={setSelectedRows} rowKeyGetter={rowKeyGetter} diff --git a/website/routes/RowGrouping.tsx b/website/routes/RowGrouping.tsx index 0d3cc493c0..1bdc10e793 100644 --- a/website/routes/RowGrouping.tsx +++ b/website/routes/RowGrouping.tsx @@ -154,7 +154,7 @@ function RowGrouping() { ]); const [expandedGroupIds, setExpandedGroupIds] = useState( (): ReadonlySet => - new Set(['United States of America', 'United States of America__2015']) + new Set(['United States of America', 'United States of America__2015']) ); function toggleOption(option: string, enabled: boolean) { diff --git a/website/routes/RowsReordering.tsx b/website/routes/RowsReordering.tsx index ebf27b36fc..3707bf7294 100644 --- a/website/routes/RowsReordering.tsx +++ b/website/routes/RowsReordering.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from 'react'; import { createFileRoute } from '@tanstack/react-router'; import { DataGrid, renderTextEditor, type Column, type RenderRowProps } from '../../src'; -import { DraggableRowRenderer } from '../components'; +import { DraggableRow } from '../components'; import { useDirection } from '../directionContext'; export const Route = createFileRoute('/RowsReordering')({ @@ -76,7 +76,7 @@ function RowsReordering() { document.startViewTransition(reorderRows); } - return key={key} {...props} onRowReorder={onRowReorder} />; + return key={key} {...props} onRowReorder={onRowReorder} />; }, []); return ( diff --git a/website/routes/TreeView.tsx b/website/routes/TreeView.tsx index 323924ce5f..d6c25820a7 100644 --- a/website/routes/TreeView.tsx +++ b/website/routes/TreeView.tsx @@ -3,7 +3,7 @@ import { createFileRoute } from '@tanstack/react-router'; import { css } from 'ecij'; import { DataGrid, type Column } from '../../src'; -import { CellExpanderFormatter, ChildRowDeleteButton } from '../components'; +import { CellExpander, ChildRowDeleteButton } from '../components'; import { useDirection } from '../directionContext'; export const Route = createFileRoute('/TreeView')({ @@ -152,7 +152,7 @@ function TreeView() { `} > {hasChildren && ( - dispatch({ id: row.id, type: 'toggleSubRow' })} diff --git a/website/utils.tsx b/website/utils.tsx index 072eca749d..d4d84d1056 100644 --- a/website/utils.tsx +++ b/website/utils.tsx @@ -37,11 +37,12 @@ function getGridContent(gridEl: HTMLDivElement) { }; function getRows(selector: string) { - return Array.from(gridEl.querySelectorAll(selector)).map((gridRow) => { - return Array.from(gridRow.querySelectorAll('.rdg-cell')).map( + return Array.from(gridEl.querySelectorAll(selector), (gridRow) => + Array.from( + gridRow.querySelectorAll('.rdg-cell'), (gridCell) => gridCell.innerText - ); - }); + ) + ); } }