fix(accounts): fire drag haptic on first tap, not after dragging (#576)

## What

On mobile, reordering accounts (dashboard + accounts page) triggered the
selection haptic only **after** the drag actually started — i.e. after
the `PointerSensor`'s 8px activation distance was met. This felt like
you had to long-press/drag before the vibration kicked in.

This moves the haptic to the drag handle's `pointerdown`, so it fires
the moment the handle is touched. dnd-kit's own `onPointerDown` is
chained afterwards so dragging keeps working.

## Changes

- `SortableGrid`: drop `onDragStart` haptic on `DndContext`.
- `SortableItem`: accept an `onActivate` callback and fire it on the
handle's `onPointerDown`, then delegate to dnd-kit's listener.

## Testing

Haptics rely on `web-haptics` (`navigator.vibrate`), which doesn't run
in jsdom, so this needs a manual check on a real mobile device: tapping
the drag handle should vibrate immediately.
This commit is contained in:
Víctor Falcón 2026-06-22 09:09:46 +02:00 committed by GitHub
parent cd3080ec52
commit c75e834b89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 11 additions and 2 deletions

View File

@ -76,13 +76,16 @@ export function SortableGrid<T>({
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={() => trigger('selection')}
onDragEnd={handleDragEnd}
>
<SortableContext items={ids} strategy={rectSortingStrategy}>
<div className={className}>
{items.map((item) => (
<SortableItem key={getId(item)} id={getId(item)}>
<SortableItem
key={getId(item)}
id={getId(item)}
onActivate={() => trigger('selection')}
>
{(dragHandle) => renderItem(item, dragHandle)}
</SortableItem>
))}
@ -95,9 +98,11 @@ export function SortableGrid<T>({
function SortableItem({
id,
onActivate,
children,
}: {
id: string;
onActivate: () => void;
children: (dragHandle: ReactNode) => ReactNode;
}) {
const {
@ -118,6 +123,10 @@ function SortableItem({
className="cursor-grab touch-none text-muted-foreground transition-colors hover:text-foreground active:cursor-grabbing"
{...attributes}
{...listeners}
onPointerDown={(event) => {
onActivate();
listeners?.onPointerDown?.(event);
}}
>
<GripVertical className="size-5" />
</button>