Skip to content

Commit 27d5dd9

Browse files
authored
Merge pull request #7 from coder/brett/polish-errors
feat: add animations to error drawer
2 parents 53a54f5 + fc9947f commit 27d5dd9

File tree

2 files changed

+98
-69
lines changed

2 files changed

+98
-69
lines changed

src/Editor.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ export const Editor: FC = () => {
7070
}, [codeCopied]);
7171

7272
return (
73-
<ResizablePanel className="flex flex-col items-start">
73+
<ResizablePanel className="relative flex flex-col items-start">
7474
{/* EDITOR TOP BAR */}
75-
<div className="flex w-full items-center justify-between border-b border-b-surface-quaternary pr-3">
75+
<div className="flex h-12 w-full items-center justify-between border-b border-b-surface-quaternary pr-3">
7676
<div className="flex">
7777
<button className="flex w-fit min-w-[120px] items-center gap-1 border-x bg-surface-secondary px-4 py-3 text-content-primary transition-colors hover:bg-surface-tertiary">
7878
<FileJsonIcon className="w-[18px] min-w-[18px]" />
@@ -125,24 +125,25 @@ export const Editor: FC = () => {
125125
</div>
126126

127127
{/* CODE EDITOR */}
128-
<div className="relative h-full w-full overflow-y-scroll">
128+
<div className="absolute mt-12 flex h-full w-full justify-end p-3">
129129
<Button
130-
className="absolute top-3 right-3 z-10"
130+
className="z-10"
131131
variant="subtle"
132132
size="sm"
133133
onClick={onCopy}
134134
>
135135
{codeCopied ? <CheckIcon /> : <CopyIcon />} Copy
136136
</Button>
137-
<div className="h-full w-full bg-surface-secondary font-mono">
138-
<CodeEditor
139-
value={$code}
140-
onValueChange={(code) => $setCode(code)}
141-
highlight={(code) => hightlightWithLineNumbers(code, languages.hcl)}
142-
textareaId="codeArea"
143-
className="editor pt-3"
144-
/>
145-
</div>
137+
</div>
138+
139+
<div className="h-full w-full overflow-y-scroll bg-surface-secondary font-mono">
140+
<CodeEditor
141+
value={$code}
142+
onValueChange={(code) => $setCode(code)}
143+
highlight={(code) => hightlightWithLineNumbers(code, languages.hcl)}
144+
textareaId="codeArea"
145+
className="editor pt-3"
146+
/>
146147
</div>
147148
</ResizablePanel>
148149
);

src/Preview.tsx

Lines changed: 84 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Button } from "@/components/Button";
22
import { ResizablePanel } from "@/components/Resizable";
3+
import { useTheme } from "@/contexts/theme";
34
import {
45
type Diagnostic,
56
type InternalDiagnostic,
@@ -10,7 +11,8 @@ import { useDebouncedValue } from "@/hooks/debounce";
1011
import { useStore } from "@/store";
1112
import { cn } from "@/utils/cn";
1213
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";
1416

1517
export const Preview: FC = () => {
1618
const $wasmState = useStore((state) => state.wasmState);
@@ -87,9 +89,23 @@ export const Preview: FC = () => {
8789
)}
8890
>
8991
<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>
93109
<Button variant="destructive">Reset form</Button>
94110
</div>
95111

@@ -98,9 +114,6 @@ export const Preview: FC = () => {
98114
"flex h-full w-full items-center justify-center overflow-x-clip rounded-xl border p-4",
99115
output && "block overflow-y-scroll",
100116
)}
101-
style={{
102-
opacity: isDebouncing && $wasmState === "loaded" ? 0.5 : 1,
103-
}}
104117
>
105118
{output ? (
106119
<div className="flex flex-col gap-4">
@@ -183,60 +196,75 @@ const ErrorPane = () => {
183196
const $errors = useStore((state) => state.errors);
184197
const $toggleShowError = useStore((state) => state.toggleShowError);
185198

186-
if ($errors.diagnostics.length === 0) {
187-
return null;
188-
}
199+
const hasErrors = useMemo(() => $errors.diagnostics.length > 0, [$errors]);
189200

190201
return (
191202
<>
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>
210223

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>
225246

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>
240268
</>
241269
);
242270
};

0 commit comments

Comments
 (0)