@kanaries/graphic-walker 0.3.8 → 0.3.10

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/index.d.ts CHANGED
@@ -1,8 +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 ShadowDomContext: React.Context<{
5
- root: ShadowRoot | null;
6
- }>;
7
- 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>>>;
8
6
  export { default as PureRenderer } from './renderer/pureRenderer';
7
+ export { embedGraphicWalker } from './vanilla';
8
+ export type { IGWProps };
@@ -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 {};
package/dist/main.d.ts CHANGED
@@ -1 +1 @@
1
- import './index.css';
1
+ 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 {};
package/dist/style.css CHANGED
@@ -0,0 +1 @@
1
+ html{margin:0;padding:0;background-color:#fff}@media (prefers-color-scheme: dark){html{background-color:#18181b}}body{margin:0;padding:0}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
@@ -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;
@@ -0,0 +1,3 @@
1
+ import './index.css';
2
+ import { IGWProps } from './App';
3
+ export declare function embedGraphicWalker(dom: HTMLElement | null, props?: IGWProps | undefined): void;
@@ -7,6 +7,7 @@ export interface IReactVegaHandler {
7
7
  downloadPNG: (filename?: string) => Promise<string[]>;
8
8
  }
9
9
  interface ReactVegaProps {
10
+ name?: string;
10
11
  rows: Readonly<IViewField[]>;
11
12
  columns: Readonly<IViewField[]>;
12
13
  dataSource: IRow[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanaries/graphic-walker",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "scripts": {
5
5
  "dev:front_end": "vite --host",
6
6
  "dev": "npm run dev:front_end",
@@ -36,7 +36,8 @@
36
36
  "dependencies": {
37
37
  "@headlessui/react": "^1.7.12",
38
38
  "@heroicons/react": "^2.0.8",
39
- "@kanaries/react-beautiful-dnd": "0.0.1",
39
+ "@kanaries/graphic-walker": "0.3.9",
40
+ "@kanaries/react-beautiful-dnd": "0.0.2",
40
41
  "@kanaries/web-data-loader": "^0.1.7",
41
42
  "autoprefixer": "^10.3.5",
42
43
  "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,53 +1,40 @@
1
- import React, { createContext, useEffect, useRef, useState } from "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 ShadowDomContext = createContext<{ root: ShadowRoot | null }>({ root: null });
15
-
16
- export const GraphicWalker: React.FC<IGWProps> = observer((props) => {
17
- const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);
18
- const rootRef = useRef<HTMLDivElement>(null);
13
+ export const GraphicWalker = observer(forwardRef<IGWHandler, IGWProps>((props, ref) => {
19
14
  const { storeRef } = props;
20
15
 
21
- useEffect(() => {
22
- if (rootRef.current) {
23
- const shadowRoot = rootRef.current.shadowRoot!;
24
- setShadowRoot(shadowRoot);
25
- DOM.setBody(shadowRoot);
26
- DOM.setHead(shadowRoot);
27
- return () => {
28
- DOM.setBody(document.body);
29
- DOM.setHead(document.head);
30
- };
31
- }
32
- }, []);
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
+ };
33
24
 
34
25
  return (
35
26
  <StoreWrapper keepAlive={props.keepAlive} storeRef={storeRef}>
36
- <root.div mode="open" ref={rootRef}>
37
- <style>{tailwindStyle}</style>
38
- <style>{style}</style>
39
- {shadowRoot && (
40
- <StyleSheetManager target={shadowRoot}>
41
- <FieldsContextWrapper>
42
- <ShadowDomContext.Provider value={{ root: shadowRoot }}>
43
- <App {...props} />
44
- </ShadowDomContext.Provider>
45
- </FieldsContextWrapper>
46
- </StyleSheetManager>
47
- )}
48
- </root.div>
27
+ <AppRoot ref={ref}>
28
+ <ShadowDom onMount={handleMount} onUnmount={handleUnmount}>
29
+ <FieldsContextWrapper>
30
+ <App {...props} />
31
+ </FieldsContextWrapper>
32
+ </ShadowDom>
33
+ </AppRoot>
49
34
  </StoreWrapper>
50
35
  );
51
- });
36
+ }));
52
37
 
53
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/main.tsx CHANGED
@@ -1,30 +1,8 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom';
3
- import { GraphicWalker } from './index';
4
-
5
1
  import { inject } from '@vercel/analytics';
6
- import './index.css';
2
+ import { embedGraphicWalker } from './vanilla';
7
3
 
8
4
  if (!import.meta.env.DEV) {
9
5
  inject();
10
6
  }
11
7
 
12
- // Example: Detect if Concurrent Mode is available
13
- const isConcurrentModeAvailable = 'createRoot' in ReactDOM;
14
-
15
- // Use the new ReactDOM.createRoot API if available, otherwise fall back to the old ReactDOM.render API
16
- if (isConcurrentModeAvailable) {
17
- if (import.meta.env.DEV) {
18
- console.warn('React 18+ detected, remove strict mode if you meet drag and drop issue. more info at https://docs.kanaries.net/graphic-walker/faq/graphic-walker-react-18')
19
- }
20
- // @ts-ignore
21
- const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
22
- root.render(<GraphicWalker themeKey="g2" />);
23
- } else {
24
- ReactDOM.render(
25
- <React.StrictMode>
26
- <GraphicWalker themeKey="g2" />
27
- </React.StrictMode>,
28
- document.getElementById('root') as HTMLElement
29
- );
30
- }
8
+ embedGraphicWalker(document.getElementById('root') as HTMLElement)
@@ -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) => transformDataService(data, allFields))
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;
@@ -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
- <SpecRenderer
79
- loading={waiting}
80
- data={viewData}
81
- ref={ref}
82
- themeKey={themeKey}
83
- dark={dark}
84
- draggableFieldState={visualState}
85
- visualConfig={visualConfig}
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
+ };
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import { GraphicWalker } from './index';
4
+
5
+ import './index.css';
6
+ import { IGWProps } from './App';
7
+
8
+ export function embedGraphicWalker (dom: HTMLElement | null, props: IGWProps | undefined = {}) {
9
+ if (!dom) {
10
+ throw("DOM element not found.")
11
+ }
12
+ // Example: Detect if Concurrent Mode is available
13
+ const isConcurrentModeAvailable = 'createRoot' in ReactDOM;
14
+
15
+ // Use the new ReactDOM.createRoot API if available, otherwise fall back to the old ReactDOM.render API
16
+ if (isConcurrentModeAvailable) {
17
+ if (import.meta.env.DEV) {
18
+ console.warn('React 18+ detected, remove strict mode if you meet drag and drop issue. more info at https://docs.kanaries.net/graphic-walker/faq/graphic-walker-react-18')
19
+ }
20
+ // @ts-ignore
21
+ const root = ReactDOM.createRoot(dom as HTMLElement);
22
+ root.render(<GraphicWalker themeKey="g2" {...props} />);
23
+ } else {
24
+ ReactDOM.render(
25
+ <React.StrictMode>
26
+ <GraphicWalker themeKey="g2" {...props} />
27
+ </React.StrictMode>,
28
+ dom as HTMLElement
29
+ );
30
+ }
31
+ }