Skip to content
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

Row Span compatibility added #949

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions packages/core/src/data-editor/data-editor-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,37 @@ export function expandSelection(

let left = r.x - rowMarkerOffset;
let right = r.x + r.width - 1 - rowMarkerOffset;
let top = r.y;
let bottom = r.y + r.height - 1;
for (const row of cells) {
for (const cell of row) {
if (cell.span === undefined) continue;
left = Math.min(cell.span[0], left);
right = Math.max(cell.span[1], right);
if (cell.span !== undefined) {
left = Math.min(cell.span[0], left);
right = Math.max(cell.span[1], right);
}
if (cell?.rowSpan !== undefined) {
top = Math.min(cell.rowSpan[0], top);
bottom = Math.max(cell.rowSpan[1], bottom);
}
}
}

if (left === r.x - rowMarkerOffset && right === r.x + r.width - 1 - rowMarkerOffset) {
if (
left === r.x - rowMarkerOffset &&
right === r.x + r.width - 1 - rowMarkerOffset &&
top === r.y &&
bottom === r.y + r.height - 1
) {
isFilled = true;
} else {
newVal = {
current: {
cell: newVal.current.cell ?? [0, 0],
range: {
x: left + rowMarkerOffset,
y: r.y,
y: top,
width: right - left + 1,
height: r.height,
height: bottom - top + 1,
},
rangeStack: newVal.current.rangeStack,
},
Expand Down
13 changes: 6 additions & 7 deletions packages/core/src/docs/examples/span-cell.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,21 @@ export const SpanCell: React.VFC = () => {
const mangledGetCellContent = React.useCallback<typeof getCellContent>(
cell => {
const [col, row] = cell;
if (row === 6 && col >= 3 && col <= 4) {
if (col === 0 && row >= 2 && row <= 4) {
return {
kind: GridCellKind.Text,
allowOverlay: false,
data: "Span Cell that is very long and will go past the cell limits",
span: [3, 4],
displayData: "Span Cell that is very long and will go past the cell limits",
data: "Row Span span",
rowSpan: [2, 4],
displayData: "we want to clip each cell individually rather than form a super clip region",
};
}
if (row === 5) {
if (row === 1 && col >= 3 && col <= 5) {
return {
kind: GridCellKind.Text,
allowOverlay: false,
data: "Span Cell that is very long and will go past the cell limits",
span: [0, 99],
span: [3, 5],
displayData: "Span Cell that is very long and will go past the cell limits",
};
}
Expand All @@ -71,7 +71,6 @@ export const SpanCell: React.VFC = () => {
const getCellsForSelection = React.useCallback(
(selection: Rectangle): CellArray => {
const result: GridCell[][] = [];

for (let y = selection.y; y < selection.y + selection.height; y++) {
const row: GridCell[] = [];
for (let x = selection.x; x < selection.x + selection.width; x++) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/internal/data-grid/data-grid-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ export interface BaseGridCell {
readonly style?: "normal" | "faded";
readonly themeOverride?: Partial<Theme>;
readonly span?: readonly [start: number, end: number];
readonly rowSpan?: readonly [startRow: number, endRow: number];
readonly contentAlign?: "left" | "right" | "center";
readonly cursor?: CSSProperties["cursor"];
readonly copyData?: string;
Expand Down
13 changes: 10 additions & 3 deletions packages/core/src/internal/data-grid/render/data-grid-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,18 @@ export function cellIsSelected(location: Item, cell: InnerGridCell, selection: G

if (location[1] !== selection.current.cell[1]) return false;

if (cell.span === undefined) {
return selection.current.cell[0] === location[0];
if (cell.span !== undefined) {
return selection.current.cell[0] >= cell.span[0] && selection.current.cell[0] <= cell.span[1];
}
if (cell.rowSpan !== undefined) {
return (
selection.current.cell[0] === location[0] &&
selection.current.cell[1] >= cell.rowSpan[0] &&
selection.current.cell[1] <= cell.rowSpan[1]
);
}

return selection.current.cell[0] >= cell.span[0] && selection.current.cell[0] <= cell.span[1];
return selection.current.cell[0] === location[0];
}

export function itemIsInRect(location: Item, rect: Rectangle): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import type { RenderStateProvider } from "../../../common/render-state-provider.
import type { ImageWindowLoader } from "../image-window-loader-interface.js";
import { intersectRect } from "../../../common/math.js";
import type { GridMouseGroupHeaderEventArgs } from "../event-args.js";
import { getSkipPoint, getSpanBounds, walkColumns, walkRowsInCol } from "./data-grid-render.walk.js";
import { getRowSpanBounds, getSkipPoint, getSpanBounds, walkColumns, walkRowsInCol } from "./data-grid-render.walk.js";

const loadingCell: InnerGridCell = {
kind: GridCellKind.Loading,
Expand Down Expand Up @@ -119,6 +119,7 @@ export function drawCells(
freezeTrailingRows > 0 ? getFreezeTrailingHeight(rows, freezeTrailingRows, getRowHeight) : 0;
let result: Rectangle[] | undefined;
let handledSpans: Set<string> | undefined = undefined;
let handledRowSpans: Set<string> | undefined = undefined;

const skipPoint = getSkipPoint(drawRegions);

Expand Down Expand Up @@ -220,9 +221,52 @@ export function drawCells(
const cell: InnerGridCell = row < rows ? getCellContent(cellIndex) : loadingCell;

let cellX = drawX;
let cellY = drawY;
let cellHeight = getRowHeight(row);
let cellWidth = c.width;
let drawingSpan = false;
let skipContents = false;

if (cell?.rowSpan !== undefined) {
const [start, end] = cell.rowSpan;
const spanKey = `${c.sourceIndex - 1},${start},${end}`;
if (handledRowSpans === undefined) handledRowSpans = new Set();
if (!handledRowSpans.has(spanKey)) {
const area = getRowSpanBounds(
cell?.rowSpan as Item,
drawX,
drawY,
c.width,
row,
getRowHeight
);
if (area !== undefined) {
cellY = area.y;
cellHeight = area.height;
handledRowSpans.add(spanKey);
ctx.restore();
prepResult = undefined;
ctx.save();
ctx.beginPath();
ctx.rect(area.x, area.y, area.width, area.height);
if (result === undefined) {
result = [];
}
result.push({
x: area.x,
y: area.y,
width: area.width,
height: area.height,
});
ctx.clip();
drawingSpan = true;
}
} else {
toDraw--;
return;
}
}

if (cell.span !== undefined) {
const [startCol, endCol] = cell.span;
const spanKey = `${row},${startCol},${endCol},${c.sticky}`; //alloc
Expand Down Expand Up @@ -280,12 +324,17 @@ export function drawCells(
selection.columns.some(
index => cell.span !== undefined && index >= cell.span[0] && index <= cell.span[1] //alloc
);
const rowSpanIsHighlighted =
cell.rowSpan !== undefined &&
selection.columns.some(
index => cell.rowSpan !== undefined && index >= cell.rowSpan[0] && index <= cell.rowSpan[1] //alloc
);
if (isSelected && !isFocused && drawFocus) {
accentCount = 0;
} else if (isSelected && drawFocus) {
accentCount = Math.max(accentCount, 1);
}
if (spanIsHighlighted) {
if (spanIsHighlighted || rowSpanIsHighlighted) {
accentCount++;
}
if (!isSelected) {
Expand Down Expand Up @@ -336,15 +385,15 @@ export function drawCells(
// we want to clip each cell individually rather than form a super clip region. The reason for
// this is passing too many clip regions to the GPU at once can cause a performance hit. This
// allows us to damage a large number of cells at once without issue.
const top = drawY + 1;
const top = cellY + 1;
const bottom = isSticky
? top + rh - 1
: Math.min(top + rh - 1, height - freezeTrailingRowsHeight);
? top + cellHeight - 1
: Math.min(top + cellHeight - 1, height - freezeTrailingRowsHeight);
const h = bottom - top;

// however, not clipping at all is even better. We want to clip if we are the left most col
// or overlapping the bottom clip area.
if (h !== rh - 1 || cellX + 1 <= clipX) {
if (h !== cellHeight - 1 || cellX + 1 <= clipX) {
didDamageClip = true;
ctx.save();
ctx.beginPath();
Expand All @@ -369,12 +418,12 @@ export function drawCells(
// because technically the bottom right corner of the outline are on other cells.
ctx.fillRect(
cellX + 1,
drawY + 1,
cellY + 1,
cellWidth - (isLastColumn ? 2 : 1),
rh - (isLastRow ? 2 : 1)
cellHeight - (isLastRow ? 2 : 1)
);
} else {
ctx.fillRect(cellX, drawY, cellWidth, rh);
ctx.fillRect(cellX, cellY, cellWidth, cellHeight);
}
}

Expand Down Expand Up @@ -405,9 +454,9 @@ export function drawCells(
isLastColumn,
isLastRow,
cellX,
drawY,
cellY,
cellWidth,
rh,
cellHeight,
accentCount > 0,
theme,
fill ?? theme.bgCell,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,37 @@ export function walkGroups(
}
}

export function getRowSpanBounds(
rowSpan: Item,
cellX: number,
cellY: number,
cellW: number,
row: number,
getRowHeight: (row: number) => number
): Rectangle | undefined {
const [startRow, endRow] = rowSpan;
const totalSpannedRows = endRow - startRow;
let tempY = cellY;
let tempH = totalSpannedRows * 34;
if (getRowHeight !== undefined) {
tempH = getRowHeight(row);
for (let x = row - 1; x >= startRow; x--) {
tempY -= getRowHeight(x);
tempH += getRowHeight(x);
}
for (let x = row + 1; x <= endRow; x++) {
tempH += getRowHeight(x);
}
}
const contentRect: Rectangle | undefined = {
x: cellX,
y: tempY,
width: cellW,
height: tempH,
};
return contentRect;
}

export function getSpanBounds(
span: Item,
cellX: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getStickyWidth, type MappedGridColumn, computeBounds, getFreezeTrailing
import { type FullTheme } from "../../../common/styles.js";
import { blend, withAlpha } from "../color-parser.js";
import { hugRectToTarget, intersectRect, rectContains, splitRectIntoRegions } from "../../../common/math.js";
import { getSpanBounds, walkColumns, walkRowsInCol } from "./data-grid-render.walk.js";
import { getRowSpanBounds, getSpanBounds, walkColumns, walkRowsInCol } from "./data-grid-render.walk.js";
import { type Highlight } from "./data-grid-render.cells.js";

export function drawHighlightRings(
Expand Down Expand Up @@ -247,7 +247,18 @@ export function drawFillHandle(
if (row !== targetRow && row !== fillHandleRow) return;

let cellX = drawX;
let cellY = drawY;
let cellWidth = col.width;
let cellHeight = getRowHeight(row);

if (cell.rowSpan !== undefined) {
const area = getRowSpanBounds(cell.rowSpan, drawX, drawY, col.width, row, getRowHeight);

if (area !== undefined) {
cellHeight = area.height;
cellY = area.y;
}
}

if (cell.span !== undefined) {
const areas = getSpanBounds(cell.span, drawX, drawY, col.width, rh, col, allColumns);
Expand All @@ -269,7 +280,7 @@ export function drawFillHandle(
ctx.clip();
}
ctx.beginPath();
ctx.rect(cellX + cellWidth - 4, drawY + rh - 4, 4, 4);
ctx.rect(cellX + cellWidth - 4, cellY + cellHeight - 4, 4, 4);
ctx.fillStyle = col.themeOverride?.accentColor ?? theme.accentColor;
ctx.fill();
};
Expand Down