feat: implement responsive row action dropdowns and enhance mobile sidebar navigation

This commit is contained in:
Anish Sarkar 2025-12-28 23:25:22 +05:30
parent a10bfe32cd
commit 3bea989868
16 changed files with 256 additions and 191 deletions

View file

@ -42,9 +42,10 @@ interface AllChatsSidebarProps {
open: boolean;
onOpenChange: (open: boolean) => void;
searchSpaceId: string;
onCloseMobileSidebar?: () => void;
}
export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsSidebarProps) {
export function AllChatsSidebar({ open, onOpenChange, searchSpaceId, onCloseMobileSidebar }: AllChatsSidebarProps) {
const t = useTranslations("sidebar");
const router = useRouter();
const params = useParams();
@ -61,6 +62,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
const [searchQuery, setSearchQuery] = useState("");
const [showArchived, setShowArchived] = useState(false);
const [mounted, setMounted] = useState(false);
const [openDropdownId, setOpenDropdownId] = useState<number | null>(null);
const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);
const isSearchMode = !!debouncedSearchQuery.trim();
@ -120,8 +122,10 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
(threadId: number) => {
router.push(`/dashboard/${searchSpaceId}/new-chat/${threadId}`);
onOpenChange(false);
// Also close the main sidebar on mobile
onCloseMobileSidebar?.();
},
[router, onOpenChange, searchSpaceId]
[router, onOpenChange, searchSpaceId, onCloseMobileSidebar]
);
// Handle thread deletion
@ -209,7 +213,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className="fixed inset-0 z-50 bg-black/50"
className="fixed inset-0 z-[70] bg-black/50"
onClick={() => onOpenChange(false)}
aria-hidden="true"
/>
@ -220,7 +224,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
animate={{ x: 0 }}
exit={{ x: "-100%" }}
transition={{ type: "spring", damping: 25, stiffness: 300 }}
className="fixed inset-y-0 left-0 z-50 w-80 bg-background shadow-xl flex flex-col"
className="fixed inset-y-0 left-0 z-[70] w-80 bg-background shadow-xl flex flex-col pointer-events-auto isolate"
role="dialog"
aria-modal="true"
aria-label={t("all_chats") || "All Chats"}
@ -345,14 +349,17 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
</Tooltip>
{/* Actions dropdown */}
<DropdownMenu>
<DropdownMenu
open={openDropdownId === thread.id}
onOpenChange={(isOpen) => setOpenDropdownId(isOpen ? thread.id : null)}
>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className={cn(
"h-6 w-6 shrink-0",
"opacity-0 group-hover:opacity-100 focus:opacity-100",
"md:opacity-0 md:group-hover:opacity-100 md:focus:opacity-100",
"transition-opacity"
)}
disabled={isBusy}
@ -365,7 +372,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
<span className="sr-only">{t("more_options") || "More options"}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
<DropdownMenuContent align="end" className="w-40 z-[80]">
<DropdownMenuItem
onClick={() => handleToggleArchive(thread.id, thread.archived)}
disabled={isArchiving}