Skip to content

Commit 2aadf30

Browse files
chore(release): prepare graphic-walker 0.5.3
1 parent 72c89e6 commit 2aadf30

12 files changed

Lines changed: 424 additions & 384 deletions

File tree

‎packages/computation-conformance/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"singleQuote": true
1414
},
1515
"dependencies": {
16-
"@kanaries/graphic-walker": "0.5.2"
16+
"@kanaries/graphic-walker": "0.5.3"
1717
},
1818
"devDependencies": {
1919
"@duckdb/duckdb-wasm": "^1.28.0",

‎packages/duckdb-wasm-computation/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
},
1717
"devDependencies": {
1818
"@duckdb/duckdb-wasm": "^1.28.0",
19-
"@kanaries/graphic-walker": "0.5.2",
19+
"@kanaries/graphic-walker": "0.5.3",
2020
"@kanaries/gw-dsl-parser": "^0.1.48-rc3",
2121
"apache-arrow": "^17.0.0",
2222
"nanoid": "^5.0.1",

‎packages/graphic-walker/package.json‎

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@kanaries/graphic-walker",
3-
"version": "0.5.2",
3+
"version": "0.5.3",
44
"scripts": {
55
"dev:front_end": "vite --host",
66
"dev": "npm run dev:front_end",
@@ -15,7 +15,10 @@
1515
},
1616
"files": [
1717
"dist",
18-
"src"
18+
"src",
19+
"README.md",
20+
"LICENSE",
21+
"LICENSE2"
1922
],
2023
"license": "Apache-2.0",
2124
"author": {
@@ -90,15 +93,12 @@
9093
"i18next-browser-languagedetector": "^6.1.5",
9194
"immer": "^9.0.15",
9295
"immutable": "^5.1.1",
93-
"jest": "^29.6.2",
9496
"leaflet": "^1.9.4",
9597
"lodash-es": "^4.17.15",
9698
"mobx": "^6.3.3",
9799
"mobx-react-lite": "^4.1.1",
98100
"nanoid": "^4.0.2",
99101
"pgsql-ast-parser": "^11.1.0",
100-
"postcss": "^8.3.7",
101-
"postinstall-postinstall": "^2.1.0",
102102
"rbush": "^3.0.1",
103103
"re-resizable": "^6.11.2",
104104
"react-color": "^2.19.3",
@@ -119,7 +119,11 @@
119119
"vega": "^5.22.1",
120120
"vega-embed": "^6.21.0",
121121
"vega-lite": "^5.6.0",
122-
"vega-webgl-renderer": "^1.0.0-beta.2"
122+
"vega-webgl-renderer": "^1.0.0-beta.2",
123+
"ts-xor": "^1.3.0",
124+
"@types/geojson": "^7946.0.10",
125+
"@types/react-beautiful-dnd": "^13.1.2",
126+
"@types/topojson-client": "^3.1.1"
123127
},
124128
"devDependencies": {
125129
"@playwright/test": "^1.62.0",
@@ -128,23 +132,22 @@
128132
"@types/d3-format": "^3.0.1",
129133
"@types/d3-scale": "^4.0.3",
130134
"@types/d3-time-format": "^4.0.0",
131-
"@types/geojson": "^7946.0.10",
132135
"@types/leaflet": "^1.9.3",
133136
"@types/lodash-es": "^4.17.12",
134137
"@types/rbush": "^3.0.3",
135138
"@types/react": "*",
136-
"@types/react-beautiful-dnd": "^13.1.2",
137139
"@types/react-color": "^3.0.6",
138140
"@types/react-dom": "*",
139-
"@types/topojson-client": "^3.1.1",
140141
"@vercel/analytics": "^1.6.1",
141142
"@vitejs/plugin-react": "^3.1.0",
142143
"ts-json-schema-generator": "^1.5.0",
143144
"ts-patch": "^3.1.2",
144-
"ts-xor": "^1.3.0",
145145
"typescript": "^5.2.2",
146146
"typescript-transform-paths": "^3.4.7",
147-
"vite": "^4.1.1"
147+
"vite": "^4.1.1",
148+
"jest": "^29.6.2",
149+
"postcss": "^8.3.7",
150+
"postinstall-postinstall": "^2.1.0"
148151
},
149152
"peerDependencies": {
150153
"react": ">=19.0.0",

‎packages/graphic-walker/src/components/toolbar/toolbar-select-button.tsx‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
2929
const opened = openedKey === id;
3030

3131
const currentOption = options.find((opt) => opt.key === value);
32-
const CurrentIcon = currentOption?.icon;
32+
// the button shows the selected option itself; the item icon is the fallback when nothing matches
33+
const ButtonIcon = currentOption?.icon ?? Icon;
3334

3435
return (
3536
<DropdownMenu modal={false} open={opened} onOpenChange={(open) => (open ? setOpenedKey(id) : setOpenedKey(''))}>
@@ -43,8 +44,11 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
4344
>
4445
<DropdownMenuTrigger disabled={disabled} asChild>
4546
<Button className="relative" disabled={disabled} variant="none" size="toolbar">
46-
<Icon className="w-[18px] h-[18px]" style={styles?.icon} />
47-
{CurrentIcon && <CurrentIcon style={styles?.icon} className="absolute w-[11px] h-[11px] right-[7px] bottom-[5px]" />}
47+
<ButtonIcon className="w-[18px] h-[18px]" style={styles?.icon} />
48+
<span
49+
aria-hidden
50+
className="absolute right-[3px] bottom-[3px] border-l-4 border-b-4 border-l-transparent border-b-current opacity-50"
51+
/>
4852
</Button>
4953
</DropdownMenuTrigger>
5054
</ToolbarItemContainer>

‎packages/graphic-walker/src/interfaces.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,11 @@ export interface IVegaChartRef {
483483
export interface IChartExportResult<T extends 'svg' | 'data-url' = 'svg' | 'data-url'> {
484484
mode: T;
485485
title: string;
486+
/**
487+
* The complete chart, with repeated Vega views composed into one image.
488+
* Individual view exports remain available in `charts`.
489+
*/
490+
combinedData?: string;
486491
nCols: number;
487492
nRows: number;
488493
charts: {

‎packages/graphic-walker/src/models/visSpecHistory.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -554,12 +554,14 @@ export const redo: (data: VisSpecWithHistory) => VisSpecWithHistory = redoWith(r
554554
export const at: (data: VisSpecWithHistory, cursor: number) => IChart = atWith(reducerT);
555555
export { freeze };
556556

557-
export const performers = Object.fromEntries(
558-
(Object.keys(Methods) as (keyof typeof Methods)[]).map((k) => [k, (data: any, ...args: any[]) => perform(data, [Methods[k], ...args] as any)])
559-
) as unknown as {
557+
type VisPerformers = {
560558
[K in keyof typeof Methods]: (data: VisSpecWithHistory, ...args: PropsMap[(typeof Methods)[K]]) => VisSpecWithHistory;
561559
};
562560

561+
export const performers: VisPerformers = Object.fromEntries(
562+
(Object.keys(Methods) as (keyof typeof Methods)[]).map((k) => [k, (data: any, ...args: any[]) => perform(data, [Methods[k], ...args] as any)])
563+
) as unknown as VisPerformers;
564+
563565
function emptyChart(visId: string, name: string, defaultConfig?: IDefaultConfig): IChart {
564566
return {
565567
config: defaultConfig?.config ? { ...emptyVisualConfig, ...defaultConfig.config } : emptyVisualConfig,
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { IVegaChartRef } from '../interfaces';
2+
import { combineCanvases, combineSVGs, getCompositeGridLayout } from './vegaApiExport';
3+
4+
const chartRef = (x: number, y: number, w: number, h: number): IVegaChartRef => ({
5+
x,
6+
y,
7+
w,
8+
h,
9+
innerWidth: w,
10+
innerHeight: h,
11+
view: null,
12+
canvas: null,
13+
});
14+
15+
describe('Vega export composition', () => {
16+
test('lays out unequal grid cells using the largest item in each row and column', () => {
17+
const layout = getCompositeGridLayout([
18+
{ x: 0, y: 0, width: 100, height: 40 },
19+
{ x: 1, y: 0, width: 120, height: 50 },
20+
{ x: 0, y: 1, width: 90, height: 60 },
21+
{ x: 1, y: 1, width: 110, height: 55 },
22+
]);
23+
24+
expect(layout).toEqual({
25+
width: 220,
26+
height: 110,
27+
items: [
28+
{ x: 0, y: 0, width: 100, height: 40, offsetX: 0, offsetY: 0 },
29+
{ x: 1, y: 0, width: 120, height: 50, offsetX: 100, offsetY: 0 },
30+
{ x: 0, y: 1, width: 90, height: 60, offsetX: 0, offsetY: 50 },
31+
{ x: 1, y: 1, width: 110, height: 55, offsetX: 100, offsetY: 50 },
32+
],
33+
});
34+
});
35+
36+
test('draws canvases into one image using their grid offsets', () => {
37+
const drawImage = jest.fn();
38+
const combinedCanvas = {
39+
width: 0,
40+
height: 0,
41+
getContext: () => ({ drawImage }),
42+
};
43+
const previousDocument = globalThis.document;
44+
Object.defineProperty(globalThis, 'document', {
45+
configurable: true,
46+
value: {
47+
createElement: (name: string) => {
48+
expect(name).toBe('canvas');
49+
return combinedCanvas;
50+
},
51+
},
52+
});
53+
54+
try {
55+
const first = { width: 100, height: 40 } as HTMLCanvasElement;
56+
const second = { width: 120, height: 50 } as HTMLCanvasElement;
57+
const result = combineCanvases([first, second], [chartRef(0, 0, 100, 40), chartRef(1, 0, 120, 50)]);
58+
59+
expect(result).toBe(combinedCanvas);
60+
expect(combinedCanvas.width).toBe(220);
61+
expect(combinedCanvas.height).toBe(50);
62+
expect(drawImage).toHaveBeenNthCalledWith(1, first, 0, 0);
63+
expect(drawImage).toHaveBeenNthCalledWith(2, second, 100, 0);
64+
} finally {
65+
Object.defineProperty(globalThis, 'document', {
66+
configurable: true,
67+
value: previousDocument,
68+
});
69+
}
70+
});
71+
72+
test('combines repeated SVG views and namespaces their definitions', () => {
73+
const first = '<svg width="100" height="40"><defs><clipPath id="clip"><path /></clipPath></defs><g clip-path="url(#clip)" /></svg>';
74+
const second =
75+
'<svg width="120" height="50"><defs><clipPath id="clip"><path /></clipPath></defs><g aria-labelledby="title"><title id="title">B</title><g clip-path="url(#clip)" /></g></svg>';
76+
77+
const combined = combineSVGs([first, second], [chartRef(0, 0, 100, 40), chartRef(1, 0, 120, 50)]);
78+
79+
expect(combined).toContain('width="220" height="50" viewBox="0 0 220 50"');
80+
expect(combined).toContain('<svg x="0" y="0" width="100" height="40">');
81+
expect(combined).toContain('<svg x="100" y="0" width="120" height="50">');
82+
expect(combined).toContain('id="gw-export-0-clip"');
83+
expect(combined).toContain('url(#gw-export-0-clip)');
84+
expect(combined).toContain('id="gw-export-1-clip"');
85+
expect(combined).toContain('url(#gw-export-1-clip)');
86+
expect(combined).toContain('aria-labelledby="gw-export-1-title"');
87+
expect(combined).toContain('id="gw-export-1-title"');
88+
});
89+
90+
test('keeps a single SVG unchanged', () => {
91+
const svg = '<svg width="100" height="40"><g /></svg>';
92+
expect(combineSVGs([svg], [chartRef(0, 0, 100, 40)])).toBe(svg);
93+
});
94+
});

0 commit comments

Comments
 (0)