@malloy-publisher/sdk 0.0.127 → 0.0.129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Model/Model.d.ts +3 -1
- package/dist/components/Model/ModelCell.d.ts +3 -1
- package/dist/components/Notebook/Notebook.d.ts +2 -1
- package/dist/components/Notebook/NotebookCell.d.ts +2 -1
- package/dist/components/RenderedResult/ResultContainer.d.ts +2 -1
- package/dist/index.cjs.js +73 -73
- package/dist/index.es.js +3892 -3800
- package/package.json +1 -1
- package/src/components/Model/Model.tsx +10 -1
- package/src/components/Model/ModelCell.tsx +53 -11
- package/src/components/Notebook/Notebook.tsx +8 -3
- package/src/components/Notebook/NotebookCell.tsx +3 -0
- package/src/components/Package/Connections.tsx +14 -4
- package/src/components/RenderedResult/ResultContainer.tsx +44 -2
package/package.json
CHANGED
|
@@ -22,13 +22,20 @@ import { useModelData } from "./useModelData";
|
|
|
22
22
|
interface ModelProps {
|
|
23
23
|
onChange?: (query: QueryExplorerResult) => void;
|
|
24
24
|
resourceUri: string;
|
|
25
|
+
runOnDemand?: boolean;
|
|
26
|
+
maxResultSize?: number;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
// Note: For this to properly render outside of publisher,
|
|
28
30
|
// you must explicitly import the styles from the package:
|
|
29
31
|
// import "@malloy-publisher/sdk/malloy-explorer.css";
|
|
30
32
|
|
|
31
|
-
export default function Model({
|
|
33
|
+
export default function Model({
|
|
34
|
+
onChange,
|
|
35
|
+
resourceUri,
|
|
36
|
+
runOnDemand = false,
|
|
37
|
+
maxResultSize = 0,
|
|
38
|
+
}: ModelProps) {
|
|
32
39
|
const { modelPath } = parseResourceUri(resourceUri);
|
|
33
40
|
const { data, isError, isLoading, error } = useModelData(resourceUri);
|
|
34
41
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
@@ -184,6 +191,8 @@ export default function Model({ onChange, resourceUri }: ModelProps) {
|
|
|
184
191
|
queryName={query.name}
|
|
185
192
|
annotations={query.annotations}
|
|
186
193
|
resourceUri={resourceUri}
|
|
194
|
+
runOnDemand={runOnDemand}
|
|
195
|
+
maxResultSize={maxResultSize}
|
|
187
196
|
/>
|
|
188
197
|
))}
|
|
189
198
|
</Stack>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
|
1
2
|
import SearchIcon from "@mui/icons-material/Search";
|
|
2
|
-
import { Box, IconButton, Typography } from "@mui/material";
|
|
3
|
+
import { Box, Button, IconButton, Typography } from "@mui/material";
|
|
3
4
|
import React, { useEffect } from "react";
|
|
4
5
|
import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
|
|
5
6
|
import { parseResourceUri } from "../../utils/formatting";
|
|
@@ -15,16 +16,21 @@ interface ModelCellProps {
|
|
|
15
16
|
noView?: boolean;
|
|
16
17
|
annotations?: string[];
|
|
17
18
|
resourceUri: string;
|
|
19
|
+
runOnDemand?: boolean;
|
|
20
|
+
maxResultSize?: number;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export function ModelCell({
|
|
21
24
|
queryName,
|
|
22
25
|
annotations,
|
|
23
26
|
resourceUri,
|
|
27
|
+
runOnDemand = false,
|
|
28
|
+
maxResultSize = 0,
|
|
24
29
|
}: ModelCellProps) {
|
|
25
30
|
const [highlightedAnnotations, setHighlightedAnnotations] =
|
|
26
31
|
React.useState<string>();
|
|
27
32
|
const [resultsDialogOpen, setResultsDialogOpen] = React.useState(false);
|
|
33
|
+
const [hasRun, setHasRun] = React.useState(false);
|
|
28
34
|
|
|
29
35
|
const { packageName, projectName, versionId, modelPath } =
|
|
30
36
|
parseResourceUri(resourceUri);
|
|
@@ -48,7 +54,7 @@ export function ModelCell({
|
|
|
48
54
|
versionId: versionId,
|
|
49
55
|
},
|
|
50
56
|
),
|
|
51
|
-
enabled: true, //
|
|
57
|
+
enabled: runOnDemand ? hasRun : true, // Execute on demand or always
|
|
52
58
|
});
|
|
53
59
|
|
|
54
60
|
useEffect(() => {
|
|
@@ -126,19 +132,55 @@ export function ModelCell({
|
|
|
126
132
|
position: "relative",
|
|
127
133
|
}}
|
|
128
134
|
>
|
|
129
|
-
{
|
|
135
|
+
{runOnDemand && !hasRun && (
|
|
136
|
+
<Box
|
|
137
|
+
sx={{
|
|
138
|
+
padding: "40px 20px",
|
|
139
|
+
textAlign: "center",
|
|
140
|
+
display: "flex",
|
|
141
|
+
flexDirection: "column",
|
|
142
|
+
alignItems: "center",
|
|
143
|
+
gap: "16px",
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
<Typography variant="body2" color="text.secondary">
|
|
147
|
+
Click Run to execute the query
|
|
148
|
+
</Typography>
|
|
149
|
+
<Button
|
|
150
|
+
variant="contained"
|
|
151
|
+
startIcon={<PlayArrowIcon />}
|
|
152
|
+
onClick={() => setHasRun(true)}
|
|
153
|
+
sx={{
|
|
154
|
+
backgroundColor: "#1976d2",
|
|
155
|
+
"&:hover": {
|
|
156
|
+
backgroundColor: "#1565c0",
|
|
157
|
+
},
|
|
158
|
+
textTransform: "none",
|
|
159
|
+
fontSize: "14px",
|
|
160
|
+
fontWeight: 600,
|
|
161
|
+
padding: "8px 24px",
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
Run Query
|
|
165
|
+
</Button>
|
|
166
|
+
</Box>
|
|
167
|
+
)}
|
|
168
|
+
{(!runOnDemand || hasRun) && isLoading && (
|
|
130
169
|
<Box sx={{ padding: "20px", textAlign: "center" }}>
|
|
131
170
|
<Typography>Loading results...</Typography>
|
|
132
171
|
</Box>
|
|
133
172
|
)}
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
173
|
+
{(!runOnDemand || hasRun) &&
|
|
174
|
+
isSuccess &&
|
|
175
|
+
queryData?.data?.result && (
|
|
176
|
+
<ResultContainer
|
|
177
|
+
result={queryData.data.result}
|
|
178
|
+
minHeight={300}
|
|
179
|
+
maxHeight={600}
|
|
180
|
+
hideToggle={false}
|
|
181
|
+
maxResultSize={maxResultSize}
|
|
182
|
+
/>
|
|
183
|
+
)}
|
|
142
184
|
</CleanMetricCard>
|
|
143
185
|
|
|
144
186
|
{/* Results Dialog */}
|
|
@@ -4,18 +4,22 @@ import { CompiledNotebook } from "../../client";
|
|
|
4
4
|
import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
|
|
5
5
|
import { ApiErrorDisplay } from "../ApiErrorDisplay";
|
|
6
6
|
|
|
7
|
+
import { parseResourceUri } from "../../utils/formatting";
|
|
7
8
|
import { Loading } from "../Loading";
|
|
9
|
+
import { useServer } from "../ServerProvider";
|
|
8
10
|
import { CleanNotebookContainer, CleanNotebookSection } from "../styles";
|
|
9
11
|
import { NotebookCell } from "./NotebookCell";
|
|
10
|
-
import { parseResourceUri } from "../../utils/formatting";
|
|
11
|
-
import { useServer } from "../ServerProvider";
|
|
12
12
|
|
|
13
13
|
interface NotebookProps {
|
|
14
14
|
resourceUri: string;
|
|
15
|
+
maxResultSize?: number;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
// Requires PackageProvider
|
|
18
|
-
export default function Notebook({
|
|
19
|
+
export default function Notebook({
|
|
20
|
+
resourceUri,
|
|
21
|
+
maxResultSize = 0,
|
|
22
|
+
}: NotebookProps) {
|
|
19
23
|
const { apiClients } = useServer();
|
|
20
24
|
const {
|
|
21
25
|
projectName,
|
|
@@ -55,6 +59,7 @@ export default function Notebook({ resourceUri }: NotebookProps) {
|
|
|
55
59
|
key={index}
|
|
56
60
|
index={index}
|
|
57
61
|
resourceUri={resourceUri}
|
|
62
|
+
maxResultSize={maxResultSize}
|
|
58
63
|
/>
|
|
59
64
|
))}
|
|
60
65
|
{isError && error.status === 404 && (
|
|
@@ -32,6 +32,7 @@ interface NotebookCellProps {
|
|
|
32
32
|
hideEmbeddingIcon?: boolean;
|
|
33
33
|
resourceUri: string;
|
|
34
34
|
index: number;
|
|
35
|
+
maxResultSize?: number;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export function NotebookCell({
|
|
@@ -40,6 +41,7 @@ export function NotebookCell({
|
|
|
40
41
|
hideEmbeddingIcon,
|
|
41
42
|
resourceUri,
|
|
42
43
|
index,
|
|
44
|
+
maxResultSize,
|
|
43
45
|
}: NotebookCellProps) {
|
|
44
46
|
const [codeDialogOpen, setCodeDialogOpen] = React.useState<boolean>(false);
|
|
45
47
|
const [embeddingDialogOpen, setEmbeddingDialogOpen] =
|
|
@@ -420,6 +422,7 @@ export function NotebookCell({
|
|
|
420
422
|
result={cell.result}
|
|
421
423
|
minHeight={300}
|
|
422
424
|
maxHeight={1000}
|
|
425
|
+
maxResultSize={maxResultSize}
|
|
423
426
|
/>
|
|
424
427
|
</Box>
|
|
425
428
|
|
|
@@ -36,7 +36,7 @@ type ConnectionProps = {
|
|
|
36
36
|
connection: ApiConnection;
|
|
37
37
|
onClick: () => void;
|
|
38
38
|
onEdit: (connection: ApiConnection) => Promise<unknown>;
|
|
39
|
-
onDelete: (connection: ApiConnection) => Promise<unknown
|
|
39
|
+
onDelete: (connection: ApiConnection) => Promise<unknown> | void;
|
|
40
40
|
isMutating: boolean;
|
|
41
41
|
mutable: boolean;
|
|
42
42
|
};
|
|
@@ -274,9 +274,19 @@ export default function Connections({ resourceUri }: ConnectionsProps) {
|
|
|
274
274
|
onEdit={(payload) =>
|
|
275
275
|
updateConnection.mutateAsync(payload)
|
|
276
276
|
}
|
|
277
|
-
onDelete={(payload) =>
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
onDelete={(payload) => {
|
|
278
|
+
if (
|
|
279
|
+
!selectedConnectionResourceUri.startsWith(
|
|
280
|
+
"publisher:",
|
|
281
|
+
)
|
|
282
|
+
) {
|
|
283
|
+
deleteConnection.mutateAsync(payload);
|
|
284
|
+
} else {
|
|
285
|
+
setNotificationMessage(
|
|
286
|
+
"Cannot delete this connection (publisher: resource)",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}}
|
|
280
290
|
isMutating={
|
|
281
291
|
updateConnection.isPending ||
|
|
282
292
|
deleteConnection.isPending
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ExpandLess, ExpandMore } from "@mui/icons-material";
|
|
2
|
-
import { Box, IconButton } from "@mui/material";
|
|
1
|
+
import { ExpandLess, ExpandMore, Warning } from "@mui/icons-material";
|
|
2
|
+
import { Box, Button, IconButton, Typography } from "@mui/material";
|
|
3
3
|
import {
|
|
4
4
|
lazy,
|
|
5
5
|
Suspense,
|
|
@@ -17,6 +17,10 @@ interface ResultContainerProps {
|
|
|
17
17
|
minHeight: number;
|
|
18
18
|
maxHeight: number;
|
|
19
19
|
hideToggle?: boolean;
|
|
20
|
+
// if Results are larger than this size, show a warning and a button to proceed
|
|
21
|
+
// this is to prevent performance issues with large results.
|
|
22
|
+
// the default is 0, which means no warning will be shown.
|
|
23
|
+
maxResultSize?: number;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
// ResultContainer is a component that renders a result, with a toggle button to expand/collapse the result.
|
|
@@ -28,6 +32,7 @@ export default function ResultContainer({
|
|
|
28
32
|
minHeight,
|
|
29
33
|
maxHeight,
|
|
30
34
|
hideToggle = false,
|
|
35
|
+
maxResultSize = 0,
|
|
31
36
|
}: ResultContainerProps) {
|
|
32
37
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
33
38
|
const [contentHeight, setContentHeight] = useState<number>(0);
|
|
@@ -36,6 +41,7 @@ export default function ResultContainer({
|
|
|
36
41
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
37
42
|
const [explicitHeight, setExplicitHeight] = useState<number>(undefined);
|
|
38
43
|
const [isFillElement, setIsFillElement] = useState(false);
|
|
44
|
+
const [userAcknowledged, setUserAcknowledged] = useState(false);
|
|
39
45
|
const handleToggle = useCallback(() => {
|
|
40
46
|
const wasExpanded = isExpanded;
|
|
41
47
|
setIsExpanded(!isExpanded);
|
|
@@ -85,6 +91,42 @@ export default function ResultContainer({
|
|
|
85
91
|
if (!result) {
|
|
86
92
|
return null;
|
|
87
93
|
}
|
|
94
|
+
|
|
95
|
+
// Check if result exceeds max size and user hasn't acknowledged yet
|
|
96
|
+
const exceedsMaxSize = maxResultSize > 0 && result.length > maxResultSize;
|
|
97
|
+
if (exceedsMaxSize && !userAcknowledged) {
|
|
98
|
+
return (
|
|
99
|
+
<Box
|
|
100
|
+
sx={{
|
|
101
|
+
minHeight: `${minHeight}px`,
|
|
102
|
+
display: "flex",
|
|
103
|
+
flexDirection: "column",
|
|
104
|
+
alignItems: "center",
|
|
105
|
+
justifyContent: "center",
|
|
106
|
+
gap: 2,
|
|
107
|
+
p: 4,
|
|
108
|
+
backgroundColor: "#fafafa",
|
|
109
|
+
border: "1px solid",
|
|
110
|
+
borderColor: "#e0e0e0",
|
|
111
|
+
borderRadius: 1,
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<Warning sx={{ fontSize: 48, color: "#757575" }} />
|
|
115
|
+
<Typography variant="h6" color="text.secondary" align="center">
|
|
116
|
+
Processing large results may cause browser performance issues.
|
|
117
|
+
Proceed?
|
|
118
|
+
</Typography>
|
|
119
|
+
<Button
|
|
120
|
+
variant="contained"
|
|
121
|
+
color="primary"
|
|
122
|
+
onClick={() => setUserAcknowledged(true)}
|
|
123
|
+
>
|
|
124
|
+
Proceed
|
|
125
|
+
</Button>
|
|
126
|
+
</Box>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
88
130
|
const loading = <Loading text="Loading..." centered={true} size={32} />;
|
|
89
131
|
const renderedHeight = isFillElement
|
|
90
132
|
? isExpanded
|