Skip to content

Commit

Permalink
fix: DataGrid selection performance improvement (#30288)
Browse files Browse the repository at this point in the history
  • Loading branch information
ling1726 authored Jan 12, 2024
1 parent 0a3d3dd commit 50176a6
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: DataGrid selection performance improvement",
"packageName": "@fluentui/react-table",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ export const useDataGridCell_unstable = (props: DataGridCellProps, ref: React.Re
ctx => (ctx.focusMode === 'cell' || ctx.focusMode === 'composite') && focusMode !== 'none',
);
const resizableColumns = useDataGridContext_unstable(ctx => ctx.resizableColumns);
const columnSizing = useDataGridContext_unstable(ctx => ctx.columnSizing_unstable);
const getTableCellProps = useDataGridContext_unstable(ctx => {
return ctx.columnSizing_unstable.getTableCellProps;
});
const focusableGroupAttr = useFocusableGroup({ tabBehavior: 'limited-trap-focus' });
return useTableCell_unstable(
{
as: 'div',
role: 'gridcell',
...(focusMode === 'group' && focusableGroupAttr),
tabIndex: tabbable ? 0 : undefined,
...(resizableColumns ? columnSizing.getTableCellProps(columnId) : {}),
...(resizableColumns ? getTableCellProps(columnId) : {}),
...props,
},
ref,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ export const useDataGridHeaderCell_unstable = (
sortable ? ctx.sort.getSortDirection(columnId) : undefined,
);
const resizableColumns = useDataGridContext_unstable(ctx => ctx.resizableColumns);
const columnSizing = useDataGridContext_unstable(ctx => ctx.columnSizing_unstable);
const getTableHeaderCellProps = useDataGridContext_unstable(ctx => {
return ctx.columnSizing_unstable.getTableHeaderCellProps;
});

// eslint-disable-next-line deprecation/deprecation -- prefer HTMLTableCellElement
const onClick = useEventCallback((e: React.MouseEvent<HTMLTableHeaderCellElement>) => {
Expand All @@ -54,7 +56,7 @@ export const useDataGridHeaderCell_unstable = (
sortDirection,
as: 'div',
tabIndex: sortable ? undefined : 0,
...(resizableColumns ? columnSizing.getTableHeaderCellProps(columnId) : {}),
...(resizableColumns ? getTableHeaderCellProps(columnId) : {}),
...props,
onClick,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { isInteractiveHTMLElement, useEventCallback, slot } from '@fluentui/reac
import { Space } from '@fluentui/keyboard-keys';
import type { DataGridRowProps, DataGridRowState } from './DataGridRow.types';
import { useTableRow_unstable } from '../TableRow/useTableRow';
import { useDataGridContext_unstable } from '../../contexts/dataGridContext';
import { dataGridContextDefaultValue, useDataGridContext_unstable } from '../../contexts/dataGridContext';
import { DataGridSelectionCell } from '../DataGridSelectionCell/DataGridSelectionCell';
import { useTableRowIdContext } from '../../contexts/rowIdContext';
import { useIsInTableHeader } from '../../contexts/tableHeaderContext';
Expand All @@ -25,6 +25,7 @@ export const useDataGridRow_unstable = (props: DataGridRowProps, ref: React.Ref<
const selected = useDataGridContext_unstable(ctx => ctx.selection.isRowSelected(rowId));
const focusMode = useDataGridContext_unstable(ctx => ctx.focusMode);
const compositeRowTabsterAttribute = useDataGridContext_unstable(ctx => ctx.compositeRowTabsterAttribute);

const tabbable = focusMode === 'row_unstable' || focusMode === 'composite';
const appearance = useDataGridContext_unstable(ctx => {
if (!isHeader && selectable && ctx.selection.isRowSelected(rowId)) {
Expand All @@ -34,7 +35,6 @@ export const useDataGridRow_unstable = (props: DataGridRowProps, ref: React.Ref<
return 'none';
});
const toggleRow = useDataGridContext_unstable(ctx => ctx.selection.toggleRow);
const dataGridContextValue = useDataGridContext_unstable(ctx => ctx);

const onClick = useEventCallback((e: React.MouseEvent<HTMLTableRowElement>) => {
if (selectable && !isHeader) {
Expand Down Expand Up @@ -81,6 +81,21 @@ export const useDataGridRow_unstable = (props: DataGridRowProps, ref: React.Ref<
}),
renderCell: props.children,
columnDefs,
dataGridContextValue,
// This context value should not be used internally
// It's intended to help power user render functions
dataGridContextValue: useStableDataGridContextValue(),
};
};

/**
* Do not rely on context changes here to trigger re-renders
* @returns - Entire DataGridContext as a stable value
*/
function useStableDataGridContextValue() {
const ref = React.useRef(dataGridContextDefaultValue);
useDataGridContext_unstable(ctx => {
ref.current = ctx;
});

return ref.current!;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { defaultTableState } from '../hooks';

const dataGridContext = createContext<DataGridContextValue | undefined>(undefined);

const dataGridContextDefaultValue: DataGridContextValue = {
export const dataGridContextDefaultValue: DataGridContextValue = {
...defaultTableState,
subtleSelection: false,
selectableRows: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,19 @@ export function useKeyboardResizing(columnResizeState: ColumnResizeState) {
return {
toggleInteractiveMode,
columnId,
getKeyboardResizingProps: (colId: TableColumnId, currentWidth: number) => ({
onKeyDown: keyboardHandler,
onBlur: disableInteractiveMode,
ref: getKeyboardResizingRef(colId),
role: 'separator',
'aria-label': 'Resize column',
'aria-valuetext': `${currentWidth} pixels`,
'aria-hidden': colId === columnId ? false : true,
tabIndex: colId === columnId ? 0 : undefined,
...tabsterAttrs,
}),
getKeyboardResizingProps: React.useCallback(
(colId: TableColumnId, currentWidth: number) => ({
onKeyDown: keyboardHandler,
onBlur: disableInteractiveMode,
ref: getKeyboardResizingRef(colId),
role: 'separator',
'aria-label': 'Resize column',
'aria-valuetext': `${currentWidth} pixels`,
'aria-hidden': colId === columnId ? false : true,
tabIndex: colId === columnId ? 0 : undefined,
...tabsterAttrs,
}),
[columnId, disableInteractiveMode, getKeyboardResizingRef, keyboardHandler, tabsterAttrs],
),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ export function useTableColumnResizeMouseHandler(columnResizeState: ColumnResize
const { targetDocument } = useFluent();
const globalWin = targetDocument?.defaultView;

const { getColumnWidth, setColumnWidth } = columnResizeState;

const recalculatePosition = React.useCallback(
(e: NativeTouchOrMouseEvent) => {
const { clientX } = getEventClientCoords(e);
const dx = clientX - mouseX.current;

// Update the local width for the column and set it
currentWidth.current += dx;
colId.current && columnResizeState.setColumnWidth(e, { columnId: colId.current, width: currentWidth.current });
colId.current && setColumnWidth(e, { columnId: colId.current, width: currentWidth.current });
mouseX.current = clientX;
},
[columnResizeState],
[setColumnWidth],
);

const onDrag = React.useCallback(
Expand Down Expand Up @@ -56,29 +58,32 @@ export function useTableColumnResizeMouseHandler(columnResizeState: ColumnResize
[onDrag, targetDocument],
);

const getOnMouseDown = (columnId: TableColumnId) => (event: ReactTouchOrMouseEvent) => {
// Keep the width locally so that we decouple the calculation of the next with from rendering.
// This makes the whole experience much faster and more precise
currentWidth.current = columnResizeState.getColumnWidth(columnId);
mouseX.current = getEventClientCoords(event).clientX;
colId.current = columnId;
const getOnMouseDown = React.useCallback(
(columnId: TableColumnId) => (event: ReactTouchOrMouseEvent) => {
// Keep the width locally so that we decouple the calculation of the next with from rendering.
// This makes the whole experience much faster and more precise
currentWidth.current = getColumnWidth(columnId);
mouseX.current = getEventClientCoords(event).clientX;
colId.current = columnId;

if (isMouseEvent(event)) {
// ignore other buttons than primary mouse button
if (event.target !== event.currentTarget || event.button !== 0) {
return;
if (isMouseEvent(event)) {
// ignore other buttons than primary mouse button
if (event.target !== event.currentTarget || event.button !== 0) {
return;
}
targetDocument?.addEventListener('mouseup', onDragEnd);
targetDocument?.addEventListener('mousemove', onDrag);
}
targetDocument?.addEventListener('mouseup', onDragEnd);
targetDocument?.addEventListener('mousemove', onDrag);
}

if (isTouchEvent(event)) {
targetDocument?.addEventListener('touchend', onDragEnd);
targetDocument?.addEventListener('touchmove', onDrag);
}
};
if (isTouchEvent(event)) {
targetDocument?.addEventListener('touchend', onDragEnd);
targetDocument?.addEventListener('touchmove', onDrag);
}
},
[getColumnWidth, onDrag, onDragEnd, targetDocument],
);

return {
getOnMouseDown: (columnId: TableColumnId) => getOnMouseDown(columnId),
getOnMouseDown,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,15 @@ export function useTableColumnResizeState<T>(
);

return {
getColumnById: (colId: TableColumnId) => getColumnById(state.columnWidthState, colId),
getColumns: () => state.columnWidthState,
getColumnWidth: (colId: TableColumnId) => getColumnWidth(state.columnWidthState, colId),
getColumnById: React.useCallback(
(colId: TableColumnId) => getColumnById(state.columnWidthState, colId),
[state.columnWidthState],
),
getColumns: React.useCallback(() => state.columnWidthState, [state.columnWidthState]),
getColumnWidth: React.useCallback(
(colId: TableColumnId) => getColumnWidth(state.columnWidthState, colId),
[state.columnWidthState],
),
setColumnWidth,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@ function useTableColumnSizingState<TItem>(
[toggleInteractiveMode],
);

const { getColumnById, setColumnWidth, getColumns } = columnResizeState;
const { getOnMouseDown } = mouseHandler;
return {
...tableState,
tableRef: measureElementRef,
// eslint-disable-next-line @typescript-eslint/naming-convention
columnSizing_unstable: {
getOnMouseDown: mouseHandler.getOnMouseDown,
setColumnWidth: (columnId: TableColumnId, w: number) =>
columnResizeState.setColumnWidth(undefined, { columnId, width: w }),
getColumnWidths: columnResizeState.getColumns,
getOnMouseDown,
setColumnWidth: (columnId: TableColumnId, w: number) => setColumnWidth(undefined, { columnId, width: w }),
getColumnWidths: getColumns,
getTableProps: (props = {}) => {
return {
...props,
Expand All @@ -85,29 +86,35 @@ function useTableColumnSizingState<TItem>(
},
};
},
getTableHeaderCellProps: (columnId: TableColumnId) => {
const col = columnResizeState.getColumnById(columnId);
const isLastColumn = columns[columns.length - 1]?.columnId === columnId;
getTableHeaderCellProps: React.useCallback(
(columnId: TableColumnId) => {
const col = getColumnById(columnId);
const isLastColumn = columns[columns.length - 1]?.columnId === columnId;

const aside = isLastColumn ? null : (
<TableResizeHandle
onMouseDown={mouseHandler.getOnMouseDown(columnId)}
onTouchStart={mouseHandler.getOnMouseDown(columnId)}
{...getKeyboardResizingProps(columnId, col?.width || 0)}
/>
);
const aside = isLastColumn ? null : (
<TableResizeHandle
onMouseDown={getOnMouseDown(columnId)}
onTouchStart={getOnMouseDown(columnId)}
{...getKeyboardResizingProps(columnId, col?.width || 0)}
/>
);

return col
? {
style: getColumnStyles(col),
aside,
}
: {};
},
getTableCellProps: (columnId: TableColumnId) => {
const col = columnResizeState.getColumnById(columnId);
return col ? { style: getColumnStyles(col) } : {};
},
return col
? {
style: getColumnStyles(col),
aside,
}
: {};
},
[getColumnById, columns, getKeyboardResizingProps, getOnMouseDown],
),
getTableCellProps: React.useCallback(
(columnId: TableColumnId) => {
const col = getColumnById(columnId);
return col ? { style: getColumnStyles(col) } : {};
},
[getColumnById],
),
enableKeyboardMode,
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ export function useTableFeatures<TItem>(
): TableFeaturesState<TItem> {
const { items, getRowId, columns } = options;

const getRows = <TRowState extends TableRowData<TItem>>(
rowEnhancer = defaultRowEnhancer as RowEnhancer<TItem, TRowState>,
) => items.map((item, i) => rowEnhancer({ item, rowId: getRowId?.(item) ?? i }));
const getRows = React.useCallback(
<TRowState extends TableRowData<TItem>>(rowEnhancer = defaultRowEnhancer as RowEnhancer<TItem, TRowState>) => {
return items.map((item, i) => rowEnhancer({ item, rowId: getRowId?.(item) ?? i }));
},
[items, getRowId],
);

const initialState: TableFeaturesState<TItem> = {
getRowId,
Expand Down
Loading

0 comments on commit 50176a6

Please sign in to comment.