* fix(app): make the sidebar resize handle usable by touch The drag handle was a 10px strip at `right: -5`, so half of it sat outside the sidebar's bounds. Web hit-testing does not clip to the parent box, but iOS `hitTest:` and Gesture Handler's Android traversal both do, leaving a ~5px target with no visible affordance — hover is the only cue, and hover never fires on native. Split the handle by input capability rather than by platform: a fine pointer keeps the hairline straddling the border, and touch gets a 24x88 grab target that sits fully inside the sidebar with a permanent grip. The pan gesture now requires horizontal intent and fails on vertical drift, so a finger on the grip still scrolls the list behind it. `useHasFinePointer` replaces the copy of the same media query that lived in the review surface. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(app): simplify touch resize handle styles * fix(app): reveal touch resize grip while pressed Keep the touch grip out of sidebar content while idle, then expose it for the full resize gesture lifecycle so it remains visible during an active drag. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
/**
|
|
* Geometry for the sidebar resize handle.
|
|
*
|
|
* A mouse can grab a hairline, so the pointer variant straddles the sidebar
|
|
* border — half the strip hangs outside the sidebar's own bounds, which is
|
|
* fine because web hit-testing does not clip to the parent box.
|
|
*
|
|
* Native hit-testing does clip: iOS `hitTest:` returns nil for points outside a
|
|
* view's bounds and Gesture Handler's Android traversal only recurses into
|
|
* children whose ancestors contain the point. A strip hanging outside the
|
|
* sidebar is dead on touch, and what remains is far too small for a finger.
|
|
* So the touch variant sits fully inside the sidebar and is sized for a thumb.
|
|
*/
|
|
|
|
export const POINTER_HANDLE_WIDTH = 10;
|
|
export const TOUCH_HANDLE_WIDTH = 24;
|
|
export const TOUCH_HANDLE_HEIGHT = 88;
|
|
|
|
/**
|
|
* A finger resting on the grip drifts before it commits. Requiring horizontal
|
|
* intent before the pan activates, and failing it once the drift is mostly
|
|
* vertical, leaves the sidebar's own scrolling reachable through the grip.
|
|
* A mouse press moves less than either threshold, so pointer drags are
|
|
* unaffected.
|
|
*/
|
|
export const SIDEBAR_RESIZE_ACTIVATION_OFFSET = 6;
|
|
export const SIDEBAR_RESIZE_FAIL_OFFSET = 12;
|
|
|
|
export type SidebarResizeEdge = "left" | "right";
|
|
|
|
export interface SidebarResizeHandleGeometry {
|
|
/**
|
|
* Distance from the sidebar edge to the near side of the hit area. Negative
|
|
* means the hit area overhangs the sidebar, which only touch forbids.
|
|
*/
|
|
edgeOffset: number;
|
|
width: number;
|
|
/** `null` stretches the hit area to the sidebar's full height. */
|
|
height: number | null;
|
|
}
|
|
|
|
export function resolveSidebarResizeHandleGeometry(
|
|
finePointer: boolean,
|
|
): SidebarResizeHandleGeometry {
|
|
if (finePointer) {
|
|
return {
|
|
edgeOffset: -POINTER_HANDLE_WIDTH / 2,
|
|
width: POINTER_HANDLE_WIDTH,
|
|
height: null,
|
|
};
|
|
}
|
|
return {
|
|
edgeOffset: 0,
|
|
width: TOUCH_HANDLE_WIDTH,
|
|
height: TOUCH_HANDLE_HEIGHT,
|
|
};
|
|
}
|