Skip to content

Commit

Permalink
[useColumnReorder] Add the ability to reorder columns using drag and …
Browse files Browse the repository at this point in the history
…drop
  • Loading branch information
DanailH committed Aug 12, 2020
1 parent 57cd9aa commit 3fe1bf7
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 35 deletions.
54 changes: 32 additions & 22 deletions packages/grid/x-grid-modules/src/components/column-header-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ interface ColumnHeaderItemProps {
headerHeight: number;
colIndex: number;
onResizeColumn?: (c: any) => void;
onColumnDragStart?: (c: ColDef, h: HTMLElement) => void;
onColumnDragEnter?: (c: ColDef) => void;
}

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

const cssClass = classnames(
Expand All @@ -36,6 +38,8 @@ export const ColumnHeaderItem = React.memo(
}

const onResize = onResizeColumn ? () => onResizeColumn(column) : undefined;
const onDragStart = onColumnDragStart ? (e) => onColumnDragStart(column, e.target) : undefined;
const onDragEnter = onColumnDragEnter ? () => onColumnDragEnter(column) : undefined;

const width = column.width!;

Expand All @@ -61,27 +65,33 @@ export const ColumnHeaderItem = React.memo(
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
draggable={!!onDragStart && !!onDragEnter}
onDragStart={onDragStart}
onDragEnter={onDragEnter}
>
{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={column.resizable} onResize={onResize} />
</div>
);
Expand Down
12 changes: 10 additions & 2 deletions packages/grid/x-grid-modules/src/components/column-headers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ export interface ColumnHeadersItemCollectionProps {
columns: Columns;
headerHeight: number;
onResizeColumn?: (col: ColDef) => void;
onColumnDragStart?: (col: ColDef, htmlEL: HTMLElement) => void;
onColumnDragEnter?: (col: ColDef) => void;
}
export const ColumnHeaderItemCollection: React.FC<ColumnHeadersItemCollectionProps> = React.memo(
({ headerHeight, onResizeColumn, columns }) => {
({ headerHeight, onResizeColumn, onColumnDragStart, onColumnDragEnter, columns }) => {
const items = columns.map((col, idx) => (
<ColumnHeaderItem
key={col.field}
column={col}
colIndex={idx}
headerHeight={headerHeight}
onResizeColumn={onResizeColumn}
onColumnDragStart={onColumnDragStart}
onColumnDragEnter={onColumnDragEnter}
/>
));

Expand All @@ -31,12 +35,14 @@ export interface ColumnsHeaderProps {
hasScrollX: boolean;
headerHeight: number;
onResizeColumn?: (col: ColDef) => void;
onColumnDragStart?: (col: ColDef, htmlEl: HTMLElement) => void;
onColumnDragEnter?: (col: ColDef) => void;
renderCtx: Partial<RenderContextProps> | null;
}

export const ColumnsHeader = React.memo(
React.forwardRef<HTMLDivElement, ColumnsHeaderProps>(
({ columns, hasScrollX, headerHeight, onResizeColumn, renderCtx }, columnsHeaderRef) => {
({ columns, hasScrollX, headerHeight, onResizeColumn, onColumnDragStart, onColumnDragEnter, renderCtx }, columnsHeaderRef) => {
const wrapperCssClasses = `material-col-cell-wrapper ${hasScrollX ? 'scroll' : ''}`;
const api = React.useContext(ApiContext);

Expand Down Expand Up @@ -78,6 +84,8 @@ export const ColumnsHeader = React.memo(
<ColumnHeaderItemCollection
columns={renderedCols}
onResizeColumn={onResizeColumn}
onColumnDragStart={onColumnDragStart}
onColumnDragEnter={onColumnDragEnter}
headerHeight={headerHeight}
/>
<RightEmptyCell key="right-empty" width={renderCtx?.rightEmptyWidth} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ export const RootStyle = styled.div<GridRootProps>`
&.checkbox-selection-header-cell .checkbox-input {
padding: 12px;
}
.dragging {
background: gray;
color: white;
padding: 0 12px;
border-radius: 10px;
opacity: 0.3;
}
}
&.scroll .material-col-cell:last-child {
border-right: none;
Expand Down
4 changes: 4 additions & 0 deletions packages/grid/x-grid-modules/src/gridComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import { GridComponentProps } from './gridComponentProps';
import {
useApiRef,
useColumnReorder,
useColumnResize,
useComponents,
usePagination,
Expand Down Expand Up @@ -88,6 +89,7 @@ export const GridComponent: React.FC<GridComponentProps> = React.memo((props) =>
apiRef,
);

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

Expand Down Expand Up @@ -189,6 +191,8 @@ export const GridComponent: React.FC<GridComponentProps> = React.memo((props) =>
hasScrollX={!!renderCtx?.hasScrollX}
headerHeight={internalOptions.headerHeight}
onResizeColumn={onResizeColumn}
onColumnDragStart={onColumnReorder.handleDragStart}
onColumnDragEnter={onColumnReorder.handleDragEnter}
renderCtx={renderCtx}
/>
</ColumnsContainer>
Expand Down
1 change: 1 addition & 0 deletions packages/grid/x-grid-modules/src/hooks/features/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './useApiRef';
export * from './useComponents';
export * from './useColumnReorder';
export * from './useColumnResize';
export * from './usePagination';
export * from './useSelection';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as React from 'react';
import { ColDef } from '../../models/colDef';
import { useLogger } from '../utils';
import { ApiRef } from '../../models';

export const useColumnReorder = (
apiRef: ApiRef
) => {
const logger = useLogger('useColumnReorder');

const dragCol = React.useRef<ColDef | null>();
const dragColNode = React.useRef<HTMLElement | null>();

const handleDragStart = React.useCallback(
(col: ColDef, htmlEl: any): void => {
logger.debug(`Start dragging col ${col.field}`);
dragCol.current = col;
dragColNode.current = htmlEl;
dragColNode.current?.addEventListener('dragend', handleDragEnd);
dragColNode.current?.classList.add('dragging');
setTimeout(() => {
dragColNode.current?.classList.remove('dragging');
}, 0);
},
[apiRef, logger]
);

const handleDragEnter = React.useCallback(
(col: ColDef): void => {
logger.debug(`Enter dragging col ${col.field}`);

if (col.field !== dragCol.current?.field) {
const targetColIndex = apiRef.current.getColumnIndex(col.field);
const dragColIndex = apiRef.current.getColumnIndex(dragCol.current!.field);
const columnsSnapshot = apiRef.current.getAllColumns();

apiRef.current.scrollToIndexes({
colIndex: targetColIndex,
rowIndex: 0
});

columnsSnapshot.splice(targetColIndex, 0, columnsSnapshot.splice(dragColIndex, 1)[0]);

apiRef.current.updateColumns(columnsSnapshot, true);
}
},
[apiRef, logger]
);

const handleDragEnd = React.useCallback(
(): void => {
logger.debug(`End dragging col ${dragCol.current!.field}`);

dragColNode.current?.removeEventListener('dragend', handleDragEnd);
dragCol.current = null;
dragColNode.current = null;
},
[logger]
);

return {
handleDragStart,
handleDragEnter
};
};
26 changes: 16 additions & 10 deletions packages/grid/x-grid-modules/src/hooks/root/useColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,23 @@ const getUpdatedColumnState = (
logger: Logger,
state: InternalColumns,
columnUpdates: ColDef[],
resetState = false
): InternalColumns => {
const newState = { ...state };
columnUpdates.forEach((newColumn) => {
const index = newState.all.findIndex((c) => c.field === newColumn.field);
const columnUpdated = { ...newState.all[index], ...newColumn };
newState.all[index] = columnUpdated;
newState.all = [...newState.all];

newState.lookup[newColumn.field] = columnUpdated;
newState.lookup = { ...newState.lookup };
});
if (resetState) {
newState.all = columnUpdates;
} else {
columnUpdates.forEach((newColumn) => {
const index = newState.all.findIndex((c) => c.field === newColumn.field);
const columnUpdated = { ...newState.all[index], ...newColumn };
newState.all[index] = columnUpdated;
newState.all = [...newState.all];

newState.lookup[newColumn.field] = columnUpdated;
newState.lookup = { ...newState.lookup };
});
}

const visible = filterVisible(logger, newState.all);
const meta = toMeta(logger, visible);
Expand Down Expand Up @@ -193,8 +199,8 @@ export function useColumns(
const getVisibleColumns: () => Columns = () => stateRef.current.visible;

const updateColumns = React.useCallback(
(cols: ColDef[]) => {
const newState = getUpdatedColumnState(logger, stateRef.current, cols);
(cols: ColDef[], resetState = false) => {
const newState = getUpdatedColumnState(logger, stateRef.current, cols, resetState);
updateState(newState, false);
},
[updateState, logger, stateRef],
Expand Down
2 changes: 1 addition & 1 deletion packages/grid/x-grid-modules/src/models/api/columnApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ export interface ColumnApi {
*
* @param cols [[ColDef[]]]
*/
updateColumns: (cols: ColDef[]) => void;
updateColumns: (cols: ColDef[], resetState: boolean) => void;
}

0 comments on commit 3fe1bf7

Please sign in to comment.