Skip to content

Commit a57eda1

Browse files
authored
Add super table enhancements (#920)
* Add ability to restrict column span range selections * Add ability to disable drawing focus ring when editor is open * Change naming * Make boolean cells not select when toggled directly * Fix test
1 parent f1a9b55 commit a57eda1

File tree

6 files changed

+79
-23
lines changed

6 files changed

+79
-23
lines changed

packages/core/src/cells/boolean-cell.tsx

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { FullTheme } from "../common/styles.js";
12
import { getSquareWidth, getSquareXPosFromAlign, getSquareBB, pointIsWithinBB } from "../common/utils.js";
23
import { toggleBoolean } from "../data-editor/data-editor-fns.js";
34
import {
@@ -6,12 +7,38 @@ import {
67
booleanCellIsEditable,
78
BooleanEmpty,
89
BooleanIndeterminate,
10+
type Rectangle,
911
} from "../internal/data-grid/data-grid-types.js";
1012
import { drawCheckbox } from "../internal/data-grid/render/draw-checkbox.js";
1113
import type { BaseDrawArgs, InternalCellRenderer } from "./cell-types.js";
1214

1315
const defaultCellMaxSize = 20;
1416

17+
function isOverEditableRegion(e: {
18+
readonly cell: BooleanCell;
19+
readonly posX: number;
20+
readonly posY: number;
21+
readonly bounds: Rectangle;
22+
readonly theme: FullTheme;
23+
}): boolean {
24+
const { cell, posX: pointerX, posY: pointerY, bounds, theme } = e;
25+
const { width, height, x: cellX, y: cellY } = bounds;
26+
const maxWidth = cell.maxSize ?? defaultCellMaxSize;
27+
const cellCenterY = Math.floor(bounds.y + height / 2);
28+
const checkBoxWidth = getSquareWidth(maxWidth, height, theme.cellVerticalPadding);
29+
const posX = getSquareXPosFromAlign(
30+
cell.contentAlign ?? "center",
31+
cellX,
32+
width,
33+
theme.cellHorizontalPadding,
34+
checkBoxWidth
35+
);
36+
const bb = getSquareBB(posX, cellCenterY, checkBoxWidth);
37+
const checkBoxClicked = pointIsWithinBB(cellX + pointerX, cellY + pointerY, bb);
38+
39+
return booleanCellIsEditable(cell) && checkBoxClicked;
40+
}
41+
1542
export const booleanCellRenderer: InternalCellRenderer<BooleanCell> = {
1643
getAccessibilityString: c => c.data?.toString() ?? "false",
1744
kind: GridCellKind.Boolean,
@@ -31,26 +58,16 @@ export const booleanCellRenderer: InternalCellRenderer<BooleanCell> = {
3158
...c,
3259
data: false,
3360
}),
61+
onSelect: e => {
62+
if (isOverEditableRegion(e)) {
63+
e.preventDefault();
64+
}
65+
},
3466
onClick: e => {
35-
const { cell, posX: pointerX, posY: pointerY, bounds, theme } = e;
36-
const { width, height, x: cellX, y: cellY } = bounds;
37-
const maxWidth = cell.maxSize ?? defaultCellMaxSize;
38-
const cellCenterY = Math.floor(bounds.y + height / 2);
39-
const checkBoxWidth = getSquareWidth(maxWidth, height, theme.cellVerticalPadding);
40-
const posX = getSquareXPosFromAlign(
41-
cell.contentAlign ?? "center",
42-
cellX,
43-
width,
44-
theme.cellHorizontalPadding,
45-
checkBoxWidth
46-
);
47-
const bb = getSquareBB(posX, cellCenterY, checkBoxWidth);
48-
const checkBoxClicked = pointIsWithinBB(cellX + pointerX, cellY + pointerY, bb);
49-
50-
if (booleanCellIsEditable(cell) && checkBoxClicked) {
67+
if (isOverEditableRegion(e)) {
5168
return {
52-
...cell,
53-
data: toggleBoolean(cell.data),
69+
...e.cell,
70+
data: toggleBoolean(e.cell.data),
5471
};
5572
}
5673
return undefined;

packages/core/src/cells/loading-cell.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export const loadingCellRenderer: InternalCellRenderer<LoadingCell> = {
3535
}
3636

3737
const hpad = theme.cellHorizontalPadding;
38+
if (width + hpad * 2 >= rect.width) {
39+
width = rect.width - hpad * 2 - 1;
40+
}
41+
3842
const rectHeight = cell.skeletonHeight ?? Math.min(18, rect.height - 2 * theme.cellVerticalPadding);
3943

4044
roundedRect(

packages/core/src/data-editor/data-editor.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ type Props = Partial<
119119
| "clientSize"
120120
| "columns"
121121
| "disabledRows"
122+
| "drawFocusRing"
122123
| "enableGroups"
123124
| "firstColAccessible"
124125
| "firstColSticky"
@@ -395,6 +396,12 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
395396
*/
396397
readonly rowSelect?: "none" | "single" | "multi";
397398

399+
/** Controls if range selection is allowed to span columns.
400+
* @group Selection
401+
* @defaultValue `true`
402+
*/
403+
readonly rangeSelectionColumnSpanning?: boolean;
404+
398405
/** Sets the initial scroll Y offset.
399406
* @see {@link scrollOffsetX}
400407
* @group Advanced
@@ -667,6 +674,8 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
667674
* If set to true, the data grid will attempt to scroll to keep the selction in view
668675
*/
669676
readonly scrollToActiveCell?: boolean;
677+
678+
readonly drawFocusRing?: boolean | "no-editor";
670679
}
671680

672681
type ScrollToFn = (
@@ -764,6 +773,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
764773
editorBloom,
765774
onHeaderClicked,
766775
onColumnProposeMove,
776+
rangeSelectionColumnSpanning = true,
767777
spanRangeBehavior = "default",
768778
onGroupHeaderClicked,
769779
onCellContextMenu,
@@ -828,7 +838,6 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
828838
onColumnResizeStart: onColumnResizeStartIn,
829839
customRenderers: additionalRenderers,
830840
fillHandle,
831-
drawFocusRing = true,
832841
experimental,
833842
fixedShadowX,
834843
fixedShadowY,
@@ -855,8 +864,11 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
855864
renderers,
856865
resizeIndicator,
857866
scrollToActiveCell = true,
867+
drawFocusRing: drawFocusRingIn = true,
858868
} = p;
859869

870+
const drawFocusRing = drawFocusRingIn === "no-editor" ? overlay === undefined : drawFocusRingIn;
871+
860872
const rowMarkersObj = typeof p.rowMarkers === "string" ? undefined : p.rowMarkers;
861873

862874
const rowMarkers = rowMarkersObj?.kind ?? (p.rowMarkers as RowMarkerOptions["kind"]) ?? "none";
@@ -1033,7 +1045,8 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
10331045
rangeSelectionBlending,
10341046
columnSelectionBlending,
10351047
rowSelectionBlending,
1036-
rangeSelect
1048+
rangeSelect,
1049+
rangeSelectionColumnSpanning
10371050
);
10381051

10391052
const mergedTheme = React.useMemo(() => {

packages/core/src/docs/examples/add-data.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const AddData: React.VFC = () => {
5757
{...defaultProps}
5858
getCellContent={getCellContent}
5959
columns={cols}
60+
rangeSelectionColumnSpanning={false}
6061
rowMarkers={"both"}
6162
onPaste={true} // we want to allow paste to just call onCellEdited
6263
onCellEdited={setCellValue} // Sets the mock cell content

packages/core/src/internal/data-grid/use-selection-behavior.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export function useSelectionBehavior(
1313
rangeBehavior: SelectionBlending,
1414
columnBehavior: SelectionBlending,
1515
rowBehavior: SelectionBlending,
16-
rangeSelect: "none" | "cell" | "rect" | "multi-cell" | "multi-rect"
16+
rangeSelect: "none" | "cell" | "rect" | "multi-cell" | "multi-rect",
17+
rangeSelectionColumnSpanning: boolean
1718
) {
1819
// if append is true, the current range will be added to the rangeStack
1920
const setCurrent = React.useCallback(
@@ -34,6 +35,18 @@ export function useSelectionBehavior(
3435
},
3536
};
3637
}
38+
39+
if (!rangeSelectionColumnSpanning && value !== undefined && value.range.width > 1) {
40+
value = {
41+
...value,
42+
range: {
43+
...value.range,
44+
width: 1,
45+
x: value.cell[0],
46+
},
47+
};
48+
}
49+
3750
const rangeMixable = rangeBehavior === "mixed" && (append || trigger === "drag");
3851
const allowColumnCoSelect = columnBehavior === "mixed" && rangeMixable;
3952
const allowRowCoSelect = rowBehavior === "mixed" && rangeMixable;
@@ -61,7 +74,15 @@ export function useSelectionBehavior(
6174
}
6275
setGridSelection(newVal, expand);
6376
},
64-
[columnBehavior, gridSelection, rangeBehavior, rangeSelect, rowBehavior, setGridSelection]
77+
[
78+
columnBehavior,
79+
gridSelection,
80+
rangeBehavior,
81+
rangeSelect,
82+
rangeSelectionColumnSpanning,
83+
rowBehavior,
84+
setGridSelection,
85+
]
6586
);
6687

6788
const setSelectedRows = React.useCallback(

packages/core/test/data-editor.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ describe("data-editor", () => {
612612

613613
const canvas = screen.getByTestId("data-grid-canvas");
614614
sendClick(canvas, {
615-
clientX: 850, // Col Boolean
615+
clientX: 830, // Col Boolean
616616
clientY: 36 * 2 + 32 + 16, // Row 2 (0 indexed)
617617
});
618618

0 commit comments

Comments
 (0)