From 8008ea69dd227f7b98c89d3d4bdef285ccb28244 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 13 Apr 2026 13:39:35 +0700 Subject: [PATCH] fix: restore wrapper View for sidebar hover stability The wrapper View with onPointerEnter/onPointerLeave is needed to keep hover state stable when the mouse moves from the row to the kebab dropdown (which opens in an overlay outside the Pressable). Without it, the Pressable fires onHoverOut and the kebab disappears. On native, isNative makes controls always-visible so this is web-only behavior which is correct (onPointerEnter works on web). --- .../src/components/sidebar-workspace-list.tsx | 273 +++++++++--------- 1 file changed, 138 insertions(+), 135 deletions(-) diff --git a/packages/app/src/components/sidebar-workspace-list.tsx b/packages/app/src/components/sidebar-workspace-list.tsx index ec4daa8a4..0e88ce3d8 100644 --- a/packages/app/src/components/sidebar-workspace-list.tsx +++ b/packages/app/src/components/sidebar-workspace-list.tsx @@ -851,8 +851,31 @@ function ProjectHeaderRow({ if (menuController) { return ( - setIsHovered(true)} onPointerLeave={() => setIsHovered(false)}> + [ + styles.projectRow, + isDragging && styles.projectRowDragging, + selected && styles.sidebarRowSelected, + isHovered && styles.projectRowHovered, + pressed && styles.projectRowPressed, + ]} + onPressIn={interaction.handlePressIn} + onTouchMove={interaction.handleTouchMove} + onPressOut={interaction.handlePressOut} + onPress={handlePress} + testID={`sidebar-project-row-${project.projectKey}`} + > + {rowChildren} + + + ); + } + + return ( + setIsHovered(true)} onPointerLeave={() => setIsHovered(false)}> + [ styles.projectRow, isDragging && styles.projectRowDragging, @@ -860,8 +883,6 @@ function ProjectHeaderRow({ isHovered && styles.projectRowHovered, pressed && styles.projectRowPressed, ]} - onHoverIn={() => setIsHovered(true)} - onHoverOut={() => setIsHovered(false)} onPressIn={interaction.handlePressIn} onTouchMove={interaction.handleTouchMove} onPressOut={interaction.handlePressOut} @@ -869,29 +890,8 @@ function ProjectHeaderRow({ testID={`sidebar-project-row-${project.projectKey}`} > {rowChildren} - - ); - } - - return ( - [ - styles.projectRow, - isDragging && styles.projectRowDragging, - selected && styles.sidebarRowSelected, - isHovered && styles.projectRowHovered, - pressed && styles.projectRowPressed, - ]} - onHoverIn={() => setIsHovered(true)} - onHoverOut={() => setIsHovered(false)} - onPressIn={interaction.handlePressIn} - onTouchMove={interaction.handleTouchMove} - onPressOut={interaction.handlePressOut} - onPress={handlePress} - testID={`sidebar-project-row-${project.projectKey}`} - > - {rowChildren} - + + ); } @@ -937,118 +937,121 @@ function WorkspaceRowInner({ }, [interaction.didLongPressRef, onPress]); return ( - [ - styles.workspaceRowContainer, - styles.workspaceRow, - isDragging && styles.workspaceRowDragging, - selected && styles.sidebarRowSelected, - isHovered && styles.workspaceRowHovered, - pressed && styles.workspaceRowPressed, - ]} - onHoverIn={() => setIsHovered(true)} - onHoverOut={() => setIsHovered(false)} - onPressIn={interaction.handlePressIn} - onTouchMove={interaction.handleTouchMove} - onPressOut={interaction.handlePressOut} - onPress={handlePress} - testID={`sidebar-workspace-row-${workspace.workspaceKey}`} + setIsHovered(true)} + onPointerLeave={() => setIsHovered(false)} > - - - - [ + styles.workspaceRow, + isDragging && styles.workspaceRowDragging, + selected && styles.sidebarRowSelected, + isHovered && styles.workspaceRowHovered, + pressed && styles.workspaceRowPressed, + ]} + onPressIn={interaction.handlePressIn} + onTouchMove={interaction.handleTouchMove} + onPressOut={interaction.handlePressOut} + onPress={handlePress} + testID={`sidebar-workspace-row-${workspace.workspaceKey}`} + > + + - {workspace.name} - - - - {isCreating ? Creating... : null} - {onArchive && (isHovered || platformIsNative || isCompact) ? ( - - [ - styles.kebabButton, - hovered && styles.kebabButtonHovered, - ]} - accessibilityRole="button" - accessibilityLabel="Workspace actions" - testID={`sidebar-workspace-kebab-${workspace.workspaceKey}`} - > - {({ hovered }) => ( - - )} - - - {onCopyPath ? ( - } - onSelect={onCopyPath} - > - Copy path - - ) : null} - {onCopyBranchName ? ( - } - onSelect={onCopyBranchName} - > - Copy branch name - - ) : null} - } - trailing={archiveShortcutKeys ? : null} - status={archiveStatus} - pendingLabel={archivePendingLabel} - onSelect={onArchive} + + + {workspace.name} + + + + {isCreating ? Creating... : null} + {onArchive && (isHovered || platformIsNative || isCompact) ? ( + + [ + styles.kebabButton, + hovered && styles.kebabButtonHovered, + ]} + accessibilityRole="button" + accessibilityLabel="Workspace actions" + testID={`sidebar-workspace-kebab-${workspace.workspaceKey}`} > - {archiveLabel ?? "Archive"} - - - - ) : workspace.diffStat ? ( - - +{workspace.diffStat.additions} - -{workspace.diffStat.deletions} - - ) : null} - {showShortcutBadge && shortcutNumber !== null ? ( - - {shortcutNumber} - - ) : null} + {({ hovered }) => ( + + )} + + + {onCopyPath ? ( + } + onSelect={onCopyPath} + > + Copy path + + ) : null} + {onCopyBranchName ? ( + } + onSelect={onCopyBranchName} + > + Copy branch name + + ) : null} + } + trailing={archiveShortcutKeys ? : null} + status={archiveStatus} + pendingLabel={archivePendingLabel} + onSelect={onArchive} + > + {archiveLabel ?? "Archive"} + + + + ) : workspace.diffStat ? ( + + +{workspace.diffStat.additions} + -{workspace.diffStat.deletions} + + ) : null} + {showShortcutBadge && shortcutNumber !== null ? ( + + {shortcutNumber} + + ) : null} + - - {prHint ? ( - - - - ) : null} - + {prHint ? ( + + + + ) : null} + + ); }