Skip to content

Commit

Permalink
Merge 7679957 into 17d3640
Browse files Browse the repository at this point in the history
  • Loading branch information
jassmith committed Feb 7, 2024
2 parents 17d3640 + 7679957 commit 0b57ad7
Show file tree
Hide file tree
Showing 10 changed files with 1,814 additions and 629 deletions.
167 changes: 140 additions & 27 deletions packages/core/src/data-editor/data-editor.tsx
Expand Up @@ -81,6 +81,8 @@ import {
} from "../internal/data-grid/event-args.js";
import { type Keybinds, useKeybindingsWithDefaults } from "./data-editor-keybindings.js";
import type { Highlight } from "../internal/data-grid/render/data-grid-render.cells.js";
import { useRowGroupingInner, type RowGroupingOptions } from "./row-grouping.js";
import { useRowGrouping } from "./row-grouping-api.js";

const DataGridOverlayEditor = React.lazy(
async () => await import("../internal/data-grid-overlay-editor/data-grid-overlay-editor.js")
Expand Down Expand Up @@ -121,6 +123,7 @@ type Props = Partial<
| "getCellContent"
| "getCellRenderer"
| "getCellsForSelection"
| "getRowThemeOverride"
| "gridRef"
| "groupHeaderHeight"
| "headerHeight"
Expand Down Expand Up @@ -443,6 +446,15 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
*/
readonly markdownDivCreateNode?: (content: string) => DocumentFragment;

/**
* Allows overriding the theme of any row
* @param row represents the row index of the row, increasing by 1 for every represented row. Collapsed rows are not included.
* @param groupRow represents the row index of the group row. Only distinct when row grouping enabled.
* @param contentRow represents the index of the row excluding group headers. Only distinct when row grouping enabled.
* @returns
*/
readonly getRowThemeOverride?: (row: number, groupRow: number, contentRow: number) => Partial<Theme> | undefined;

/** Callback for providing a custom editor for a cell.
* @group Editing
*/
Expand Down Expand Up @@ -580,6 +592,11 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
*/
readonly verticalBorder?: DataGridSearchProps["verticalBorder"] | boolean;

/**
* Controls the grouping of rows to be drawn in the grid.
*/
readonly rowGrouping?: RowGroupingOptions;

/**
* Called when data is pasted into the grid. If left undefined, the `DataEditor` will operate in a
* fallback mode and attempt to paste the text buffer into the current cell assuming the current cell is not
Expand Down Expand Up @@ -722,12 +739,12 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr

const {
imageEditorOverride,
getRowThemeOverride,
getRowThemeOverride: getRowThemeOverrideIn,
markdownDivCreateNode,
width,
height,
columns: columnsIn,
rows,
rows: rowsIn,
getCellContent,
onCellClicked,
onCellActivated,
Expand Down Expand Up @@ -778,6 +795,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
onHeaderMenuClick,
onHeaderIndicatorClick,
getGroupDetails,
rowGrouping,
onSearchClose: onSearchCloseIn,
onItemHovered,
onSelectionCleared,
Expand Down Expand Up @@ -846,22 +864,28 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
return window.getComputedStyle(document.documentElement);
}, []);

const remSize = React.useMemo(() => Number.parseFloat(docStyle.fontSize), [docStyle]);
const {
rows,
rowNumberMapper,
rowHeight: rowHeightPostGrouping,
getRowThemeOverride,
} = useRowGroupingInner(rowGrouping, rowsIn, rowHeightIn, getRowThemeOverrideIn);

const remSize = React.useMemo(() => Number.parseFloat(docStyle.fontSize), [docStyle]);
const { rowHeight, headerHeight, groupHeaderHeight, theme, overscrollX, overscrollY } = useRemAdjuster({
groupHeaderHeight: groupHeaderHeightIn,
headerHeight: headerHeightIn,
overscrollX: overscrollXIn,
overscrollY: overscrollYIn,
remSize,
rowHeight: rowHeightIn,
rowHeight: rowHeightPostGrouping,
scaleToRem,
theme: themeIn,
});

const keybindings = useKeybindingsWithDefaults(keybindingsIn);

const rowMarkerWidth = rowMarkerWidthRaw ?? (rows > 10_000 ? 48 : rows > 1000 ? 44 : rows > 100 ? 36 : 32);
const rowMarkerWidth = rowMarkerWidthRaw ?? (rowsIn > 10_000 ? 48 : rowsIn > 1000 ? 44 : rowsIn > 100 ? 36 : 32);
const hasRowMarkers = rowMarkers !== "none";
const rowMarkerOffset = hasRowMarkers ? 1 : 0;
const showTrailingBlankRow = onRowAppended !== undefined;
Expand Down Expand Up @@ -1266,13 +1290,15 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
if (isTrailing) {
return loadingCell;
}
const mappedRow = rowNumberMapper(row);
if (mappedRow === undefined) return loadingCell;
return {
kind: InnerGridCellKind.Marker,
allowOverlay: false,
checkboxStyle: rowMarkerCheckboxStyle,
checked: gridSelection?.rows.hasIndex(row) === true,
markerKind: rowMarkers === "clickable-number" ? "number" : rowMarkers,
row: rowMarkerStartIndex + row,
row: rowMarkerStartIndex + mappedRow,
drawHandle: onRowMoved !== undefined,
cursor: rowMarkers === "clickable-number" ? "pointer" : undefined,
};
Expand Down Expand Up @@ -1334,6 +1360,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
showTrailingBlankRow,
mangledRows,
hasRowMarkers,
rowNumberMapper,
rowMarkerCheckboxStyle,
gridSelection?.rows,
rowMarkers,
Expand Down Expand Up @@ -1720,6 +1747,10 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
[getRowThemeOverride, mangledCols, mergedTheme]
);

const { mapper } = useRowGrouping(rowGrouping, rowsIn);

const rowGroupingNavBehavior = rowGrouping?.navigationBehavior;

const handleSelect = React.useCallback(
(args: GridMouseEventArgs) => {
const isMultiKey = browserIsOSX.value ? args.metaKey : args.ctrlKey;
Expand Down Expand Up @@ -1817,6 +1848,11 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
return;
}
}

if (rowGroupingNavBehavior === "block" && mapper(row).isGroupHeader) {
return;
}

const isLastStickyRow = lastRowSticky && row === rows;

const startedFromLastSticky =
Expand Down Expand Up @@ -1926,28 +1962,30 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
}
},
[
appendRow,
rowSelect,
columnSelect,
focus,
getCellRenderer,
getCustomNewRowTargetColumn,
getMangledCellContent,
gridSelection,
hasRowMarkers,
lastRowSticky,
onSelectionCleared,
onRowMoved,
rowMarkerOffset,
showTrailingBlankRow,
rows,
rowMarkers,
rowSelect,
getMangledCellContent,
onRowMoved,
focus,
rowSelectionMode,
rows,
getCellRenderer,
themeForCell,
setSelectedRows,
getCustomNewRowTargetColumn,
appendRow,
rowGroupingNavBehavior,
mapper,
lastRowSticky,
setCurrent,
setGridSelection,
setSelectedColumns,
setSelectedRows,
showTrailingBlankRow,
themeForCell,
setGridSelection,
onSelectionCleared,
]
);
const isActivelyDraggingHeader = React.useRef(false);
Expand Down Expand Up @@ -2541,6 +2579,27 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
isActivelyDragging.current = false;
}, []);

const rowGroupingSelectionBehavior = rowGrouping?.selectionBehavior;

const getSelectionRowLimits = React.useCallback(
(selectedRow: number): readonly [number, number] | undefined => {
if (rowGroupingSelectionBehavior !== "block-spanning") return undefined;

const { isGroupHeader, path, groupRows } = mapper(selectedRow);

if (isGroupHeader) {
return [selectedRow, selectedRow];
}

const groupRowIndex = path[path.length - 1];
const lowerBounds = selectedRow - groupRowIndex;
const upperBounds = selectedRow + groupRows - groupRowIndex - 1;

return [lowerBounds, upperBounds];
},
[mapper, rowGroupingSelectionBehavior]
);

const hoveredRef = React.useRef<GridMouseEventArgs>();
const onItemHoveredImpl = React.useCallback(
(args: GridMouseEventArgs) => {
Expand Down Expand Up @@ -2595,6 +2654,10 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
}

col = Math.max(col, rowMarkerOffset);
const clampLimits = getSelectionRowLimits(selectedRow);
row = clampLimits === undefined ? row : clamp(row, clampLimits[0], clampLimits[1]);

// FIXME: Restrict row based on rowGrouping.selectionBehavior here

const deltaX = col - selectedCol;
const deltaY = row - selectedRow;
Expand All @@ -2621,7 +2684,6 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
onItemHovered?.({ ...args, location: [args.location[0] - rowMarkerOffset, args.location[1]] as any });
},
[
allowedFillDirections,
mouseState,
rowMarkerOffset,
rowSelect,
Expand All @@ -2631,6 +2693,8 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
setSelectedRows,
showTrailingBlankRow,
rows,
allowedFillDirections,
getSelectionRowLimits,
setCurrent,
]
);
Expand Down Expand Up @@ -2675,20 +2739,23 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
let top = old.y;
let bottom = old.y + old.height;

const [minRow, maxRowRaw] = getSelectionRowLimits(row) ?? [0, rows - 1];
const maxRow = maxRowRaw + 1; // we need an inclusive value

// take care of vertical first in case new spans come in
if (y !== 0) {
switch (y) {
case 2: {
// go to end
bottom = rows;
bottom = maxRow;
top = row;
scrollTo(0, bottom, "vertical");

break;
}
case -2: {
// go to start
top = 0;
top = minRow;
bottom = row + 1;
scrollTo(0, top, "vertical");

Expand All @@ -2700,7 +2767,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
top++;
scrollTo(0, top, "vertical");
} else {
bottom = Math.min(rows, bottom + 1);
bottom = Math.min(maxRow, bottom + 1);
scrollTo(0, bottom, "vertical");
}

Expand All @@ -2712,7 +2779,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
bottom--;
scrollTo(0, bottom, "vertical");
} else {
top = Math.max(0, top - 1);
top = Math.max(minRow, top - 1);
scrollTo(0, top, "vertical");
}

Expand Down Expand Up @@ -2815,7 +2882,16 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
"keyboard-select"
);
},
[getCellsForSelection, gridSelection, mangledCols.length, rowMarkerOffset, rows, scrollTo, setCurrent]
[
getCellsForSelection,
getSelectionRowLimits,
gridSelection,
mangledCols.length,
rowMarkerOffset,
rows,
scrollTo,
setCurrent,
]
);

const updateSelectedCell = React.useCallback(
Expand All @@ -2824,7 +2900,10 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
col = clamp(col, rowMarkerOffset, columns.length - 1 + rowMarkerOffset);
row = clamp(row, 0, rowMax);

if (col === currentCell?.[0] && row === currentCell?.[1]) return false;
const curCol = currentCell?.[0];
const curRow = currentCell?.[1];

if (col === curCol && row === curRow) return false;
if (freeMove && gridSelection.current !== undefined) {
const newStack = [...gridSelection.current.rangeStack];
if (gridSelection.current.range.width > 1 || gridSelection.current.range.height > 1) {
Expand Down Expand Up @@ -3040,6 +3119,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr

if (gridSelection.current === undefined) return false;
let [col, row] = gridSelection.current.cell;
const [, startRow] = gridSelection.current.cell;
let freeMove = false;
let cancelOnlyOnMove = false;

Expand Down Expand Up @@ -3173,6 +3253,37 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
}
// #endregion

const mustRestrictRow = rowGroupingNavBehavior !== undefined && rowGroupingNavBehavior !== "normal";

if (mustRestrictRow && row !== startRow) {
const skipUp =
rowGroupingNavBehavior === "skip-up" ||
rowGroupingNavBehavior === "skip" ||
rowGroupingNavBehavior === "block";
const skipDown =
rowGroupingNavBehavior === "skip-down" ||
rowGroupingNavBehavior === "skip" ||
rowGroupingNavBehavior === "block";
const didMoveUp = row < startRow;
if (didMoveUp && skipUp) {
while (row >= 0 && mapper(row).isGroupHeader) {
row--;
}

if (row < 0) {
row = startRow;
}
} else if (!didMoveUp && skipDown) {
while (row < rows && mapper(row).isGroupHeader) {
row++;
}

if (row >= rows) {
row = startRow;
}
}
}

const moved = updateSelectedCell(col, row, false, freeMove);

const didMatch = details.didMatch;
Expand All @@ -3184,13 +3295,15 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
return didMatch;
},
[
rowGroupingNavBehavior,
overlayOpen,
gridSelection,
keybindings,
columnSelect,
rowSelect,
rangeSelect,
rowMarkerOffset,
mapper,
rows,
updateSelectedCell,
setGridSelection,
Expand Down

0 comments on commit 0b57ad7

Please sign in to comment.