@marimo-team/islands 0.24.1-dev95 → 0.24.1-dev96
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/{common-D0TXZImz.js → common-BEKbkdQp.js} +1 -1
- package/dist/main.js +3 -3
- package/dist/{reveal-component-q9Ml1yx9.js → reveal-component-BkBZwd8P.js} +1 -1
- package/dist/{vega-component-PYsP9Jod.js → vega-component-LgU-A6O9.js} +172 -162
- package/package.json +1 -1
- package/src/plugins/impl/vega/__tests__/__snapshots__/make-selectable.test.ts.snap +185 -185
- package/src/plugins/impl/vega/__tests__/make-selectable.test.ts +78 -2
- package/src/plugins/impl/vega/params.ts +26 -10
- package/src/plugins/impl/vega/vega-component.tsx +7 -2
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
// @ts-expect-error - vega-parser is not typed
|
|
4
4
|
import { parse } from "vega-parser";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { invariant } from "@/utils/invariant";
|
|
7
7
|
import { makeSelectable } from "../make-selectable";
|
|
8
|
-
import { getSelectionParamNames } from "../params";
|
|
8
|
+
import { getPanZoomModifier, getSelectionParamNames } from "../params";
|
|
9
9
|
import type { VegaLiteSpec } from "../types";
|
|
10
10
|
|
|
11
|
+
// Pin the platform so the pan/zoom modifier (and therefore the snapshots
|
|
12
|
+
// below) is deterministic regardless of the host running the tests. We use a
|
|
13
|
+
// non-Mac platform, which resolves the modifier to `ctrlKey` — this matches
|
|
14
|
+
// the Linux/Windows environment from issue #3812.
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
vi.spyOn(window.navigator, "platform", "get").mockReturnValue("Linux x86_64");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
vi.restoreAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
11
23
|
describe("makeSelectable", () => {
|
|
12
24
|
it("should return correctly if mark is not string", () => {
|
|
13
25
|
const spec = {
|
|
@@ -1268,3 +1280,67 @@ describe("makeSelectable", () => {
|
|
|
1268
1280
|
expect(getSelectionParamNames(newSpec)).toEqual(["my_selection"]);
|
|
1269
1281
|
});
|
|
1270
1282
|
});
|
|
1283
|
+
|
|
1284
|
+
describe("pan/zoom modifier key (issue #3812)", () => {
|
|
1285
|
+
afterAll(() => {
|
|
1286
|
+
vi.restoreAllMocks();
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
function mockPlatform(platform: string) {
|
|
1290
|
+
// jsdom's navigator has no `userAgentData`, so the detection helper falls
|
|
1291
|
+
// back to `navigator.platform`, which is what we override here.
|
|
1292
|
+
vi.spyOn(window.navigator, "platform", "get").mockReturnValue(platform);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
it("uses metaKey (Command) on macOS", () => {
|
|
1296
|
+
mockPlatform("MacIntel");
|
|
1297
|
+
expect(getPanZoomModifier()).toBe("metaKey");
|
|
1298
|
+
|
|
1299
|
+
const spec = { mark: "point" } as VegaLiteSpec;
|
|
1300
|
+
const newSpec = makeSelectable(spec, {});
|
|
1301
|
+
const panZoom = newSpec.params?.find((p) => p.name === "pan_zoom");
|
|
1302
|
+
invariant(panZoom && "select" in panZoom, "expected pan_zoom param");
|
|
1303
|
+
const select = panZoom.select as { on: string; zoom: string };
|
|
1304
|
+
expect(select.on).toContain("event.metaKey");
|
|
1305
|
+
expect(select.on).not.toContain("event.ctrlKey");
|
|
1306
|
+
expect(select.zoom).toBe("wheel![event.metaKey]");
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
it.each(["Linux x86_64", "Win32"])(
|
|
1310
|
+
"uses ctrlKey on non-macOS platform %s",
|
|
1311
|
+
(platform) => {
|
|
1312
|
+
mockPlatform(platform);
|
|
1313
|
+
expect(getPanZoomModifier()).toBe("ctrlKey");
|
|
1314
|
+
|
|
1315
|
+
const spec = { mark: "point" } as VegaLiteSpec;
|
|
1316
|
+
const newSpec = makeSelectable(spec, {});
|
|
1317
|
+
const panZoom = newSpec.params?.find((p) => p.name === "pan_zoom");
|
|
1318
|
+
invariant(panZoom && "select" in panZoom, "expected pan_zoom param");
|
|
1319
|
+
const select = panZoom.select as { on: string; zoom: string };
|
|
1320
|
+
// Must not use metaKey, which maps to the Super/Windows key here and is
|
|
1321
|
+
// captured by the OS/desktop environment before the chart sees it.
|
|
1322
|
+
expect(select.on).toContain("event.ctrlKey");
|
|
1323
|
+
expect(select.on).not.toContain("event.metaKey");
|
|
1324
|
+
expect(select.zoom).toBe("wheel![event.ctrlKey]");
|
|
1325
|
+
},
|
|
1326
|
+
);
|
|
1327
|
+
|
|
1328
|
+
it("keeps point/interval selection gated on the same modifier as pan/zoom", () => {
|
|
1329
|
+
mockPlatform("Linux x86_64");
|
|
1330
|
+
const spec = { mark: "bar" } as VegaLiteSpec;
|
|
1331
|
+
const newSpec = makeSelectable(spec, {});
|
|
1332
|
+
const mod = getPanZoomModifier();
|
|
1333
|
+
|
|
1334
|
+
for (const param of newSpec.params ?? []) {
|
|
1335
|
+
if (!("select" in param) || typeof param.select !== "object") {
|
|
1336
|
+
continue;
|
|
1337
|
+
}
|
|
1338
|
+
const select = param.select as { on?: string; translate?: string };
|
|
1339
|
+
// The selection gating and pan/zoom must agree on the modifier, otherwise
|
|
1340
|
+
// one gesture triggers both.
|
|
1341
|
+
if (typeof select.on === "string" && select.on.includes("event.")) {
|
|
1342
|
+
expect(select.on).toContain(`event.${mod}`);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { TopLevelSpec } from "vega-lite";
|
|
4
4
|
import type { NonNormalizedSpec } from "vega-lite/types_unstable/spec/index.js";
|
|
5
|
+
import { isPlatformMac } from "@/core/hotkeys/shortcuts";
|
|
5
6
|
import { uniq } from "@/utils/arrays";
|
|
6
7
|
import { Marks } from "./marks";
|
|
7
8
|
import {
|
|
@@ -51,6 +52,21 @@ export const ParamNames = {
|
|
|
51
52
|
},
|
|
52
53
|
};
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* The keyboard modifier used to gate pan/zoom (and to exclude it from
|
|
57
|
+
* point/interval selection).
|
|
58
|
+
*
|
|
59
|
+
* On macOS we use the Command key (`metaKey`), which is conflict-free. On other
|
|
60
|
+
* platforms `metaKey` maps to the Super/Windows key, which GNOME/Wayland and
|
|
61
|
+
* Windows reserve for system actions (workspace switching, the activities
|
|
62
|
+
* overview, etc.), so pan/zoom never reaches the chart. We fall back to the
|
|
63
|
+
* Control key there, matching the common convention for chart pan/zoom on
|
|
64
|
+
* Linux and Windows. See https://github.com/marimo-team/marimo/issues/3812.
|
|
65
|
+
*/
|
|
66
|
+
export function getPanZoomModifier(): "metaKey" | "ctrlKey" {
|
|
67
|
+
return isPlatformMac() ? "metaKey" : "ctrlKey";
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
export const Params = {
|
|
55
71
|
highlight() {
|
|
56
72
|
return {
|
|
@@ -62,6 +78,7 @@ export const Params = {
|
|
|
62
78
|
spec: VegaLiteUnitSpec,
|
|
63
79
|
layerNum: number | undefined,
|
|
64
80
|
): SelectionParameter<"interval"> {
|
|
81
|
+
const mod = getPanZoomModifier();
|
|
65
82
|
return {
|
|
66
83
|
name: ParamNames.interval(layerNum),
|
|
67
84
|
select: {
|
|
@@ -73,10 +90,9 @@ export const Params = {
|
|
|
73
90
|
stroke: "#669EFF",
|
|
74
91
|
strokeOpacity: 0.4,
|
|
75
92
|
},
|
|
76
|
-
// So this does not conflict with pan/zoom
|
|
77
|
-
on:
|
|
78
|
-
translate:
|
|
79
|
-
"[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
|
|
93
|
+
// So this does not conflict with pan/zoom, which uses the same modifier
|
|
94
|
+
on: `[mousedown[!event.${mod}], mouseup] > mousemove[!event.${mod}]`,
|
|
95
|
+
translate: `[mousedown[!event.${mod}], mouseup] > mousemove[!event.${mod}]`,
|
|
80
96
|
},
|
|
81
97
|
};
|
|
82
98
|
},
|
|
@@ -89,7 +105,7 @@ export const Params = {
|
|
|
89
105
|
select: {
|
|
90
106
|
type: "point",
|
|
91
107
|
encodings: getEncodingAxisForMark(spec),
|
|
92
|
-
on:
|
|
108
|
+
on: `click[!event.${getPanZoomModifier()}]`,
|
|
93
109
|
},
|
|
94
110
|
};
|
|
95
111
|
},
|
|
@@ -103,7 +119,7 @@ export const Params = {
|
|
|
103
119
|
name: ParamNames.binColoring(layerNum),
|
|
104
120
|
select: {
|
|
105
121
|
type: "point",
|
|
106
|
-
on:
|
|
122
|
+
on: `click[!event.${getPanZoomModifier()}]`,
|
|
107
123
|
},
|
|
108
124
|
};
|
|
109
125
|
},
|
|
@@ -118,15 +134,15 @@ export const Params = {
|
|
|
118
134
|
};
|
|
119
135
|
},
|
|
120
136
|
panZoom(): SelectionParameter<"interval"> {
|
|
137
|
+
const mod = getPanZoomModifier();
|
|
121
138
|
return {
|
|
122
139
|
name: ParamNames.PAN_ZOOM,
|
|
123
140
|
bind: "scales",
|
|
124
141
|
select: {
|
|
125
142
|
type: "interval",
|
|
126
|
-
on:
|
|
127
|
-
translate:
|
|
128
|
-
|
|
129
|
-
zoom: "wheel![event.metaKey]",
|
|
143
|
+
on: `[mousedown[event.${mod}], window:mouseup] > window:mousemove!`,
|
|
144
|
+
translate: `[mousedown[event.${mod}], window:mouseup] > window:mousemove!`,
|
|
145
|
+
zoom: `wheel![event.${mod}]`,
|
|
130
146
|
},
|
|
131
147
|
};
|
|
132
148
|
},
|
|
@@ -13,6 +13,7 @@ import { tooltipHandler } from "@/components/charts/tooltip";
|
|
|
13
13
|
import type { SignalListener } from "@/components/charts/types";
|
|
14
14
|
import { Alert, AlertTitle } from "@/components/ui/alert";
|
|
15
15
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
16
|
+
import { isPlatformMac } from "@/core/hotkeys/shortcuts";
|
|
16
17
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
17
18
|
import { useDeepCompareMemoize } from "@/hooks/useDeepCompareMemoize";
|
|
18
19
|
import { useTheme } from "@/theme/useTheme";
|
|
@@ -229,9 +230,13 @@ const LoadedVegaComponent = ({
|
|
|
229
230
|
}
|
|
230
231
|
|
|
231
232
|
if (ParamNames.hasPanZoom(names)) {
|
|
233
|
+
// On macOS pan/zoom is gated behind the Command key; elsewhere the
|
|
234
|
+
// Super/Windows key is reserved by the OS, so we use Control instead.
|
|
235
|
+
// Keep this in sync with `getPanZoomModifier` in params.ts.
|
|
236
|
+
const modifierKey = isPlatformMac() ? "⌘" : "ctrl";
|
|
232
237
|
hints.push(
|
|
233
|
-
["Pan",
|
|
234
|
-
["Zoom",
|
|
238
|
+
["Pan", `hold the ${modifierKey} key and drag`],
|
|
239
|
+
["Zoom", `hold the ${modifierKey} key and scroll`],
|
|
235
240
|
);
|
|
236
241
|
}
|
|
237
242
|
|