1
1
import { Button } from "@/components/Button" ;
2
2
import { ResizablePanel } from "@/components/Resizable" ;
3
+ import { useTheme } from "@/contexts/theme" ;
3
4
import {
4
5
type Diagnostic ,
5
6
type InternalDiagnostic ,
@@ -10,7 +11,8 @@ import { useDebouncedValue } from "@/hooks/debounce";
10
11
import { useStore } from "@/store" ;
11
12
import { cn } from "@/utils/cn" ;
12
13
import { ActivityIcon , ExternalLinkIcon , LoaderIcon } from "lucide-react" ;
13
- import { type FC , useEffect , useState } from "react" ;
14
+ import { AnimatePresence , motion } from "motion/react" ;
15
+ import { type FC , useEffect , useMemo , useState } from "react" ;
14
16
15
17
export const Preview : FC = ( ) => {
16
18
const $wasmState = useStore ( ( state ) => state . wasmState ) ;
@@ -87,9 +89,23 @@ export const Preview: FC = () => {
87
89
) }
88
90
>
89
91
< div className = "flex w-full items-center justify-between" >
90
- < p className = "font-semibold text-3xl text-content-primary" >
91
- Parameters
92
- </ p >
92
+ < div className = "flex items-center justify-center gap-4" >
93
+ < p className = "font-semibold text-3xl text-content-primary" >
94
+ Parameters
95
+ </ p >
96
+
97
+ < AnimatePresence >
98
+ { isDebouncing && $wasmState === "loaded" ? (
99
+ < motion . div
100
+ initial = { { opacity : 0 , scale : 0.75 } }
101
+ animate = { { opacity : 1 , scale : 1 } }
102
+ exit = { { opacity : 0 , scale : 0.75 } }
103
+ >
104
+ < LoaderIcon className = "animate-spin text-content-primary" />
105
+ </ motion . div >
106
+ ) : null }
107
+ </ AnimatePresence >
108
+ </ div >
93
109
< Button variant = "destructive" > Reset form</ Button >
94
110
</ div >
95
111
@@ -98,9 +114,6 @@ export const Preview: FC = () => {
98
114
"flex h-full w-full items-center justify-center overflow-x-clip rounded-xl border p-4" ,
99
115
output && "block overflow-y-scroll" ,
100
116
) }
101
- style = { {
102
- opacity : isDebouncing && $wasmState === "loaded" ? 0.5 : 1 ,
103
- } }
104
117
>
105
118
{ output ? (
106
119
< div className = "flex flex-col gap-4" >
@@ -183,60 +196,75 @@ const ErrorPane = () => {
183
196
const $errors = useStore ( ( state ) => state . errors ) ;
184
197
const $toggleShowError = useStore ( ( state ) => state . toggleShowError ) ;
185
198
186
- if ( $errors . diagnostics . length === 0 ) {
187
- return null ;
188
- }
199
+ const hasErrors = useMemo ( ( ) => $errors . diagnostics . length > 0 , [ $errors ] ) ;
189
200
190
201
return (
191
202
< >
192
- { /*
193
- * biome-ignore lint/a11y/useKeyWithClickEvents: key events don't seem to
194
- * work for divs, and I'm otherwise not sure how to make this element
195
- * more accesible. But I think it's fine since the functionality is able to
196
- * be used with the button.
197
- */ }
198
- < div
199
- aria-hidden = { true }
200
- className = { cn (
201
- "absolute top-0 left-0 hidden h-full w-full transition-all" ,
202
- $errors . show && "block cursor-pointer bg-black/20 dark:bg-black/50" ,
203
- ) }
204
- onClick = { ( ) => {
205
- $toggleShowError ( false ) ;
206
- } }
207
- >
208
- { /* OVERLAY */ }
209
- </ div >
203
+ < AnimatePresence propagate = { true } >
204
+ { $errors . show && hasErrors ? (
205
+ // lint/a11y/useKeyWithClickEvents: key events don't seem to
206
+ // work for divs, and I'm otherwise not sure how to make this element
207
+ // more accesible. But I think it's fine since the functionality is able to
208
+ // be used with the button below.
209
+ < motion . div
210
+ initial = { { opacity : 0 } }
211
+ animate = { { opacity : 1 } }
212
+ exit = { { opacity : 0 } }
213
+ aria-hidden = { true }
214
+ className = "absolute top-0 left-0 h-full w-full cursor-pointer bg-black/10 dark:bg-black/50"
215
+ onClick = { ( ) => {
216
+ $toggleShowError ( false ) ;
217
+ } }
218
+ >
219
+ { /* OVERLAY */ }
220
+ </ motion . div >
221
+ ) : null }
222
+ </ AnimatePresence >
210
223
211
- < div
212
- role = "alertdialog"
213
- className = { cn (
214
- "absolute bottom-0 left-0 flex max-h-[60%] w-full flex-col justify-start" ,
215
- $errors . show && "h-auto" ,
216
- ) }
217
- >
218
- < button
219
- className = "flex h-4 min-h-4 w-full items-center justify-center rounded-t-xl bg-border-destructive"
220
- onClick = { ( ) => $toggleShowError ( ) }
221
- aria-label = { $errors . show ? "Hide error dialog" : "Show error dialog" }
222
- >
223
- < div className = "h-0.5 w-2/3 max-w-32 rounded-full bg-white/40" > </ div >
224
- </ button >
224
+ < AnimatePresence propagate = { true } >
225
+ { hasErrors ? (
226
+ < motion . div
227
+ role = "alertdialog"
228
+ transition = { {
229
+ when : "afterChildren" ,
230
+ } }
231
+ exit = { { opacity : 0 } }
232
+ className = { cn (
233
+ "absolute bottom-0 left-0 flex max-h-[60%] w-full flex-col justify-start" ,
234
+ $errors . show && "h-auto" ,
235
+ ) }
236
+ >
237
+ < motion . button
238
+ className = "flex h-4 min-h-4 w-full items-center justify-center rounded-t-xl bg-border-destructive"
239
+ onClick = { ( ) => $toggleShowError ( ) }
240
+ aria-label = {
241
+ $errors . show ? "Hide error dialog" : "Show error dialog"
242
+ }
243
+ >
244
+ < div className = "h-0.5 w-2/3 max-w-32 rounded-full bg-white/40" > </ div >
245
+ </ motion . button >
225
246
226
- < div
227
- aria-hidden = { ! $errors . show }
228
- className = { cn (
229
- "flex flex-col gap-6 overflow-y-scroll bg-surface-secondary p-6" ,
230
- ! $errors . show && "pointer-events-none h-0 p-0" ,
231
- ) }
232
- >
233
- < div className = "flex w-full flex-col gap-3" >
234
- { $errors . diagnostics . map ( ( diagnostic , index ) => (
235
- < ErrorBlock diagnostic = { diagnostic } key = { index } />
236
- ) ) }
237
- </ div >
238
- </ div >
239
- </ div >
247
+ < AnimatePresence propagate = { true } >
248
+ { $errors . show ? (
249
+ < motion . div
250
+ initial = { { height : 0 } }
251
+ animate = { {
252
+ height : "auto" ,
253
+ } }
254
+ exit = { { height : 0 } }
255
+ className = "flex flex-col gap-6 overflow-y-scroll bg-surface-secondary"
256
+ >
257
+ < div className = "flex w-full flex-col gap-3 p-6" >
258
+ { $errors . diagnostics . map ( ( diagnostic , index ) => (
259
+ < ErrorBlock diagnostic = { diagnostic } key = { index } />
260
+ ) ) }
261
+ </ div >
262
+ </ motion . div >
263
+ ) : null }
264
+ </ AnimatePresence >
265
+ </ motion . div >
266
+ ) : null }
267
+ </ AnimatePresence >
240
268
</ >
241
269
) ;
242
270
} ;
0 commit comments