Browse Source

refactor(desktop): enhance project tile interaction with suppress hover functionality (#15214)

OpeOginni 1 month ago
parent
commit
5745ee87ba
1 changed files with 43 additions and 16 deletions
  1. 43 16
      packages/app/src/pages/layout/sidebar-project.tsx

+ 43 - 16
packages/app/src/pages/layout/sidebar-project.tsx

@@ -1,4 +1,5 @@
-import { createEffect, createMemo, createSignal, For, Show, type Accessor, type JSX } from "solid-js"
+import { createEffect, createMemo, For, Show, type Accessor, type JSX } from "solid-js"
+import { createStore } from "solid-js/store"
 import { base64Encode } from "@opencode-ai/util/encode"
 import { Button } from "@opencode-ai/ui/button"
 import { ContextMenu } from "@opencode-ai/ui/context-menu"
@@ -7,7 +8,7 @@ import { Icon } from "@opencode-ai/ui/icon"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { Tooltip } from "@opencode-ai/ui/tooltip"
 import { createSortable } from "@thisbeyond/solid-dnd"
-import { type LocalProject } from "@/context/layout"
+import { useLayout, type LocalProject } from "@/context/layout"
 import { useGlobalSync } from "@/context/global-sync"
 import { useLanguage } from "@/context/language"
 import { useNotification } from "@/context/notification"
@@ -60,6 +61,7 @@ const ProjectTile = (props: {
   selected: Accessor<boolean>
   active: Accessor<boolean>
   overlay: Accessor<boolean>
+  suppressHover: Accessor<boolean>
   dirs: Accessor<string[]>
   onProjectMouseEnter: (worktree: string, event: MouseEvent) => void
   onProjectMouseLeave: (worktree: string) => void
@@ -71,9 +73,11 @@ const ProjectTile = (props: {
   closeProject: (directory: string) => void
   setMenu: (value: boolean) => void
   setOpen: (value: boolean) => void
+  setSuppressHover: (value: boolean) => void
   language: ReturnType<typeof useLanguage>
 }): JSX.Element => {
   const notification = useNotification()
+  const layout = useLayout()
   const unseenCount = createMemo(() =>
     props.dirs().reduce((total, directory) => total + notification.project.unseenCount(directory), 0),
   )
@@ -107,17 +111,28 @@ const ProjectTile = (props: {
         }}
         onMouseEnter={(event: MouseEvent) => {
           if (!props.overlay()) return
+          if (props.suppressHover()) return
           props.onProjectMouseEnter(props.project.worktree, event)
         }}
         onMouseLeave={() => {
+          if (props.suppressHover()) props.setSuppressHover(false)
           if (!props.overlay()) return
           props.onProjectMouseLeave(props.project.worktree)
         }}
         onFocus={() => {
           if (!props.overlay()) return
+          if (props.suppressHover()) return
           props.onProjectFocus(props.project.worktree)
         }}
-        onClick={() => props.navigateToProject(props.project.worktree)}
+        onClick={() => {
+          if (props.selected()) {
+            props.setSuppressHover(true)
+            layout.sidebar.toggle()
+            return
+          }
+          props.setSuppressHover(false)
+          props.navigateToProject(props.project.worktree)
+        }}
         onBlur={() => props.setOpen(false)}
       >
         <ProjectIcon project={props.project} notify />
@@ -278,16 +293,19 @@ export const SortableProject = (props: {
   const workspaces = createMemo(() => props.ctx.workspaceIds(props.project).slice(0, 2))
   const workspaceEnabled = createMemo(() => props.ctx.workspacesEnabled(props.project))
   const dirs = createMemo(() => props.ctx.workspaceIds(props.project))
-  const [open, setOpen] = createSignal(false)
-  const [menu, setMenu] = createSignal(false)
+  const [state, setState] = createStore({
+    open: false,
+    menu: false,
+    suppressHover: false,
+  })
 
   const preview = createMemo(() => !props.mobile && props.ctx.sidebarOpened())
   const overlay = createMemo(() => !props.mobile && !props.ctx.sidebarOpened())
   const active = createMemo(() =>
     projectTileActive({
-      menu: menu(),
+      menu: state.menu,
       preview: preview(),
-      open: open(),
+      open: state.open,
       overlay: overlay(),
       hoverProject: props.ctx.hoverProject(),
       worktree: props.project.worktree,
@@ -296,8 +314,14 @@ export const SortableProject = (props: {
 
   createEffect(() => {
     if (preview()) return
-    if (!open()) return
-    setOpen(false)
+    if (!state.open) return
+    setState("open", false)
+  })
+
+  createEffect(() => {
+    if (!selected()) return
+    if (!state.open) return
+    setState("open", false)
   })
 
   const label = (directory: string) => {
@@ -328,6 +352,7 @@ export const SortableProject = (props: {
       selected={selected}
       active={active}
       overlay={overlay}
+      suppressHover={() => state.suppressHover}
       dirs={dirs}
       onProjectMouseEnter={props.ctx.onProjectMouseEnter}
       onProjectMouseLeave={props.ctx.onProjectMouseLeave}
@@ -337,8 +362,9 @@ export const SortableProject = (props: {
       toggleProjectWorkspaces={props.ctx.toggleProjectWorkspaces}
       workspacesEnabled={props.ctx.workspacesEnabled}
       closeProject={props.ctx.closeProject}
-      setMenu={setMenu}
-      setOpen={setOpen}
+      setMenu={(value) => setState("menu", value)}
+      setOpen={(value) => setState("open", value)}
+      setSuppressHover={(value) => setState("suppressHover", value)}
       language={language}
     />
   )
@@ -346,17 +372,18 @@ export const SortableProject = (props: {
   return (
     // @ts-ignore
     <div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
-      <Show when={preview()} fallback={tile()}>
+      <Show when={preview() && !selected()} fallback={tile()}>
         <HoverCard
-          open={open() && !menu()}
+          open={!state.suppressHover && state.open && !state.menu}
           openDelay={0}
           closeDelay={0}
           placement="right-start"
           gutter={6}
           trigger={tile()}
           onOpenChange={(value) => {
-            if (menu()) return
-            setOpen(value)
+            if (state.menu) return
+            if (value && state.suppressHover) return
+            setState("open", value)
             if (value) props.ctx.setHoverSession(undefined)
           }}
         >
@@ -371,7 +398,7 @@ export const SortableProject = (props: {
             projectChildren={projectChildren}
             workspaceSessions={workspaceSessions}
             workspaceChildren={workspaceChildren}
-            setOpen={setOpen}
+            setOpen={(value) => setState("open", value)}
             ctx={props.ctx}
             language={language}
           />