mirror of
https://github.com/anomalyco/opencode.git
synced 2026-02-01 22:48:16 +00:00
feat: transitions, review, dialog
This commit is contained in:
@@ -88,7 +88,7 @@ const ModelList: Component<{
|
||||
|
||||
export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
|
||||
provider?: string
|
||||
children?: JSX.Element
|
||||
children?: JSX.Element | ((open: boolean) => JSX.Element)
|
||||
triggerAs?: T
|
||||
triggerProps?: ComponentProps<T>
|
||||
}) {
|
||||
@@ -101,13 +101,44 @@ export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
|
||||
}
|
||||
const language = useLanguage()
|
||||
|
||||
// Handle ESC key to close
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
const renderChildren = () => {
|
||||
if (typeof props.children === "function") {
|
||||
return props.children(open())
|
||||
}
|
||||
return props.children
|
||||
}
|
||||
|
||||
return (
|
||||
<Kobalte open={open()} onOpenChange={setOpen} placement="top-start" gutter={8}>
|
||||
<Kobalte.Trigger as={props.triggerAs ?? "div"} {...(props.triggerProps as any)}>
|
||||
{props.children}
|
||||
<Kobalte.Trigger
|
||||
as={props.triggerAs ?? "div"}
|
||||
{...(props.triggerProps as any)}
|
||||
data-active={open() ? "true" : undefined}
|
||||
>
|
||||
{renderChildren()}
|
||||
</Kobalte.Trigger>
|
||||
<Kobalte.Portal>
|
||||
<Kobalte.Content class="w-72 h-80 flex flex-col rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden">
|
||||
<Show when={open()}>
|
||||
<div
|
||||
class="fixed inset-0 z-40"
|
||||
onClick={() => setOpen(false)}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</Show>
|
||||
<Kobalte.Content
|
||||
class="w-72 h-80 flex flex-col rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden"
|
||||
data-component="model-popover-content"
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<Kobalte.Title class="sr-only">{language.t("dialog.model.select.title")}</Kobalte.Title>
|
||||
<ModelList
|
||||
provider={props.provider}
|
||||
|
||||
@@ -59,8 +59,8 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
|
||||
}
|
||||
|
||||
const circle = () => (
|
||||
<div class="p-1">
|
||||
<ProgressCircle size={16} strokeWidth={2} percentage={context()?.percentage ?? 0} />
|
||||
<div class="p-1 text-icon-base">
|
||||
<ProgressCircle size={16} strokeWidth={1} percentage={context()?.percentage ?? 0} />
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -99,7 +99,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
class="size-6"
|
||||
class="size-6 text-icon-base"
|
||||
onClick={openContext}
|
||||
aria-label={language.t("context.usage.view")}
|
||||
>
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
}
|
||||
|
||||
&[data-closed] {
|
||||
animation: hover-card-close 0.15s ease-out;
|
||||
animation: hover-card-close var(--transition-duration) var(--transition-easing);
|
||||
}
|
||||
|
||||
&[data-expanded] {
|
||||
animation: hover-card-open 0.15s ease-out;
|
||||
animation: hover-card-open var(--transition-duration) var(--transition-easing);
|
||||
}
|
||||
|
||||
[data-slot="hover-card-body"] {
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
user-select: none;
|
||||
aspect-ratio: 1;
|
||||
flex-shrink: 0;
|
||||
transition-property: background-color, color, opacity, box-shadow;
|
||||
transition-duration: var(--transition-duration);
|
||||
transition-timing-function: var(--transition-easing);
|
||||
|
||||
&[data-variant="primary"] {
|
||||
background-color: var(--icon-strong-base);
|
||||
@@ -99,7 +102,7 @@
|
||||
/* color: var(--icon-active); */
|
||||
/* } */
|
||||
}
|
||||
&:selected:not(:disabled) {
|
||||
&[data-selected]:not(:disabled) {
|
||||
background-color: var(--surface-raised-base-active);
|
||||
/* [data-slot="icon-svg"] { */
|
||||
/* color: var(--icon-selected); */
|
||||
|
||||
@@ -27,12 +27,9 @@
|
||||
content: "";
|
||||
opacity: var(--indicator-opacity, 1);
|
||||
position: absolute;
|
||||
transition:
|
||||
opacity 300ms ease-in-out,
|
||||
box-shadow 100ms ease-in-out,
|
||||
width 150ms ease,
|
||||
height 150ms ease,
|
||||
transform 150ms ease;
|
||||
transition-property: opacity, box-shadow, width, height, transform;
|
||||
transition-duration: var(--transition-duration);
|
||||
transition-timing-function: var(--transition-easing);
|
||||
}
|
||||
|
||||
[data-slot="radio-group-item"] {
|
||||
@@ -46,7 +43,9 @@
|
||||
content: "";
|
||||
inset: 6px 0;
|
||||
position: absolute;
|
||||
transition: opacity 150ms ease;
|
||||
transition-property: opacity;
|
||||
transition-duration: var(--transition-duration);
|
||||
transition-timing-function: var(--transition-easing);
|
||||
width: 1px;
|
||||
transform: translateX(-0.5px);
|
||||
}
|
||||
@@ -72,9 +71,9 @@
|
||||
padding: 6px 12px;
|
||||
place-content: center;
|
||||
position: relative;
|
||||
transition-duration: 150ms;
|
||||
transition-property: color, opacity;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-duration: var(--transition-duration);
|
||||
transition-timing-function: var(--transition-easing);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease-in-out;
|
||||
transition-property: opacity;
|
||||
transition-duration: var(--transition-duration);
|
||||
transition-timing-function: var(--transition-easing);
|
||||
}
|
||||
|
||||
&:hover::after,
|
||||
|
||||
@@ -56,12 +56,8 @@
|
||||
|
||||
[data-slot="accordion-item"] {
|
||||
[data-slot="accordion-content"] {
|
||||
display: none;
|
||||
}
|
||||
&[data-expanded] {
|
||||
[data-slot="accordion-content"] {
|
||||
display: block;
|
||||
}
|
||||
/* Use grid-template-rows for smooth height transition */
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +181,9 @@
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
transition-property: opacity, background-color, color;
|
||||
transition-duration: var(--transition-duration);
|
||||
transition-timing-function: var(--transition-easing);
|
||||
|
||||
&:hover {
|
||||
color: var(--text-strong);
|
||||
|
||||
@@ -25,10 +25,15 @@ const Context = createContext<ReturnType<typeof init>>()
|
||||
|
||||
function init() {
|
||||
const [active, setActive] = createSignal<Active | undefined>()
|
||||
const [renders, setRenders] = createSignal<Record<string, JSX.Element>>({})
|
||||
|
||||
const close = () => {
|
||||
const current = active()
|
||||
if (!current) return
|
||||
setRenders((renders) => {
|
||||
const { [current.id]: _, ...rest } = renders
|
||||
return rest
|
||||
})
|
||||
current.onClose?.()
|
||||
current.dispose()
|
||||
setActive(undefined)
|
||||
@@ -66,12 +71,28 @@ function init() {
|
||||
setActive({ id, node, dispose, owner, onClose })
|
||||
}
|
||||
|
||||
const render = (element: JSX.Element, id: string, owner: Owner) => {
|
||||
setRenders((renders) => ({ ...renders, [id]: element }))
|
||||
show(() => element, owner, () => {
|
||||
setRenders((renders) => {
|
||||
const { [id]: _, ...rest } = renders
|
||||
return rest
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const isActive = (id: string) => {
|
||||
return renders()[id] !== undefined
|
||||
}
|
||||
|
||||
return {
|
||||
get active() {
|
||||
return active()
|
||||
},
|
||||
isActive,
|
||||
close,
|
||||
show,
|
||||
render,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,10 +121,17 @@ export function useDialog() {
|
||||
get active() {
|
||||
return ctx.active
|
||||
},
|
||||
isActive(id: string) {
|
||||
return ctx.isActive(id)
|
||||
},
|
||||
show(element: DialogElement, onClose?: () => void) {
|
||||
const base = ctx.active?.owner ?? owner
|
||||
ctx.show(element, base, onClose)
|
||||
},
|
||||
render(element: JSX.Element, id: string) {
|
||||
const base = ctx.active?.owner ?? owner
|
||||
ctx.render(element, id, base)
|
||||
},
|
||||
close() {
|
||||
ctx.close()
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user