From ad00efe31793d2096b4a8945dd24990c809b93f5 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:14:21 +0100 Subject: [PATCH] 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. --- .../src/ee/base/components/grid/grid-cell.tsx | 46 ++++++++++++++++++- .../client/src/ee/base/styles/grid.module.css | 7 +++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/apps/client/src/ee/base/components/grid/grid-cell.tsx b/apps/client/src/ee/base/components/grid/grid-cell.tsx index 6ba599e17..f587909f4 100644 --- a/apps/client/src/ee/base/components/grid/grid-cell.tsx +++ b/apps/client/src/ee/base/components/grid/grid-cell.tsx @@ -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; 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) => { 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) => { + if (e.pointerType === "mouse") return; + suppressClickRef.current = false; + tapStartRef.current = { x: e.clientX, y: e.clientY }; + }, + [], + ); + + const handlePointerUp = useCallback( + (e: React.PointerEvent) => { + 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} >