diff --git a/src/renderer/src/components/sidebar/SearchBar.tsx b/src/renderer/src/components/sidebar/SearchBar.tsx index 172606bb987..b49ec748c32 100644 --- a/src/renderer/src/components/sidebar/SearchBar.tsx +++ b/src/renderer/src/components/sidebar/SearchBar.tsx @@ -1,76 +1,14 @@ import React, { useCallback } from 'react' -import { Search, X, Activity, ListFilter, FolderPlus } from 'lucide-react' +import { Search, X } from 'lucide-react' import { useAppStore } from '@/store' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' -import { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger -} from '@/components/ui/dropdown-menu' -import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' -import { cn } from '@/lib/utils' -import RepoDotLabel from '@/components/repo/RepoDotLabel' const SearchBar = React.memo(function SearchBar() { const searchQuery = useAppStore((s) => s.searchQuery) const setSearchQuery = useAppStore((s) => s.setSearchQuery) - const showActiveOnly = useAppStore((s) => s.showActiveOnly) - const setShowActiveOnly = useAppStore((s) => s.setShowActiveOnly) - const filterRepoIds = useAppStore((s) => s.filterRepoIds) - const setFilterRepoIds = useAppStore((s) => s.setFilterRepoIds) - const repos = useAppStore((s) => s.repos) - const addRepo = useAppStore((s) => s.addRepo) - const selectedRepos = repos.filter((r) => filterRepoIds.includes(r.id)) - - const handleToggleRepo = useCallback( - (repoId: string) => { - setFilterRepoIds( - filterRepoIds.includes(repoId) - ? filterRepoIds.filter((id) => id !== repoId) - : [...filterRepoIds, repoId] - ) - }, - [filterRepoIds, setFilterRepoIds] - ) const handleClear = useCallback(() => setSearchQuery(''), [setSearchQuery]) - const handleToggleActive = useCallback( - () => setShowActiveOnly(!showActiveOnly), - [showActiveOnly, setShowActiveOnly] - ) - const canFilterRepos = repos.length > 1 - const hasRepoFilter = canFilterRepos && selectedRepos.length > 0 - const hasAnyFilter = showActiveOnly || hasRepoFilter - const activeFilterCount = (showActiveOnly ? 1 : 0) + (hasRepoFilter ? selectedRepos.length : 0) - - const filterSummary = (() => { - if (!hasAnyFilter) { - return null - } - if (showActiveOnly && !hasRepoFilter) { - return ( - - - Active - - ) - } - if (!showActiveOnly && hasRepoFilter && selectedRepos.length === 1) { - return ( - - ) - } - return {activeFilterCount} filters - })() return (
@@ -83,96 +21,18 @@ const SearchBar = React.memo(function SearchBar() { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder="Search..." - className="h-7 pl-7 pr-20 text-[12px] border-none bg-muted/50 shadow-none focus-visible:ring-1 focus-visible:ring-ring/30 placeholder:text-muted-foreground/70" + className="h-7 pl-7 pr-8 text-[12px] border-none bg-muted/50 shadow-none focus-visible:ring-1 focus-visible:ring-ring/30 placeholder:text-muted-foreground/70" /> -
- {searchQuery && ( - - )} - - - - - - - - - {hasAnyFilter ? 'Edit filters' : 'Filter worktrees'} - - - - Status - event.preventDefault()} - > - - Active only - - {canFilterRepos && ( - <> - - Repositories - {repos.map((r) => ( - handleToggleRepo(r.id)} - onSelect={(event) => event.preventDefault()} - > - - - ))} - - )} - {hasAnyFilter && ( - <> - - { - setShowActiveOnly(false) - setFilterRepoIds([]) - }} - > - - Clear filters - - - )} - {canFilterRepos && ( - <> - - { - addRepo() - }} - > - - Add project - - - )} - - -
+ {searchQuery && ( + + )}
) diff --git a/src/renderer/src/components/sidebar/SidebarFilter.tsx b/src/renderer/src/components/sidebar/SidebarFilter.tsx new file mode 100644 index 00000000000..6de1675a495 --- /dev/null +++ b/src/renderer/src/components/sidebar/SidebarFilter.tsx @@ -0,0 +1,140 @@ +import React, { useCallback } from 'react' +import { Activity, ListFilter, FolderPlus, X } from 'lucide-react' +import { useAppStore } from '@/store' +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from '@/components/ui/dropdown-menu' +import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' +import RepoDotLabel from '@/components/repo/RepoDotLabel' + +const SidebarFilter = React.memo(function SidebarFilter() { + const showActiveOnly = useAppStore((s) => s.showActiveOnly) + const setShowActiveOnly = useAppStore((s) => s.setShowActiveOnly) + const filterRepoIds = useAppStore((s) => s.filterRepoIds) + const setFilterRepoIds = useAppStore((s) => s.setFilterRepoIds) + const repos = useAppStore((s) => s.repos) + const addRepo = useAppStore((s) => s.addRepo) + + const handleToggleRepo = useCallback( + (repoId: string) => { + setFilterRepoIds( + filterRepoIds.includes(repoId) + ? filterRepoIds.filter((id) => id !== repoId) + : [...filterRepoIds, repoId] + ) + }, + [filterRepoIds, setFilterRepoIds] + ) + + const handleToggleActive = useCallback( + () => setShowActiveOnly(!showActiveOnly), + [showActiveOnly, setShowActiveOnly] + ) + const canFilterRepos = repos.length > 1 + // Why: derive from the current repos list so stale IDs in filterRepoIds + // (e.g. lingering after a repo is removed) don't inflate the active-filter + // count or falsely signal an applied filter. + const selectedRepos = canFilterRepos ? repos.filter((r) => filterRepoIds.includes(r.id)) : [] + const hasRepoFilter = selectedRepos.length > 0 + const hasAnyFilter = showActiveOnly || hasRepoFilter + const activeFilterCount = (showActiveOnly ? 1 : 0) + selectedRepos.length + + return ( + + + + + + + + + {hasAnyFilter ? 'Edit filters' : 'Filter workspaces'} + + + + Status + event.preventDefault()} + > + + Active only + + {canFilterRepos && ( + <> + + Repositories + {repos.map((r) => ( + handleToggleRepo(r.id)} + onSelect={(event) => event.preventDefault()} + > + + + ))} + + )} + {hasAnyFilter && ( + <> + + { + setShowActiveOnly(false) + setFilterRepoIds([]) + }} + > + + Clear filters + + + )} + {canFilterRepos && ( + <> + + { + addRepo() + }} + > + + Add project + + + )} + + + ) +}) + +export default SidebarFilter diff --git a/src/renderer/src/components/sidebar/SidebarHeader.tsx b/src/renderer/src/components/sidebar/SidebarHeader.tsx index 3aceafaefe7..3afe12f8309 100644 --- a/src/renderer/src/components/sidebar/SidebarHeader.tsx +++ b/src/renderer/src/components/sidebar/SidebarHeader.tsx @@ -16,6 +16,7 @@ import { DropdownMenuRadioItem } from '@/components/ui/dropdown-menu' import type { WorktreeCardProperty } from '../../../../shared/types' +import SidebarFilter from './SidebarFilter' const GROUP_BY_OPTIONS = [ { id: 'none', label: 'All' }, @@ -72,6 +73,7 @@ const SidebarHeader = React.memo(function SidebarHeader() { Workspaces
+