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
@@ -16,6 +16,7 @@ import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Shortcut } from "@/components/ui/shortcut";
|
||||
import { isWeb } from "@/constants/platform";
|
||||
import { useHasFinePointer } from "@/hooks/use-fine-pointer";
|
||||
import { inlineUnistylesStyle } from "@/styles/unistyles-inline-style";
|
||||
import type { Theme } from "@/styles/theme";
|
||||
import type { ShortcutKey } from "@/utils/format-shortcut";
|
||||
@@ -46,33 +47,6 @@ function getWebTextInputElement(input: TextInput | null): HTMLElement | null {
|
||||
return element instanceof HTMLElement ? element : null;
|
||||
}
|
||||
|
||||
function getCanShowReviewKeyboardHints(): boolean {
|
||||
if (!isWeb || typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
||||
return false;
|
||||
}
|
||||
return window.matchMedia("(hover: hover) and (pointer: fine)").matches;
|
||||
}
|
||||
|
||||
function useCanShowReviewKeyboardHints(): boolean {
|
||||
const [canShowHints, setCanShowHints] = useState(getCanShowReviewKeyboardHints);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isWeb || typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
const mediaQuery = window.matchMedia("(hover: hover) and (pointer: fine)");
|
||||
const handleChange = () => setCanShowHints(mediaQuery.matches);
|
||||
handleChange();
|
||||
mediaQuery.addEventListener?.("change", handleChange);
|
||||
return () => {
|
||||
mediaQuery.removeEventListener?.("change", handleChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return canShowHints;
|
||||
}
|
||||
|
||||
export const INLINE_REVIEW_COMMENT_HEIGHT = 72;
|
||||
export const INLINE_REVIEW_EDITOR_HEIGHT = 132;
|
||||
const INLINE_REVIEW_GAP = 6;
|
||||
@@ -533,7 +507,7 @@ export function InlineReviewEditor({
|
||||
const { t } = useTranslation();
|
||||
const inputRef = useRef<TextInput | null>(null);
|
||||
const focus = useWorkspaceFocusRestoration();
|
||||
const canShowKeyboardHints = useCanShowReviewKeyboardHints();
|
||||
const canShowKeyboardHints = useHasFinePointer();
|
||||
const [body, setBody] = useState(initialBody);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const trimmedBody = body.trim();
|
||||
|
||||
Reference in New Issue
Block a user