Can't navigate using keyboard when searching branch (vibe-kanban) (#495)

* Cleanup script changes for task attempt 69217bab-8e49-498d-b892-6e720f7c6026

* Commit changes from coding agent for task attempt 69217bab-8e49-498d-b892-6e720f7c6026

* Cleanup script changes for task attempt 69217bab-8e49-498d-b892-6e720f7c6026
This commit is contained in:
Louis Knight-Webb
2025-08-15 17:48:09 +01:00
committed by GitHub
parent 2d16f1e9b6
commit 805de837b4

View File

@@ -1,4 +1,4 @@
import { useState, useMemo, useRef } from 'react';
import { useState, useMemo, useRef, useEffect } from 'react';
import { Button } from '@/components/ui/button.tsx';
import { ArrowDown, GitBranch as GitBranchIcon, Search } from 'lucide-react';
import {
@@ -35,7 +35,10 @@ function BranchSelector({
excludeCurrentBranch = false,
}: Props) {
const [branchSearchTerm, setBranchSearchTerm] = useState('');
const [highlighted, setHighlighted] = useState<number | null>(null);
const [open, setOpen] = useState(false);
const searchInputRef = useRef<HTMLInputElement>(null);
const itemRefs = useRef<Array<HTMLDivElement | null>>([]);
// Filter branches based on search term and options
const filteredBranches = useMemo(() => {
@@ -65,10 +68,44 @@ function BranchSelector({
const handleBranchSelect = (branchName: string) => {
onBranchSelect(branchName);
setBranchSearchTerm('');
setHighlighted(null);
setOpen(false);
};
const moveHighlight = (delta: 1 | -1) => {
if (filteredBranches.length === 0) return;
setHighlighted((prev) => {
const next =
prev === null
? delta === 1
? 0
: filteredBranches.length - 1
: (prev + delta + filteredBranches.length) % filteredBranches.length;
// Focus the matching item for scroll behavior
setTimeout(
() => itemRefs.current[next]?.scrollIntoView({ block: 'nearest' }),
0
);
return next;
});
};
// Reset highlight when filtered branches change
useEffect(() => {
if (highlighted !== null && highlighted >= filteredBranches.length) {
setHighlighted(null);
}
}, [filteredBranches, highlighted]);
// Reset highlight when search changes
useEffect(() => {
setHighlighted(null);
}, [branchSearchTerm]);
return (
<DropdownMenu>
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
@@ -93,8 +130,39 @@ function BranchSelector({
onChange={(e) => setBranchSearchTerm(e.target.value)}
className="pl-8"
onKeyDown={(e) => {
// Prevent the dropdown from closing when typing
e.stopPropagation();
// Handle keyboard navigation
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
e.stopPropagation();
moveHighlight(1);
break;
case 'ArrowUp':
e.preventDefault();
e.stopPropagation();
moveHighlight(-1);
break;
case 'Enter':
if (highlighted !== null && filteredBranches[highlighted]) {
e.preventDefault();
e.stopPropagation();
const branch = filteredBranches[highlighted];
const isCurrentAndExcluded =
excludeCurrentBranch && branch.is_current;
if (!isCurrentAndExcluded) {
handleBranchSelect(branch.name);
}
}
break;
case 'Escape':
e.preventDefault();
e.stopPropagation();
setOpen(false);
break;
default:
// Prevent dropdown from closing when typing
e.stopPropagation();
}
}}
autoFocus
/>
@@ -107,22 +175,25 @@ function BranchSelector({
No branches found
</div>
) : (
filteredBranches.map((branch) => {
filteredBranches.map((branch, idx) => {
const isCurrentAndExcluded =
excludeCurrentBranch && branch.is_current;
const isHighlighted = idx === highlighted;
const menuItem = (
<DropdownMenuItem
key={branch.name}
ref={(el) => (itemRefs.current[idx] = el)}
onClick={() => {
if (!isCurrentAndExcluded) {
handleBranchSelect(branch.name);
}
}}
onMouseEnter={() => setHighlighted(idx)}
disabled={isCurrentAndExcluded}
className={`${selectedBranch === branch.name ? 'bg-accent' : ''} ${
isCurrentAndExcluded ? 'opacity-50 cursor-not-allowed' : ''
}`}
} ${isHighlighted ? 'bg-muted' : ''}`}
>
<div className="flex items-center justify-between w-full">
<span className={branch.is_current ? 'font-medium' : ''}>