feat(sidebar): enhance document filtering with Drawer component

- Integrated Drawer component in EmbeddedDocumentsMenu for mobile-friendly document type filtering.
- Updated UI to improve accessibility and user experience with new button interactions and layout adjustments.
- Refactored document type display logic to streamline filtering options and enhance visual clarity.
This commit is contained in:
Anish Sarkar 2026-07-07 21:58:47 +05:30
parent 2461353312
commit b911a36bcf
2 changed files with 105 additions and 44 deletions

View file

@ -3,6 +3,7 @@
import { useQuery } from "@rocicorp/zero/react";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import {
Check,
FolderInput,
FolderPlus,
FolderSync,
@ -50,6 +51,7 @@ import {
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
@ -69,6 +71,7 @@ import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import type { DocumentTypeEnum } from "@/contracts/types/document.types";
import { useIsMobile } from "@/hooks/use-mobile";
import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
import { documentsApiService } from "@/lib/apis/documents-api.service";
import { foldersApiService } from "@/lib/apis/folders-api.service";
@ -127,60 +130,111 @@ export function EmbeddedDocumentsMenu({
onToggleType: (type: DocumentTypeEnum, checked: boolean) => void;
onCreateFolder: () => void;
}) {
const isMobile = useIsMobile();
const [filterDrawerOpen, setFilterDrawerOpen] = useState(false);
const documentTypes = useMemo(
() => Object.keys(typeCounts).sort() as DocumentTypeEnum[],
[typeCounts]
);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon"
className="relative h-7 w-7 text-muted-foreground hover:bg-accent hover:text-accent-foreground"
aria-label="Document actions"
<>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon"
className="relative h-7 w-7 text-muted-foreground hover:bg-accent hover:text-accent-foreground"
aria-label="Document actions"
>
<SlidersVertical className="h-3.5 w-3.5" />
{activeTypes.length > 0 ? (
<span className="absolute right-0.5 top-0.5 h-1.5 w-1.5 rounded-full bg-primary" />
) : null}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-44">
<DropdownMenuItem onSelect={onCreateFolder}>
<FolderPlus className="h-4 w-4" />
New folder
</DropdownMenuItem>
{isMobile ? (
<DropdownMenuItem onSelect={() => setFilterDrawerOpen(true)}>
<ListFilter className="h-4 w-4" />
<span className="flex-1">Filter by type</span>
</DropdownMenuItem>
) : (
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<ListFilter className="h-4 w-4" />
Filter by type
</DropdownMenuSubTrigger>
<DropdownMenuSubContent className="w-52 max-h-72 overflow-y-auto">
{documentTypes.length > 0 ? (
documentTypes.map((type) => (
<DropdownMenuCheckboxItem
key={type}
checked={activeTypes.includes(type)}
onCheckedChange={(checked) => onToggleType(type, checked === true)}
onSelect={(event) => event.preventDefault()}
>
{getDocumentTypeIcon(type, "h-4 w-4")}
<span className="min-w-0 flex-1 truncate">{getDocumentTypeLabel(type)}</span>
<span className="ml-auto text-xs text-muted-foreground">
{typeCounts[type] ?? 0}
</span>
</DropdownMenuCheckboxItem>
))
) : (
<DropdownMenuItem disabled>No document types</DropdownMenuItem>
)}
</DropdownMenuSubContent>
</DropdownMenuSub>
)}
</DropdownMenuContent>
</DropdownMenu>
<Drawer
open={filterDrawerOpen}
onOpenChange={setFilterDrawerOpen}
shouldScaleBackground={false}
>
<DrawerContent
className="z-80 max-h-[75vh] rounded-t-2xl border bg-popover text-popover-foreground"
overlayClassName="z-80"
>
<SlidersVertical className="h-3.5 w-3.5" />
{activeTypes.length > 0 ? (
<span className="absolute right-0.5 top-0.5 h-1.5 w-1.5 rounded-full bg-primary" />
) : null}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-44">
<DropdownMenuItem onSelect={onCreateFolder}>
<FolderPlus className="h-4 w-4" />
New folder
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<ListFilter className="h-4 w-4" />
<DrawerHandle className="mt-3 h-1.5 w-10" />
<DrawerTitle className="px-4 pb-2 pt-3 text-center text-base font-semibold">
Filter by type
</DropdownMenuSubTrigger>
<DropdownMenuSubContent className="w-52 max-h-72 overflow-y-auto">
</DrawerTitle>
<div className="px-4 pb-6 pt-1">
{documentTypes.length > 0 ? (
documentTypes.map((type) => (
<DropdownMenuCheckboxItem
key={type}
checked={activeTypes.includes(type)}
onCheckedChange={(checked) => onToggleType(type, checked === true)}
onSelect={(event) => event.preventDefault()}
>
{getDocumentTypeIcon(type, "h-4 w-4")}
<span className="min-w-0 flex-1 truncate">{getDocumentTypeLabel(type)}</span>
<span className="ml-auto text-xs text-muted-foreground">
{typeCounts[type] ?? 0}
</span>
</DropdownMenuCheckboxItem>
))
documentTypes.map((type, index) => {
const isActive = activeTypes.includes(type);
return (
<div key={type}>
{index > 0 && <div className="mx-3 h-px bg-popover-border" />}
<button
type="button"
className="flex h-12 w-full items-center gap-3 rounded-lg px-3 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
onClick={() => onToggleType(type, !isActive)}
>
{getDocumentTypeIcon(type, "h-4 w-4")}
<span className="min-w-0 flex-1 truncate">{getDocumentTypeLabel(type)}</span>
<span className="text-xs text-muted-foreground">{typeCounts[type] ?? 0}</span>
{isActive && <Check className="h-4 w-4 shrink-0 text-primary" />}
</button>
</div>
);
})
) : (
<DropdownMenuItem disabled>No document types</DropdownMenuItem>
<p className="px-3 py-4 text-sm text-muted-foreground">No document types</p>
)}
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent>
</DropdownMenu>
</div>
</DrawerContent>
</Drawer>
</>
);
}

View file

@ -5,6 +5,7 @@ import Link from "next/link";
import { useParams } from "next/navigation";
import { useTranslations } from "next-intl";
import { type ReactNode, useMemo, useState } from "react";
import { ConnectAgentDialog } from "@/components/mcp/connect-agent-dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
@ -322,10 +323,16 @@ export function Sidebar({
/>
)}
{!isCollapsed && (
<div className="shrink-0 py-1.5">
<ConnectAgentDialog className="w-[calc(100%-1rem)]" />
</div>
)}
<SidebarUsageFooter
pageUsage={pageUsage}
isCollapsed={isCollapsed}
hasNavSectionAbove={footerNavItems.length > 0}
hasNavSectionAbove={footerNavItems.length > 0 || !isCollapsed}
onNavigate={onNavigate}
/>