@kanaries/graphic-walker 0.3.9 → 0.3.11
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/assets/transform.worker-90e4f506.js.map +1 -1
- package/dist/assets/{viewQuery.worker-95bc9cf6.js.map → viewQuery.worker-03404216.js.map} +1 -1
- package/dist/components/appRoot.d.ts +7 -0
- package/dist/graphic-walker.es.js +21167 -23955
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +123 -147
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/interfaces.d.ts +21 -0
- package/dist/renderer/pureRenderer.d.ts +1 -0
- package/dist/renderer/specRenderer.d.ts +1 -0
- package/dist/shadow-dom.d.ts +10 -0
- package/dist/utils/vegaApiExport.d.ts +10 -0
- package/dist/vis/react-vega.d.ts +1 -0
- package/package.json +2 -3
- package/src/components/appRoot.tsx +32 -0
- package/src/components/callout.tsx +1 -1
- package/src/components/tooltip.tsx +1 -1
- package/src/index.tsx +23 -38
- package/src/interfaces.ts +24 -1
- package/src/renderer/hooks.ts +9 -1
- package/src/renderer/index.tsx +3 -1
- package/src/renderer/pureRenderer.tsx +20 -9
- package/src/renderer/specRenderer.tsx +3 -1
- package/src/shadow-dom.tsx +48 -0
- package/src/utils/vegaApiExport.ts +102 -0
- package/src/vis/react-vega.tsx +22 -43
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { IGWProps } from "./App";
|
|
3
|
+
import type { IGWHandler } from "./interfaces";
|
|
3
4
|
import "./empty_sheet.css";
|
|
4
|
-
export declare const
|
|
5
|
-
root: ShadowRoot | null;
|
|
6
|
-
}>;
|
|
7
|
-
export type { IGWProps };
|
|
8
|
-
export { embedGraphicWalker } from './vanilla';
|
|
9
|
-
export declare const GraphicWalker: React.FC<IGWProps>;
|
|
5
|
+
export declare const GraphicWalker: React.MemoExoticComponent<React.ForwardRefExoticComponent<Pick<IGWProps & React.RefAttributes<IGWHandler>, "key" | keyof IGWProps> & React.RefAttributes<IGWHandler>>>;
|
|
10
6
|
export { default as PureRenderer } from './renderer/pureRenderer';
|
|
7
|
+
export { embedGraphicWalker } from './vanilla';
|
|
8
|
+
export type { IGWProps };
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -191,3 +191,24 @@ export declare enum ISegmentKey {
|
|
|
191
191
|
export type IThemeKey = 'vega' | 'g2';
|
|
192
192
|
export type IDarkMode = 'media' | 'light' | 'dark';
|
|
193
193
|
export type VegaGlobalConfig = VgConfig | VlConfig;
|
|
194
|
+
export interface IChartExportResult<T extends 'svg' | 'data-url' = 'svg' | 'data-url'> {
|
|
195
|
+
mode: T;
|
|
196
|
+
title: string;
|
|
197
|
+
nCols: number;
|
|
198
|
+
nRows: number;
|
|
199
|
+
charts: {
|
|
200
|
+
colIndex: number;
|
|
201
|
+
rowIndex: number;
|
|
202
|
+
width: number;
|
|
203
|
+
height: number;
|
|
204
|
+
data: string;
|
|
205
|
+
}[];
|
|
206
|
+
}
|
|
207
|
+
interface IExportChart {
|
|
208
|
+
<T extends Extract<IChartExportResult['mode'], 'svg'>>(mode?: T): Promise<IChartExportResult<T>>;
|
|
209
|
+
<T extends IChartExportResult['mode']>(mode: T): Promise<IChartExportResult<T>>;
|
|
210
|
+
}
|
|
211
|
+
export interface IGWHandler {
|
|
212
|
+
exportChart: IExportChart;
|
|
213
|
+
}
|
|
214
|
+
export {};
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import type { IDarkMode, IRow, IThemeKey, DraggableFieldState, IVisualConfig } from '../interfaces';
|
|
3
3
|
import type { IReactVegaHandler } from '../vis/react-vega';
|
|
4
4
|
interface IPureRendererProps {
|
|
5
|
+
name?: string;
|
|
5
6
|
themeKey?: IThemeKey;
|
|
6
7
|
dark?: IDarkMode;
|
|
7
8
|
rawData?: IRow[];
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { IReactVegaHandler } from '../vis/react-vega';
|
|
3
3
|
import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig } from '../interfaces';
|
|
4
4
|
interface SpecRendererProps {
|
|
5
|
+
name?: string;
|
|
5
6
|
themeKey?: IThemeKey;
|
|
6
7
|
dark?: IDarkMode;
|
|
7
8
|
data: IRow[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React, { HTMLAttributes } from "react";
|
|
2
|
+
export declare const ShadowDomContext: React.Context<{
|
|
3
|
+
root: ShadowRoot | null;
|
|
4
|
+
}>;
|
|
5
|
+
interface IShadowDomProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
onMount?: (shadowRoot: ShadowRoot) => void;
|
|
7
|
+
onUnmount?: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const ShadowDom: React.FC<IShadowDomProps>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ForwardedRef, type MutableRefObject } from "react";
|
|
2
|
+
import type { View } from "vega";
|
|
3
|
+
import type { IReactVegaHandler } from "../vis/react-vega";
|
|
4
|
+
export declare const useVegaExportApi: (name: string | undefined, viewsRef: MutableRefObject<{
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
view: View;
|
|
10
|
+
}[]>, ref: ForwardedRef<IReactVegaHandler>) => void;
|
package/dist/vis/react-vega.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanaries/graphic-walker",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev:front_end": "vite --host",
|
|
6
6
|
"dev": "npm run dev:front_end",
|
|
@@ -36,8 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@headlessui/react": "^1.7.12",
|
|
38
38
|
"@heroicons/react": "^2.0.8",
|
|
39
|
-
"@kanaries/
|
|
40
|
-
"@kanaries/react-beautiful-dnd": "0.0.1",
|
|
39
|
+
"@kanaries/react-beautiful-dnd": "0.0.2",
|
|
41
40
|
"@kanaries/web-data-loader": "^0.1.7",
|
|
42
41
|
"autoprefixer": "^10.3.5",
|
|
43
42
|
"i18next": "^21.9.1",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, { createContext, forwardRef, useImperativeHandle, type ForwardedRef, useContext } from "react";
|
|
2
|
+
import type { IGWHandler } from "../interfaces";
|
|
3
|
+
|
|
4
|
+
const AppRootContext = createContext<ForwardedRef<IGWHandler>>(null!);
|
|
5
|
+
|
|
6
|
+
export const useAppRootContext = () => {
|
|
7
|
+
return useContext(AppRootContext);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const AppRoot = forwardRef<IGWHandler, { children: any }>(({ children }, ref) => {
|
|
11
|
+
useImperativeHandle(ref, () => {
|
|
12
|
+
return {
|
|
13
|
+
exportChart: (async mode => {
|
|
14
|
+
return {
|
|
15
|
+
mode,
|
|
16
|
+
title: '',
|
|
17
|
+
nCols: 0,
|
|
18
|
+
nRows: 0,
|
|
19
|
+
charts: [],
|
|
20
|
+
};
|
|
21
|
+
}) as IGWHandler['exportChart'],
|
|
22
|
+
};
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<AppRootContext.Provider value={ref}>
|
|
27
|
+
{children}
|
|
28
|
+
</AppRootContext.Provider>
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export default AppRoot;
|
|
@@ -2,7 +2,7 @@ import React, { memo, ReactNode, useContext, useEffect, useState } from "react";
|
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
3
|
import styled from "styled-components";
|
|
4
4
|
import type { IDarkMode } from "../interfaces";
|
|
5
|
-
import { ShadowDomContext } from "
|
|
5
|
+
import { ShadowDomContext } from "../shadow-dom";
|
|
6
6
|
import { useCurrentMediaTheme } from "../utils/media";
|
|
7
7
|
|
|
8
8
|
export interface CalloutProps {
|
|
@@ -2,7 +2,7 @@ import React, { memo, useContext, useEffect, useMemo, useRef, useState } from "r
|
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
3
|
import styled from "styled-components";
|
|
4
4
|
import type { IDarkMode } from "../interfaces";
|
|
5
|
-
import { ShadowDomContext } from "
|
|
5
|
+
import { ShadowDomContext } from "../shadow-dom";
|
|
6
6
|
import { useCurrentMediaTheme } from "../utils/media";
|
|
7
7
|
|
|
8
8
|
export interface TooltipProps {
|
package/src/index.tsx
CHANGED
|
@@ -1,55 +1,40 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { StyleSheetManager } from "styled-components";
|
|
3
|
-
import root from "react-shadow";
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
4
2
|
import { DOM } from "@kanaries/react-beautiful-dnd";
|
|
5
3
|
import { observer } from "mobx-react-lite";
|
|
6
4
|
import App, { IGWProps } from "./App";
|
|
7
5
|
import { StoreWrapper } from "./store/index";
|
|
8
6
|
import { FieldsContextWrapper } from "./fields/fieldsContext";
|
|
7
|
+
import { ShadowDom } from "./shadow-dom";
|
|
8
|
+
import AppRoot from "./components/appRoot";
|
|
9
|
+
import type { IGWHandler } from "./interfaces";
|
|
9
10
|
|
|
10
11
|
import "./empty_sheet.css";
|
|
11
|
-
import tailwindStyle from "tailwindcss/tailwind.css?inline";
|
|
12
|
-
import style from "./index.css?inline";
|
|
13
12
|
|
|
14
|
-
export const
|
|
15
|
-
export type { IGWProps }
|
|
16
|
-
export { embedGraphicWalker } from './vanilla';
|
|
17
|
-
|
|
18
|
-
export const GraphicWalker: React.FC<IGWProps> = observer((props) => {
|
|
19
|
-
const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);
|
|
20
|
-
const rootRef = useRef<HTMLDivElement>(null);
|
|
13
|
+
export const GraphicWalker = observer(forwardRef<IGWHandler, IGWProps>((props, ref) => {
|
|
21
14
|
const { storeRef } = props;
|
|
22
15
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
DOM.setHead(document.head);
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
}, []);
|
|
16
|
+
const handleMount = (shadowRoot: ShadowRoot) => {
|
|
17
|
+
DOM.setBody(shadowRoot);
|
|
18
|
+
DOM.setHead(shadowRoot);
|
|
19
|
+
};
|
|
20
|
+
const handleUnmount = () => {
|
|
21
|
+
DOM.setBody(document.body);
|
|
22
|
+
DOM.setHead(document.head);
|
|
23
|
+
};
|
|
35
24
|
|
|
36
25
|
return (
|
|
37
26
|
<StoreWrapper keepAlive={props.keepAlive} storeRef={storeRef}>
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<App {...props} />
|
|
46
|
-
</ShadowDomContext.Provider>
|
|
47
|
-
</FieldsContextWrapper>
|
|
48
|
-
</StyleSheetManager>
|
|
49
|
-
)}
|
|
50
|
-
</root.div>
|
|
27
|
+
<AppRoot ref={ref}>
|
|
28
|
+
<ShadowDom onMount={handleMount} onUnmount={handleUnmount}>
|
|
29
|
+
<FieldsContextWrapper>
|
|
30
|
+
<App {...props} />
|
|
31
|
+
</FieldsContextWrapper>
|
|
32
|
+
</ShadowDom>
|
|
33
|
+
</AppRoot>
|
|
51
34
|
</StoreWrapper>
|
|
52
35
|
);
|
|
53
|
-
});
|
|
36
|
+
}));
|
|
54
37
|
|
|
55
38
|
export { default as PureRenderer } from './renderer/pureRenderer';
|
|
39
|
+
export { embedGraphicWalker } from './vanilla';
|
|
40
|
+
export type { IGWProps };
|
package/src/interfaces.ts
CHANGED
|
@@ -223,4 +223,27 @@ export enum ISegmentKey {
|
|
|
223
223
|
export type IThemeKey = 'vega' | 'g2';
|
|
224
224
|
export type IDarkMode = 'media' | 'light' | 'dark';
|
|
225
225
|
|
|
226
|
-
export type VegaGlobalConfig = VgConfig | VlConfig;
|
|
226
|
+
export type VegaGlobalConfig = VgConfig | VlConfig;
|
|
227
|
+
|
|
228
|
+
export interface IChartExportResult<T extends 'svg' | 'data-url' = 'svg' | 'data-url'> {
|
|
229
|
+
mode: T;
|
|
230
|
+
title: string;
|
|
231
|
+
nCols: number;
|
|
232
|
+
nRows: number;
|
|
233
|
+
charts: {
|
|
234
|
+
colIndex: number;
|
|
235
|
+
rowIndex: number;
|
|
236
|
+
width: number;
|
|
237
|
+
height: number;
|
|
238
|
+
data: string;
|
|
239
|
+
}[];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
interface IExportChart {
|
|
243
|
+
<T extends Extract<IChartExportResult['mode'], 'svg'>>(mode?: T): Promise<IChartExportResult<T>>;
|
|
244
|
+
<T extends IChartExportResult['mode']>(mode: T): Promise<IChartExportResult<T>>;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface IGWHandler {
|
|
248
|
+
exportChart: IExportChart;
|
|
249
|
+
}
|
package/src/renderer/hooks.ts
CHANGED
|
@@ -30,8 +30,16 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
|
30
30
|
const taskId = ++taskIdRef.current;
|
|
31
31
|
setComputing(true);
|
|
32
32
|
applyFilter(data, filters)
|
|
33
|
-
.then((data) =>
|
|
33
|
+
.then((data) => {
|
|
34
|
+
if (viewDimensions.length === 0 && viewMeasures.length === 0) {
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
return transformDataService(data, allFields);
|
|
38
|
+
})
|
|
34
39
|
.then((d) => {
|
|
40
|
+
if (viewDimensions.length === 0 && viewMeasures.length === 0) {
|
|
41
|
+
return data;
|
|
42
|
+
}
|
|
35
43
|
// setViewData(d);
|
|
36
44
|
const dims = viewDimensions;
|
|
37
45
|
const meas = viewMeasures;
|
package/src/renderer/index.tsx
CHANGED
|
@@ -20,7 +20,8 @@ interface RendererProps {
|
|
|
20
20
|
const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, ref) {
|
|
21
21
|
const { themeKey, dark } = props;
|
|
22
22
|
const { vizStore, commonStore } = useGlobalStore();
|
|
23
|
-
const { allFields, viewFilters, viewDimensions, viewMeasures, visualConfig, draggableFieldState } = vizStore;
|
|
23
|
+
const { allFields, viewFilters, viewDimensions, viewMeasures, visualConfig, draggableFieldState, visList, visIndex } = vizStore;
|
|
24
|
+
const chart = visList[visIndex];
|
|
24
25
|
const { currentDataset } = commonStore;
|
|
25
26
|
const { dataSource } = currentDataset;
|
|
26
27
|
|
|
@@ -83,6 +84,7 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
|
|
|
83
84
|
|
|
84
85
|
return (
|
|
85
86
|
<SpecRenderer
|
|
87
|
+
name={chart?.name}
|
|
86
88
|
loading={waiting}
|
|
87
89
|
data={viewData}
|
|
88
90
|
ref={ref}
|
|
@@ -2,6 +2,8 @@ import React, { useState, useEffect, forwardRef, useMemo, useRef } from 'react';
|
|
|
2
2
|
import { unstable_batchedUpdates } from 'react-dom';
|
|
3
3
|
import { toJS } from 'mobx';
|
|
4
4
|
import { observer } from 'mobx-react-lite';
|
|
5
|
+
import { ShadowDom } from '../shadow-dom';
|
|
6
|
+
import AppRoot from '../components/appRoot';
|
|
5
7
|
import type { IDarkMode, IViewField, IRow, IThemeKey, DraggableFieldState, IVisualConfig } from '../interfaces';
|
|
6
8
|
import type { IReactVegaHandler } from '../vis/react-vega';
|
|
7
9
|
import SpecRenderer from './specRenderer';
|
|
@@ -9,6 +11,7 @@ import { useRenderer } from './hooks';
|
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
interface IPureRendererProps {
|
|
14
|
+
name?: string;
|
|
12
15
|
themeKey?: IThemeKey;
|
|
13
16
|
dark?: IDarkMode;
|
|
14
17
|
rawData?: IRow[];
|
|
@@ -22,6 +25,7 @@ interface IPureRendererProps {
|
|
|
22
25
|
*/
|
|
23
26
|
const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer (props, ref) {
|
|
24
27
|
const {
|
|
28
|
+
name,
|
|
25
29
|
themeKey,
|
|
26
30
|
dark,
|
|
27
31
|
rawData,
|
|
@@ -75,15 +79,22 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
|
|
|
75
79
|
}, [waiting]);
|
|
76
80
|
|
|
77
81
|
return (
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
<AppRoot>
|
|
83
|
+
<ShadowDom>
|
|
84
|
+
<div className="relative">
|
|
85
|
+
<SpecRenderer
|
|
86
|
+
name={name}
|
|
87
|
+
loading={waiting}
|
|
88
|
+
data={viewData}
|
|
89
|
+
ref={ref}
|
|
90
|
+
themeKey={themeKey}
|
|
91
|
+
dark={dark}
|
|
92
|
+
draggableFieldState={visualState}
|
|
93
|
+
visualConfig={visualConfig}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
</ShadowDom>
|
|
97
|
+
</AppRoot>
|
|
87
98
|
);
|
|
88
99
|
});
|
|
89
100
|
|
|
@@ -10,6 +10,7 @@ import { useCurrentMediaTheme } from '../utils/media';
|
|
|
10
10
|
import { builtInThemes } from '../vis/theme';
|
|
11
11
|
|
|
12
12
|
interface SpecRendererProps {
|
|
13
|
+
name?: string;
|
|
13
14
|
themeKey?: IThemeKey;
|
|
14
15
|
dark?: IDarkMode;
|
|
15
16
|
data: IRow[];
|
|
@@ -24,7 +25,7 @@ interface SpecRendererProps {
|
|
|
24
25
|
* This is a pure component, which means it will not depend on any global state.
|
|
25
26
|
*/
|
|
26
27
|
const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
27
|
-
{ themeKey, dark, data, loading, draggableFieldState, visualConfig, onGeomClick, onChartResize },
|
|
28
|
+
{ name, themeKey, dark, data, loading, draggableFieldState, visualConfig, onGeomClick, onChartResize },
|
|
28
29
|
ref
|
|
29
30
|
) {
|
|
30
31
|
// const { draggableFieldState, visualConfig } = vizStore;
|
|
@@ -123,6 +124,7 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
123
124
|
>
|
|
124
125
|
{loading && <LoadingLayer />}
|
|
125
126
|
<ReactVega
|
|
127
|
+
name={name}
|
|
126
128
|
vegaConfig={vegaConfig}
|
|
127
129
|
// format={format}
|
|
128
130
|
layoutMode={size.mode}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { HTMLAttributes, createContext, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { StyleSheetManager } from "styled-components";
|
|
3
|
+
import root from "react-shadow";
|
|
4
|
+
|
|
5
|
+
import tailwindStyle from "tailwindcss/tailwind.css?inline";
|
|
6
|
+
import style from "./index.css?inline";
|
|
7
|
+
|
|
8
|
+
export const ShadowDomContext = createContext<{ root: ShadowRoot | null }>({ root: null });
|
|
9
|
+
|
|
10
|
+
interface IShadowDomProps extends HTMLAttributes<HTMLDivElement> {
|
|
11
|
+
onMount?: (shadowRoot: ShadowRoot) => void;
|
|
12
|
+
onUnmount?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ShadowDom: React.FC<IShadowDomProps> = function ShadowDom ({ onMount, onUnmount, children, ...attrs }) {
|
|
16
|
+
const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);
|
|
17
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
18
|
+
|
|
19
|
+
const onMountRef = useRef(onMount);
|
|
20
|
+
onMountRef.current = onMount;
|
|
21
|
+
const onUnmountRef = useRef(onUnmount);
|
|
22
|
+
onUnmountRef.current = onUnmount;
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (rootRef.current) {
|
|
26
|
+
const shadowRoot = rootRef.current.shadowRoot!;
|
|
27
|
+
setShadowRoot(shadowRoot);
|
|
28
|
+
onMountRef.current?.(shadowRoot);
|
|
29
|
+
return () => {
|
|
30
|
+
onUnmountRef.current?.();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<root.div {...attrs} mode="open" ref={rootRef}>
|
|
37
|
+
<style>{tailwindStyle}</style>
|
|
38
|
+
<style>{style}</style>
|
|
39
|
+
{shadowRoot && (
|
|
40
|
+
<StyleSheetManager target={shadowRoot}>
|
|
41
|
+
<ShadowDomContext.Provider value={{ root: shadowRoot }}>
|
|
42
|
+
{children}
|
|
43
|
+
</ShadowDomContext.Provider>
|
|
44
|
+
</StyleSheetManager>
|
|
45
|
+
)}
|
|
46
|
+
</root.div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useImperativeHandle, type ForwardedRef, type MutableRefObject, useEffect } from "react";
|
|
2
|
+
import type { View } from "vega";
|
|
3
|
+
import { useAppRootContext } from "../components/appRoot";
|
|
4
|
+
import type { IReactVegaHandler } from "../vis/react-vega";
|
|
5
|
+
import type { IChartExportResult } from "../interfaces";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export const useVegaExportApi = (name: string | undefined, viewsRef: MutableRefObject<{ x: number; y: number; w: number; h: number; view: View }[]>, ref: ForwardedRef<IReactVegaHandler>) => {
|
|
9
|
+
const renderHandle = {
|
|
10
|
+
getSVGData() {
|
|
11
|
+
return Promise.all(viewsRef.current.map(item => item.view.toSVG()));
|
|
12
|
+
},
|
|
13
|
+
async getCanvasData() {
|
|
14
|
+
const canvases = await Promise.all(viewsRef.current.map(item => item.view.toCanvas(2)));
|
|
15
|
+
return canvases.map(canvas => canvas.toDataURL('image/png', 1));
|
|
16
|
+
},
|
|
17
|
+
async downloadSVG(filename = `gw chart ${Date.now() % 1_000_000}`.padStart(6, '0')) {
|
|
18
|
+
const data = await Promise.all(viewsRef.current.map(item => item.view.toSVG()));
|
|
19
|
+
const files: string[] = [];
|
|
20
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
21
|
+
const d = data[i];
|
|
22
|
+
const file = new File([d], `${filename}${data.length > 1 ? `_${i + 1}` : ''}.svg`);
|
|
23
|
+
const url = URL.createObjectURL(file);
|
|
24
|
+
const a = document.createElement('a');
|
|
25
|
+
a.download = file.name;
|
|
26
|
+
a.href = url;
|
|
27
|
+
a.click();
|
|
28
|
+
requestAnimationFrame(() => {
|
|
29
|
+
URL.revokeObjectURL(url);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return files;
|
|
33
|
+
},
|
|
34
|
+
async downloadPNG(filename = `gw chart ${Date.now() % 1_000_000}`.padStart(6, '0')) {
|
|
35
|
+
const canvases = await Promise.all(viewsRef.current.map(item => item.view.toCanvas(2)));
|
|
36
|
+
const data = canvases.map(canvas => canvas.toDataURL('image/png', 1));
|
|
37
|
+
const files: string[] = [];
|
|
38
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
39
|
+
const d = data[i];
|
|
40
|
+
const a = document.createElement('a');
|
|
41
|
+
a.download = `${filename}${data.length > 1 ? `_${i + 1}` : ''}.png`;
|
|
42
|
+
a.href = d.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
|
|
43
|
+
a.click();
|
|
44
|
+
}
|
|
45
|
+
return files;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
useImperativeHandle(ref, () => renderHandle);
|
|
50
|
+
|
|
51
|
+
const appRef = useAppRootContext();
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (appRef && 'current' in appRef && appRef.current) {
|
|
55
|
+
appRef.current.exportChart = (async (mode: IChartExportResult['mode'] = 'svg') => {
|
|
56
|
+
const res: IChartExportResult = {
|
|
57
|
+
mode,
|
|
58
|
+
title: name || 'untitled',
|
|
59
|
+
nCols: viewsRef.current.map(item => item.x).reduce((a, b) => Math.max(a, b), 0) + 1,
|
|
60
|
+
nRows: viewsRef.current.map(item => item.y).reduce((a, b) => Math.max(a, b), 0) + 1,
|
|
61
|
+
charts: viewsRef.current.map(item => ({
|
|
62
|
+
rowIndex: item.y,
|
|
63
|
+
colIndex: item.x,
|
|
64
|
+
width: item.w,
|
|
65
|
+
height: item.h,
|
|
66
|
+
data: '',
|
|
67
|
+
})),
|
|
68
|
+
};
|
|
69
|
+
if (mode === 'data-url') {
|
|
70
|
+
const imgData = await renderHandle.getCanvasData();
|
|
71
|
+
if (imgData) {
|
|
72
|
+
for (let i = 0; i < imgData.length; i += 1) {
|
|
73
|
+
res.charts[i].data = imgData[i];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} else if (mode === 'svg') {
|
|
77
|
+
const svgData = await renderHandle.getSVGData();
|
|
78
|
+
if (svgData) {
|
|
79
|
+
for (let i = 0; i < svgData.length; i += 1) {
|
|
80
|
+
res.charts[i].data = svgData[i];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return res;
|
|
85
|
+
}) as typeof appRef.current.exportChart;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
return () => {
|
|
91
|
+
if (appRef && 'current' in appRef && appRef.current) {
|
|
92
|
+
appRef.current.exportChart = async mode => ({
|
|
93
|
+
mode,
|
|
94
|
+
title: '',
|
|
95
|
+
nCols: 0,
|
|
96
|
+
nRows: 0,
|
|
97
|
+
charts: [],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}, []);
|
|
102
|
+
};
|