Make the sidebar resize handle usable by touch (#2953)
* 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>
This commit is contained in:
co-authored by
Claude Opus 5
Mohamed Boudra
parent
b9b3a24d58
commit
1e0ce69c54
@@ -22,10 +22,15 @@ import {
|
||||
} from "react-native";
|
||||
import { Gesture } from "react-native-gesture-handler";
|
||||
import Animated, { runOnJS, useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
||||
import { scheduleOnRN } from "react-native-worklets";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
|
||||
import { resolveDesktopSidebarWidth } from "@/components/desktop-sidebar-layout";
|
||||
import {
|
||||
SIDEBAR_RESIZE_ACTIVATION_OFFSET,
|
||||
SIDEBAR_RESIZE_FAIL_OFFSET,
|
||||
} from "@/components/sidebar-resize-handle-layout";
|
||||
import { HostPicker } from "@/components/hosts/host-picker";
|
||||
import { SidebarHeaderRow } from "@/components/sidebar/sidebar-header-row";
|
||||
import { SidebarDisplayPreferencesMenu } from "@/components/sidebar/display-preferences/menu";
|
||||
@@ -771,6 +776,9 @@ function DesktopSidebar({
|
||||
|
||||
const startWidthRef = useRef(visibleSidebarWidth);
|
||||
const resizeWidth = useSharedValue(visibleSidebarWidth);
|
||||
const [resizePressed, setResizePressed] = useState(false);
|
||||
const showResizeGrip = useCallback(() => setResizePressed(true), []);
|
||||
const hideResizeGrip = useCallback(() => setResizePressed(false), []);
|
||||
|
||||
useEffect(() => {
|
||||
resizeWidth.value = visibleSidebarWidth;
|
||||
@@ -780,8 +788,16 @@ function DesktopSidebar({
|
||||
() =>
|
||||
Gesture.Pan()
|
||||
.hitSlop({ left: 8, right: 8, top: 0, bottom: 0 })
|
||||
.onStart(() => {
|
||||
startWidthRef.current = visibleSidebarWidth;
|
||||
.onBegin(() => {
|
||||
scheduleOnRN(showResizeGrip);
|
||||
})
|
||||
// Horizontal intent only, so a finger dragging down the touch grip scrolls
|
||||
// the workspace list instead of resizing. Anchoring the start width to the
|
||||
// activation translation keeps the extra threshold from jumping the edge.
|
||||
.activeOffsetX([-SIDEBAR_RESIZE_ACTIVATION_OFFSET, SIDEBAR_RESIZE_ACTIVATION_OFFSET])
|
||||
.failOffsetY([-SIDEBAR_RESIZE_FAIL_OFFSET, SIDEBAR_RESIZE_FAIL_OFFSET])
|
||||
.onStart((event) => {
|
||||
startWidthRef.current = visibleSidebarWidth - event.translationX;
|
||||
resizeWidth.value = visibleSidebarWidth;
|
||||
})
|
||||
.onUpdate((event) => {
|
||||
@@ -794,8 +810,18 @@ function DesktopSidebar({
|
||||
})
|
||||
.onEnd(() => {
|
||||
runOnJS(setSidebarWidth)(resizeWidth.value);
|
||||
})
|
||||
.onFinalize(() => {
|
||||
scheduleOnRN(hideResizeGrip);
|
||||
}),
|
||||
[resizeWidth, setSidebarWidth, viewportWidth, visibleSidebarWidth],
|
||||
[
|
||||
hideResizeGrip,
|
||||
resizeWidth,
|
||||
setSidebarWidth,
|
||||
showResizeGrip,
|
||||
viewportWidth,
|
||||
visibleSidebarWidth,
|
||||
],
|
||||
);
|
||||
|
||||
const resizeAnimatedStyle = useAnimatedStyle(() => ({
|
||||
@@ -894,6 +920,7 @@ function DesktopSidebar({
|
||||
<SidebarResizeHandle
|
||||
edge="right"
|
||||
gesture={resizeGesture}
|
||||
pressed={resizePressed}
|
||||
testID="left-sidebar-resize-handle"
|
||||
/>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user