forked from anomalyco/opencode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtoast.tsx
More file actions
100 lines (90 loc) · 2.8 KB
/
toast.tsx
File metadata and controls
100 lines (90 loc) · 2.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { createContext, useContext, type ParentProps, Show } from "solid-js"
import { createStore } from "solid-js/store"
import { useTheme } from "@tui/context/theme"
import { useTerminalDimensions } from "@opentui/solid"
import { SplitBorder } from "../component/border"
import { TextAttributes } from "@opentui/core"
import z from "zod"
import { TuiEvent } from "../event"
export type ToastOptions = z.infer<typeof TuiEvent.ToastShow.properties>
export function Toast() {
const toast = useToast()
const { theme } = useTheme()
const dimensions = useTerminalDimensions()
return (
<Show when={toast.currentToast}>
{(current) => (
<box
position="absolute"
justifyContent="center"
alignItems="flex-start"
top={2}
right={2}
maxWidth={Math.min(60, dimensions().width - 6)}
paddingLeft={2}
paddingRight={2}
paddingTop={1}
paddingBottom={1}
backgroundColor={theme.backgroundPanel}
borderColor={theme[current().variant]}
border={["left", "right"]}
customBorderChars={SplitBorder.customBorderChars}
>
<Show when={current().title}>
<text attributes={TextAttributes.BOLD} marginBottom={1} fg={theme.text}>
{current().title}
</text>
</Show>
<text fg={theme.text} wrapMode="word" width="100%">
{current().message}
</text>
</box>
)}
</Show>
)
}
function init() {
const [store, setStore] = createStore({
currentToast: null as ToastOptions | null,
})
let timeoutHandle: NodeJS.Timeout | null = null
const toast = {
show(options: ToastOptions) {
const parsedOptions = TuiEvent.ToastShow.properties.parse(options)
const { duration, ...currentToast } = parsedOptions
setStore("currentToast", currentToast)
if (timeoutHandle) clearTimeout(timeoutHandle)
timeoutHandle = setTimeout(() => {
setStore("currentToast", null)
}, duration).unref()
},
error: (err: any) => {
if (err instanceof Error)
return toast.show({
variant: "error",
message: err.message,
})
toast.show({
variant: "error",
message: "An unknown error has occurred",
})
},
get currentToast(): ToastOptions | null {
return store.currentToast
},
}
return toast
}
export type ToastContext = ReturnType<typeof init>
const ctx = createContext<ToastContext>()
export function ToastProvider(props: ParentProps) {
const value = init()
return <ctx.Provider value={value}>{props.children}</ctx.Provider>
}
export function useToast() {
const value = useContext(ctx)
if (!value) {
throw new Error("useToast must be used within a ToastProvider")
}
return value
}