@marimo-team/islands 0.21.1-dev11 → 0.21.1-dev13
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/{ConnectedDataExplorerComponent-Bh11efrC.js → ConnectedDataExplorerComponent-Bgr96_Mw.js} +5 -4
- package/dist/{loader-DsE3MiYo.js → loader-V1UqqlAy.js} +11 -2
- package/dist/main.js +39 -35
- package/dist/style.css +1 -1
- package/dist/{useDeepCompareMemoize-DLS-bHHT.js → useDeepCompareMemoize-CJr2MRe6.js} +1 -1
- package/dist/{vega-component-CnO3mkFC.js → vega-component-Cq-KtS_2.js} +7 -4
- package/package.json +1 -1
- package/src/components/data-table/charts/lazy-chart.tsx +2 -0
- package/src/components/data-table/column-summary/column-summary.tsx +1 -0
- package/src/components/datasources/column-preview.tsx +1 -0
- package/src/components/editor/Output.tsx +2 -0
- package/src/components/editor/chrome/panels/context-aware-panel/context-aware-panel.tsx +6 -0
- package/src/components/editor/chrome/wrapper/__tests__/utils.test.ts +35 -0
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +12 -14
- package/src/components/editor/chrome/wrapper/utils.ts +5 -1
- package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +6 -3
- package/src/plugins/impl/vega/__tests__/utils.test.ts +36 -0
- package/src/plugins/impl/vega/utils.ts +14 -0
- package/src/plugins/impl/vega/vega-component.tsx +6 -7
- package/src/plugins/impl/vega/vega.css +1 -1
|
@@ -9,7 +9,7 @@ import { a as cva, g as Logger, y as cn } from "./button-DQpBib29.js";
|
|
|
9
9
|
import { t as require_jsx_runtime } from "./jsx-runtime-CTBg5pdT.js";
|
|
10
10
|
import { r as KnownQueryParams } from "./constants-CytQ_3LM.js";
|
|
11
11
|
import { f as waitFor, p as isIslands, u as store, y as atom } from "./useTheme-D0rdoMBF.js";
|
|
12
|
-
import { i as tableFromIPC } from "./loader-
|
|
12
|
+
import { i as tableFromIPC } from "./loader-V1UqqlAy.js";
|
|
13
13
|
var CircleQuestionMark = createLucideIcon("circle-question-mark", [
|
|
14
14
|
["circle", {
|
|
15
15
|
cx: "12",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
2
2
|
import { t as require_react } from "./react-Bs6Z0kvn.js";
|
|
3
3
|
import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
|
|
4
|
-
import { S as CircleQuestionMark, a as AlertTitle, m as asRemoteURL, n as arrow, o as isValid, r as Alert, t as useDeepCompareMemoize } from "./useDeepCompareMemoize-
|
|
4
|
+
import { S as CircleQuestionMark, a as AlertTitle, m as asRemoteURL, n as arrow, o as isValid, r as Alert, t as useDeepCompareMemoize } from "./useDeepCompareMemoize-CJr2MRe6.js";
|
|
5
5
|
import { d as Objects, g as Logger, h as Events, y as cn } from "./button-DQpBib29.js";
|
|
6
6
|
import "./Combination-Dk6JxauT.js";
|
|
7
7
|
import { t as require_jsx_runtime } from "./jsx-runtime-CTBg5pdT.js";
|
|
@@ -9,7 +9,7 @@ import "./react-dom-CqtLRVZP.js";
|
|
|
9
9
|
import { t as Tooltip } from "./tooltip-SPkubVH3.js";
|
|
10
10
|
import { i as debounce_default } from "./constants-CytQ_3LM.js";
|
|
11
11
|
import { C as useEvent_default, n as useTheme } from "./useTheme-D0rdoMBF.js";
|
|
12
|
-
import { a as
|
|
12
|
+
import { a as getContainerWidth, n as vegaLoadData, s as tooltipHandler } from "./loader-V1UqqlAy.js";
|
|
13
13
|
import { t as uniq_default } from "./uniq-H2E5nMLq.js";
|
|
14
14
|
import { n as ErrorBanner } from "./error-banner-BctofTCP.js";
|
|
15
15
|
import { n as formats } from "./vega-loader.browser-CQ-lnUkI.js";
|
|
@@ -598,9 +598,12 @@ var VegaComponent = (e) => {
|
|
|
598
598
|
children: B.stack
|
|
599
599
|
})]
|
|
600
600
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
601
|
-
className: cn("relative"
|
|
601
|
+
className: cn("relative"),
|
|
602
602
|
onPointerDown: Events.stopPropagation(),
|
|
603
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
603
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
604
|
+
ref: R,
|
|
605
|
+
"data-container-width": getContainerWidth(W)
|
|
606
|
+
}), Q()]
|
|
604
607
|
})] });
|
|
605
608
|
};
|
|
606
609
|
function convertSetToList(e) {
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import React from "react";
|
|
|
4
4
|
import type { TopLevelSpec } from "vega-lite";
|
|
5
5
|
import { LazyVegaEmbed } from "@/components/charts/lazy";
|
|
6
6
|
import { tooltipHandler } from "@/components/charts/tooltip";
|
|
7
|
+
import { getContainerWidth } from "@/plugins/impl/vega/utils";
|
|
7
8
|
import { useTheme } from "@/theme/useTheme";
|
|
8
9
|
import type { ErrorMessage } from "./chart-spec/spec";
|
|
9
10
|
import { augmentSpecWithData } from "./chart-spec/spec";
|
|
@@ -30,6 +31,7 @@ export const LazyChart: React.FC<{
|
|
|
30
31
|
<React.Suspense fallback={<LoadingChart />}>
|
|
31
32
|
<LazyVegaEmbed
|
|
32
33
|
spec={spec}
|
|
34
|
+
data-container-width={getContainerWidth(spec)}
|
|
33
35
|
options={{
|
|
34
36
|
theme: theme === "dark" ? "dark" : undefined,
|
|
35
37
|
height: height,
|
|
@@ -224,6 +224,7 @@ export function renderChart(chartSpec: string, theme: Theme) {
|
|
|
224
224
|
return (
|
|
225
225
|
<Suspense fallback={LoadingChart}>
|
|
226
226
|
<LazyVegaEmbed
|
|
227
|
+
data-container-width="container"
|
|
227
228
|
spec={updateSpec(JSON.parse(chartSpec) as TopLevelFacetedUnitSpec)}
|
|
228
229
|
options={{
|
|
229
230
|
theme: theme === "dark" ? "dark" : "vox",
|
|
@@ -27,6 +27,7 @@ import { useOverflowDetection } from "@/hooks/useOverflowDetection";
|
|
|
27
27
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
28
28
|
import { Banner } from "@/plugins/impl/common/error-banner";
|
|
29
29
|
import type { TopLevelFacetedUnitSpec } from "@/plugins/impl/data-explorer/queries/types";
|
|
30
|
+
import { getContainerWidth } from "@/plugins/impl/vega/utils";
|
|
30
31
|
import { useTheme } from "@/theme/useTheme";
|
|
31
32
|
import { Events } from "@/utils/events";
|
|
32
33
|
import { invariant } from "@/utils/invariant";
|
|
@@ -205,6 +206,7 @@ export const OutputRenderer: React.FC<{
|
|
|
205
206
|
<Suspense fallback={<ChartLoadingState />}>
|
|
206
207
|
<LazyVegaEmbed
|
|
207
208
|
spec={parsedJsonData as TopLevelFacetedUnitSpec}
|
|
209
|
+
data-container-width={getContainerWidth(parsedJsonData)}
|
|
208
210
|
options={{
|
|
209
211
|
theme: theme === "dark" ? "dark" : undefined,
|
|
210
212
|
mode: "vega-lite",
|
|
@@ -5,6 +5,7 @@ import { useAtom } from "jotai";
|
|
|
5
5
|
import { CrosshairIcon, PinIcon, PinOffIcon, XIcon } from "lucide-react";
|
|
6
6
|
import type { PropsWithChildren } from "react";
|
|
7
7
|
import { Panel, PanelResizeHandle } from "react-resizable-panels";
|
|
8
|
+
import { raf2 } from "@/components/editor/navigation/focus-utils";
|
|
8
9
|
import { Button } from "@/components/ui/button";
|
|
9
10
|
import { Toggle } from "@/components/ui/toggle";
|
|
10
11
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
@@ -151,6 +152,11 @@ const ResizableComponent = ({ children }: ResizableComponentProps) => {
|
|
|
151
152
|
startingWidth: 400,
|
|
152
153
|
minWidth: 300,
|
|
153
154
|
maxWidth: 1500,
|
|
155
|
+
onResize: () => {
|
|
156
|
+
raf2(() => {
|
|
157
|
+
window.dispatchEvent(new Event("resize"));
|
|
158
|
+
});
|
|
159
|
+
},
|
|
154
160
|
});
|
|
155
161
|
|
|
156
162
|
return (
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { handleDragging } from "../utils";
|
|
5
|
+
|
|
6
|
+
describe("handleDragging", () => {
|
|
7
|
+
it("should dispatch a resize event after dragging ends", async () => {
|
|
8
|
+
const listener = vi.fn();
|
|
9
|
+
window.addEventListener("resize", listener);
|
|
10
|
+
|
|
11
|
+
handleDragging(false);
|
|
12
|
+
|
|
13
|
+
// raf2: wait two animation frames
|
|
14
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
15
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
16
|
+
|
|
17
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
18
|
+
|
|
19
|
+
window.removeEventListener("resize", listener);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should not dispatch a resize event while dragging", async () => {
|
|
23
|
+
const listener = vi.fn();
|
|
24
|
+
window.addEventListener("resize", listener);
|
|
25
|
+
|
|
26
|
+
handleDragging(true);
|
|
27
|
+
|
|
28
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
29
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
30
|
+
|
|
31
|
+
expect(listener).not.toHaveBeenCalled();
|
|
32
|
+
|
|
33
|
+
window.removeEventListener("resize", listener);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -17,6 +17,7 @@ import "./app-chrome.css";
|
|
|
17
17
|
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
|
18
18
|
import { useAtom, useAtomValue } from "jotai";
|
|
19
19
|
import { XIcon } from "lucide-react";
|
|
20
|
+
import useEvent from "react-use-event-hook";
|
|
20
21
|
import { Button } from "@/components/ui/button";
|
|
21
22
|
import { ReorderableList } from "@/components/ui/reorderable-list";
|
|
22
23
|
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
@@ -26,6 +27,7 @@ import { capabilitiesAtom } from "@/core/config/capabilities";
|
|
|
26
27
|
import { getFeatureFlag } from "@/core/config/feature-flag";
|
|
27
28
|
import { cn } from "@/utils/cn";
|
|
28
29
|
import { ErrorBoundary } from "../../boundary/ErrorBoundary";
|
|
30
|
+
import { raf2 } from "../../navigation/focus-utils";
|
|
29
31
|
import { ContextAwarePanel } from "../panels/context-aware-panel/context-aware-panel";
|
|
30
32
|
import { PanelSectionProvider } from "../panels/panel-context";
|
|
31
33
|
import { panelLayoutAtom, useChromeActions, useChromeState } from "../state";
|
|
@@ -147,6 +149,14 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
147
149
|
});
|
|
148
150
|
}, [panelLayout.sidebar, capabilities]);
|
|
149
151
|
|
|
152
|
+
const emitResizeEvent = useEvent(() => {
|
|
153
|
+
// HACK: Unfortunately, we have to do this twice to make sure the
|
|
154
|
+
// panel is fully expanded before we dispatch the resize event
|
|
155
|
+
raf2(() => {
|
|
156
|
+
window.dispatchEvent(new Event("resize"));
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
150
160
|
// sync sidebar
|
|
151
161
|
useEffect(() => {
|
|
152
162
|
if (!sidebarRef.current) {
|
|
@@ -162,13 +172,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
// Dispatch a resize event so widgets know to resize
|
|
165
|
-
|
|
166
|
-
// HACK: Unfortunately, we have to do this twice to make sure it the
|
|
167
|
-
// panel is fully expanded before we dispatch the resize event
|
|
168
|
-
requestAnimationFrame(() => {
|
|
169
|
-
window.dispatchEvent(new Event("resize"));
|
|
170
|
-
});
|
|
171
|
-
});
|
|
175
|
+
emitResizeEvent();
|
|
172
176
|
}, [isSidebarOpen]);
|
|
173
177
|
|
|
174
178
|
// sync panel
|
|
@@ -186,13 +190,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
186
190
|
}
|
|
187
191
|
|
|
188
192
|
// Dispatch a resize event so widgets know to resize
|
|
189
|
-
|
|
190
|
-
// HACK: Unfortunately, we have to do this twice to make sure it the
|
|
191
|
-
// panel is fully expanded before we dispatch the resize event
|
|
192
|
-
requestAnimationFrame(() => {
|
|
193
|
-
window.dispatchEvent(new Event("resize"));
|
|
194
|
-
});
|
|
195
|
-
});
|
|
193
|
+
emitResizeEvent();
|
|
196
194
|
}, [isDeveloperPanelOpen]);
|
|
197
195
|
|
|
198
196
|
// Auto-correct developer panel selection when the selected tab is no longer available
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import { raf2 } from "../../navigation/focus-utils";
|
|
4
|
+
|
|
3
5
|
export function handleDragging(isDragging: boolean) {
|
|
4
6
|
if (!isDragging) {
|
|
5
7
|
// Once the user is done dragging, dispatch a resize event
|
|
6
|
-
|
|
8
|
+
raf2(() => {
|
|
9
|
+
window.dispatchEvent(new Event("resize"));
|
|
10
|
+
});
|
|
7
11
|
}
|
|
8
12
|
}
|
|
@@ -17,6 +17,7 @@ import { cn } from "@/utils/cn";
|
|
|
17
17
|
import { Objects } from "@/utils/objects";
|
|
18
18
|
import { ErrorBanner } from "../common/error-banner";
|
|
19
19
|
import { vegaLoadData } from "../vega/loader";
|
|
20
|
+
import { getContainerWidth } from "../vega/utils";
|
|
20
21
|
import { ColumnSummary } from "./components/column-summary";
|
|
21
22
|
import { QueryForm } from "./components/query-form";
|
|
22
23
|
import type { SpecificEncoding } from "./encoding";
|
|
@@ -140,16 +141,18 @@ export const DataExplorerComponent = ({
|
|
|
140
141
|
const responsiveSpec = makeResponsive(spec);
|
|
141
142
|
// TODO: We can optimize by updating the data dynamically. https://github.com/vega/react-vega?tab=readme-ov-file#recipes
|
|
142
143
|
const augmentedSpec = augmentSpecWithData(responsiveSpec, chartData);
|
|
143
|
-
const isContainerWidth = responsiveSpec.width === "container";
|
|
144
144
|
|
|
145
145
|
return (
|
|
146
146
|
<div
|
|
147
147
|
className={cn(
|
|
148
148
|
"flex overflow-y-auto justify-center items-center flex-1 w-[90%]",
|
|
149
|
-
isContainerWidth && "vega-container-width",
|
|
150
149
|
)}
|
|
151
150
|
>
|
|
152
|
-
<VegaEmbed
|
|
151
|
+
<VegaEmbed
|
|
152
|
+
data-container-width={getContainerWidth(augmentedSpec)}
|
|
153
|
+
spec={augmentedSpec}
|
|
154
|
+
options={chartOptions(theme)}
|
|
155
|
+
/>
|
|
153
156
|
</div>
|
|
154
157
|
);
|
|
155
158
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { getContainerWidth } from "../utils";
|
|
5
|
+
|
|
6
|
+
describe("getContainerWidth", () => {
|
|
7
|
+
it('should return "container" when spec width is "container"', () => {
|
|
8
|
+
expect(getContainerWidth({ width: "container" })).toBe("container");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should return a numeric width", () => {
|
|
12
|
+
expect(getContainerWidth({ width: 500 })).toBe(500);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should return undefined when spec has no width", () => {
|
|
16
|
+
expect(getContainerWidth({ height: 300 })).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should return undefined for null", () => {
|
|
20
|
+
expect(getContainerWidth(null)).toBeUndefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should return undefined for undefined", () => {
|
|
24
|
+
expect(getContainerWidth(undefined)).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should return undefined for non-object values", () => {
|
|
28
|
+
expect(getContainerWidth("string")).toBeUndefined();
|
|
29
|
+
expect(getContainerWidth(42)).toBeUndefined();
|
|
30
|
+
expect(getContainerWidth(true)).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should return undefined when width is explicitly undefined", () => {
|
|
34
|
+
expect(getContainerWidth({ width: undefined })).toBeUndefined();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
import { Objects } from "@/utils/objects";
|
|
4
4
|
import type { DataType, FieldTypes, VegaDataType } from "./vega-loader";
|
|
5
5
|
|
|
6
|
+
export type ContainerWidth = number | "container";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the container width from a VegaLite spec.
|
|
10
|
+
* @param spec - The VegaLite spec.
|
|
11
|
+
* @returns The container width.
|
|
12
|
+
*/
|
|
13
|
+
export function getContainerWidth(spec: unknown): ContainerWidth | undefined {
|
|
14
|
+
if (typeof spec === "object" && spec !== null && "width" in spec) {
|
|
15
|
+
return spec.width as ContainerWidth | undefined;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
export function mergeAsArrays<T>(
|
|
7
21
|
left: T | T[] | undefined,
|
|
8
22
|
right: T | T[] | undefined,
|
|
@@ -27,6 +27,7 @@ import { makeSelectable } from "./make-selectable";
|
|
|
27
27
|
import { getSelectionParamNames, ParamNames } from "./params";
|
|
28
28
|
import { resolveVegaSpecData } from "./resolve-data";
|
|
29
29
|
import type { VegaLiteSpec } from "./types";
|
|
30
|
+
import { getContainerWidth } from "./utils";
|
|
30
31
|
|
|
31
32
|
// register arrow reader under type 'arrow'
|
|
32
33
|
formats("arrow", arrow);
|
|
@@ -305,16 +306,14 @@ const LoadedVegaComponent = ({
|
|
|
305
306
|
</Alert>
|
|
306
307
|
)}
|
|
307
308
|
<div
|
|
308
|
-
className={cn(
|
|
309
|
-
"relative",
|
|
310
|
-
"width" in selectableSpec &&
|
|
311
|
-
selectableSpec.width === "container" &&
|
|
312
|
-
"vega-container-width",
|
|
313
|
-
)}
|
|
309
|
+
className={cn("relative")}
|
|
314
310
|
// Capture the pointer down event to prevent the parent from handling it
|
|
315
311
|
onPointerDown={Events.stopPropagation()}
|
|
316
312
|
>
|
|
317
|
-
<div
|
|
313
|
+
<div
|
|
314
|
+
ref={vegaRef}
|
|
315
|
+
data-container-width={getContainerWidth(selectableSpec)}
|
|
316
|
+
/>
|
|
318
317
|
{renderHelpContent()}
|
|
319
318
|
</div>
|
|
320
319
|
</>
|