feat(app): improve servers UI (#30961)

This commit is contained in:
Aarav Sareen
2026-06-05 22:08:11 +00:00
committed by GitHub
parent 015e79fa59
commit e3a55db5b5
10 changed files with 628 additions and 62 deletions
+63 -30
View File
@@ -11,6 +11,7 @@ import {
useContext,
type JSX,
startTransition,
For,
} from "solid-js"
import { Dialog as Kobalte } from "@kobalte/core/dialog"
import { makeEventListener } from "@solid-primitives/event-listener"
@@ -29,7 +30,7 @@ type Active = {
const Context = createContext<ReturnType<typeof init>>()
function init() {
const [active, setActive] = createSignal<Active | undefined>()
const [stack, setStack] = createSignal<Active[]>([])
const timer = { current: undefined as ReturnType<typeof setTimeout> | undefined }
const lock = { value: false }
@@ -39,14 +40,15 @@ function init() {
timer.current = undefined
})
const close = () => {
const current = active()
const close = (id?: string) => {
const items = stack()
const current = id ? items.find((item) => item.id === id) : items.at(-1)
if (!current || lock.value) return
lock.value = true
current.onClose?.()
current.setClosing(true)
const id = current.id
const closed = current.id
if (timer.current !== undefined) {
clearTimeout(timer.current)
timer.current = undefined
@@ -55,13 +57,13 @@ function init() {
timer.current = setTimeout(() => {
timer.current = undefined
current.dispose()
if (active()?.id === id) setActive(undefined)
setStack((items) => items.filter((item) => item.id !== closed))
lock.value = false
}, 100)
}
createEffect(() => {
if (!active()) return
if (stack().length === 0) return
const onKeyDown = (event: KeyboardEvent) => {
if (event.key !== "Escape") return
@@ -73,21 +75,9 @@ function init() {
makeEventListener(window, "keydown", onKeyDown, { capture: true })
})
const show = (element: DialogElement, owner: Owner, onClose?: () => void) => {
// Immediately dispose any existing dialog when showing a new one
const current = active()
if (current) {
current.dispose()
setActive(undefined)
}
if (timer.current !== undefined) {
clearTimeout(timer.current)
timer.current = undefined
}
lock.value = false
const mount = (element: DialogElement, owner: Owner, onClose: (() => void) | undefined, layer: number) => {
const id = Math.random().toString(36).slice(2)
const zIndex = 50 + layer * 10
let dispose: (() => void) | undefined
let setClosing: ((closing: boolean) => void) | undefined
@@ -102,12 +92,29 @@ function init() {
open={!closing()}
onOpenChange={(open: boolean) => {
if (open) return
close()
close(id)
}}
>
<Kobalte.Portal>
<Kobalte.Overlay data-component="dialog-overlay" onClick={close} />
{element()}
<Kobalte.Overlay
data-component="dialog-overlay"
style={{ "z-index": String(zIndex) }}
onClick={() => close(id)}
/>
<div
data-dialog-layer={layer}
style={{
position: "fixed",
inset: "0",
"z-index": String(zIndex),
display: "flex",
"align-items": "center",
"justify-content": "center",
"pointer-events": "none",
}}
>
{element()}
</div>
</Kobalte.Portal>
</Kobalte>
)
@@ -116,15 +123,35 @@ function init() {
if (!dispose || !setClosing) return
setActive({ id, node, dispose, owner, onClose, setClosing })
const active: Active = { id, node, dispose, owner, onClose, setClosing }
setStack((items) => [...items, active])
}
const push = (element: DialogElement, owner: Owner, onClose?: () => void) => {
if (timer.current !== undefined) {
clearTimeout(timer.current)
timer.current = undefined
}
lock.value = false
mount(element, owner, onClose, stack().length)
}
const show = (element: DialogElement, owner: Owner, onClose?: () => void) => {
for (const item of stack()) item.dispose()
setStack([])
if (timer.current !== undefined) {
clearTimeout(timer.current)
timer.current = undefined
}
lock.value = false
mount(element, owner, onClose, 0)
}
return {
get active() {
return active()
},
stack,
close,
show,
push,
}
}
@@ -133,7 +160,9 @@ export function DialogProvider(props: ParentProps) {
return (
<Context.Provider value={ctx}>
{props.children}
<div data-component="dialog-stack">{ctx.active?.node}</div>
<div data-component="dialog-stack">
<For each={ctx.stack()}>{(item) => item.node}</For>
</div>
</Context.Provider>
)
}
@@ -151,12 +180,16 @@ export function useDialog() {
return {
get active() {
return ctx.active
return ctx.stack().at(-1)
},
show(element: DialogElement, onClose?: () => void) {
const base = ctx.active?.owner ?? owner
const base = ctx.stack().at(-1)?.owner ?? owner
return startTransition(() => ctx.show(element, base, onClose))
},
push(element: DialogElement, onClose?: () => void) {
const base = ctx.stack().at(-1)?.owner ?? owner
return startTransition(() => ctx.push(element, base, onClose))
},
close() {
ctx.close()
},