Skip to content

Commit 08ff520

Browse files
committed
feat: cell activation info
1 parent e7d8fce commit 08ff520

File tree

8 files changed

+133
-27
lines changed

8 files changed

+133
-27
lines changed

packages/core/API.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -624,13 +624,14 @@ export type ProvideEditorCallbackResult<T extends InnerGridCell> =
624624
| undefined;
625625

626626
export type ProvideEditorCallback<T extends InnerGridCell> = (
627-
cell: T & { location?: Item }
627+
cell: T & { location?: Item; activation?: CellActivatedEventArgs }
628628
) => ProvideEditorCallbackResult<T>;
629629

630630
provideEditor?: ProvideEditorCallback<GridCell>;
631631
```
632632

633633
When provided the `provideEditor` callbacks job is to be a constructor for functional components which have the correct properties to be used by the data grid as an editor. The editor must implement `onChange` and `onFinishedEditing` callbacks as well support the `isHighlighted` flag which tells the editor to begin with any editable text pre-selected so typing will immediately begin to overwrite it.
634+
The `cell` passed to this callback includes a `location` of the activated cell and an `activation` event describing how the editor was opened.
634635

635636
---
636637

@@ -1186,10 +1187,18 @@ onCellClicked?: (cell: Item) => void;
11861187
## onCellActivated
11871188

11881189
```ts
1189-
onCellActivated?: (cell: Item) => void;
1190+
onCellActivated?: (
1191+
cell: Item,
1192+
event: CellActivatedEventArgs
1193+
) => void;
11901194
```
11911195

1192-
`onCellActivated` is called whenever the user double clicks, presses Enter or Space, or begins typing with a cell selected.
1196+
`onCellActivated` is called whenever the user double clicks, presses Enter or Space, or begins typing with a cell selected. The second argument describes how the activation occurred.
1197+
1198+
The `event` parameter is one of:
1199+
1200+
- `KeyboardCellActivatedEvent` – contains `inputType: "keyboard"` and a `key` field with the physical key pressed.
1201+
- `PointerCellActivatedEvent` – contains `inputType: "pointer"`, a `pointerActivation` reason such as `"double-click"` or `"single-click"`, and an optional `pointerType` (`"mouse"`, `"touch"`, or `"pen"`).
11931202

11941203
---
11951204

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

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import {
7878
type GridDragEventArgs,
7979
mouseEventArgsAreEqual,
8080
type GridKeyEventArgs,
81+
type CellActivatedEventArgs,
8182
} from "../internal/data-grid/event-args.js";
8283
import { type Keybinds, useKeybindingsWithDefaults } from "./data-editor-keybindings.js";
8384
import type { Highlight } from "../internal/data-grid/render/data-grid-render.cells.js";
@@ -241,7 +242,7 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
241242
/** Emitted when a cell is activated, by pressing Enter, Space or double clicking it.
242243
* @group Events
243244
*/
244-
readonly onCellActivated?: (cell: Item) => void;
245+
readonly onCellActivated?: (cell: Item, event: CellActivatedEventArgs) => void;
245246

246247
/**
247248
* Emitted whenever the user initiats a pattern fill using the fill handle. This event provides both
@@ -771,6 +772,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
771772
cell: Item;
772773
highlight: boolean;
773774
forceEditMode: boolean;
775+
activation: CellActivatedEventArgs;
774776
}>();
775777
const searchInputRef = React.useRef<HTMLInputElement | null>(null);
776778
const canvasRef = React.useRef<HTMLCanvasElement | null>(null);
@@ -1431,7 +1433,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
14311433
);
14321434

14331435
const reselect = React.useCallback(
1434-
(bounds: Rectangle, fromKeyboard: boolean, initialValue?: string) => {
1436+
(bounds: Rectangle, activation: CellActivatedEventArgs, initialValue?: string) => {
14351437
if (gridSelection.current === undefined) return;
14361438

14371439
const [col, row] = gridSelection.current.cell;
@@ -1466,8 +1468,9 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
14661468
cell: [col, row],
14671469
highlight: initialValue === undefined,
14681470
forceEditMode: initialValue !== undefined,
1471+
activation,
14691472
});
1470-
} else if (c.kind === GridCellKind.Boolean && fromKeyboard && c.readonly !== true) {
1473+
} else if (c.kind === GridCellKind.Boolean && activation.inputType === "keyboard" && c.readonly !== true) {
14711474
mangledOnCellsEdited([
14721475
{
14731476
location: gridSelection.current.cell,
@@ -1502,6 +1505,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
15021505
highlight: true,
15031506
cell: [col, row],
15041507
forceEditMode: true,
1508+
activation: { inputType: "keyboard", key: "Enter" },
15051509
});
15061510
},
15071511
[getMangledCellContent, scrollRef, setOverlaySimple]
@@ -2058,6 +2062,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
20582062
mapper,
20592063
lastRowSticky,
20602064
setCurrent,
2065+
headerRowMarkerDisabled,
20612066
setSelectedColumns,
20622067
setGridSelection,
20632068
onSelectionCleared,
@@ -2387,8 +2392,16 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
23872392
}
23882393
}
23892394
if (shouldActivate) {
2390-
onCellActivated?.([col - rowMarkerOffset, row]);
2391-
reselect(a.bounds, false);
2395+
const act = a.isDoubleClick === true
2396+
? "double-click"
2397+
: (c.activationBehaviorOverride ?? cellActivationBehavior);
2398+
const activationEvent: CellActivatedEventArgs = {
2399+
inputType: "pointer",
2400+
pointerActivation: act,
2401+
pointerType: a.isTouch ? "touch" : "mouse",
2402+
};
2403+
onCellActivated?.([col - rowMarkerOffset, row], activationEvent);
2404+
reselect(a.bounds, activationEvent);
23922405
return true;
23932406
}
23942407
}
@@ -3265,8 +3278,12 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
32653278
void appendRow(customTargetColumn ?? col);
32663279
}, 0);
32673280
} else {
3268-
onCellActivated?.([col - rowMarkerOffset, row]);
3269-
reselect(bounds, true);
3281+
const activationEvent: CellActivatedEventArgs = {
3282+
inputType: "keyboard",
3283+
key: event.key,
3284+
};
3285+
onCellActivated?.([col - rowMarkerOffset, row], activationEvent);
3286+
reselect(bounds, activationEvent);
32703287
}
32713288
} else if (gridSelection.current.range.height > 1 && isHotkey(keys.downFill, event, details)) {
32723289
fillDown();
@@ -3477,8 +3494,12 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
34773494
) {
34783495
return;
34793496
}
3480-
onCellActivated?.([col - rowMarkerOffset, row]);
3481-
reselect(event.bounds, true, event.key);
3497+
const activationEvent: CellActivatedEventArgs = {
3498+
inputType: "keyboard",
3499+
key: event.key,
3500+
};
3501+
onCellActivated?.([col - rowMarkerOffset, row], activationEvent);
3502+
reselect(event.bounds, activationEvent, event.key);
34823503
event.stopPropagation();
34833504
event.preventDefault();
34843505
}
@@ -3543,7 +3564,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
35433564
formatted?: string | string[]
35443565
): EditListItem | undefined {
35453566
const stringifiedRawValue =
3546-
typeof rawValue === "object" ? rawValue?.join("\n") ?? "" : rawValue?.toString() ?? "";
3567+
typeof rawValue === "object" ? (rawValue?.join("\n") ?? "") : (rawValue?.toString() ?? "");
35473568

35483569
if (!isInnerOnlyCell(inner) && isReadWriteCell(inner) && inner.readonly !== true) {
35493570
const coerced = coercePasteValue?.(stringifiedRawValue, inner);

packages/core/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ export * from "./internal/data-grid/data-grid-types.js";
1111
export type {
1212
BaseGridMouseEventArgs,
1313
CellClickedEventArgs,
14+
CellActivatedEventArgs,
15+
KeyboardCellActivatedEvent,
16+
PointerCellActivatedEvent,
1417
DragHandler,
1518
FillPatternEventArgs,
1619
GridDragEventArgs,

packages/core/src/internal/data-grid-overlay-editor/data-grid-overlay-editor.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
type Rectangle,
1717
type ValidatedGridCell,
1818
} from "../data-grid/data-grid-types.js";
19+
20+
import type { CellActivatedEventArgs } from "../data-grid/event-args.js";
1921
import { DataGridOverlayEditorStyle } from "./data-grid-overlay-editor-style.js";
2022
import type { OverlayImageEditorProps } from "./private/image-overlay-editor.js";
2123
import { useStayOnScreen } from "./use-stay-on-screen.js";
@@ -39,6 +41,7 @@ interface DataGridOverlayEditorProps {
3941
readonly getCellRenderer: GetCellRendererCallback;
4042
readonly markdownDivCreateNode?: (content: string) => DocumentFragment;
4143
readonly provideEditor?: ProvideEditorCallback<GridCell>;
44+
readonly activation: CellActivatedEventArgs;
4245
readonly validateCell?: (
4346
cell: Item,
4447
newValue: EditableGridCell,
@@ -69,6 +72,7 @@ const DataGridOverlayEditor: React.FunctionComponent<DataGridOverlayEditorProps>
6972
provideEditor,
7073
isOutsideClick,
7174
customEventTarget,
75+
activation,
7276
} = p;
7377

7478
const [tempValue, setTempValueRaw] = React.useState<GridCell | undefined>(forceEditMode ? content : undefined);
@@ -154,13 +158,14 @@ const DataGridOverlayEditor: React.FunctionComponent<DataGridOverlayEditorProps>
154158

155159
const [editorProvider, useLabel] = React.useMemo((): [ProvideEditorCallbackResult<GridCell>, boolean] | [] => {
156160
if (isInnerOnlyCell(content)) return [];
157-
const cellWithLocation = { ...content, location: cell } as GridCell & {
161+
const cellWithLocation = { ...content, location: cell, activation } as GridCell & {
158162
location: Item;
163+
activation: CellActivatedEventArgs;
159164
};
160165
const external = provideEditor?.(cellWithLocation);
161166
if (external !== undefined) return [external, false];
162167
return [getCellRenderer(content)?.provideEditor?.(cellWithLocation), false];
163-
}, [cell, content, getCellRenderer, provideEditor]);
168+
}, [cell, content, getCellRenderer, provideEditor, activation]);
164169

165170
const { ref, style: stayOnScreenStyle } = useStayOnScreen();
166171

@@ -181,6 +186,7 @@ const DataGridOverlayEditor: React.FunctionComponent<DataGridOverlayEditorProps>
181186
<CustomEditor
182187
portalElementRef={portalElementRef}
183188
isHighlighted={highlight}
189+
activation={activation}
184190
onChange={setTempValue}
185191
value={targetValue}
186192
initialValue={initialValue}

packages/core/src/internal/data-grid/data-grid-types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { Theme } from "../../common/styles.js";
55
import { assertNever, proveType } from "../../common/support.js";
66
import type { OverlayImageEditorProps } from "../data-grid-overlay-editor/private/image-overlay-editor.js";
77
import type { SpriteManager } from "./data-grid-sprites.js";
8-
import type { BaseGridMouseEventArgs } from "./event-args.js";
8+
import type { BaseGridMouseEventArgs, CellActivatedEventArgs } from "./event-args.js";
99
import type { ImageWindowLoader } from "./image-window-loader-interface.js";
1010

1111
// Thoughts:
@@ -396,6 +396,7 @@ export type ProvideEditorComponent<T extends InnerGridCell> = React.FunctionComp
396396
readonly isValid?: boolean;
397397
readonly theme: Theme;
398398
readonly portalElementRef?: React.RefObject<HTMLElement>;
399+
readonly activation: CellActivatedEventArgs;
399400
}>;
400401

401402
type ObjectEditorCallbackResult<T extends InnerGridCell> = {
@@ -424,7 +425,7 @@ export function isObjectEditorCallbackResult<T extends InnerGridCell>(
424425

425426
/** @category Renderers */
426427
export type ProvideEditorCallback<T extends InnerGridCell> = (
427-
cell: T & { location?: Item }
428+
cell: T & { location?: Item; activation?: CellActivatedEventArgs }
428429
) => ProvideEditorCallbackResult<T>;
429430

430431
/** @category Cells */

packages/core/src/internal/data-grid/data-grid.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1718,7 +1718,7 @@ const DataGrid: React.ForwardRefRenderFunction<DataGridRef, DataGridProps> = (p,
17181718
return getMouseArgsForPosition(canvasRef.current, posX, posY, ev);
17191719
}
17201720
}),
1721-
[canvasRef, damage, getBoundsForItem]
1721+
[canvasRef, damage, getBoundsForItem, getMouseArgsForPosition]
17221722
);
17231723

17241724
const lastFocusedSubdomNode = React.useRef<Item>();

packages/core/src/internal/data-grid/event-args.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Item, Rectangle } from "./data-grid-types.js";
1+
import type { Item, Rectangle, CellActiviationBehavior } from "./data-grid-types.js";
22

33
/** @category Types */
44
export interface BaseGridMouseEventArgs {
@@ -101,6 +101,26 @@ export interface HeaderClickedEventArgs extends GridMouseHeaderEventArgs, Preven
101101
/** @category Types */
102102
export interface GroupHeaderClickedEventArgs extends GridMouseGroupHeaderEventArgs, PreventableEvent {}
103103

104+
export interface BaseCellActivatedEvent {}
105+
106+
/** Keyboard-initiated activation */
107+
export interface KeyboardCellActivatedEvent extends BaseCellActivatedEvent {
108+
readonly inputType: "keyboard";
109+
readonly key: string;
110+
}
111+
112+
/** Pointer-initiated activation */
113+
export interface PointerCellActivatedEvent extends BaseCellActivatedEvent {
114+
readonly inputType: "pointer";
115+
readonly pointerActivation: CellActiviationBehavior;
116+
readonly pointerType?: "mouse" | "touch" | "pen";
117+
}
118+
119+
/** The public event type the grid emits */
120+
export type CellActivatedEventArgs =
121+
| KeyboardCellActivatedEvent
122+
| PointerCellActivatedEvent;
123+
104124
export interface FillPatternEventArgs extends PreventableEvent {
105125
patternSource: Rectangle;
106126
fillDestination: Rectangle;

0 commit comments

Comments
 (0)