BranchSelector.tsx105 lines · main
1import { Check, GitMerge, Shield } from 'lucide-react'
2import { useState } from 'react'
3import {
4 Command,
5 CommandEmpty,
6 CommandGroup,
7 CommandInput,
8 CommandItem,
9 CommandList,
10 Popover,
11 PopoverContent,
12 PopoverTrigger,
13 ScrollArea,
14} from 'ui'
15
16import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
17import { Branch } from '@/data/branches/branches-query'
18
19interface BranchSelectorProps {
20 branches: Branch[]
21 onBranchSelected?: (branch: Branch) => void
22 disabled?: boolean
23 isUpdating?: boolean
24 type?: 'primary' | 'outline'
25 align?: 'end' | 'center'
26}
27
28export const BranchSelector = ({
29 branches,
30 onBranchSelected,
31 disabled = false,
32 isUpdating = false,
33 type = 'primary',
34 align = 'end',
35}: BranchSelectorProps) => {
36 const [open, setOpen] = useState(false)
37 const [selectedBranch, setSelectedBranch] = useState<Branch | null>(null)
38
39 const availableBranches = branches.filter((branch) => !branch.is_default)
40
41 const handleBranchSelect = (branch: Branch) => {
42 setSelectedBranch(branch)
43 setOpen(false)
44 onBranchSelected?.(branch)
45 }
46
47 return (
48 <Popover open={open} onOpenChange={setOpen} modal={false}>
49 <PopoverTrigger asChild>
50 <ButtonTooltip
51 icon={<GitMerge size={14} strokeWidth={1.5} />}
52 type={type}
53 disabled={
54 disabled || isUpdating || branches.length === 0 || availableBranches.length === 0
55 }
56 tooltip={{
57 content: {
58 side: 'bottom',
59 align,
60 text:
61 branches.length === 0 || availableBranches.length === 0
62 ? 'Create a branch first to start a merge request'
63 : undefined,
64 },
65 }}
66 >
67 {isUpdating ? 'Creating...' : 'New merge request'}
68 </ButtonTooltip>
69 </PopoverTrigger>
70 <PopoverContent className="p-0 w-80" side="bottom" align="end">
71 <Command>
72 <CommandInput placeholder="Find branch to review..." />
73 <CommandList>
74 <CommandEmpty>No available branches found</CommandEmpty>
75 <CommandGroup>
76 <ScrollArea className="max-h-[210px] overflow-y-auto">
77 {availableBranches.map((branch) => (
78 <CommandItem
79 key={branch.id}
80 value={branch.name.replaceAll('"', '')}
81 className="cursor-pointer w-full flex items-center justify-between"
82 onSelect={() => handleBranchSelect(branch)}
83 disabled={isUpdating || !!branch.git_branch || !!branch.review_requested_at}
84 >
85 <div className="flex items-center gap-2">
86 {branch.is_default && <Shield size={14} className="text-amber-900" />}
87 <span className="truncate" title={branch.name}>
88 {branch.name}
89 </span>
90 </div>
91 {selectedBranch?.id === branch.id && (
92 <Check size={14} strokeWidth={1.5} className="text-brand" />
93 )}
94 {branch.git_branch && <span>Synced to a Git branch</span>}
95 {branch.review_requested_at && <span>Merge request opened</span>}
96 </CommandItem>
97 ))}
98 </ScrollArea>
99 </CommandGroup>
100 </CommandList>
101 </Command>
102 </PopoverContent>
103 </Popover>
104 )
105}