Skip to content

Commit 1a3d82a

Browse files
authored
Merge pull request #93 from tanmay5114/fix/issue-86/cursor-freezing-after-deleting-document
Fix/issue 86/cursor freezing after deleting document
2 parents 010834b + f5c1752 commit 1a3d82a

File tree

3 files changed

+82
-35
lines changed

3 files changed

+82
-35
lines changed

apps/saru/app/api/history/route.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NextResponse } from 'next/server';
2-
import { auth } from "@/lib/auth"; // Import Better Auth
3-
import { headers } from 'next/headers'; // Import headers
4-
import { getDocumentsById, getChatsByUserId } from '@/lib/db/queries'; // Import Drizzle queries
2+
import { auth } from "@/lib/auth";
3+
import { headers } from 'next/headers';
4+
import { getDocumentsById, getChatsByUserId } from '@/lib/db/queries';
55

66
export async function GET() {
77
// --- Authentication ---

apps/saru/components/chat/chat-header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function PureChatHeader({
6464
return (
6565
<header
6666
className={cn(
67-
'flex sticky top-0 bg-background/80 backdrop-blur-sm z-10 border-b border-border border-r border-border items-center px-3 h-[45px] gap-2 transition-all duration-200',
67+
'flex sticky top-0 bg-background/80 backdrop-blur-sm z-10 border-b border-r border-border items-center px-3 h-[45px] gap-2 transition-all duration-200',
6868
className,
6969
)}
7070
>

apps/saru/components/sidebar/sidebar-documents.tsx

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ import { isToday, isYesterday, subMonths, subWeeks } from 'date-fns';
44
import Link from 'next/link';
55
import { useRouter, usePathname } from 'next/navigation';
66
import type { User } from '@/lib/auth';
7-
import { memo, useCallback, useEffect, useState } from 'react';
7+
import { memo, useCallback, useEffect, useRef, useState } from 'react';
88
import { toast } from 'sonner';
99
import useSWR from 'swr';
1010
import { cn, fetcher } from '@/lib/utils';
1111
import {
12-
FileIcon,
1312
MoreHorizontalIcon,
1413
PlusIcon,
1514
TrashIcon,
@@ -24,12 +23,6 @@ import {
2423
AlertDialogHeader,
2524
AlertDialogTitle,
2625
} from '@/components/ui/alert-dialog';
27-
import {
28-
DropdownMenu,
29-
DropdownMenuContent,
30-
DropdownMenuItem,
31-
DropdownMenuTrigger,
32-
} from '@/components/ui/dropdown-menu';
3326
import {
3427
SidebarGroup,
3528
SidebarGroupContent,
@@ -45,7 +38,6 @@ import { Input } from '@/components/ui/input';
4538
import { Button } from '@/components/ui/button';
4639
import { Checkbox } from '@/components/ui/checkbox';
4740
import useSWRInfinite from 'swr/infinite';
48-
import { motion } from 'framer-motion';
4941

5042
type GroupedDocuments = {
5143
today: Document[];
@@ -104,9 +96,21 @@ const PureDocumentItem = ({
10496
}, [document, isActive, isSelectionMode, isSelected, onToggleSelect, setOpenMobile, onSelect]);
10597

10698
const router = useRouter();
99+
const buttonRef = useRef<HTMLButtonElement>(null);
100+
const menuRef = useRef<HTMLDivElement>(null);
101+
const closeTimerRef = useRef<number | null>(null);
102+
const [showCustomMenu, setShowCustomMenu] = useState(false);
107103

104+
useEffect(() => {
105+
return () => {
106+
if (closeTimerRef.current) {
107+
clearTimeout(closeTimerRef.current);
108+
}
109+
};
110+
}, []);
111+
108112
return (
109-
<SidebarMenuItem>
113+
<SidebarMenuItem className="relative">
110114
<div className="flex items-center w-full">
111115
{isSelectionMode && (
112116
<div className="flex items-center pl-2 pr-1">
@@ -132,27 +136,70 @@ const PureDocumentItem = ({
132136
</div>
133137

134138
{!isSelectionMode && (
135-
<DropdownMenu modal={true}>
136-
<DropdownMenuTrigger asChild>
137-
<SidebarMenuAction
138-
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground mr-0.5"
139-
showOnHover={!isActive}
140-
>
141-
<MoreHorizontalIcon />
142-
<span className="sr-only">More</span>
143-
</SidebarMenuAction>
144-
</DropdownMenuTrigger>
145-
146-
<DropdownMenuContent side="bottom" align="end">
147-
<DropdownMenuItem
148-
className="cursor-pointer text-destructive focus:bg-destructive/15 focus:text-destructive dark:text-red-500"
149-
onSelect={() => onDelete(document.id)}
150-
>
151-
<TrashIcon />
152-
<span>Delete</span>
153-
</DropdownMenuItem>
154-
</DropdownMenuContent>
155-
</DropdownMenu>
139+
<SidebarMenuAction
140+
ref={buttonRef}
141+
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground mr-0.5"
142+
showOnHover={!isActive}
143+
data-state={showCustomMenu ? 'open' : 'closed'}
144+
aria-haspopup="menu"
145+
aria-expanded={showCustomMenu}
146+
aria-controls={`doc-menu-${document.id}`}
147+
onBlur={(e) => {
148+
if (closeTimerRef.current) {
149+
clearTimeout(closeTimerRef.current);
150+
closeTimerRef.current = null;
151+
}
152+
const next = e.relatedTarget as Node | null;
153+
if (menuRef.current && next && menuRef.current.contains(next)) return;
154+
closeTimerRef.current = window.setTimeout(() => setShowCustomMenu(false), 100);
155+
}}
156+
onClick={() => setShowCustomMenu((v) => !v)}
157+
onKeyDown={(e) => {
158+
if (e.key === 'Escape') {
159+
e.stopPropagation();
160+
setShowCustomMenu(false);
161+
}
162+
if (e.key === 'Enter' || e.key === ' ') {
163+
e.preventDefault();
164+
setShowCustomMenu((v) => !v);
165+
}
166+
}}
167+
>
168+
<MoreHorizontalIcon />
169+
<span className="sr-only">More</span>
170+
</SidebarMenuAction>
171+
)}
172+
173+
{!isSelectionMode && showCustomMenu && (
174+
<div
175+
ref={menuRef}
176+
id={`doc-menu-${document.id}`}
177+
role="menu"
178+
aria-labelledby={`doc-menu-${document.id}`}
179+
className="absolute right-0 top-full z-50 min-w-[8rem] overflow-visible rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md"
180+
style={{
181+
marginTop: '2px'
182+
}}
183+
onMouseDown={(e) => e.preventDefault()}
184+
onKeyDown={(e) => {
185+
if (e.key === 'Escape') {
186+
e.stopPropagation();
187+
setShowCustomMenu(false);
188+
buttonRef.current?.focus();
189+
}
190+
}}
191+
>
192+
<Button
193+
variant="destructive"
194+
size="sm"
195+
role="menuitem"
196+
className="h-7 text-sm w-full"
197+
onClick={() => { setShowCustomMenu(false); onDelete(document.id); }}
198+
>
199+
Delete
200+
<TrashIcon />
201+
</Button>
202+
</div>
156203
)}
157204
</SidebarMenuItem>
158205
);

0 commit comments

Comments
 (0)