Преглед изворни кода

feat(app): collapsible servers (#33384)

Aarav Sareen пре 2 месеци
родитељ
комит
3cc626b5da
2 измењених фајлова са 37 додато и 2 уклоњено
  1. 2 0
      packages/app/src/i18n/en.ts
  2. 35 2
      packages/app/src/pages/home.tsx

+ 2 - 0
packages/app/src/i18n/en.ts

@@ -589,6 +589,8 @@ export const dict = {
   "home.title": "Home",
   "home.title": "Home",
   "home.projects": "Projects",
   "home.projects": "Projects",
   "home.project.add": "Add project",
   "home.project.add": "Add project",
+  "home.server.collapse": "Collapse server projects",
+  "home.server.expand": "Expand server projects",
   "home.sessions.search.placeholder": "Search sessions",
   "home.sessions.search.placeholder": "Search sessions",
   "home.sessions.search.sessions": "Sessions",
   "home.sessions.search.sessions": "Sessions",
   "home.sessions.search.noResults": "No sessions found for {{query}}",
   "home.sessions.search.noResults": "No sessions found for {{query}}",

+ 35 - 2
packages/app/src/pages/home.tsx

@@ -46,6 +46,7 @@ import { useCommand } from "@/context/command"
 import { ServerRowMenu } from "@/components/server/server-row-menu"
 import { ServerRowMenu } from "@/components/server/server-row-menu"
 import { ServerHealthIndicator } from "@/components/server/server-row"
 import { ServerHealthIndicator } from "@/components/server/server-row"
 import { type ServerHealth } from "@/utils/server-health"
 import { type ServerHealth } from "@/utils/server-health"
+import { Persist, persisted } from "@/utils/persist"
 
 
 const HOME_SESSION_LIMIT = 64
 const HOME_SESSION_LIMIT = 64
 const HOME_ROW_LAYOUT =
 const HOME_ROW_LAYOUT =
@@ -448,6 +449,10 @@ function HomeProjectColumn(props: {
   const global = useGlobal()
   const global = useGlobal()
   const dialog = useDialog()
   const dialog = useDialog()
   const controller = useServerManagementController({ navigateOnAdd: false })
   const controller = useServerManagementController({ navigateOnAdd: false })
+  const [state, setState] = persisted(
+    Persist.global("home.servers", ["home.servers.v1"]),
+    createStore({ collapsed: {} as Record<string, boolean> }),
+  )
   return (
   return (
     <aside
     <aside
       class="mt-6 flex min-w-0 flex-col gap-4 lg:mt-14 lg:pt-[52px]"
       class="mt-6 flex min-w-0 flex-col gap-4 lg:mt-14 lg:pt-[52px]"
@@ -476,20 +481,23 @@ function HomeProjectColumn(props: {
             const key = ServerConnection.key(item)
             const key = ServerConnection.key(item)
             const healthy = () => !!global.servers.health[key]?.healthy
             const healthy = () => !!global.servers.health[key]?.healthy
             const serverCtx = global.createServerCtx(item)
             const serverCtx = global.createServerCtx(item)
+            const collapsed = () => !!state.collapsed[key]
             return (
             return (
               <div class="flex max-h-[min(572px,calc(100vh_-_300px))] min-w-0 flex-col gap-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
               <div class="flex max-h-[min(572px,calc(100vh_-_300px))] min-w-0 flex-col gap-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
                 <HomeServerRow
                 <HomeServerRow
                   server={item}
                   server={item}
                   selected={props.selected.server === key && !props.selected.directory}
                   selected={props.selected.server === key && !props.selected.directory}
                   healthy={healthy()}
                   healthy={healthy()}
+                  collapsed={collapsed()}
                   health={global.servers.health[key]}
                   health={global.servers.health[key]}
                   controller={controller}
                   controller={controller}
                   focusServer={props.focusServer}
                   focusServer={props.focusServer}
                   chooseProject={props.chooseProject}
                   chooseProject={props.chooseProject}
                   openEdit={(server) => dialog.show(() => <DialogServerV2 mode="edit" server={server} />)}
                   openEdit={(server) => dialog.show(() => <DialogServerV2 mode="edit" server={server} />)}
+                  toggleCollapsed={() => setState("collapsed", key, !state.collapsed[key])}
                   language={props.language}
                   language={props.language}
                 />
                 />
-                <Show when={healthy()}>
+                <Show when={healthy() && !collapsed()}>
                   <div class="mx-3 h-px bg-v2-border-border-base" />
                   <div class="mx-3 h-px bg-v2-border-border-base" />
                   <HomeProjectList {...props} server={item} projects={serverCtx.projects.list()} />
                   <HomeProjectList {...props} server={item} projects={serverCtx.projects.list()} />
                 </Show>
                 </Show>
@@ -540,11 +548,13 @@ function HomeServerRow(props: {
   server: ServerConnection.Any
   server: ServerConnection.Any
   selected: boolean
   selected: boolean
   healthy: boolean
   healthy: boolean
+  collapsed: boolean
   health: ServerHealth | undefined
   health: ServerHealth | undefined
   controller: ReturnType<typeof useServerManagementController>
   controller: ReturnType<typeof useServerManagementController>
   focusServer: (server: ServerConnection.Any) => void
   focusServer: (server: ServerConnection.Any) => void
   chooseProject: (server: ServerConnection.Any) => void
   chooseProject: (server: ServerConnection.Any) => void
   openEdit: (server: ServerConnection.Http) => void
   openEdit: (server: ServerConnection.Http) => void
+  toggleCollapsed: () => void
   language: ReturnType<typeof useLanguage>
   language: ReturnType<typeof useLanguage>
 }) {
 }) {
   const [state, setState] = createStore({ menuOpen: false })
   const [state, setState] = createStore({ menuOpen: false })
@@ -557,7 +567,30 @@ function HomeServerRow(props: {
         disabled={!props.healthy}
         disabled={!props.healthy}
         onClick={() => props.focusServer(props.server)}
         onClick={() => props.focusServer(props.server)}
       >
       >
-        <div class="flex size-4 shrink-0 items-center justify-center">
+        <Show when={props.healthy}>
+          <span
+            data-action="home-server-collapse"
+            class="inline-flex -ml-0.5 -mr-1.5 size-5 shrink-0 items-center justify-center rounded-[4px] text-v2-icon-icon-muted hover:bg-v2-overlay-simple-overlay-hover"
+            aria-label={
+              props.collapsed ? props.language.t("home.server.expand") : props.language.t("home.server.collapse")
+            }
+            aria-expanded={!props.collapsed}
+            onClick={(event) => {
+              event.preventDefault()
+              event.stopPropagation()
+              props.toggleCollapsed()
+            }}
+            onPointerDown={(event) => event.preventDefault()}
+          >
+            <IconV2
+              name="chevron-down"
+              size="small"
+              class="transition-transform duration-150 ease-in-out"
+              style={{ transform: `rotate(${props.collapsed ? -90 : 0}deg)` }}
+            />
+          </span>
+        </Show>
+        <div class="flex size-4 shrink-0 items-center justify-center -mr-0.5">
           <ServerHealthIndicator health={props.health} />
           <ServerHealthIndicator health={props.health} />
         </div>
         </div>
         <span class="flex min-w-0 items-center gap-1">
         <span class="flex min-w-0 items-center gap-1">