-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Add internal useActivePosition hook
#3990
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
Open
nstepien
wants to merge
4
commits into
main
Choose a base branch
from
useActivePosition
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import { | |
| HeaderRowSelectionChangeContext, | ||
| HeaderRowSelectionContext, | ||
| RowSelectionChangeContext, | ||
| useActivePosition, | ||
| useCalculatedColumns, | ||
| useColumnWidths, | ||
| useGridDimensions, | ||
|
|
@@ -77,16 +78,6 @@ import { | |
| } from './style/core'; | ||
| import SummaryRow from './SummaryRow'; | ||
|
|
||
| interface ActiveCellState extends Position { | ||
| readonly mode: 'ACTIVE'; | ||
| } | ||
|
|
||
| interface EditCellState<R> extends Position { | ||
| readonly mode: 'EDIT'; | ||
| readonly row: R; | ||
| readonly originalRow: R; | ||
| } | ||
|
|
||
| export type DefaultColumnOptions<R, SR> = Pick< | ||
| Column<R, SR>, | ||
| | 'renderCell' | ||
|
|
@@ -365,33 +356,50 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| enableVirtualization | ||
| }); | ||
|
|
||
| /** | ||
| * computed values | ||
| */ | ||
| const isTreeGrid = role === 'treegrid'; | ||
| const topSummaryRowsCount = topSummaryRows?.length ?? 0; | ||
| const bottomSummaryRowsCount = bottomSummaryRows?.length ?? 0; | ||
| const summaryRowsCount = topSummaryRowsCount + bottomSummaryRowsCount; | ||
| const headerAndTopSummaryRowsCount = headerRowsCount + topSummaryRowsCount; | ||
| const groupedColumnHeaderRowsCount = headerRowsCount - 1; | ||
| const minRowIdx = -headerAndTopSummaryRowsCount; | ||
| const mainHeaderRowIdx = minRowIdx + groupedColumnHeaderRowsCount; | ||
| const maxRowIdx = rows.length + bottomSummaryRowsCount - 1; | ||
| const frozenShadowStyles: React.CSSProperties = { | ||
| gridColumnStart: lastFrozenColumnIndex + 2, | ||
| insetInlineStart: totalFrozenColumnWidth | ||
| }; | ||
|
|
||
| const [activePosition, setActivePosition] = useState<ActiveCellState | EditCellState<R>>( | ||
| getInitialActivePosition | ||
| ); | ||
|
|
||
| /** | ||
| * computed values | ||
| */ | ||
| const isTreeGrid = role === 'treegrid'; | ||
| const mainHeaderRowIdx = minRowIdx + groupedColumnHeaderRowsCount; | ||
| const maxColIdx = columns.length - 1; | ||
| const headerRowsHeight = headerRowsCount * headerRowHeight; | ||
| const summaryRowsHeight = summaryRowsCount * summaryRowHeight; | ||
| const clientHeight = gridHeight - headerRowsHeight - summaryRowsHeight; | ||
| const isSelectable = selectedRows != null && onSelectedRowsChange != null; | ||
| const { leftKey, rightKey } = getLeftRightKey(direction); | ||
| const ariaRowCount = rawAriaRowCount ?? headerRowsCount + rows.length + summaryRowsCount; | ||
| const frozenShadowStyles: React.CSSProperties = { | ||
| gridColumnStart: lastFrozenColumnIndex + 2, | ||
| insetInlineStart: totalFrozenColumnWidth | ||
| }; | ||
|
|
||
| const { | ||
| activePosition, | ||
| setActivePosition, | ||
| activePositionIsInActiveBounds, | ||
| activePositionIsInViewport, | ||
| activePositionIsRow, | ||
| activePositionIsCellInViewport, | ||
| validatePosition, | ||
| getActiveColumn, | ||
| getActiveRow | ||
| } = useActivePosition<R, SR>({ | ||
| columns, | ||
| rows, | ||
| isTreeGrid, | ||
| maxColIdx, | ||
| minRowIdx, | ||
| maxRowIdx, | ||
| setDraggedOverRowIdx, | ||
| setShouldFocusPosition | ||
| }); | ||
|
|
||
| const defaultGridComponents = useMemo( | ||
| () => ({ | ||
|
|
@@ -441,14 +449,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| enableVirtualization | ||
| }); | ||
|
|
||
| const maxColIdx = columns.length - 1; | ||
| const { | ||
| isPositionInActiveBounds: activePositionIsInActiveBounds, | ||
| isPositionInViewport: activePositionIsInViewport, | ||
| isRowInActiveBounds: activePositionIsRow, | ||
| isCellInViewport: activePositionIsCellInViewport | ||
| } = validatePosition(activePosition); | ||
|
|
||
| const { | ||
| viewportColumns, | ||
| iterateOverViewportColumnsForRow, | ||
|
|
@@ -656,29 +656,32 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
|
|
||
| function commitEditorChanges() { | ||
| if (activePosition.mode !== 'EDIT') return; | ||
| updateRow(columns[activePosition.idx], activePosition.rowIdx, activePosition.row); | ||
| updateRow(getActiveColumn(), activePosition.rowIdx, activePosition.row); | ||
| } | ||
|
|
||
| function handleCellCopy(event: CellClipboardEvent) { | ||
| if (!activePositionIsCellInViewport) return; | ||
| const { idx, rowIdx } = activePosition; | ||
| onCellCopy?.({ row: rows[rowIdx], column: columns[idx] }, event); | ||
| onCellCopy?.({ row: getActiveRow(), column: getActiveColumn() }, event); | ||
| } | ||
|
|
||
| function handleCellPaste(event: CellClipboardEvent) { | ||
| if (!onCellPaste || !onRowsChange || !isCellEditable(activePosition)) { | ||
| if ( | ||
| typeof onCellPaste !== 'function' || | ||
| typeof onRowsChange !== 'function' || | ||
| !isCellEditable(activePosition) | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const { idx, rowIdx } = activePosition; | ||
| const column = columns[idx]; | ||
| const updatedRow = onCellPaste({ row: rows[rowIdx], column }, event); | ||
| updateRow(column, rowIdx, updatedRow); | ||
| const column = getActiveColumn(); | ||
| const row = getActiveRow(); | ||
| const updatedRow = onCellPaste({ row, column }, event); | ||
| updateRow(column, activePosition.rowIdx, updatedRow); | ||
| } | ||
|
|
||
| function handleCellInput(event: KeyboardEvent<HTMLDivElement>) { | ||
| if (!activePositionIsCellInViewport) return; | ||
| const row = rows[activePosition.rowIdx]; | ||
| const row = getActiveRow(); | ||
| const { key, shiftKey } = event; | ||
|
|
||
| // Select the row on Shift + Space | ||
|
|
@@ -764,9 +767,9 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| function updateRows(startRowIdx: number, endRowIdx: number) { | ||
| if (onRowsChange == null) return; | ||
|
|
||
| const { rowIdx, idx } = activePosition; | ||
| const column = columns[idx]; | ||
| const sourceRow = rows[rowIdx]; | ||
| const { idx } = activePosition; | ||
| const column = getActiveColumn(); | ||
| const sourceRow = getActiveRow(); | ||
| const updatedRows = [...rows]; | ||
| const indexes: number[] = []; | ||
| for (let i = startRowIdx; i < endRowIdx; i++) { | ||
|
|
@@ -784,50 +787,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * utils | ||
| */ | ||
| function getInitialActivePosition(): ActiveCellState { | ||
| return { idx: -1, rowIdx: minRowIdx - 1, mode: 'ACTIVE' }; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the given position represents a valid cell or row position in the grid. | ||
| * Active bounds: any valid position in the grid | ||
| * Viewport: any valid position in the grid outside of header rows and summary rows | ||
| * Row selection is only allowed in TreeDataGrid | ||
| */ | ||
| function validatePosition({ idx, rowIdx }: Position) { | ||
| // check column position | ||
| const isColumnPositionAllColumns = isTreeGrid && idx === -1; | ||
| const isColumnPositionInActiveBounds = idx >= 0 && idx <= maxColIdx; | ||
|
|
||
| // check row position | ||
| const isRowPositionInActiveBounds = rowIdx >= minRowIdx && rowIdx <= maxRowIdx; | ||
| const isRowPositionInViewport = rowIdx >= 0 && rowIdx < rows.length; | ||
|
|
||
| // row status | ||
| const isRowInActiveBounds = isColumnPositionAllColumns && isRowPositionInActiveBounds; | ||
| const isRowInViewport = isColumnPositionAllColumns && isRowPositionInViewport; | ||
|
|
||
| // cell status | ||
| const isCellInActiveBounds = isColumnPositionInActiveBounds && isRowPositionInActiveBounds; | ||
| const isCellInViewport = isColumnPositionInActiveBounds && isRowPositionInViewport; | ||
|
|
||
| // position status | ||
| const isPositionInActiveBounds = isRowInActiveBounds || isCellInActiveBounds; | ||
| const isPositionInViewport = isRowInViewport || isCellInViewport; | ||
|
|
||
| return { | ||
| isPositionInActiveBounds, | ||
| isPositionInViewport, | ||
| isRowInActiveBounds, | ||
| isRowInViewport, | ||
| isCellInActiveBounds, | ||
| isCellInViewport | ||
| }; | ||
| } | ||
|
|
||
| function isCellEditable(position: Position): boolean { | ||
| return ( | ||
| validatePosition(position).isCellInViewport && | ||
|
|
@@ -977,19 +936,19 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| } | ||
|
|
||
| function getDragHandle() { | ||
| if (onFill == null || activePosition.mode === 'EDIT' || !activePositionIsCellInViewport) { | ||
| if (onFill == null || activePosition.mode !== 'ACTIVE' || !activePositionIsCellInViewport) { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've changed the |
||
| return; | ||
| } | ||
|
|
||
| const { idx, rowIdx } = activePosition; | ||
| const column = columns[idx]; | ||
| const { rowIdx } = activePosition; | ||
| const column = getActiveColumn(); | ||
| if (column.renderEditCell == null || column.editable === false) { | ||
| return; | ||
| } | ||
|
|
||
| const isLastRow = rowIdx === maxRowIdx; | ||
| const columnWidth = getColumnWidth(column); | ||
| const colSpan = column.colSpan?.({ type: 'ROW', row: rows[rowIdx] }) ?? 1; | ||
| const colSpan = column.colSpan?.({ type: 'ROW', row: getActiveRow() }) ?? 1; | ||
| const { insetInlineStart, ...style } = getCellStyle(column, colSpan); | ||
| const marginEnd = 'calc(var(--rdg-drag-handle-size) * -0.5 + 1px)'; | ||
| const isLastColumn = column.idx + colSpan - 1 === maxColIdx; | ||
|
|
@@ -1023,22 +982,21 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| if ( | ||
| !activePositionIsCellInViewport || | ||
| activePosition.rowIdx !== rowIdx || | ||
| activePosition.mode === 'ACTIVE' | ||
| activePosition.mode !== 'EDIT' | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const { idx, row } = activePosition; | ||
| const column = columns[idx]; | ||
| const { row } = activePosition; | ||
| const column = getActiveColumn(); | ||
| const colSpan = getColSpan(column, lastFrozenColumnIndex, { type: 'ROW', row }); | ||
| const closeOnExternalRowChange = column.editorOptions?.closeOnExternalRowChange ?? true; | ||
|
|
||
| const closeEditor = (shouldFocus: boolean) => { | ||
| function closeEditor(shouldFocus: boolean) { | ||
| setShouldFocusPosition(shouldFocus); | ||
| setActivePosition(({ idx, rowIdx }) => ({ idx, rowIdx, mode: 'ACTIVE' })); | ||
| }; | ||
| } | ||
|
|
||
| const onRowChange = (row: R, commitChanges: boolean, shouldFocus: boolean) => { | ||
| function onRowChange(row: R, commitChanges: boolean, shouldFocus: boolean) { | ||
| if (commitChanges) { | ||
| // Prevents two issues when editor is closed by clicking on a different cell | ||
| // | ||
|
|
@@ -1051,11 +1009,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| } else { | ||
| setActivePosition((position) => ({ ...position, row })); | ||
| } | ||
| }; | ||
|
|
||
| if (closeOnExternalRowChange && rows[activePosition.rowIdx] !== activePosition.originalRow) { | ||
| // Discard changes if rows are updated from outside | ||
| closeEditor(false); | ||
| } | ||
|
|
||
| return ( | ||
|
|
@@ -1135,12 +1088,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr | |
| .toArray(); | ||
| } | ||
|
|
||
| // Reset the positions if the current values are no longer valid. This can happen if a column or row is removed | ||
| if (activePosition.idx > maxColIdx || activePosition.rowIdx > maxRowIdx) { | ||
| setActivePosition(getInitialActivePosition()); | ||
| setDraggedOverRowIdx(undefined); | ||
| } | ||
|
|
||
| // Keep the state and prop in sync | ||
| if (isColumnWidthsControlled && columnWidthsInternal !== columnWidthsRaw) { | ||
| setColumnWidthsInternal(columnWidthsRaw); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYT about
getActiveRow()/getActiveColumn()? Worth it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. Should we return variables instead
activeRow, activeColumn?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd have to check if they're
undefinedin many placesThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could also add
queryActive*()🤔