fix(sidebar): prevent text selection when dragging project/workspace rows

The drag handle was attached only to the left sub-view of each row, so
grabbing the right half initiated no drag, and pointerdown on the branch
or project title raced the browser's text-selection start before
dnd-kit's activation threshold — often producing a text selection
instead of a drag.

Move dragHandleProps to the outer row wrapper, add userSelect: none to
the row styles, and tighten PointerSensor activation distance.
This commit is contained in:
Mohamed Boudra
2026-04-20 10:49:46 +07:00
parent fb02bc8795
commit 002946d4dd
2 changed files with 22 additions and 15 deletions
@@ -142,7 +142,7 @@ export function DraggableList<T>({
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor, { useSensor(PointerSensor, {
activationConstraint: { activationConstraint: {
distance: 8, distance: 6,
}, },
}), }),
useSensor(KeyboardSensor, { useSensor(KeyboardSensor, {
@@ -830,12 +830,7 @@ function ProjectHeaderRow({
const rowChildren = ( const rowChildren = (
<> <>
<View <View style={styles.projectRowLeft}>
{...(dragHandleProps?.attributes as any)}
{...(dragHandleProps?.listeners as any)}
ref={dragHandleProps?.setActivatorNodeRef as any}
style={styles.projectRowLeft}
>
<ProjectLeadingVisual <ProjectLeadingVisual
displayName={displayName} displayName={displayName}
iconDataUri={iconDataUri} iconDataUri={iconDataUri}
@@ -912,7 +907,13 @@ function ProjectHeaderRow({
if (menuController) { if (menuController) {
return ( return (
<View onPointerEnter={() => setIsHovered(true)} onPointerLeave={() => setIsHovered(false)}> <View
{...(dragHandleProps?.attributes as any)}
{...(dragHandleProps?.listeners as any)}
ref={dragHandleProps?.setActivatorNodeRef as any}
onPointerEnter={() => setIsHovered(true)}
onPointerLeave={() => setIsHovered(false)}
>
<ContextMenuTrigger <ContextMenuTrigger
enabledOnMobile={false} enabledOnMobile={false}
style={({ pressed }) => [ style={({ pressed }) => [
@@ -935,7 +936,13 @@ function ProjectHeaderRow({
} }
return ( return (
<View onPointerEnter={() => setIsHovered(true)} onPointerLeave={() => setIsHovered(false)}> <View
{...(dragHandleProps?.attributes as any)}
{...(dragHandleProps?.listeners as any)}
ref={dragHandleProps?.setActivatorNodeRef as any}
onPointerEnter={() => setIsHovered(true)}
onPointerLeave={() => setIsHovered(false)}
>
<Pressable <Pressable
style={({ pressed }) => [ style={({ pressed }) => [
styles.projectRow, styles.projectRow,
@@ -1010,6 +1017,9 @@ function WorkspaceRowInner({
return ( return (
<WorkspaceHoverCard workspace={workspace} prHint={prHint} isDragging={isDragging}> <WorkspaceHoverCard workspace={workspace} prHint={prHint} isDragging={isDragging}>
<View <View
{...(dragHandleProps?.attributes as any)}
{...(dragHandleProps?.listeners as any)}
ref={dragHandleProps?.setActivatorNodeRef as any}
style={styles.workspaceRowContainer} style={styles.workspaceRowContainer}
onPointerEnter={() => setIsHovered(true)} onPointerEnter={() => setIsHovered(true)}
onPointerLeave={() => setIsHovered(false)} onPointerLeave={() => setIsHovered(false)}
@@ -1033,12 +1043,7 @@ function WorkspaceRowInner({
testID={`sidebar-workspace-row-${workspace.workspaceKey}`} testID={`sidebar-workspace-row-${workspace.workspaceKey}`}
> >
<View style={styles.workspaceRowMain}> <View style={styles.workspaceRowMain}>
<View <View style={styles.workspaceRowLeft}>
{...(dragHandleProps?.attributes as any)}
{...(dragHandleProps?.listeners as any)}
ref={dragHandleProps?.setActivatorNodeRef as any}
style={styles.workspaceRowLeft}
>
<WorkspaceStatusIndicator <WorkspaceStatusIndicator
bucket={workspace.statusBucket} bucket={workspace.statusBucket}
workspaceKind={workspace.workspaceKind} workspaceKind={workspace.workspaceKind}
@@ -2266,6 +2271,7 @@ const styles = StyleSheet.create((theme) => ({
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
gap: theme.spacing[2], gap: theme.spacing[2],
userSelect: "none",
}, },
projectRowHovered: { projectRowHovered: {
backgroundColor: theme.colors.surfaceSidebarHover, backgroundColor: theme.colors.surfaceSidebarHover,
@@ -2409,6 +2415,7 @@ const styles = StyleSheet.create((theme) => ({
alignItems: "stretch", alignItems: "stretch",
justifyContent: "center", justifyContent: "center",
gap: theme.spacing[1], gap: theme.spacing[1],
userSelect: "none",
}, },
workspaceRowMain: { workspaceRowMain: {
flexDirection: "row", flexDirection: "row",