Skip to content

Commit

Permalink
[DataGrid] New virtualization implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
m4theushw committed Oct 6, 2021
1 parent f64dfdf commit afb4b91
Show file tree
Hide file tree
Showing 23 changed files with 886 additions and 727 deletions.
4 changes: 3 additions & 1 deletion docs/scripts/generateProptypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as prettier from 'prettier';
import * as ttp from '@material-ui/monorepo/packages/typescript-to-proptypes/src';
import { fixBabelGeneratorIssues, fixLineEndings } from 'docs/scripts/helpers';

const ignoredComponents = ['GridCell'];

const tsconfig = ttp.loadConfig(path.resolve(__dirname, '../../tsconfig.json'));

const prettierConfig = prettier.resolveConfig.sync(process.cwd(), {
Expand Down Expand Up @@ -109,7 +111,7 @@ async function run() {
components.forEach((component) => {
const componentName = path.basename(component).replace('.tsx', '');
const isExported = exports.find((e) => e.name === componentName);
if (isExported) {
if (isExported && !ignoredComponents.includes(componentName)) {
componentsToAddPropTypes.push(component);
}
});
Expand Down
232 changes: 175 additions & 57 deletions packages/grid/_modules_/grid/components/GridRow.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/interactive-supports-focus */
/* eslint-disable jsx-a11y/mouse-events-have-key-events */
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { GridEvents } from '../constants/eventsConstants';
import { GridRowId } from '../models/gridRows';
import { GridEditModes, GridRowModes } from '../models/gridEditRowModel';
import { gridDensityRowHeightSelector } from '../hooks/features/density';
import { GridEditModes, GridRowModes, GridEditRowsModel } from '../models/gridEditRowModel';
import { useGridApiContext } from '../hooks/root/useGridApiContext';
import { useGridSelector } from '../hooks/features/core/useGridSelector';
import { composeClasses } from '../utils/material-ui-utils';
import { getDataGridUtilityClass } from '../gridClasses';
import { getDataGridUtilityClass, gridClasses } from '../gridClasses';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
import { GridComponentProps } from '../GridComponentProps';
import { GridStateColDef } from '../models/colDef/gridColDef';
import { GridCell } from './cell/GridCell';
import { GridCellParams } from '../models/params/gridCellParams';
import { isFunction } from '../utils/utils';
import { GridCellIdentifier } from '../hooks/features/focus/gridFocusState';
import { GridScrollBarState } from '../models/gridContainerProps';
import { gridColumnsMetaSelector } from '../hooks/features/columns/gridColumnsSelector';
import { useGridSelector } from '../hooks/features/core/useGridSelector';

export interface GridRowProps {
id: GridRowId;
selected: boolean;
rowIndex: number;
children: React.ReactNode;
firstColumnToRender: number;
lastColumnToRender: number;
visibleColumns: GridStateColDef[];
columns: GridStateColDef[];
style?: React.CSSProperties;
rowHeight: number;
cellFocus: GridCellIdentifier | null;
cellTabIndex: GridCellIdentifier | null;
editRowsState: GridEditRowsModel;
scrollBarState: GridScrollBarState;
}

type OwnerState = GridRowProps & {
type OwnerState = Pick<GridRowProps, 'selected'> & {
editable: boolean;
editing: boolean;
classes?: GridComponentProps['classes'];
Expand All @@ -35,55 +52,71 @@ const useUtilityClasses = (ownerState: OwnerState) => {
return composeClasses(slots, getDataGridUtilityClass, classes);
};

const EmptyCell = ({ width, height }) => {
if (!width || !height) {
return null;
}

const style = { width, height };

return <div className="MuiDataGrid-cell" style={style} />; // TODO change to .MuiDataGrid-emptyCell
};

function GridRow(props: GridRowProps) {
const { selected, id, rowIndex, children } = props;
const ariaRowIndex = rowIndex + 2; // 1 for the header row and 1 as it's 1 based
const {
selected,
id,
rowIndex,
rowHeight,
columns,
visibleColumns,
firstColumnToRender,
// lastColumnToRender,
cellFocus,
cellTabIndex,
editRowsState,
scrollBarState,
} = props; // TODO apply the rest of the props to the root
const ariaRowIndex = rowIndex + 2; // 1 for the header row and 1 as it's 1-based
const apiRef = useGridApiContext();
const rootProps = useGridRootProps();
const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector);
const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector);

const ownerState = {
...props,
selected,
classes: rootProps.classes,
editing: apiRef.current.getRowMode(id) === GridRowModes.Edit,
editable: rootProps.editMode === GridEditModes.Row,
};

const classes = useUtilityClasses(ownerState);

const publish = React.useCallback(
(eventName: string) => (event: React.MouseEvent) => {
// Ignore portal
// The target is not an element when triggered by a Select inside the cell
// See https://github.com/mui-org/material-ui/issues/10534
if (
(event.target as any).nodeType === 1 &&
!event.currentTarget.contains(event.target as Element)
) {
return;
}

// The row might have been deleted
if (!apiRef.current.getRow(id)) {
return;
}

apiRef.current.publishEvent(eventName, apiRef.current.getRowParams(id), event);
},
[apiRef, id],
);
const publish = (eventName: string) => (event: React.MouseEvent) => {
// Ignore portal
// The target is not an element when triggered by a Select inside the cell
// See https://github.com/mui-org/material-ui/issues/10534
if (
(event.target as any).nodeType === 1 &&
!event.currentTarget.contains(event.target as Element)
) {
return;
}

// The row might have been deleted
if (!apiRef.current.getRow(id)) {
return;
}

apiRef.current.publishEvent(eventName, apiRef.current.getRowParams(id), event);
};

const mouseEventsHandlers = React.useMemo<Partial<React.HTMLAttributes<HTMLDivElement>>>(
() => ({
onClick: publish(GridEvents.rowClick),
onDoubleClick: publish(GridEvents.rowDoubleClick),
onMouseOver: publish(GridEvents.rowOver),
onMouseOut: publish(GridEvents.rowOut),
onMouseEnter: publish(GridEvents.rowEnter),
onMouseLeave: publish(GridEvents.rowLeave),
}),
[publish],
);
const eventHandlers = {
onClick: publish(GridEvents.rowClick),
onDoubleClick: publish(GridEvents.rowDoubleClick),
// onMouseOver: publish(GridEvents.rowOver),
// onMouseOut: publish(GridEvents.rowOut),
// onMouseEnter: publish(GridEvents.rowEnter),
// onMouseLeave: publish(GridEvents.rowLeave),
};

const style = {
maxHeight: rowHeight,
Expand All @@ -94,32 +127,117 @@ function GridRow(props: GridRowProps) {
typeof rootProps.getRowClassName === 'function' &&
rootProps.getRowClassName(apiRef.current.getRowParams(id));

const cells: JSX.Element[] = [];

for (let i = 0; i < columns.length; i += 1) {
const column = columns[i];
const indexRelativeToAllColumns = firstColumnToRender + 1;

const isLastColumn = indexRelativeToAllColumns === visibleColumns.length - 1;
const removeLastBorderRight =
isLastColumn && scrollBarState.hasScrollX && !scrollBarState.hasScrollY;
const showRightBorder = !isLastColumn
? rootProps.showCellRightBorder
: !removeLastBorderRight && rootProps.disableExtendRowFullWidth;

const cellParams: GridCellParams = apiRef.current.getCellParams(id, column.field);

const classNames: string[] = [];

if (column.cellClassName) {
classNames.push(
clsx(
isFunction(column.cellClassName)
? column.cellClassName(cellParams)
: column.cellClassName,
),
);
}

const editCellState = editRowsState[id] ? editRowsState[id][column.field] : null;
let content: React.ReactNode = null;

if (editCellState == null && column.renderCell) {
content = column.renderCell({ ...cellParams, api: apiRef.current });
// TODO move to GridCell
classNames.push(
clsx(gridClasses['cell--withRenderer'], rootProps.classes?.['cell--withRenderer']),
);
}

if (editCellState != null && column.renderEditCell) {
const params = { ...cellParams, ...editCellState, api: apiRef.current };
content = column.renderEditCell(params);
// TODO move to GridCell
classNames.push(clsx(gridClasses['cell--editing'], rootProps.classes?.['cell--editing']));
}

if (rootProps.getCellClassName) {
// TODO move to GridCell
classNames.push(rootProps.getCellClassName(cellParams));
}

const hasFocus = cellFocus !== null && cellFocus.id === id && cellFocus.field === column.field;

const tabIndex =
cellTabIndex !== null &&
cellTabIndex.id === id &&
cellTabIndex.field === column.field &&
cellParams.cellMode === 'view'
? 0
: -1;

cells.push(
<GridCell
key={i}
value={cellParams.value}
field={column.field}
width={column.computedWidth}
rowId={id}
height={rowHeight}
showRightBorder={showRightBorder}
formattedValue={cellParams.formattedValue}
align={column.align || 'left'}
cellMode={cellParams.cellMode}
colIndex={indexRelativeToAllColumns}
isEditable={cellParams.isEditable}
hasFocus={hasFocus}
tabIndex={tabIndex}
className={clsx(classNames)}
>
{content}
</GridCell>,
);
}

const emptyCellWidth = apiRef.current.windowRef!.current!.clientWidth - columnsMeta.totalWidth;

return (
<div
key={id}
data-id={id}
data-rowindex={rowIndex}
role="row"
className={clsx(rowClassName, classes.root)}
aria-rowindex={ariaRowIndex}
aria-selected={selected}
style={style}
{...mouseEventsHandlers}
{...eventHandlers}
>
{children}
{cells}
{emptyCellWidth > 0 && <EmptyCell width={emptyCellWidth} height={rowHeight} />}
</div>
);
}

GridRow.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
children: PropTypes.node,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
rowIndex: PropTypes.number.isRequired,
selected: PropTypes.bool.isRequired,
} as any;
// GridRow.propTypes = {
// // ----------------------------- Warning --------------------------------
// // | These PropTypes are generated from the TypeScript type definitions |
// // | To update them edit the TypeScript types and run "yarn proptypes" |
// // ----------------------------------------------------------------------
// children: PropTypes.node,
// id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
// rowIndex: PropTypes.number.isRequired,
// selected: PropTypes.bool.isRequired,
// } as any;

export { GridRow };
Loading

0 comments on commit afb4b91

Please sign in to comment.