/
Alex_Ural
/
opencode
Обзор
Документация
Войти
/
Alex_Ural
/
opencode
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/ui/src/context/dialog.tsx
197 строк
5 KB
Aarav Sareen
feat(app): improve servers UI (#30961)
06 июн 2026, 01:08
Не верифицирован
06 июн 2026, 01:08
e3a55db
Код
Авторство
О чём код?
import { createContext, createEffect, createRoot, createSignal, getOwner, onCleanup, type Owner, type ParentProps, runWithOwner, useContext, type JSX, startTransition, For, } from "solid-js" import { Dialog as Kobalte } from "@kobalte/core/dialog" import { makeEventListener } from "@solid-primitives/event-listener" type DialogElement = () => JSX.Element type Active = { id: string node: JSX.Element dispose: () => void owner: Owner onClose?: () => void setClosing: (closing: boolean) => void } const Context = createContext<ReturnType<typeof init>>() function init() { const [stack, setStack] = createSignal<Active[]>([]) const timer = { current: undefined as ReturnType<typeof setTimeout> | undefined } const lock = { value: false } onCleanup(() => { if (timer.current === undefined) return clearTimeout(timer.current) timer.current = undefined }) 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 closed = current.id if (timer.current !== undefined) { clearTimeout(timer.current) timer.current = undefined } timer.current = setTimeout(() => { timer.current = undefined current.dispose() setStack((items) => items.filter((item) => item.id !== closed)) lock.value = false }, 100) } createEffect(() => { if (stack().length === 0) return const onKeyDown = (event: KeyboardEvent) => { if (event.key !== "Escape") return close() event.preventDefault() event.stopPropagation() } makeEventListener(window, "keydown", onKeyDown, { capture: true }) }) 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 const node = runWithOwner(owner, () => createRoot((d: () => void) => { dispose = d const [closing, setClosingSignal] = createSignal(false) setClosing = setClosingSignal return ( <Kobalte modal open={!closing()} onOpenChange={(open: boolean) => { if (open) return close(id) }} > <Kobalte.Portal> <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> ) }), ) if (!dispose || !setClosing) return 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 { stack, close, show, push, } } export function DialogProvider(props: ParentProps) { const ctx = init() return ( <Context.Provider value={ctx}> {props.children} <div data-component="dialog-stack"> <For each={ctx.stack()}>{(item) => item.node}</For> </div> </Context.Provider> ) } export function useDialog() { const ctx = useContext(Context) const owner = getOwner() if (!owner) { throw new Error("useDialog must be used within a DialogProvider") } if (!ctx) { throw new Error("useDialog must be used within a DialogProvider") } return { get active() { return ctx.stack().at(-1) }, show(element: DialogElement, onClose?: () => void) { 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() }, } }