@@ -61,6 +61,7 @@ import {
6161} from "./desktopUpdate.logic" ;
6262import { Alert , AlertAction , AlertDescription , AlertTitle } from "./ui/alert" ;
6363import { Button } from "./ui/button" ;
64+ import { Collapsible , CollapsibleContent } from "./ui/collapsible" ;
6465import { Tooltip , TooltipPopup , TooltipTrigger } from "./ui/tooltip" ;
6566import {
6667 SidebarContent ,
@@ -231,18 +232,20 @@ function SortableProjectItem({
231232 const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver } =
232233 useSortable ( { id : projectId } ) ;
233234 return (
234- < div
235+ < li
235236 ref = { setNodeRef }
236237 style = { {
237238 transform : CSS . Translate . toString ( transform ) ,
238239 transition,
239240 } }
240- className = { `rounded-md ${
241+ className = { `group/menu-item relative rounded-md ${
241242 isDragging ? "z-20 opacity-80" : ""
242243 } ${ isOver && ! isDragging ? "ring-1 ring-primary/40" : "" } `}
244+ data-sidebar = "menu-item"
245+ data-slot = "sidebar-menu-item"
243246 >
244247 { children ( { attributes, listeners } ) }
245- </ div >
248+ </ li >
246249 ) ;
247250}
248251
@@ -294,14 +297,7 @@ export default function Sidebar() {
294297 const renamingCommittedRef = useRef ( false ) ;
295298 const renamingInputRef = useRef < HTMLInputElement | null > ( null ) ;
296299 const dragInProgressRef = useRef ( false ) ;
297- const suppressProjectClickFromGestureRef = useRef ( false ) ;
298- const suppressProjectClickResetTimerRef = useRef < number | null > ( null ) ;
299- const projectTitlePointerRef = useRef < {
300- pointerId : number ;
301- startX : number ;
302- startY : number ;
303- moved : boolean ;
304- } | null > ( null ) ;
300+ const suppressProjectClickAfterDragRef = useRef ( false ) ;
305301 const [ desktopUpdateState , setDesktopUpdateState ] = useState < DesktopUpdateState | null > ( null ) ;
306302 const shouldBrowseForProjectImmediately = isElectron ;
307303 const shouldShowProjectPathEntry = addingProject && ! shouldBrowseForProjectImmediately ;
@@ -861,64 +857,20 @@ export default function Sidebar() {
861857
862858 const handleProjectDragStart = useCallback ( ( _event : DragStartEvent ) => {
863859 dragInProgressRef . current = true ;
860+ suppressProjectClickAfterDragRef . current = true ;
864861 } , [ ] ) ;
865862
866863 const handleProjectDragCancel = useCallback ( ( _event : DragCancelEvent ) => {
867864 dragInProgressRef . current = false ;
868865 } , [ ] ) ;
869866
870- const handleProjectTitlePointerDownCapture = useCallback (
871- ( event : React . PointerEvent < HTMLButtonElement > ) => {
872- projectTitlePointerRef . current = {
873- pointerId : event . pointerId ,
874- startX : event . clientX ,
875- startY : event . clientY ,
876- moved : false ,
877- } ;
878- } ,
879- [ ] ,
880- ) ;
881-
882- const handleProjectTitlePointerMoveCapture = useCallback (
883- ( event : React . PointerEvent < HTMLButtonElement > ) => {
884- const pointer = projectTitlePointerRef . current ;
885- if ( ! pointer || pointer . pointerId !== event . pointerId ) return ;
886- const movedX = Math . abs ( event . clientX - pointer . startX ) ;
887- const movedY = Math . abs ( event . clientY - pointer . startY ) ;
888- if ( movedX > 3 || movedY > 3 ) {
889- pointer . moved = true ;
890- }
891- } ,
892- [ ] ,
893- ) ;
894-
895- const handleProjectTitlePointerUpCapture = useCallback (
896- ( event : React . PointerEvent < HTMLButtonElement > ) => {
897- const pointer = projectTitlePointerRef . current ;
898- if ( pointer ?. pointerId === event . pointerId ) {
899- if ( pointer . moved ) {
900- suppressProjectClickFromGestureRef . current = true ;
901- if ( suppressProjectClickResetTimerRef . current !== null ) {
902- window . clearTimeout ( suppressProjectClickResetTimerRef . current ) ;
903- }
904- suppressProjectClickResetTimerRef . current = window . setTimeout ( ( ) => {
905- suppressProjectClickFromGestureRef . current = false ;
906- suppressProjectClickResetTimerRef . current = null ;
907- } , 0 ) ;
908- }
909- projectTitlePointerRef . current = null ;
910- }
911- } ,
912- [ ] ,
913- ) ;
914-
915- const handleProjectTitlePointerCancelCapture = useCallback ( ( ) => {
916- projectTitlePointerRef . current = null ;
867+ const handleProjectTitlePointerDownCapture = useCallback ( ( ) => {
868+ suppressProjectClickAfterDragRef . current = false ;
917869 } , [ ] ) ;
918870
919871 const handleProjectTitleClick = useCallback (
920872 ( event : React . MouseEvent < HTMLButtonElement > , projectId : ProjectId ) => {
921- if ( dragInProgressRef . current || suppressProjectClickFromGestureRef . current ) {
873+ if ( dragInProgressRef . current || suppressProjectClickAfterDragRef . current ) {
922874 event . preventDefault ( ) ;
923875 event . stopPropagation ( ) ;
924876 return ;
@@ -940,14 +892,6 @@ export default function Sidebar() {
940892 [ toggleProject ] ,
941893 ) ;
942894
943- useEffect ( ( ) => {
944- return ( ) => {
945- if ( suppressProjectClickResetTimerRef . current !== null ) {
946- window . clearTimeout ( suppressProjectClickResetTimerRef . current ) ;
947- }
948- } ;
949- } , [ ] ) ;
950-
951895 useEffect ( ( ) => {
952896 const onWindowKeyDown = ( event : KeyboardEvent ) => {
953897 const activeThread = routeThreadId
@@ -1318,17 +1262,17 @@ export default function Sidebar() {
13181262 return (
13191263 < SortableProjectItem key = { project . id } projectId = { project . id } >
13201264 { ( dragHandleProps ) => (
1321- < SidebarMenuItem className = "group/collapsible" >
1265+ < Collapsible
1266+ className = "group/collapsible"
1267+ open = { project . expanded }
1268+ >
13221269 < div className = "group/project-header relative" >
13231270 < SidebarMenuButton
13241271 size = "sm"
13251272 className = "gap-2 px-2 py-1.5 text-left cursor-grab active:cursor-grabbing hover:bg-accent group-hover/project-header:bg-accent group-hover/project-header:text-sidebar-accent-foreground"
13261273 { ...dragHandleProps . attributes }
13271274 { ...dragHandleProps . listeners }
13281275 onPointerDownCapture = { handleProjectTitlePointerDownCapture }
1329- onPointerMoveCapture = { handleProjectTitlePointerMoveCapture }
1330- onPointerUpCapture = { handleProjectTitlePointerUpCapture }
1331- onPointerCancelCapture = { handleProjectTitlePointerCancelCapture }
13321276 onClick = { ( event ) => handleProjectTitleClick ( event , project . id ) }
13331277 onKeyDown = { ( event ) => handleProjectTitleKeyDown ( event , project . id ) }
13341278 onContextMenu = { ( event ) => {
@@ -1379,15 +1323,8 @@ export default function Sidebar() {
13791323 </ Tooltip >
13801324 </ div >
13811325
1382- < div
1383- className = { `grid transition-[grid-template-rows,opacity] duration-200 ease-out ${
1384- project . expanded
1385- ? "grid-rows-[1fr] opacity-100"
1386- : "pointer-events-none grid-rows-[0fr] opacity-0"
1387- } `}
1388- >
1389- < div className = "min-h-0 overflow-hidden" >
1390- < SidebarMenuSub className = "mx-1 my-0 w-full translate-x-0 gap-0 px-1.5 py-0" >
1326+ < CollapsibleContent keepMounted >
1327+ < SidebarMenuSub className = "mx-1 my-0 w-full translate-x-0 gap-0 px-1.5 py-0" >
13911328 { visibleThreads . map ( ( thread ) => {
13921329 const isActive = routeThreadId === thread . id ;
13931330 const threadStatus = resolveThreadStatusPill ( {
@@ -1558,9 +1495,8 @@ export default function Sidebar() {
15581495 </ SidebarMenuSubItem >
15591496 ) }
15601497 </ SidebarMenuSub >
1561- </ div >
1562- </ div >
1563- </ SidebarMenuItem >
1498+ </ CollapsibleContent >
1499+ </ Collapsible >
15641500 ) }
15651501 </ SortableProjectItem >
15661502 ) ;
0 commit comments