fix(dnd): mobile item reorder works via long-press instead of scrolling
PointerSensor also receives touch-derived pointer events and its 5px distance constraint fired before the TouchSensor's hold delay - the browser claimed the gesture for scrolling (pointercancel) and the drag died. Replaced with MouseSensor (desktop unchanged), TouchSensor now activates on a 300ms hold (tolerance 8px), and all drag handles carry touch-action: none so the browser never starts a scroll from them. Applies to DocumentItemsEditor (offers/issued orders) and the invoice items list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
MouseSensor,
|
||||
TouchSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
@@ -170,7 +170,11 @@ function SortableItemRow({
|
||||
{...listeners}
|
||||
title="Přetáhnout"
|
||||
aria-label="Přetáhnout"
|
||||
sx={{ cursor: "grab", color: "text.secondary" }}
|
||||
sx={{
|
||||
cursor: "grab",
|
||||
color: "text.secondary",
|
||||
touchAction: "none",
|
||||
}}
|
||||
>
|
||||
{DragIcon}
|
||||
</IconButton>
|
||||
@@ -316,7 +320,11 @@ function SortableItemRow({
|
||||
{...listeners}
|
||||
title="Přetáhnout"
|
||||
aria-label="Přetáhnout"
|
||||
sx={{ cursor: "grab", color: "text.secondary" }}
|
||||
sx={{
|
||||
cursor: "grab",
|
||||
color: "text.secondary",
|
||||
touchAction: "none",
|
||||
}}
|
||||
>
|
||||
{DragIcon}
|
||||
</IconButton>
|
||||
@@ -455,10 +463,16 @@ export default function DocumentItemsEditor({
|
||||
}: DocumentItemsEditorProps) {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||
// MouseSensor (NOT PointerSensor): PointerSensor also receives touch-derived
|
||||
// pointer events and its 5px distance constraint fires before the
|
||||
// TouchSensor's long-press delay — the browser then claims the gesture for
|
||||
// scrolling (pointercancel) and the drag dies. Mouse handles desktop,
|
||||
// TouchSensor handles touch via long-press; the drag handles additionally
|
||||
// carry touch-action: none so the browser never starts a scroll from them.
|
||||
const dndSensors = useSensors(
|
||||
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
||||
useSensor(MouseSensor, { activationConstraint: { distance: 5 } }),
|
||||
useSensor(TouchSensor, {
|
||||
activationConstraint: { delay: 200, tolerance: 5 },
|
||||
activationConstraint: { delay: 300, tolerance: 8 },
|
||||
}),
|
||||
useSensor(KeyboardSensor),
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
MouseSensor,
|
||||
TouchSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
@@ -278,7 +278,11 @@ function SortableInvoiceRow({
|
||||
{...listeners}
|
||||
title="Přetáhnout"
|
||||
aria-label="Přetáhnout"
|
||||
sx={{ cursor: "grab", color: "text.secondary" }}
|
||||
sx={{
|
||||
cursor: "grab",
|
||||
color: "text.secondary",
|
||||
touchAction: "none",
|
||||
}}
|
||||
>
|
||||
{DragIcon}
|
||||
</IconButton>
|
||||
@@ -388,7 +392,7 @@ function SortableInvoiceRow({
|
||||
{...listeners}
|
||||
title="Přetáhnout"
|
||||
aria-label="Přetáhnout"
|
||||
sx={{ cursor: "grab", color: "text.secondary" }}
|
||||
sx={{ cursor: "grab", color: "text.secondary", touchAction: "none" }}
|
||||
>
|
||||
{DragIcon}
|
||||
</IconButton>
|
||||
@@ -511,10 +515,16 @@ export default function InvoiceDetail() {
|
||||
[],
|
||||
);
|
||||
|
||||
// MouseSensor (NOT PointerSensor): PointerSensor also receives touch-derived
|
||||
// pointer events and its 5px distance constraint fires before the
|
||||
// TouchSensor's long-press delay — the browser then claims the gesture for
|
||||
// scrolling (pointercancel) and the drag dies. Mouse handles desktop,
|
||||
// TouchSensor handles touch via long-press; the drag handles additionally
|
||||
// carry touch-action: none so the browser never starts a scroll from them.
|
||||
const dndSensors = useSensors(
|
||||
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
||||
useSensor(MouseSensor, { activationConstraint: { distance: 5 } }),
|
||||
useSensor(TouchSensor, {
|
||||
activationConstraint: { delay: 200, tolerance: 5 },
|
||||
activationConstraint: { delay: 300, tolerance: 8 },
|
||||
}),
|
||||
useSensor(KeyboardSensor),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user