Skip to content

Commit eff01f0

Browse files
authored
feat: add an overlay to show general preview errs (#1974)
This adds an overlay much like the connection error and copy error ones, but it is for general use in the various preview widgets.
1 parent 5b36d04 commit eff01f0

2 files changed

Lines changed: 93 additions & 1 deletion

File tree

frontend/app/view/preview/preview.tsx

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { BlockNodeModel } from "@/app/block/blocktypes";
55
import { Button } from "@/app/element/button";
6+
import { CopyButton } from "@/app/element/copybutton";
67
import { CenteredDiv } from "@/app/element/quickelems";
78
import { TypeAheadModal } from "@/app/modals/typeaheadmodal";
89
import { ContextMenuModel } from "@/app/store/contextmenu";
@@ -39,9 +40,10 @@ import {
3940
} from "@/util/util";
4041
import { Monaco } from "@monaco-editor/react";
4142
import clsx from "clsx";
42-
import { Atom, atom, Getter, PrimitiveAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai";
43+
import { Atom, atom, Getter, PrimitiveAtom, useAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai";
4344
import { loadable } from "jotai/utils";
4445
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
46+
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
4547
import { createRef, memo, useCallback, useEffect, useMemo, useState } from "react";
4648
import { TransformComponent, TransformWrapper, useControls } from "react-zoom-pan-pinch";
4749
import { CSVView } from "./csvview";
@@ -161,6 +163,7 @@ export class PreviewModel implements ViewModel {
161163
fileContent: WritableAtom<Promise<string>, [string], void>;
162164
newFileContent: PrimitiveAtom<string | null>;
163165
connectionError: PrimitiveAtom<string>;
166+
errorMsgAtom: PrimitiveAtom<ErrorMsg>;
164167

165168
openFileModal: PrimitiveAtom<boolean>;
166169
openFileModalDelay: PrimitiveAtom<boolean>;
@@ -195,6 +198,7 @@ export class PreviewModel implements ViewModel {
195198
this.filterOutNowsh = atom(true);
196199
this.monacoRef = createRef();
197200
this.connectionError = atom("");
201+
this.errorMsgAtom = atom(null) as PrimitiveAtom<ErrorMsg | null>;
198202
this.viewIcon = atom((get) => {
199203
const blockData = get(this.blockAtom);
200204
if (blockData?.meta?.icon) {
@@ -1123,6 +1127,7 @@ function PreviewView({
11231127
model: PreviewModel;
11241128
}) {
11251129
const connStatus = useAtomValue(model.connStatus);
1130+
const [errorMsg, setErrorMsg] = useAtom(model.errorMsgAtom);
11261131
if (connStatus?.status != "connected") {
11271132
return null;
11281133
}
@@ -1143,6 +1148,7 @@ function PreviewView({
11431148
<>
11441149
{/* <OpenFileModal blockId={blockId} model={model} blockRef={blockRef} /> */}
11451150
<div key="fullpreview" className="full-preview scrollbar-hide-until-hover">
1151+
{errorMsg && <ErrorOverlay errorMsg={errorMsg} resetOverlay={() => setErrorMsg(null)} />}
11461152
<div ref={contentRef} className="full-preview-content">
11471153
<SpecializedView parentRef={contentRef} model={model} />
11481154
</div>
@@ -1226,4 +1232,76 @@ const OpenFileModal = memo(
12261232
}
12271233
);
12281234

1235+
const ErrorOverlay = memo(({ errorMsg, resetOverlay }: { errorMsg: ErrorMsg; resetOverlay: () => void }) => {
1236+
const showDismiss = errorMsg.showDismiss ?? true;
1237+
const buttonClassName = "outlined grey font-size-11 vertical-padding-3 horizontal-padding-7";
1238+
1239+
let iconClass = "fa-solid fa-circle-exclamation text-[var(--error-color)] text-base";
1240+
if (errorMsg.level == "warning") {
1241+
iconClass = "fa-solid fa-triangle-exclamation text-[var(--warning-color)] text-base";
1242+
}
1243+
1244+
const handleCopyToClipboard = useCallback(async () => {
1245+
await navigator.clipboard.writeText(errorMsg.text);
1246+
}, [errorMsg.text]);
1247+
1248+
return (
1249+
<div className="absolute top-[0] left-1.5 right-1.5 z-[var(--zindex-block-mask-inner)] overflow-hidden bg-[var(--conn-status-overlay-bg-color)] backdrop-blur-[50px] rounded-md shadow-lg">
1250+
<div className="flex flex-row justify-between p-2.5 pl-3 font-[var(--base-font)] text-[var(--secondary-text-color)]">
1251+
<div
1252+
className={clsx("flex flex-row items-center gap-3 grow min-w-0", {
1253+
"items-start": true,
1254+
})}
1255+
>
1256+
<i className={iconClass}></i>
1257+
1258+
<div className="flex flex-col items-start gap-1 grow w-full">
1259+
<div className="max-w-full text-xs font-semibold leading-4 tracking-[0.11px] text-white">
1260+
{errorMsg.status}
1261+
</div>
1262+
1263+
<OverlayScrollbarsComponent
1264+
className="group text-xs font-normal leading-[15px] tracking-[0.11px] text-wrap max-h-20 rounded-lg py-1.5 pl-0 relative w-full"
1265+
options={{ scrollbars: { autoHide: "leave" } }}
1266+
>
1267+
<CopyButton
1268+
className="invisible group-hover:visible flex absolute top-0 right-1 rounded backdrop-blur-lg p-1 items-center justify-end gap-1"
1269+
onClick={handleCopyToClipboard}
1270+
title="Copy"
1271+
/>
1272+
<div>{errorMsg.text}</div>
1273+
</OverlayScrollbarsComponent>
1274+
{errorMsg.buttons?.map((buttonDef) => (
1275+
<Button
1276+
className={buttonClassName}
1277+
onClick={() => {
1278+
buttonDef.onClick();
1279+
resetOverlay();
1280+
}}
1281+
key={crypto.randomUUID()}
1282+
>
1283+
{buttonDef.text}
1284+
</Button>
1285+
))}
1286+
</div>
1287+
1288+
{showDismiss && (
1289+
<div className="flex items-start">
1290+
<Button
1291+
className={clsx(buttonClassName, "fa-xmark fa-solid")}
1292+
onClick={() => {
1293+
if (errorMsg.closeAction) {
1294+
errorMsg.closeAction();
1295+
}
1296+
resetOverlay();
1297+
}}
1298+
/>
1299+
</div>
1300+
)}
1301+
</div>
1302+
</div>
1303+
</div>
1304+
);
1305+
});
1306+
12291307
export { PreviewView };

frontend/types/custom.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,20 @@ declare global {
430430
absParent: string;
431431
relName: string;
432432
};
433+
434+
type ErrorButtonDef = {
435+
text: string;
436+
onClick: () => void;
437+
};
438+
439+
type ErrorMsg = {
440+
status: string;
441+
text: string;
442+
level?: "error" | "warning";
443+
buttons?: Array<ErrorButtonDef>;
444+
closeAction?: () => void;
445+
showDismiss?: boolean;
446+
};
433447
}
434448

435449
export {};

0 commit comments

Comments
 (0)