|
|
@@ -1,6 +1,14 @@
|
|
|
-import { createEffect, For, onCleanup, Show, splitProps, type Accessor, type ComponentProps } from "solid-js"
|
|
|
+import {
|
|
|
+ createEffect,
|
|
|
+ createSignal,
|
|
|
+ For,
|
|
|
+ onCleanup,
|
|
|
+ Show,
|
|
|
+ splitProps,
|
|
|
+ type Accessor,
|
|
|
+ type ComponentProps,
|
|
|
+} from "solid-js"
|
|
|
import { createStore } from "solid-js/store"
|
|
|
-import { Dynamic } from "solid-js/web"
|
|
|
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
|
|
import { Icon } from "@opencode-ai/ui/icon"
|
|
|
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
|
|
@@ -185,9 +193,28 @@ export function PromptProjectSelector(props: {
|
|
|
controller: PromptProjectController
|
|
|
placement?: "bottom" | "bottom-start"
|
|
|
}) {
|
|
|
+ const [triggerReady, setTriggerReady] = createSignal(false)
|
|
|
let contentRef: HTMLDivElement | undefined
|
|
|
+ let triggerFrame: number | undefined
|
|
|
let restoreTrigger = true
|
|
|
|
|
|
+ // Floating UI requires a connected anchor; route transitions can construct this trigger before adoption.
|
|
|
+ const setTriggerRef = (element: HTMLButtonElement) => {
|
|
|
+ const ready = () => {
|
|
|
+ if (!element.isConnected) {
|
|
|
+ triggerFrame = requestAnimationFrame(ready)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ triggerFrame = undefined
|
|
|
+ setTriggerReady(true)
|
|
|
+ }
|
|
|
+ ready()
|
|
|
+ }
|
|
|
+
|
|
|
+ onCleanup(() => {
|
|
|
+ if (triggerFrame !== undefined) cancelAnimationFrame(triggerFrame)
|
|
|
+ })
|
|
|
+
|
|
|
const activeItem = () =>
|
|
|
props.controller.active()
|
|
|
? contentRef?.querySelector<HTMLElement>(`[data-option-key="${CSS.escape(props.controller.active())}"]`)
|
|
|
@@ -258,13 +285,13 @@ export function PromptProjectSelector(props: {
|
|
|
|
|
|
return (
|
|
|
<DropdownMenu
|
|
|
- open={props.controller.open()}
|
|
|
+ open={triggerReady() && props.controller.open()}
|
|
|
placement={props.placement ?? "bottom"}
|
|
|
gutter={4}
|
|
|
modal={false}
|
|
|
onOpenChange={(open) => props.controller.setOpen(open)}
|
|
|
>
|
|
|
- <DropdownMenu.Trigger as={ProjectTrigger} controller={props.controller} />
|
|
|
+ <DropdownMenu.Trigger as={ProjectTrigger} ref={setTriggerRef} controller={props.controller} />
|
|
|
<DropdownMenu.Portal>
|
|
|
<DropdownMenu.Content
|
|
|
ref={contentRef}
|
|
|
@@ -434,9 +461,7 @@ function ProjectTrigger(props: ComponentProps<"button"> & { controller: PromptPr
|
|
|
const [local, rest] = splitProps(props, ["controller", "class", "classList", "onClick", "onKeyDown"])
|
|
|
const project = () => local.controller.selected()
|
|
|
return (
|
|
|
- // Avoid an inert template clone that Floating UI can measure before browser adoption.
|
|
|
- <Dynamic
|
|
|
- component="button"
|
|
|
+ <button
|
|
|
{...rest}
|
|
|
data-action="prompt-project"
|
|
|
type="button"
|
|
|
@@ -473,7 +498,7 @@ function ProjectTrigger(props: ComponentProps<"button"> & { controller: PromptPr
|
|
|
{project() ? displayName(project()!) : local.controller.labels.new()}
|
|
|
</span>
|
|
|
<Icon name="chevron-down" size="small" class="shrink-0 text-v2-icon-icon-muted" />
|
|
|
- </Dynamic>
|
|
|
+ </button>
|
|
|
)
|
|
|
}
|
|
|
|