fix: address widget review feedback
This commit is contained in:
1 parent
b1b17ad69f
commit
a38e731209
79 files changed
+2138
-244
No files matched your search
+4
@@ -0,0 +1,4 @@
|
||||
.suppressTouchCallout {
|
||||
user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
+37
-7
@@ -17,6 +17,10 @@ import { ItemSelectModal } from "../../items/item-select-modal";
|
||||
import { useBoardPermissions } from "../../permissions/client";
|
||||
import type { BoardPlacementHint } from "../../items/actions/create-item";
|
||||
import type { UseGridstackRefs } from "./use-gridstack";
|
||||
import classes from "./board-empty-space-context-menu.module.css";
|
||||
import { hasExceededLongPressMoveTolerance } from "./long-press";
|
||||
|
||||
const suppressTouchCalloutClass = classes.suppressTouchCallout as string;
|
||||
|
||||
interface MenuState {
|
||||
x: number;
|
||||
@@ -39,18 +43,25 @@ export const BoardEmptySpaceContextMenu = ({ section, refs, children, ...boxProp
|
||||
const [isEditMode] = useEditMode();
|
||||
const [menu, setMenu] = useState<MenuState | null>(null);
|
||||
const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const longPressPointer = useRef<{ pointerId: number; x: number; y: number } | null>(null);
|
||||
const touchGuardElement = useRef<HTMLDivElement | null>(null);
|
||||
const returnFocusRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
const enabled = hasChangeAccess && settings.enableRightClickOnWidgets;
|
||||
const clearLongPress = () => {
|
||||
if (longPressTimer.current === null) return;
|
||||
clearTimeout(longPressTimer.current);
|
||||
longPressTimer.current = null;
|
||||
if (longPressTimer.current !== null) {
|
||||
clearTimeout(longPressTimer.current);
|
||||
longPressTimer.current = null;
|
||||
}
|
||||
touchGuardElement.current?.classList.remove(suppressTouchCalloutClass);
|
||||
touchGuardElement.current = null;
|
||||
longPressPointer.current = null;
|
||||
};
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (longPressTimer.current !== null) clearTimeout(longPressTimer.current);
|
||||
touchGuardElement.current?.classList.remove(suppressTouchCalloutClass);
|
||||
},
|
||||
[],
|
||||
);
|
||||
@@ -89,12 +100,31 @@ export const BoardEmptySpaceContextMenu = ({ section, refs, children, ...boxProp
|
||||
const placement = resolvePlacement(event.target, event.pageX, event.pageY);
|
||||
if (!placement) return;
|
||||
clearLongPress();
|
||||
longPressPointer.current = { pointerId: event.pointerId, x: event.clientX, y: event.clientY };
|
||||
touchGuardElement.current = event.currentTarget;
|
||||
touchGuardElement.current.classList.add(suppressTouchCalloutClass);
|
||||
longPressTimer.current = setTimeout(() => {
|
||||
setMenu({ x: event.clientX, y: event.clientY, placement });
|
||||
navigator.vibrate?.(20);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const handlePointerMove = (event: PointerEvent<HTMLDivElement>) => {
|
||||
const pointer = longPressPointer.current;
|
||||
if (
|
||||
!pointer ||
|
||||
pointer.pointerId !== event.pointerId ||
|
||||
!hasExceededLongPressMoveTolerance(pointer, { x: event.clientX, y: event.clientY })
|
||||
) {
|
||||
return;
|
||||
}
|
||||
clearLongPress();
|
||||
};
|
||||
|
||||
const handlePointerEnd = (event: PointerEvent<HTMLDivElement>) => {
|
||||
if (longPressPointer.current?.pointerId === event.pointerId) clearLongPress();
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
!enabled ||
|
||||
@@ -134,10 +164,10 @@ export const BoardEmptySpaceContextMenu = ({ section, refs, children, ...boxProp
|
||||
onContextMenu={handleContextMenu}
|
||||
onKeyDown={handleKeyDown}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMove={clearLongPress}
|
||||
onPointerUp={clearLongPress}
|
||||
onPointerCancel={clearLongPress}
|
||||
onPointerLeave={clearLongPress}
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerEnd}
|
||||
onPointerCancel={handlePointerEnd}
|
||||
onPointerLeave={handlePointerEnd}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { hasExceededLongPressMoveTolerance } from "./long-press";
|
||||
|
||||
describe("hasExceededLongPressMoveTolerance", () => {
|
||||
test("allows small touch jitter", () => {
|
||||
expect(hasExceededLongPressMoveTolerance({ x: 100, y: 100 }, { x: 106, y: 106 })).toBe(false);
|
||||
});
|
||||
|
||||
test("allows movement exactly at the threshold", () => {
|
||||
expect(hasExceededLongPressMoveTolerance({ x: 100, y: 100 }, { x: 106, y: 108 })).toBe(false);
|
||||
});
|
||||
|
||||
test("cancels a deliberate scroll gesture", () => {
|
||||
expect(hasExceededLongPressMoveTolerance({ x: 100, y: 100 }, { x: 100, y: 116 })).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export const longPressMoveTolerancePx = 10;
|
||||
|
||||
export const hasExceededLongPressMoveTolerance = (
|
||||
start: { x: number; y: number },
|
||||
current: { x: number; y: number },
|
||||
tolerance = longPressMoveTolerancePx,
|
||||
) => Math.hypot(current.x - start.x, current.y - start.y) > tolerance;
|
||||
Reference in new issue
Block a user