Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[XGrid] Second iteration on resizing logic #436

Merged
merged 5 commits into from
Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/grid/_modules_/grid/GridComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const GridComponent = React.forwardRef<HTMLDivElement, GridComponentProps
);

const onColumnReorder = useColumnReorder(columnsHeaderRef, apiRef);
const onResizeColumn = useColumnResize(columnsHeaderRef, apiRef, internalOptions.headerHeight);
const separatorProps = useColumnResize(columnsHeaderRef, apiRef);
const paginationProps = usePagination(internalRows, internalColumns, internalOptions, apiRef);

React.useEffect(() => {
Expand Down Expand Up @@ -222,7 +222,7 @@ export const GridComponent = React.forwardRef<HTMLDivElement, GridComponentProps
ref={columnsHeaderRef}
columns={internalColumns.visible || []}
hasScrollX={!!renderCtx?.hasScrollX}
onResizeColumn={onResizeColumn}
separatorProps={separatorProps}
onColumnHeaderDragOver={onColumnReorder.handleColumnHeaderDragOver}
onColumnDragStart={onColumnReorder.handleDragStart}
onColumnDragEnter={onColumnReorder.handleDragEnter}
Expand Down
4 changes: 2 additions & 2 deletions packages/grid/_modules_/grid/components/AutoSizer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { useForkRef, ownerWindow, useEventCallback } from '@material-ui/core/utils';
import { useForkRef, ownerWindow } from '@material-ui/core/utils';
import { useEventCallback } from '../utils/material-ui-utils';
import createDetectElementResize from '../lib/createDetectElementResize';
// TODO replace with https://caniuse.com/resizeobserver.

Expand Down Expand Up @@ -118,7 +119,6 @@ export const AutoSizer = React.forwardRef<HTMLDivElement, AutoSizerProps>(functi

const detectElementResize = createDetectElementResize(nonce, win);
detectElementResize.addResizeListener(parentElement.current, handleResize);
// @ts-expect-error fixed in v5
handleResize();

return () => {
Expand Down
203 changes: 110 additions & 93 deletions packages/grid/_modules_/grid/components/column-header-item.tsx
Original file line number Diff line number Diff line change
@@ -1,120 +1,137 @@
import * as React from 'react';
import { ColDef } from '../models/colDef';
import { ApiRef } from '../models/api';
import { ApiContext } from './api-context';
import { HEADER_CELL_CSS_CLASS } from '../constants/cssClassesConstants';
import { COL_RESIZE_START, COL_RESIZE_STOP } from '../constants/eventsConstants';
import { classnames } from '../utils';
import { useApiEventHandler } from '../hooks/root/useApiEventHandler';
import { ColumnHeaderSortIcon } from './column-header-sort-icon';
import { ColumnHeaderTitle } from './column-header-title';
import { ColumnHeaderSeparator } from './column-header-separator';
import { OptionsContext } from './options-context';
import { CursorCoordinates } from '../hooks/features/useColumnReorder';

interface ColumnHeaderItemProps {
column: ColDef;
colIndex: number;
onResizeColumn?: (c: any) => void;
onColumnDragStart?: (col: ColDef, currentTarget: HTMLElement) => void;
column: ColDef;
onColumnDragEnter?: (event: Event) => void;
onColumnDragOver?: (col: ColDef, coordinates: CursorCoordinates) => void;
onColumnDragStart?: (col: ColDef, currentTarget: HTMLElement) => void;
separatorProps: React.HTMLAttributes<HTMLDivElement>;
oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
}

export const ColumnHeaderItem = React.memo(
({
column,
export const ColumnHeaderItem = React.memo((props: ColumnHeaderItemProps) => {
const {
colIndex,
onResizeColumn,
onColumnDragStart,
column,
onColumnDragEnter,
onColumnDragOver,
}: ColumnHeaderItemProps) => {
const api = React.useContext(ApiContext);
const { showColumnRightBorder, disableColumnResize, disableColumnReorder } = React.useContext(
OptionsContext,
);
onColumnDragStart,
separatorProps,
} = props;
const apiRef = React.useContext(ApiContext) as ApiRef;
const { showColumnRightBorder, disableColumnResize, disableColumnReorder } = React.useContext(
OptionsContext,
);

const [resizing, setResizing] = React.useState(false);
const handleResizeStart = React.useCallback(
(params) => {
if (column.field === params.field) {
setResizing(true);
}
},
[column.field],
);
const handleResizeStop = React.useCallback(() => {
setResizing(false);
}, []);
useApiEventHandler(apiRef, COL_RESIZE_START, handleResizeStart);
useApiEventHandler(apiRef, COL_RESIZE_STOP, handleResizeStop);

let headerComponent: React.ReactElement | null = null;
if (column.renderHeader) {
headerComponent = column.renderHeader({
api: api!.current!,
colDef: column,
colIndex,
field: column.field,
});
}
let headerComponent: React.ReactElement | null = null;
if (column.renderHeader) {
headerComponent = column.renderHeader({
api: apiRef.current!,
colDef: column,
colIndex,
field: column.field,
});
}

const handleResize = onResizeColumn && (() => onResizeColumn(column));
const dragConfig = {
draggable:
!disableColumnReorder && !!onColumnDragStart && !!onColumnDragEnter && !!onColumnDragOver,
onDragStart: onColumnDragStart && ((event) => onColumnDragStart(column, event.currentTarget)),
onDragEnter: onColumnDragEnter && ((event) => onColumnDragEnter(event)),
onDragOver:
onColumnDragOver &&
((event) => {
onColumnDragOver(column, {
x: event.clientX,
y: event.clientY,
});
}),
};
const width = column.width!;
const dragConfig = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usememo? avoid to recreate the functions and rerender the subtree...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind that if you review with:

Capture d’écran 2020-10-19 à 15 42 29

You will see that this line is not linked to the resizing change.

Copy link
Member Author

@oliviertassinari oliviertassinari Oct 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the value is spread, so no performance issues here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the value is spread but when you do (event)=> something(event) it creates a new function on each render and so shallow equal will never work and the sub tree will always rerender

Copy link
Member Author

@oliviertassinari oliviertassinari Oct 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's applied on a div, so using a memo won't help performance. It won't prune a large react rendering tree, the only case where it helps with performance.

draggable:
!disableColumnReorder && !!onColumnDragStart && !!onColumnDragEnter && !!onColumnDragOver,
onDragStart: onColumnDragStart && ((event) => onColumnDragStart(column, event.currentTarget)),
onDragEnter: onColumnDragEnter && ((event) => onColumnDragEnter(event)),
onDragOver:
onColumnDragOver &&
((event) => {
onColumnDragOver(column, {
x: event.clientX,
y: event.clientY,
});
}),
};
const width = column.width!;

let ariaSort: any;
if (column.sortDirection != null) {
ariaSort = { 'aria-sort': column.sortDirection === 'asc' ? 'ascending' : 'descending' };
}
let ariaSort: any;
if (column.sortDirection != null) {
ariaSort = { 'aria-sort': column.sortDirection === 'asc' ? 'ascending' : 'descending' };
}

return (
<div
className={classnames(
HEADER_CELL_CSS_CLASS,
showColumnRightBorder ? 'MuiDataGrid-withBorder' : '',
column.headerClassName,
column.headerAlign === 'center' && 'MuiDataGrid-colCellCenter',
column.headerAlign === 'right' && 'MuiDataGrid-colCellRight',
{ 'MuiDataGrid-colCellSortable': column.sortable },
return (
<div
className={classnames(
HEADER_CELL_CSS_CLASS,
showColumnRightBorder ? 'MuiDataGrid-withBorder' : '',
column.headerClassName,
column.headerAlign === 'center' && 'MuiDataGrid-colCellCenter',
column.headerAlign === 'right' && 'MuiDataGrid-colCellRight',
{ 'MuiDataGrid-colCellSortable': column.sortable },
)}
key={column.field}
data-field={column.field}
style={{
width,
minWidth: width,
maxWidth: width,
}}
role="columnheader"
tabIndex={-1}
aria-colindex={colIndex + 1}
{...ariaSort}
>
<div className="MuiDataGrid-colCell-draggable" {...dragConfig}>
{column.type === 'number' && (
<ColumnHeaderSortIcon
direction={column.sortDirection}
index={column.sortIndex}
hide={column.hideSortIcons}
/>
)}
{headerComponent || (
<ColumnHeaderTitle
label={column.headerName || column.field}
description={column.description}
columnWidth={width}
/>
)}
{column.type !== 'number' && (
<ColumnHeaderSortIcon
direction={column.sortDirection}
index={column.sortIndex}
hide={column.hideSortIcons}
/>
)}
key={column.field}
data-field={column.field}
style={{
width,
minWidth: width,
maxWidth: width,
}}
role="columnheader"
tabIndex={-1}
aria-colindex={colIndex + 1}
{...ariaSort}
>
<div className="MuiDataGrid-colCell-draggable" {...dragConfig}>
{column.type === 'number' && (
<ColumnHeaderSortIcon
direction={column.sortDirection}
index={column.sortIndex}
hide={column.hideSortIcons}
/>
)}
{headerComponent || (
<ColumnHeaderTitle
label={column.headerName || column.field}
description={column.description}
columnWidth={width}
/>
)}
{column.type !== 'number' && (
<ColumnHeaderSortIcon
direction={column.sortDirection}
index={column.sortIndex}
hide={column.hideSortIcons}
/>
)}
</div>
<ColumnHeaderSeparator
resizable={!disableColumnResize && column.resizable}
onResize={handleResize}
/>
</div>
);
},
);
<ColumnHeaderSeparator
resizable={disableColumnResize ? false : Boolean(column.resizable)}
resizing={resizing}
{...separatorProps}
/>
</div>
);
});
ColumnHeaderItem.displayName = 'ColumnHeaderItem';
49 changes: 24 additions & 25 deletions packages/grid/_modules_/grid/components/column-header-separator.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
import * as React from 'react';
import { useIcons } from '../hooks/utils/useIcons';
import { classnames } from '../utils';
import { OptionsContext } from './options-context';

export interface ColumnHeaderSeparatorProps {
resizable: boolean | undefined;
onResize?: () => void;
export interface ColumnHeaderSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
resizable: boolean;
resizing: boolean;
}

export const ColumnHeaderSeparator: React.FC<ColumnHeaderSeparatorProps> = React.memo(
({ onResize, resizable }) => {
const icons = useIcons();
const { showColumnRightBorder, headerHeight } = React.useContext(OptionsContext);
export const ColumnHeaderSeparator = React.memo(function ColumnHeaderSeparator(
props: ColumnHeaderSeparatorProps,
) {
const { resizable, resizing, ...other } = props;
const icons = useIcons();
const { showColumnRightBorder, headerHeight } = React.useContext(OptionsContext);
const Icon = icons!.columnResize!;

const resizeIconProps = {
className: `MuiDataGrid-iconSeparator ${resizable ? 'MuiDataGrid-resizable' : ''}`,
...(resizable && onResize ? { onMouseDown: onResize } : {}),
};

const icon = React.createElement(icons!.columnResize!, resizeIconProps);

return (
<div
className="MuiDataGrid-columnSeparator"
style={{ minHeight: headerHeight, opacity: showColumnRightBorder ? 0 : 1 }}
>
{icon}
</div>
);
},
);
ColumnHeaderSeparator.displayName = 'ColumnHeaderSeparator';
return (
<div
className={classnames('MuiDataGrid-columnSeparator', {
'MuiDataGrid-columnSeparatorResizable': resizable,
'Mui-resizing': resizing,
})}
style={{ minHeight: headerHeight, opacity: showColumnRightBorder ? 0 : 1 }}
{...other}
>
<Icon className="MuiDataGrid-iconSeparator" />
</div>
);
});
Loading