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

[DataGrid] Add column reorder support #165

Merged
merged 65 commits into from Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
25d8c0e
[useColumnReorder] Add the ability to reorder columns using drag and …
DanailH Aug 12, 2020
33f6bd5
[useColumnReorder] Fix review comments
DanailH Aug 13, 2020
32bf5d3
fix broking functionality
DanailH Aug 18, 2020
133b7a6
Pull latest from upstream
DanailH Sep 10, 2020
d11941c
Fix issues related to pulling the latest changes
DanailH Sep 10, 2020
ef13ca2
{useColumnReorder] Refactor the feature hook and add drag scroll supp…
DanailH Sep 24, 2020
567c7bb
[useColumnReorder] Fix lint and format issues
DanailH Sep 24, 2020
63c2336
Get latest changes from upstream
DanailH Sep 24, 2020
ff1372b
[useColumnsReorder] Fix build and formatting
DanailH Sep 25, 2020
b8b86f2
[useColumnReorder] Fix lint errors
DanailH Sep 25, 2020
c5fd005
Fix styles downgrade when dragging a column cell
DanailH Sep 25, 2020
1eacbb1
[useColumnReorder] Added option to disable the feature, added docs, a…
DanailH Oct 2, 2020
9c3cb8f
[useColumnReorder] Fix formatting and enable all the tests
DanailH Oct 2, 2020
d30bbab
Remove unnessesary code from the mouse.test.ts file
DanailH Oct 2, 2020
0897968
Fix formatting
DanailH Oct 2, 2020
bc42a57
fix formatting
DanailH Oct 2, 2020
79a8ada
Merge branch 'master' of https://github.com/mui-org/material-ui-x int…
DanailH Oct 2, 2020
23cad59
Emit event when drag enters a column
DanailH Oct 5, 2020
f370a62
Working on PR comments
DanailH Oct 6, 2020
7eb275f
Merge branch 'master' of https://github.com/mui-org/material-ui-x int…
DanailH Oct 6, 2020
f27447e
Fix inifinte loop when dragging smaller cal over large col
DanailH Oct 6, 2020
167f089
Rename drag handlers prefix
DanailH Oct 6, 2020
9da410e
Prep codebase for commit and push
DanailH Oct 6, 2020
e1ce144
Run prettier
DanailH Oct 6, 2020
703f38c
Fix lint errors
DanailH Oct 6, 2020
5b0ce67
Rename file to match coding style
DanailH Oct 6, 2020
5177611
Rename columnsHeaderRef to ref
DanailH Oct 6, 2020
d718f7d
Fix PR comments
DanailH Oct 6, 2020
aab8634
Update docs/pages/api-docs/x-grid.md
DanailH Oct 6, 2020
37b54cc
Update docs/pages/api-docs/x-grid.md
DanailH Oct 6, 2020
983a945
Update docs/pages/api-docs/x-grid.md
DanailH Oct 6, 2020
2e1c9ae
Add relevant documentation about coumn reorder on the /components/dat…
DanailH Oct 7, 2020
195c256
Fix docs formatting
DanailH Oct 7, 2020
2c21bc4
Merge branch 'feature/columns-reorder' of https://github.com/DanailH/…
DanailH Oct 7, 2020
cd12854
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Oct 8, 2020
6632ec2
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Oct 8, 2020
36a88fd
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Oct 8, 2020
ace5cbf
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Oct 8, 2020
0894cec
Update code example
DanailH Oct 8, 2020
d5f245d
Fix PR comments
DanailH Oct 8, 2020
8c804e3
Merge branch 'feature/columns-reorder' of https://github.com/DanailH/…
DanailH Oct 8, 2020
a0bbbc0
Fix the doc example code
DanailH Oct 8, 2020
a58832f
Update docs/src/pages/components/data-grid/columns/ColumnOrderingGrid…
DanailH Oct 8, 2020
5f64ab1
Fix column reordering issue while dragging over the same cell
DanailH Oct 9, 2020
f5bb8f5
Make scrolling while dragging excelerate
DanailH Oct 9, 2020
e8613ca
Format and lint the changes
DanailH Oct 9, 2020
018176c
Fix scroll left issue
DanailH Oct 9, 2020
f43a35e
Fix build
DanailH Oct 9, 2020
88081fe
fix scroll smoothness
oliviertassinari Oct 9, 2020
800f2c9
Fix Col header item alignment visuall regression
DanailH Oct 10, 2020
6350d48
Final polishing of the useColumnReorder hook
DanailH Oct 10, 2020
1e74521
Fix typings
DanailH Oct 10, 2020
4a1e936
the logic never runs on Node.js
oliviertassinari Oct 10, 2020
bd33569
clear timeout when unmounting, avoid edge-case leak
oliviertassinari Oct 10, 2020
2339b32
use design token
oliviertassinari Oct 10, 2020
a919b63
no shorthand
oliviertassinari Oct 10, 2020
e35ded5
Use CSS inherit
oliviertassinari Oct 10, 2020
05c2285
simpler class name logic
oliviertassinari Oct 10, 2020
cf6a0c0
we have strong constraint that these value should be defined. It shou…
oliviertassinari Oct 10, 2020
99bcbec
no shorthands
oliviertassinari Oct 10, 2020
c7e54b0
the logic depends on the element to be the current target, not the ta…
oliviertassinari Oct 10, 2020
3cfbd4f
add visual clue about where the column is, outline shouldn't be visible
oliviertassinari Oct 10, 2020
b6443c2
Remove unnecessary checks related to disableColumnReorder flag
DanailH Oct 12, 2020
e6631f9
Fix PR comments related
Oct 15, 2020
5274172
remove raf
oliviertassinari Oct 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/src/pages/components/data-grid/columns/ColumnOrderingGrid.js
@@ -0,0 +1,17 @@
import * as React from 'react';
import { XGrid } from '@material-ui/x-grid';
import { useDemoData } from '@material-ui/x-grid-data-generator';

