Skip to content

Commit 866d4a7

Browse files
Fix add-project menu clipping: portal + open to the right
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7aa266e commit 866d4a7

1 file changed

Lines changed: 51 additions & 24 deletions

File tree

packages/app/src/App.tsx

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useEffect, useMemo, useRef, useState } from 'react';
2+
import { createPortal } from 'react-dom';
23
import {
34
Blocks,
45
ChevronRight,
@@ -401,20 +402,40 @@ function SectionLabel({ children, action }: { children: React.ReactNode; action?
401402
/** Projects "+" button → popup with "start from scratch" / "open existing". */
402403
function AddProjectMenu({ onCreate, onOpen }: { onCreate: () => void; onOpen: () => void }): React.ReactElement {
403404
const [open, setOpen] = useState(false);
405+
const [pos, setPos] = useState<{ top: number; left: number } | null>(null);
404406
const ref = useRef<HTMLDivElement>(null);
407+
const btnRef = useRef<HTMLButtonElement>(null);
408+
const menuRef = useRef<HTMLDivElement>(null);
409+
405410
useEffect(() => {
406411
if (!open) return;
407412
const onDown = (e: MouseEvent): void => {
408-
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
413+
if (
414+
ref.current && !ref.current.contains(e.target as Node) &&
415+
menuRef.current && !menuRef.current.contains(e.target as Node)
416+
) {
417+
setOpen(false);
418+
}
409419
};
410420
document.addEventListener('mousedown', onDown);
411421
return () => document.removeEventListener('mousedown', onDown);
412422
}, [open]);
413423

424+
const toggle = (): void => {
425+
if (open) {
426+
setOpen(false);
427+
return;
428+
}
429+
const r = btnRef.current?.getBoundingClientRect();
430+
if (r) setPos({ top: r.bottom + 6, left: r.left });
431+
setOpen(true);
432+
};
433+
414434
return (
415435
<div ref={ref} className="relative">
416436
<button
417-
onClick={() => setOpen((o) => !o)}
437+
ref={btnRef}
438+
onClick={toggle}
418439
title="Add a project folder"
419440
className={cls(
420441
'no-drag flex h-5 w-5 items-center justify-center rounded transition-colors hover:bg-panel2 hover:text-text',
@@ -423,28 +444,34 @@ function AddProjectMenu({ onCreate, onOpen }: { onCreate: () => void; onOpen: ()
423444
>
424445
<FolderPlus className="h-3.5 w-3.5" strokeWidth={2} />
425446
</button>
426-
{open && (
427-
<div className="absolute right-0 top-full z-50 mt-1.5 w-60 overflow-hidden rounded-lg border border-border bg-panel py-1 shadow-xl shadow-black/40">
428-
<AddProjectRow
429-
icon={Sparkles}
430-
label="Start from scratch"
431-
hint="Create a new empty folder"
432-
onClick={() => {
433-
setOpen(false);
434-
onCreate();
435-
}}
436-
/>
437-
<AddProjectRow
438-
icon={FolderOpen}
439-
label="Use an existing folder"
440-
hint="Pick a folder on your machine"
441-
onClick={() => {
442-
setOpen(false);
443-
onOpen();
444-
}}
445-
/>
446-
</div>
447-
)}
447+
{open && pos &&
448+
createPortal(
449+
<div
450+
ref={menuRef}
451+
style={{ top: pos.top, left: pos.left }}
452+
className="fixed z-50 w-60 overflow-hidden rounded-lg border border-border bg-panel py-1 shadow-xl shadow-black/40"
453+
>
454+
<AddProjectRow
455+
icon={Sparkles}
456+
label="Start from scratch"
457+
hint="Create a new empty folder"
458+
onClick={() => {
459+
setOpen(false);
460+
onCreate();
461+
}}
462+
/>
463+
<AddProjectRow
464+
icon={FolderOpen}
465+
label="Use an existing folder"
466+
hint="Pick a folder on your machine"
467+
onClick={() => {
468+
setOpen(false);
469+
onOpen();
470+
}}
471+
/>
472+
</div>,
473+
document.body,
474+
)}
448475
</div>
449476
);
450477
}

0 commit comments

Comments
 (0)