Просмотр исходного кода

fix(app): consolidate add project action (#34086)

Brendan Allan 2 месяцев назад
Родитель
Сommit
1aea999d7c
1 измененных файлов с 77 добавлено и 19 удалено
  1. 77 19
      packages/app/src/components/prompt-project-selector.tsx

+ 77 - 19
packages/app/src/components/prompt-project-selector.tsx

@@ -69,12 +69,14 @@ export function createPromptProjectController(input: {
     if (servers().length <= 1) {
     if (servers().length <= 1) {
       return [...projects().map(projectKey), actionKey(servers()[0]?.key)]
       return [...projects().map(projectKey), actionKey(servers()[0]?.key)]
     }
     }
-    return servers().flatMap((server) => [
-      ...projects()
-        .filter((project) => project.server?.key === server!.key)
-        .map(projectKey),
-      actionKey(server!.key),
-    ])
+    return [
+      ...servers().flatMap((server) =>
+        projects()
+          .filter((project) => project.server?.key === server!.key)
+          .map(projectKey),
+      ),
+      actionKey(),
+    ]
   }
   }
   const initialActive = () => {
   const initialActive = () => {
     const selectedKey = selected() ? projectKey(selected()!) : undefined
     const selectedKey = selected() ? projectKey(selected()!) : undefined
@@ -130,7 +132,10 @@ export function createPromptProjectController(input: {
       const first = input
       const first = input
         .controls()
         .controls()
         .available.find((project) => !search || displayName(project).toLowerCase().includes(search))
         .available.find((project) => !search || displayName(project).toLowerCase().includes(search))
-      setStore({ search: value, active: first ? projectKey(first) : actionKey(servers()[0]?.key) })
+      setStore({
+        search: value,
+        active: first ? projectKey(first) : actionKey(servers().length > 1 ? undefined : servers()[0]?.key),
+      })
     },
     },
     clearSearch() {
     clearSearch() {
       setStore({ search: "", active: initialActive() })
       setStore({ search: "", active: initialActive() })
@@ -156,6 +161,9 @@ export function createPromptProjectController(input: {
         ? decodeURIComponent(store.active.slice(actionPrefix.length)) || undefined
         ? decodeURIComponent(store.active.slice(actionPrefix.length)) || undefined
         : undefined
         : undefined
     },
     },
+    activeAction() {
+      return store.active.startsWith(actionPrefix)
+    },
     setSearchRef(el: HTMLInputElement) {
     setSearchRef(el: HTMLInputElement) {
       searchRef = el
       searchRef = el
     },
     },
@@ -204,6 +212,12 @@ export function PromptProjectSelector(props: {
       selectProject(project)
       selectProject(project)
       return
       return
     }
     }
+    if (props.controller.activeAction() && props.controller.servers().length > 1) {
+      const item = activeItem()
+      item?.focus()
+      item?.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowRight", bubbles: true }))
+      return
+    }
     selectAction(props.controller.activeServer())
     selectAction(props.controller.activeServer())
   }
   }
   const moveActive = (delta: number) => {
   const moveActive = (delta: number) => {
@@ -320,7 +334,13 @@ export function PromptProjectSelector(props: {
                 </DropdownMenu.RadioGroup>
                 </DropdownMenu.RadioGroup>
               }
               }
             >
             >
-              <For each={props.controller.servers()}>
+              <For
+                each={props.controller
+                  .servers()
+                  .filter((server) =>
+                    props.controller.projects().some((project) => project.server?.key === server!.key),
+                  )}
+              >
                 {(server) => (
                 {(server) => (
                   <div>
                   <div>
                     <div class="flex h-7 select-none items-center pl-1.5 pr-3 text-[11px] font-[530] leading-none tracking-[0.05px] text-v2-text-text-faint">
                     <div class="flex h-7 select-none items-center pl-1.5 pr-3 text-[11px] font-[530] leading-none tracking-[0.05px] text-v2-text-text-faint">
@@ -333,22 +353,49 @@ export function PromptProjectSelector(props: {
                         )}
                         )}
                       </For>
                       </For>
                     </DropdownMenu.RadioGroup>
                     </DropdownMenu.RadioGroup>
-                    <ProjectAction server={server!.key} controller={props.controller} onSelect={selectAction} />
                   </div>
                   </div>
                 )}
                 )}
               </For>
               </For>
             </Show>
             </Show>
           </div>
           </div>
-          <Show when={props.controller.servers().length <= 1}>
-            <div class="h-px bg-v2-border-border-muted" />
-            <div class="flex flex-col p-0.5">
-              <ProjectAction
-                server={props.controller.servers()[0]?.key}
-                controller={props.controller}
-                onSelect={selectAction}
-              />
-            </div>
-          </Show>
+          <div class="h-px bg-v2-border-border-muted" />
+          <div class="flex flex-col p-0.5">
+            <Show
+              when={props.controller.servers().length > 1}
+              fallback={
+                <ProjectAction
+                  server={props.controller.servers()[0]?.key}
+                  controller={props.controller}
+                  onSelect={selectAction}
+                />
+              }
+            >
+              <DropdownMenu.Sub>
+                <DropdownMenu.SubTrigger
+                  id={props.controller.actionKey()}
+                  data-option-key={props.controller.actionKey()}
+                  class={projectActionClass}
+                  classList={{
+                    "!bg-v2-overlay-simple-overlay-hover": props.controller.active() === props.controller.actionKey(),
+                  }}
+                  onMouseEnter={() => props.controller.setActive(props.controller.actionKey())}
+                >
+                  <Icon name="plus" size="small" />
+                  <span data-slot="dropdown-menu-item-label" class="min-w-0 flex-1 truncate leading-5">
+                    {props.controller.labels.add()}
+                  </span>
+                  <Icon name="chevron-right" size="small" class="shrink-0 text-v2-icon-icon-muted" />
+                </DropdownMenu.SubTrigger>
+                <DropdownMenu.Portal>
+                  <DropdownMenu.SubContent class="min-w-[180px] overflow-hidden rounded-md border-0 bg-v2-background-bg-layer-01 p-0.5 shadow-[var(--v2-elevation-floating)] focus:outline-none">
+                    <For each={props.controller.servers()}>
+                      {(server) => <ServerAction server={server!} onSelect={selectAction} />}
+                    </For>
+                  </DropdownMenu.SubContent>
+                </DropdownMenu.Portal>
+              </DropdownMenu.Sub>
+            </Show>
+          </div>
         </DropdownMenu.Content>
         </DropdownMenu.Content>
       </DropdownMenu.Portal>
       </DropdownMenu.Portal>
     </DropdownMenu>
     </DropdownMenu>
@@ -457,6 +504,9 @@ function ProjectItem(props: {
   )
   )
 }
 }
 
 
+const projectActionClass =
+  "h-7 gap-2 rounded-sm px-3 text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-base [font-family:var(--v2-font-family-sans)] data-[highlighted]:!bg-v2-overlay-simple-overlay-hover"
+
 function ProjectAction(props: {
 function ProjectAction(props: {
   server?: string
   server?: string
   controller: PromptProjectController
   controller: PromptProjectController
@@ -491,3 +541,11 @@ function ProjectAction(props: {
     </DropdownMenu.Item>
     </DropdownMenu.Item>
   )
   )
 }
 }
+
+function ServerAction(props: { server: { key: string; name: string }; onSelect: (server: string) => void }) {
+  return (
+    <DropdownMenu.Item class={projectActionClass} onSelect={() => props.onSelect(props.server.key)}>
+      <DropdownMenu.ItemLabel class="min-w-0 flex-1 truncate leading-5">{props.server.name}</DropdownMenu.ItemLabel>
+    </DropdownMenu.Item>
+  )
+}