11import React , { useEffect , useMemo , useRef , useState } from 'react' ;
2+ import { createPortal } from 'react-dom' ;
23import {
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". */
402403function 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