export default function ColumnOrderingGrid() {
const { data } = useDemoData({
dataSet: 'Commodity',
rowLength: 20,
maxColumns: 20,
});

return (
<div style={{ height: 400, width: '100%' }}>
<XGrid {...data} />
</div>
);
}
17 changes: 17 additions & 0 deletions docs/src/pages/components/data-grid/columns/ColumnOrderingGrid.tsx
@@ -0,0 +1,17 @@
import * as React from 'react';
import { XGrid } from '@material-ui/x-grid';
import { useDemoData } from '@material-ui/x-grid-data-generator';

export default function ColumnOrderingGrid() {
const { data } = useDemoData({
dataSet: 'Commodity',
rowLength: 20,
maxColumns: 20,
});

return (
<div style={{ height: 400, width: '100%' }}>
<XGrid {...data} />
</div>
);
}
24 changes: 16 additions & 8 deletions docs/src/pages/components/data-grid/columns/columns.md
Expand Up @@ -103,21 +103,29 @@ const usdPrice: ColTypeDef = {

{{"demo": "pages/components/data-grid/columns/CustomColumnTypesGrid.js", "bg": "inline"}}

## 🚧 Column groups
## Column reorder [<span role="img" title="Enterprise">⚡️</span>](https://material-ui.com/store/items/material-ui-x/)

> ⚠️ This feature isn't implemented yet. It's coming.
>
> 👍 Upvote [issue #195](https://github.com/mui-org/material-ui-x/issues/195) if you want to see it land faster.
By default, `XGrid` allows all column reordering by dragging the header cells and moving them left or right.

Grouping columns allows you to have multiple levels of columns in your header and the ability, if needed, to 'open and close' column groups to show and hide additional columns.
To disable column reordering, set the prop `disableColumnReorder={true}`.

In addition, column reordering emits the following events that can be imported:

## 🚧 Column reorder [<span role="img" title="Enterprise">⚡️</span>](https://material-ui.com/store/items/material-ui-x/)
- `COL_REORDER_START`: emitted when dragging of a header cell starts.
- `COL_REORDER_DRAG_ENTER`: emitted when the cursor enters another header cell while dragging.
- `COL_REORDER_DRAG_OVER`: emitted when dragging a header cell over another header cell.
- `COL_REORDER_DRAG_OVER_HEADER`: emitted when dragging a header cell over the `ColumnsHeader` component.
- `COL_REORDER_STOP`: emitted when dragging of a header cell stops.

{{"demo": "pages/components/data-grid/columns/ColumnOrderingGrid.js", "disableAd": true, "bg": "inline"}}

## 🚧 Column groups

> ⚠️ This feature isn't implemented yet. It's coming.
>
> 👍 Upvote [issue #194](https://github.com/mui-org/material-ui-x/issues/194) if you want to see it land faster.
> 👍 Upvote [issue #195](https://github.com/mui-org/material-ui-x/issues/195) if you want to see it land faster.

Column reordering enables reordering the columns by dragging the header cells.
Grouping columns allows you to have multiple levels of columns in your header and the ability, if needed, to 'open and close' column groups to show and hide additional columns.

## 🚧 Column pinning [<span role="img" title="Enterprise">⚡️</span>](https://material-ui.com/store/items/material-ui-x/)

Expand Down
6 changes: 6 additions & 0 deletions packages/grid/_modules_/grid/GridComponent.tsx
Expand Up @@ -3,6 +3,7 @@ import { useForkRef } from '@material-ui/core/utils';
import { GridComponentProps } from './GridComponentProps';
import {
useApiRef,
useColumnReorder,
useColumnResize,
useComponents,
usePagination,
Expand Down Expand Up @@ -92,6 +93,7 @@ export const GridComponent = React.forwardRef<HTMLDivElement, GridComponentProps
apiRef,
);

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

Expand Down Expand Up @@ -209,6 +211,10 @@ export const GridComponent = React.forwardRef<HTMLDivElement, GridComponentProps
columns={internalColumns.visible || []}
hasScrollX={!!renderCtx?.hasScrollX}
onResizeColumn={onResizeColumn}
onColumnHeaderDragOver={onColumnReorder.handleColumnHeaderDragOver}
onColumnDragStart={onColumnReorder.handleDragStart}
onColumnDragEnter={onColumnReorder.handleDragEnter}
onColumnDragOver={onColumnReorder.handleDragOver}
renderCtx={renderCtx}
/>
</GridColumnsContainer>
Expand Down
4 changes: 2 additions & 2 deletions packages/grid/_modules_/grid/components/AutoSizer.tsx
Expand Up @@ -70,8 +70,8 @@ export const AutoSizer = React.forwardRef<HTMLDivElement, AutoSizerProps>(functi
width: defaultWidth,
});

const rootRef = React.useRef<HTMLDivElement>(null);
const parentElement = React.useRef(null) as React.MutableRefObject<HTMLElement | null>;
const rootRef = React.useRef<HTMLDivElement | null>(null);
const parentElement = React.useRef<HTMLElement | null>(null);

const handleResize = useEventCallback(() => {
// Guard against AutoSizer component being removed from the DOM immediately after being added.
Expand Down
80 changes: 80 additions & 0 deletions packages/grid/_modules_/grid/components/ScrollArea.tsx
@@ -0,0 +1,80 @@
import * as React from 'react';
import { COL_REORDER_START, COL_REORDER_STOP, SCROLLING } from '../constants/eventsConstants';
import { ScrollParams, useApiEventHandler } from '../hooks';
import { ApiRef } from '../models';
import { classnames } from '../utils';
import { ApiContext } from './api-context';

interface ScrollAreaProps {
scrollDirection: 'left' | 'right';
}

export const ScrollArea = React.memo(function ScrollArea(props: ScrollAreaProps) {
const { scrollDirection } = props;
const rootRef = React.useRef<HTMLDivElement>(null);
const api = React.useContext(ApiContext);
const rafRef = React.useRef(0);
const timeout = React.useRef<number>();
const [dragging, setDragging] = React.useState<boolean>(false);
const scrollPosition = React.useRef<ScrollParams>({
left: 0,
top: 0,
});

const handleScrolling = React.useCallback((newScrollPosition) => {
scrollPosition.current = newScrollPosition;
}, []);

const handleDragOver = React.useCallback(
(event) => {
let diff;

if (scrollDirection === 'left') {
diff = event.clientX - rootRef.current!.getBoundingClientRect().right;
} else if (scrollDirection === 'right') {
diff = Math.max(1, event.clientX - rootRef.current!.getBoundingClientRect().left);
} else {
throw new Error('wrong dir');
}

diff = (diff - 1) * 1.5 + 1;
DanailH marked this conversation as resolved.
Show resolved Hide resolved

// Throttle, max 60 Hz
cancelAnimationFrame(rafRef.current);
oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
rafRef.current = requestAnimationFrame(() => {
// Wait for the frame to pain before updating the scroll position
clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
api!.current.scroll({
left: scrollPosition.current.left + diff,
top: scrollPosition.current.top,
});
});
});
},
[scrollDirection, api],
);

React.useEffect(() => {
return () => {
cancelAnimationFrame(rafRef.current);
clearTimeout(timeout.current);
};
}, []);

const toggleDragging = React.useCallback(() => {
setDragging((prevdragging) => !prevdragging);
}, []);

useApiEventHandler(api as ApiRef, SCROLLING, handleScrolling);
useApiEventHandler(api as ApiRef, COL_REORDER_START, toggleDragging);
useApiEventHandler(api as ApiRef, COL_REORDER_STOP, toggleDragging);

return dragging ? (
<div
ref={rootRef}
className={classnames('MuiDataGrid-scrollArea', `MuiDataGrid-scrollArea-${scrollDirection}`)}
onDragOver={handleDragOver}
/>
) : null;
});
109 changes: 68 additions & 41 deletions packages/grid/_modules_/grid/components/column-header-item.tsx
Expand Up @@ -7,33 +7,31 @@ 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;
onColumnDragEnter?: (event: Event) => void;
onColumnDragOver?: (col: ColDef, coordinates: CursorCoordinates) => void;
}
const headerAlignPropToCss = {
center: 'MuiDataGrid-colCellCenter',
right: 'MuiDataGrid-colCellRight',
};

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

const cssClass = classnames(
HEADER_CELL_CSS_CLASS,
showColumnRightBorder ? 'MuiDataGrid-withBorder' : '',
column.headerClassName,
column.headerAlign &&
column.headerAlign !== 'left' &&
headerAlignPropToCss[column.headerAlign],
{ 'MuiDataGrid-colCellSortable': column.sortable },
);

let headerComponent: React.ReactElement | null = null;
if (column.renderHeader) {
headerComponent = column.renderHeader({
Expand All @@ -44,7 +42,21 @@ export const ColumnHeaderItem = React.memo(
});
}

const handleResize = onResizeColumn ? () => onResizeColumn(column) : undefined;
const handleResize =
onResizeColumn && !disableColumnReorder ? () => onResizeColumn(column) : undefined;
DanailH marked this conversation as resolved.
Show resolved Hide resolved
const handleDragStart =
onColumnDragStart && !disableColumnReorder
? (event) => onColumnDragStart(column, event.currentTarget)
: undefined;
DanailH marked this conversation as resolved.
Show resolved Hide resolved
const handleDragEnter = onColumnDragEnter ? (event) => onColumnDragEnter(event) : undefined;
const handleDragOver = onColumnDragOver
DanailH marked this conversation as resolved.
Show resolved Hide resolved
? (event) => {
onColumnDragOver(column, {
x: event.clientX,
y: event.clientY,
});
}
: undefined;

const width = column.width!;

Expand All @@ -55,42 +67,57 @@ export const ColumnHeaderItem = React.memo(

return (
<div
className={cssClass}
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,
maxHeight: headerHeight,
minHeight: headerHeight,
}}
role="columnheader"
tabIndex={-1}
aria-colindex={colIndex + 1}
{...ariaSort}
>
{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
className="MuiDataGrid-colCell-draggable"
oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
draggable={
!disableColumnReorder && !!handleDragStart && !!handleDragEnter && !!handleDragOver
}
onDragStart={handleDragStart}
DanailH marked this conversation as resolved.
Show resolved Hide resolved
onDragOver={handleDragOver}
onDragEnter={handleDragEnter}
>
{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}
Expand Down