Skip to content

Commit

Permalink
[XGrid] Second iteration on resizing logic (mui#436)
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari authored and dtassone committed Nov 9, 2020
1 parent afbbd0e commit 58021a7
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 390 deletions.
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>;
}

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 = {
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

0 comments on commit 58021a7

Please sign in to comment.