Add minimize buttons to FilterBar and Newest Scripts sections

- Add minimize/collapse functionality to FilterBar component
- Add minimize/collapse functionality to Newest Scripts section
- Hide Newest Scripts section when user is searching, filtering, or viewing a category
- Both sections can now be minimized to save screen space
This commit is contained in:
Michel Roegl-Brunner
2025-11-07 12:58:22 +01:00
parent 6bd402abea
commit 45ba67c827
2 changed files with 126 additions and 71 deletions

View File

@@ -41,6 +41,7 @@ export function FilterBar({
}: FilterBarProps) { }: FilterBarProps) {
const [isTypeDropdownOpen, setIsTypeDropdownOpen] = useState(false); const [isTypeDropdownOpen, setIsTypeDropdownOpen] = useState(false);
const [isSortDropdownOpen, setIsSortDropdownOpen] = useState(false); const [isSortDropdownOpen, setIsSortDropdownOpen] = useState(false);
const [isMinimized, setIsMinimized] = useState(false);
const updateFilters = (updates: Partial<FilterState>) => { const updateFilters = (updates: Partial<FilterState>) => {
onFiltersChange({ ...filters, ...updates }); onFiltersChange({ ...filters, ...updates });
@@ -98,10 +99,36 @@ export function FilterBar({
{!isLoadingFilters && ( {!isLoadingFilters && (
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<h3 className="text-lg font-medium text-foreground">Filter Scripts</h3> <h3 className="text-lg font-medium text-foreground">Filter Scripts</h3>
<div className="flex items-center gap-2">
<ContextualHelpIcon section="available-scripts" tooltip="Help with filtering and searching" /> <ContextualHelpIcon section="available-scripts" tooltip="Help with filtering and searching" />
<Button
onClick={() => setIsMinimized(!isMinimized)}
variant="ghost"
size="icon"
className="h-8 w-8 text-muted-foreground hover:text-foreground"
title={isMinimized ? "Expand filters" : "Minimize filters"}
>
<svg
className={`h-4 w-4 transition-transform ${isMinimized ? "" : "rotate-180"}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 15l7-7 7 7"
/>
</svg>
</Button>
</div>
</div> </div>
)} )}
{/* Filter Content - Conditionally rendered based on minimized state */}
{!isMinimized && !isLoadingFilters && (
<>
{/* Search Bar */} {/* Search Bar */}
<div className="mb-4"> <div className="mb-4">
<div className="relative max-w-md w-full"> <div className="relative max-w-md w-full">
@@ -431,6 +458,8 @@ export function FilterBar({
</Button> </Button>
)} )}
</div> </div>
</>
)}
{/* Click outside to close dropdowns */} {/* Click outside to close dropdowns */}
{(isTypeDropdownOpen || isSortDropdownOpen) && ( {(isTypeDropdownOpen || isSortDropdownOpen) && (

View File

@@ -34,6 +34,7 @@ export function ScriptsGrid({ onInstallScript }: ScriptsGridProps) {
}); });
const [saveFiltersEnabled, setSaveFiltersEnabled] = useState(false); const [saveFiltersEnabled, setSaveFiltersEnabled] = useState(false);
const [isLoadingFilters, setIsLoadingFilters] = useState(true); const [isLoadingFilters, setIsLoadingFilters] = useState(true);
const [isNewestMinimized, setIsNewestMinimized] = useState(false);
const gridRef = useRef<HTMLDivElement>(null); const gridRef = useRef<HTMLDivElement>(null);
const { data: scriptCardsData, isLoading: githubLoading, error: githubError, refetch } = api.scripts.getScriptCardsWithCategories.useQuery(); const { data: scriptCardsData, isLoading: githubLoading, error: githubError, refetch } = api.scripts.getScriptCardsWithCategories.useQuery();
@@ -689,8 +690,8 @@ export function ScriptsGrid({ onInstallScript }: ScriptsGridProps) {
onViewModeChange={setViewMode} onViewModeChange={setViewMode}
/> />
{/* Newest Scripts Carousel - Always show when there are newest scripts */} {/* Newest Scripts Carousel - Only show when no search, filters, or category is active */}
{newestScripts.length > 0 && ( {newestScripts.length > 0 && !hasActiveFilters && !selectedCategory && (
<div className="mb-8"> <div className="mb-8">
<div className="bg-card border-l-4 border-l-primary border border-border rounded-lg p-6 shadow-lg"> <div className="bg-card border-l-4 border-l-primary border border-border rounded-lg p-6 shadow-lg">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
@@ -698,11 +699,35 @@ export function ScriptsGrid({ onInstallScript }: ScriptsGridProps) {
<Clock className="h-6 w-6 text-primary" /> <Clock className="h-6 w-6 text-primary" />
Newest Scripts Newest Scripts
</h2> </h2>
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
{newestScripts.length} recently added {newestScripts.length} recently added
</span> </span>
<Button
onClick={() => setIsNewestMinimized(!isNewestMinimized)}
variant="ghost"
size="icon"
className="h-8 w-8 text-muted-foreground hover:text-foreground"
title={isNewestMinimized ? "Expand newest scripts" : "Minimize newest scripts"}
>
<svg
className={`h-4 w-4 transition-transform ${isNewestMinimized ? "" : "rotate-180"}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 15l7-7 7 7"
/>
</svg>
</Button>
</div>
</div> </div>
{!isNewestMinimized && (
<div className="overflow-x-auto scrollbar-thin scrollbar-thumb-gray-300 dark:scrollbar-thumb-gray-600 scrollbar-track-transparent"> <div className="overflow-x-auto scrollbar-thin scrollbar-thumb-gray-300 dark:scrollbar-thumb-gray-600 scrollbar-track-transparent">
<div className="flex gap-4 pb-2" style={{ minWidth: 'max-content' }}> <div className="flex gap-4 pb-2" style={{ minWidth: 'max-content' }}>
{newestScripts.map((script, index) => { {newestScripts.map((script, index) => {
@@ -731,6 +756,7 @@ export function ScriptsGrid({ onInstallScript }: ScriptsGridProps) {
})} })}
</div> </div>
</div> </div>
)}
</div> </div>
</div> </div>
)} )}