fix(base): enable grid cell editing on touch devices

Cells could only enter edit mode via double-click or a physical keyboard,
so touch devices had no way to edit a cell. Treat a touch/pen tap as the
edit gesture, distinguishing a tap from a scroll by movement and branching
per pointer type so mouse double-click stays unchanged. Also reveal the
row expand button on hover-less devices so the row detail view stays
reachable.
This commit is contained in:
Philipinho
2026-06-16 12:14:21 +01:00
parent 4deb13e73e
commit ad00efe317
2 changed files with 52 additions and 1 deletions
@@ -1,4 +1,4 @@
import { memo, useCallback, useMemo } from "react";
import { memo, useCallback, useMemo, useRef } from "react";
import { Cell } from "@tanstack/react-table";
import { Popover, Tooltip } from "@mantine/core";
import { IconArrowsDiagonal } from "@tabler/icons-react";
@@ -24,6 +24,8 @@ import { useRowExpand } from "@/ee/base/context/row-expand";
import { RowNumberCell } from "./row-number-cell";
import classes from "@/ee/base/styles/grid.module.css";
const TOUCH_TAP_SLOP_PX = 10;
type GridCellProps = {
cell: Cell<IBaseRow, unknown>;
rowIndex: number;
@@ -72,6 +74,9 @@ export const GridCell = memo(function GridCell({
editingCell?.propertyId === property?.id &&
(editable || property?.type === "file");
const tapStartRef = useRef<{ x: number; y: number } | null>(null);
const suppressClickRef = useRef(false);
const handleDoubleClick = useCallback(() => {
if (!property || isRowNumber) return;
if (property.type === "checkbox") return;
@@ -102,6 +107,10 @@ export const GridCell = memo(function GridCell({
const handleClick = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
if (!property) return;
if (suppressClickRef.current) {
suppressClickRef.current = false;
return;
}
setFocusedCell({ rowId, propertyId: property.id });
(e.currentTarget.closest('[role="grid"]') as HTMLElement | null)?.focus({
preventScroll: true,
@@ -110,6 +119,38 @@ export const GridCell = memo(function GridCell({
[property, rowId, setFocusedCell],
);
const handlePointerDown = useCallback(
(e: React.PointerEvent<HTMLDivElement>) => {
if (e.pointerType === "mouse") return;
suppressClickRef.current = false;
tapStartRef.current = { x: e.clientX, y: e.clientY };
},
[],
);
const handlePointerUp = useCallback(
(e: React.PointerEvent<HTMLDivElement>) => {
if (e.pointerType === "mouse") return;
const start = tapStartRef.current;
tapStartRef.current = null;
if (!start) return;
if (
Math.abs(e.clientX - start.x) > TOUCH_TAP_SLOP_PX ||
Math.abs(e.clientY - start.y) > TOUCH_TAP_SLOP_PX
) {
return;
}
if ((e.target as HTMLElement).closest("button, a, input")) return;
suppressClickRef.current = true;
handleDoubleClick();
},
[handleDoubleClick],
);
const handlePointerCancel = useCallback(() => {
tapStartRef.current = null;
}, []);
const cellReadOnly = property
? readOnly || isSystemPropertyType(property.type)
: false;
@@ -200,6 +241,9 @@ export const GridCell = memo(function GridCell({
onClick={handleClick}
onMouseDown={handleMouseDown}
onDoubleClick={handleDoubleClick}
onPointerDown={handlePointerDown}
onPointerUp={handlePointerUp}
onPointerCancel={handlePointerCancel}
>
<CellComponent
value={value}
@@ -479,6 +479,13 @@
pointer-events: auto;
}
@media (hover: none) {
.rowExpandButton {
opacity: 1;
pointer-events: auto;
}
}
.rowExpandButton:hover {
background-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
color: light-dark(var(--mantine-color-blue-6), var(--mantine-color-blue-4));