Skip to content

Commit

Permalink
Merge branch '1477-stuck-to-bottom' into e2e-context-menu
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanalvizo committed Oct 20, 2023
2 parents c0aca30 + 6c2b017 commit f4d8672
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 33 deletions.
83 changes: 60 additions & 23 deletions packages/grid/src/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ type LegacyCanvasRenderingContext2D = CanvasRenderingContext2D & {
backingStorePixelRatio?: number;
};

export type StickyOptions = {
shouldStickBottom?: boolean;
shouldStickRight?: boolean;
};

export type GridProps = typeof Grid.defaultProps & {
// Options to set on the canvas
canvasOptions?: CanvasRenderingContext2DSettings;
Expand Down Expand Up @@ -1181,26 +1186,42 @@ class Grid extends PureComponent<GridProps, GridState> {
* @param deltaColumn Number of columns to move the cursor
* @param deltaRow Number of rows to move the cursor
* @param extendSelection True if the current selection should be extended, false to start a new selection
* @param stickyOptions Options for sticky behavior
*/
moveCursor(
deltaColumn: number,
deltaRow: number,
extendSelection: boolean
extendSelection: boolean,
stickyOptions?: StickyOptions
): void {
const { cursorRow, cursorColumn, selectionEndColumn, selectionEndRow } =
this.state;
const column = extendSelection ? selectionEndColumn : cursorColumn;
const row = extendSelection ? selectionEndRow : cursorRow;
if (row === null || column === null) {
const { left, top } = this.state;
this.moveCursorToPosition(left, top, extendSelection);
this.moveCursorToPosition(
left,
top,
extendSelection,
true,
false,
stickyOptions
);
} else {
const { model } = this.props;
const { columnCount, rowCount } = model;

const left = clamp(column + deltaColumn, 0, columnCount - 1);
const top = clamp(row + deltaRow, 0, rowCount - 1);
this.moveCursorToPosition(left, top, extendSelection);
this.moveCursorToPosition(
left,
top,
extendSelection,
true,
false,
stickyOptions
);
}
}

Expand Down Expand Up @@ -1260,13 +1281,15 @@ class Grid extends PureComponent<GridProps, GridState> {
* @param extendSelection Whether to extend the current selection (eg. holding Shift)
* @param keepCursorInView Whether to move the viewport so that the cursor is in view
* @param maximizePreviousRange With this and `extendSelection` true, it will maximize/add to the previous range only, ignoring where the selection was started
* @param stickyOptions Options for sticky behavior
*/
moveCursorToPosition(
column: GridRangeIndex,
row: GridRangeIndex,
extendSelection = false,
keepCursorInView = true,
maximizePreviousRange = false
maximizePreviousRange = false,
stickyOptions?: StickyOptions
): void {
if (!extendSelection) {
this.beginSelection(column, row);
Expand All @@ -1275,7 +1298,7 @@ class Grid extends PureComponent<GridProps, GridState> {
this.moveSelection(column, row, extendSelection, maximizePreviousRange);

if (keepCursorInView) {
this.moveViewToCell(column, row);
this.moveViewToCell(column, row, stickyOptions);
}
}

Expand All @@ -1284,8 +1307,13 @@ class Grid extends PureComponent<GridProps, GridState> {
*
* @param column The column index to bring into view
* @param row The row index to bring into view
* @param stickyOptions Options for sticky behavior
*/
moveViewToCell(column: GridRangeIndex, row: GridRangeIndex): void {
moveViewToCell(
column: GridRangeIndex,
row: GridRangeIndex,
stickyOptions?: StickyOptions
): void {
if (!this.metrics) throw new Error('metrics not set');

const { metricCalculator } = this;
Expand Down Expand Up @@ -1314,46 +1342,47 @@ class Grid extends PureComponent<GridProps, GridState> {
}
}

this.setViewState({ top, left, topOffset, leftOffset });
this.setViewState(
{ top, left, topOffset, leftOffset },
false,
stickyOptions
);
}

/**
* Checks the `top` and `left` properties that are set and updates the isStuckToBottom/Right properties
* Should be called when user interaction occurs
* @param viewState New state properties to set.
* @param forceUpdate Whether to force an update.
* @param stickyOptions Options for sticky behavior
*/

setViewState(
viewState: Partial<GridState>,
forceUpdate = false,
eventType: WheelEvent | GridKeyboardEvent | null = null
stickyOptions?: StickyOptions
): void {
if (!this.metrics) throw new Error('metrics not set');

const { isStickyBottom, isStickyRight } = this.props;
const { top, left } = viewState;
const { lastTop, lastLeft } = this.metrics;
let isUserInputDown = false;

if (eventType instanceof WheelEvent) {
isUserInputDown = eventType.deltaY > 0;
} else if (
eventType instanceof KeyboardEvent ||
(eventType && 'nativeEvent' in eventType) // used to catch the case that a synthetic react keyboard event is passed
) {
isUserInputDown =
eventType.key === 'ArrowDown' ||
eventType.key === 'End' ||
eventType.key === 'PageDown';
}
if (top != null) {
this.setState({
isStuckToBottom: isStickyBottom && top >= lastTop && isUserInputDown,
isStuckToBottom:
isStickyBottom &&
top >= lastTop &&
(stickyOptions?.shouldStickBottom ?? false),
});
}
if (left != null) {
this.setState({ isStuckToRight: isStickyRight && left >= lastLeft });
this.setState({
isStuckToRight:
isStickyRight &&
left >= lastLeft &&
(stickyOptions?.shouldStickRight ?? false),
});
}

this.setState(viewState as GridState);
Expand Down Expand Up @@ -1998,7 +2027,15 @@ class Grid extends PureComponent<GridProps, GridState> {
}
}

this.setViewState({ top, left, leftOffset, topOffset }, false, event);
const stickyOptions: StickyOptions = {
shouldStickBottom: event.deltaY > 0,
shouldStickRight: event.deltaX > 0,
};
this.setViewState(
{ top, left, leftOffset, topOffset },
false,
stickyOptions
);

event.stopPropagation();
event.preventDefault();
Expand Down
32 changes: 22 additions & 10 deletions packages/grid/src/key-handlers/SelectionKeyHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint class-methods-use-this: "off" */
import clamp from 'lodash.clamp';
import { EventHandlerResult } from '../EventHandlerResult';
import Grid from '../Grid';
import Grid, { StickyOptions } from '../Grid';
import GridRange from '../GridRange';
import GridUtils from '../GridUtils';
import KeyHandler, { GridKeyboardEvent } from '../KeyHandler';
Expand Down Expand Up @@ -177,6 +177,13 @@ class SelectionKeyHandler extends KeyHandler {

const { theme } = grid.props;
const { autoSelectRow = false, autoSelectColumn = false } = theme;
const stickyOptions: StickyOptions = {
shouldStickBottom:
event.key === 'ArrowDown' ||
event.key === 'End' ||
event.key === 'PageDown',
shouldStickRight: event.key === 'ArrowRight',
};
if (autoSelectRow && deltaColumn !== 0) {
const { lastLeft } = grid.metrics;
let { left } = grid.state;
Expand All @@ -185,7 +192,7 @@ class SelectionKeyHandler extends KeyHandler {

grid.moveCursorToPosition(left, cursorRow, isShiftKey, false);

grid.setViewState({ left }, false, event);
grid.setViewState({ left }, false, stickyOptions);
} else if (autoSelectColumn && deltaRow !== 0) {
const { lastTop } = grid.metrics;
let { top } = grid.state;
Expand All @@ -194,12 +201,9 @@ class SelectionKeyHandler extends KeyHandler {

grid.moveCursorToPosition(top, cursorColumn, isShiftKey, false);

grid.setViewState({ top }, false, event);
grid.setViewState({ top }, false, stickyOptions);
} else {
const { lastLeft, lastTop } = grid.metrics;

grid.moveCursor(deltaColumn, deltaRow, isShiftKey);
grid.setViewState({ left: lastLeft, top: lastTop }, false, event); // need to call setViewState again since moveCursor calls a series of functions that sets view state
grid.moveCursor(deltaColumn, deltaRow, isShiftKey, stickyOptions);
}
}
return true;
Expand Down Expand Up @@ -245,8 +249,8 @@ class SelectionKeyHandler extends KeyHandler {
return true;
}

handlePageDown(e: GridKeyboardEvent, grid: Grid): boolean {
const isShiftKey = e.shiftKey;
handlePageDown(event: GridKeyboardEvent, grid: Grid): boolean {
const isShiftKey = event.shiftKey;

if (isShiftKey) {
grid.trimSelectedRanges();
Expand Down Expand Up @@ -282,7 +286,15 @@ class SelectionKeyHandler extends KeyHandler {
isShiftKey,
false
);
grid.setViewState({ top: viewportPosition }, false, e);

const stickyOptions: StickyOptions = {
shouldStickBottom:
event.key === 'ArrowDown' ||
event.key === 'End' ||
event.key === 'PageDown',
shouldStickRight: event.key === 'ArrowRight',
};
grid.setViewState({ top: viewportPosition }, false, stickyOptions);

return true;
}
Expand Down

0 comments on commit f4d8672

Please sign in to comment.