@malloy-publisher/sdk 0.0.76 → 0.0.78
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/RenderedResult-BAZuT25g.js +117 -0
- package/dist/RenderedResult-DW6glKC-.cjs +1 -0
- package/dist/components/Model/SourcesExplorer.d.ts +1 -1
- package/dist/components/Notebook/NotebookCell.d.ts +1 -2
- package/dist/components/Package/PackageProvider.d.ts +1 -1
- package/dist/components/QueryResult/QueryResult.d.ts +11 -1
- package/dist/components/RenderedResult/RenderedResult.d.ts +3 -4
- package/dist/index.cjs.js +108 -45
- package/dist/index.es.js +26938 -8206
- package/dist/sdk.css +1 -1
- package/package.json +1 -1
- package/src/components/Model/ModelCell.tsx +9 -12
- package/src/components/Model/SourcesExplorer.tsx +89 -23
- package/src/components/MutableNotebook/MutableCell.tsx +3 -3
- package/src/components/Notebook/Notebook.tsx +0 -13
- package/src/components/Notebook/NotebookCell.tsx +9 -2
- package/src/components/Package/PackageProvider.tsx +2 -2
- package/src/components/QueryResult/QueryResult.tsx +70 -1
- package/src/components/RenderedResult/RenderedResult.tsx +124 -101
- package/dist/RenderedResult-C4b9CwSp.cjs +0 -1
- package/dist/RenderedResult-DXlUZo0k.js +0 -79
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/* eslint-disable react/prop-types */
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import React, {
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useState,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
Suspense,
|
|
8
|
+
} from "react";
|
|
5
9
|
|
|
6
|
-
type MalloyRenderElement = HTMLElement &
|
|
10
|
+
type MalloyRenderElement = HTMLElement & Record<string, unknown>;
|
|
7
11
|
|
|
8
12
|
declare global {
|
|
9
13
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
@@ -22,23 +26,25 @@ interface RenderedResultProps {
|
|
|
22
26
|
height?: number;
|
|
23
27
|
isFillElement?: (boolean) => void;
|
|
24
28
|
onSizeChange?: (height: number) => void;
|
|
25
|
-
onDrill?: (element:
|
|
29
|
+
onDrill?: (element: unknown) => void;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
// Simple dynamic import function
|
|
33
|
+
const createRenderer = async (onDrill?: (element: unknown) => void) => {
|
|
34
|
+
// Only import when we're in a browser environment
|
|
35
|
+
if (typeof window === "undefined") {
|
|
36
|
+
throw new Error("MalloyRenderer can only be used in browser environment");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { MalloyRenderer } = await import("@malloydata/render");
|
|
40
|
+
const renderer = new MalloyRenderer({
|
|
41
|
+
onClick: onDrill,
|
|
42
|
+
});
|
|
43
|
+
return renderer.createViz();
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Inner component that actually renders the visualization
|
|
47
|
+
function RenderedResultInner({
|
|
42
48
|
result,
|
|
43
49
|
height,
|
|
44
50
|
isFillElement,
|
|
@@ -47,66 +53,47 @@ export default function RenderedResult({
|
|
|
47
53
|
}: RenderedResultProps) {
|
|
48
54
|
const ref = useRef<HTMLDivElement>(null);
|
|
49
55
|
const [isRendered, setIsRendered] = useState(false);
|
|
50
|
-
const [renderingStarted, setRenderingStarted] = useState(false);
|
|
51
|
-
const [wasMeasured, setWasMeasured] = useState(false);
|
|
52
|
-
// Each component instance manages its own promise and resolver
|
|
53
|
-
const renderingPromiseRef = useRef<Promise<void> | null>(null);
|
|
54
|
-
const renderingResolverRef = useRef<(() => void) | null>(null);
|
|
55
56
|
|
|
56
|
-
//
|
|
57
|
+
// Render the visualization once the component mounts
|
|
57
58
|
useLayoutEffect(() => {
|
|
58
|
-
if (ref.current
|
|
59
|
-
setRenderingStarted(true);
|
|
59
|
+
if (!ref.current || !result) return;
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
renderingPromiseRef.current = new Promise<void>((resolve) => {
|
|
64
|
-
renderingResolverRef.current = resolve;
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const renderer = new MalloyRenderer({
|
|
69
|
-
onClick: onDrill,
|
|
70
|
-
});
|
|
71
|
-
const viz = renderer.createViz();
|
|
61
|
+
let isMounted = true;
|
|
62
|
+
const element = ref.current;
|
|
72
63
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
// Clear previous content
|
|
65
|
+
while (element.firstChild) {
|
|
66
|
+
element.removeChild(element.firstChild);
|
|
67
|
+
}
|
|
77
68
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}, 50); // Small delay to ensure content is fully rendered
|
|
102
|
-
break;
|
|
69
|
+
createRenderer(onDrill)
|
|
70
|
+
.then((viz) => {
|
|
71
|
+
if (!isMounted) return;
|
|
72
|
+
|
|
73
|
+
// Set up a mutation observer to detect when content is added
|
|
74
|
+
const observer = new MutationObserver((mutations) => {
|
|
75
|
+
for (const mutation of mutations) {
|
|
76
|
+
if (
|
|
77
|
+
mutation.type === "childList" &&
|
|
78
|
+
mutation.addedNodes.length > 0
|
|
79
|
+
) {
|
|
80
|
+
const hasContent = Array.from(mutation.addedNodes).some(
|
|
81
|
+
(node) => node.nodeType === Node.ELEMENT_NODE,
|
|
82
|
+
);
|
|
83
|
+
if (hasContent) {
|
|
84
|
+
observer.disconnect();
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
if (isMounted) {
|
|
87
|
+
setIsRendered(true);
|
|
88
|
+
}
|
|
89
|
+
}, 50);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
103
92
|
}
|
|
104
93
|
}
|
|
105
|
-
}
|
|
106
|
-
});
|
|
94
|
+
});
|
|
107
95
|
|
|
108
|
-
|
|
109
|
-
observer.observe(ref.current, {
|
|
96
|
+
observer.observe(element, {
|
|
110
97
|
childList: true,
|
|
111
98
|
subtree: true,
|
|
112
99
|
characterData: true,
|
|
@@ -114,33 +101,27 @@ export default function RenderedResult({
|
|
|
114
101
|
|
|
115
102
|
try {
|
|
116
103
|
viz.setResult(JSON.parse(result));
|
|
117
|
-
viz.render(
|
|
104
|
+
viz.render(element);
|
|
118
105
|
} catch (error) {
|
|
119
106
|
console.error("Error rendering visualization:", error);
|
|
120
107
|
observer.disconnect();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
renderingResolverRef.current();
|
|
124
|
-
renderingResolverRef.current = null;
|
|
125
|
-
renderingPromiseRef.current = null;
|
|
108
|
+
if (isMounted) {
|
|
109
|
+
setIsRendered(true);
|
|
126
110
|
}
|
|
127
111
|
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
112
|
+
})
|
|
113
|
+
.catch((error) => {
|
|
114
|
+
console.error("Failed to create renderer:", error);
|
|
115
|
+
if (isMounted) {
|
|
116
|
+
setIsRendered(true);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return () => {
|
|
121
|
+
isMounted = false;
|
|
122
|
+
};
|
|
123
|
+
}, [result, onDrill]);
|
|
131
124
|
|
|
132
|
-
// Reset rendering state when result changes
|
|
133
|
-
useEffect(() => {
|
|
134
|
-
setIsRendered(false);
|
|
135
|
-
setRenderingStarted(false);
|
|
136
|
-
renderingPromiseRef.current = null;
|
|
137
|
-
renderingResolverRef.current = null;
|
|
138
|
-
}, [result]);
|
|
139
|
-
|
|
140
|
-
// If rendering has started but not completed, throw the promise to trigger Suspense
|
|
141
|
-
if (renderingStarted && !isRendered && renderingPromiseRef.current) {
|
|
142
|
-
throw renderingPromiseRef.current;
|
|
143
|
-
}
|
|
144
125
|
// Set up size measurement using scrollHeight instead of ResizeObserver
|
|
145
126
|
useEffect(() => {
|
|
146
127
|
if (!ref.current || !isRendered) return;
|
|
@@ -151,7 +132,9 @@ export default function RenderedResult({
|
|
|
151
132
|
if (element) {
|
|
152
133
|
const measuredHeight = element.offsetHeight;
|
|
153
134
|
if (measuredHeight > 0) {
|
|
154
|
-
|
|
135
|
+
if (onSizeChange) {
|
|
136
|
+
onSizeChange(measuredHeight);
|
|
137
|
+
}
|
|
155
138
|
} else if (isFillElement && element.firstChild) {
|
|
156
139
|
// HACK- we If there's a child and it's height is 0, then we're in a fill element
|
|
157
140
|
// We use the callback `isFillElement` to notify the parent that we're in a fill element
|
|
@@ -172,14 +155,12 @@ export default function RenderedResult({
|
|
|
172
155
|
|
|
173
156
|
let observer: MutationObserver | null = null;
|
|
174
157
|
// Also measure when the malloy result changes
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
});
|
|
182
|
-
}
|
|
158
|
+
observer = new MutationObserver(measureSize);
|
|
159
|
+
observer.observe(element, {
|
|
160
|
+
childList: true,
|
|
161
|
+
subtree: true,
|
|
162
|
+
attributes: true,
|
|
163
|
+
});
|
|
183
164
|
|
|
184
165
|
// Cleanup
|
|
185
166
|
return () => {
|
|
@@ -198,3 +179,45 @@ export default function RenderedResult({
|
|
|
198
179
|
/>
|
|
199
180
|
);
|
|
200
181
|
}
|
|
182
|
+
|
|
183
|
+
// Main component with error boundary and fallback
|
|
184
|
+
export default function RenderedResult(props: RenderedResultProps) {
|
|
185
|
+
// Show loading state if we're in server-side rendering
|
|
186
|
+
if (typeof window === "undefined") {
|
|
187
|
+
return (
|
|
188
|
+
<div
|
|
189
|
+
style={{
|
|
190
|
+
width: "100%",
|
|
191
|
+
height: props.height ? `${props.height}px` : "100%",
|
|
192
|
+
display: "flex",
|
|
193
|
+
alignItems: "center",
|
|
194
|
+
justifyContent: "center",
|
|
195
|
+
color: "#666",
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
Loading...
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<Suspense
|
|
205
|
+
fallback={
|
|
206
|
+
<div
|
|
207
|
+
style={{
|
|
208
|
+
width: "100%",
|
|
209
|
+
height: props.height ? `${props.height}px` : "100%",
|
|
210
|
+
display: "flex",
|
|
211
|
+
alignItems: "center",
|
|
212
|
+
justifyContent: "center",
|
|
213
|
+
color: "#666",
|
|
214
|
+
}}
|
|
215
|
+
>
|
|
216
|
+
Loading visualization...
|
|
217
|
+
</div>
|
|
218
|
+
}
|
|
219
|
+
>
|
|
220
|
+
<RenderedResultInner {...props} />
|
|
221
|
+
</Suspense>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("react/jsx-runtime"),S=require("@malloydata/render"),t=require("react");function C({result:i,height:b,isFillElement:f,onSizeChange:l,onDrill:m}){const e=t.useRef(null),[a,h]=t.useState(!1),[R,v]=t.useState(!1),[y,N]=t.useState(!1),s=t.useRef(null),n=t.useRef(null);if(t.useLayoutEffect(()=>{if(e.current&&i&&!R){v(!0),s.current||(s.current=new Promise(r=>{n.current=r}));const o=new S.MalloyRenderer({onClick:m}).createViz();for(;e.current.firstChild;)e.current.removeChild(e.current.firstChild);const d=new MutationObserver(r=>{for(const c of r)if(c.type==="childList"&&c.addedNodes.length>0&&Array.from(c.addedNodes).some(g=>g.nodeType===Node.ELEMENT_NODE)){d.disconnect(),setTimeout(()=>{h(!0),n.current&&(n.current(),n.current=null,s.current=null)},50);break}});if(e.current){d.observe(e.current,{childList:!0,subtree:!0,characterData:!0});try{o.setResult(JSON.parse(i)),o.render(e.current)}catch(r){console.error("Error rendering visualization:",r),d.disconnect(),h(!0),n.current&&(n.current(),n.current=null,s.current=null)}}}},[i,m,R]),t.useEffect(()=>{h(!1),v(!1),s.current=null,n.current=null},[i]),R&&!a&&s.current)throw s.current;return t.useEffect(()=>{if(!e.current||!a)return;const u=e.current,o=()=>{if(u){const c=u.offsetHeight;c>0?l&&l(c):f&&u.firstChild&&(u.firstChild.offsetHeight==0?f(!0):f(!1))}},d=setTimeout(o,100);let r=null;return y||(r=new MutationObserver(o),r.observe(u,{childList:!0,subtree:!0,attributes:!0})),()=>{clearTimeout(d),r?.disconnect()}},[l,i,f,a]),M.jsx("div",{ref:e,style:{width:"100%",height:b?`${b}px`:"100%"}})}exports.default=C;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { jsx as M } from "react/jsx-runtime";
|
|
2
|
-
import { MalloyRenderer as N } from "@malloydata/render";
|
|
3
|
-
import { useRef as m, useState as v, useLayoutEffect as H, useEffect as w } from "react";
|
|
4
|
-
function x({
|
|
5
|
-
result: c,
|
|
6
|
-
height: R,
|
|
7
|
-
isFillElement: d,
|
|
8
|
-
onSizeChange: f,
|
|
9
|
-
onDrill: b
|
|
10
|
-
}) {
|
|
11
|
-
const e = m(null), [l, a] = v(!1), [h, g] = v(!1), [y, L] = v(!1), n = m(null), t = m(null);
|
|
12
|
-
if (H(() => {
|
|
13
|
-
if (e.current && c && !h) {
|
|
14
|
-
g(!0), n.current || (n.current = new Promise((r) => {
|
|
15
|
-
t.current = r;
|
|
16
|
-
}));
|
|
17
|
-
const i = new N({
|
|
18
|
-
onClick: b
|
|
19
|
-
}).createViz();
|
|
20
|
-
for (; e.current.firstChild; )
|
|
21
|
-
e.current.removeChild(e.current.firstChild);
|
|
22
|
-
const o = new MutationObserver((r) => {
|
|
23
|
-
for (const u of r)
|
|
24
|
-
if (u.type === "childList" && u.addedNodes.length > 0 && Array.from(u.addedNodes).some(
|
|
25
|
-
(p) => p.nodeType === Node.ELEMENT_NODE
|
|
26
|
-
)) {
|
|
27
|
-
o.disconnect(), setTimeout(() => {
|
|
28
|
-
a(!0), t.current && (t.current(), t.current = null, n.current = null);
|
|
29
|
-
}, 50);
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
if (e.current) {
|
|
34
|
-
o.observe(e.current, {
|
|
35
|
-
childList: !0,
|
|
36
|
-
subtree: !0,
|
|
37
|
-
characterData: !0
|
|
38
|
-
});
|
|
39
|
-
try {
|
|
40
|
-
i.setResult(JSON.parse(c)), i.render(e.current);
|
|
41
|
-
} catch (r) {
|
|
42
|
-
console.error("Error rendering visualization:", r), o.disconnect(), a(!0), t.current && (t.current(), t.current = null, n.current = null);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}, [c, b, h]), w(() => {
|
|
47
|
-
a(!1), g(!1), n.current = null, t.current = null;
|
|
48
|
-
}, [c]), h && !l && n.current)
|
|
49
|
-
throw n.current;
|
|
50
|
-
return w(() => {
|
|
51
|
-
if (!e.current || !l) return;
|
|
52
|
-
const s = e.current, i = () => {
|
|
53
|
-
if (s) {
|
|
54
|
-
const u = s.offsetHeight;
|
|
55
|
-
u > 0 ? f && f(u) : d && s.firstChild && (s.firstChild.offsetHeight == 0 ? d(!0) : d(!1));
|
|
56
|
-
}
|
|
57
|
-
}, o = setTimeout(i, 100);
|
|
58
|
-
let r = null;
|
|
59
|
-
return y || (r = new MutationObserver(i), r.observe(s, {
|
|
60
|
-
childList: !0,
|
|
61
|
-
subtree: !0,
|
|
62
|
-
attributes: !0
|
|
63
|
-
})), () => {
|
|
64
|
-
clearTimeout(o), r?.disconnect();
|
|
65
|
-
};
|
|
66
|
-
}, [f, c, d, l]), /* @__PURE__ */ M(
|
|
67
|
-
"div",
|
|
68
|
-
{
|
|
69
|
-
ref: e,
|
|
70
|
-
style: {
|
|
71
|
-
width: "100%",
|
|
72
|
-
height: R ? `${R}px` : "100%"
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
export {
|
|
78
|
-
x as default
|
|
79
|
-
};
|