@kanaries/graphic-walker 0.4.12 → 0.4.13

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/main.d.ts CHANGED
@@ -1 +1 @@
1
- export {};
1
+ import './index.css';
@@ -7,6 +7,9 @@ interface RendererProps {
7
7
  dark?: IDarkMode;
8
8
  computationFunction: IComputationFunction;
9
9
  channelScales?: IChannelScales;
10
+ csvRef?: React.MutableRefObject<{
11
+ download: () => void;
12
+ }>;
10
13
  }
11
14
  declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<Pick<RendererProps & React.RefAttributes<IReactVegaHandler>, "key" | keyof RendererProps> & React.RefAttributes<IReactVegaHandler>>>;
12
15
  export default _default;
package/dist/style.css CHANGED
@@ -1 +0,0 @@
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}
package/dist/vanilla.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- import './index.css';
2
1
  import { IGWProps } from './App';
3
2
  export declare function embedGraphicWalker(dom: HTMLElement | null, props?: IGWProps | undefined): void;
@@ -5,6 +5,9 @@ import { ToolbarItemProps } from '../components/toolbar';
5
5
  interface IVisualSettings {
6
6
  darkModePreference: IDarkMode;
7
7
  rendererHandler?: React.RefObject<IReactVegaHandler>;
8
+ csvHandler?: React.MutableRefObject<{
9
+ download: () => void;
10
+ }>;
8
11
  exclude?: string[];
9
12
  extra?: ToolbarItemProps[];
10
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanaries/graphic-walker",
3
- "version": "0.4.12",
3
+ "version": "0.4.13",
4
4
  "scripts": {
5
5
  "dev:front_end": "vite --host",
6
6
  "dev": "npm run dev:front_end",
package/src/App.tsx CHANGED
@@ -174,6 +174,8 @@ const App = observer<IGWProps>(function App(props) {
174
174
 
175
175
  const rendererRef = useRef<IReactVegaHandler>(null);
176
176
 
177
+ const downloadCSVRef = useRef<{ download: () => void }>({download() {}});
178
+
177
179
  const reportError = useCallback((msg: string, code?: number) => {
178
180
  const err = new Error(`Error${code ? `(${code})`: ''}: ${msg}`);
179
181
  console.error(err);
@@ -208,7 +210,7 @@ const App = observer<IGWProps>(function App(props) {
208
210
  {enhanceAPI?.features?.askviz && (
209
211
  <AskViz api={typeof enhanceAPI.features.askviz === 'string' ? enhanceAPI.features.askviz : ''} headers={enhanceAPI?.header} />
210
212
  )}
211
- <VisualSettings rendererHandler={rendererRef} darkModePreference={dark} exclude={toolbar?.exclude} extra={toolbar?.extra} />
213
+ <VisualSettings csvHandler={downloadCSVRef} rendererHandler={rendererRef} darkModePreference={dark} exclude={toolbar?.exclude} extra={toolbar?.extra} />
212
214
  <CodeExport />
213
215
  <ExplainData themeKey={themeKey} dark={darkMode}/>
214
216
  <VisualConfig />
@@ -239,7 +241,7 @@ const App = observer<IGWProps>(function App(props) {
239
241
  }}
240
242
  >
241
243
  {datasets.length > 0 && (
242
- <ReactiveRenderer ref={rendererRef} themeKey={themeKey} themeConfig={themeConfig} dark={dark} computationFunction={vizStore.computationFunction} channelScales={props.channelScales} />
244
+ <ReactiveRenderer csvRef={downloadCSVRef} ref={rendererRef} themeKey={themeKey} themeConfig={themeConfig} dark={dark} computationFunction={vizStore.computationFunction} channelScales={props.channelScales} />
243
245
  )}
244
246
  {vizEmbededMenu.show && (
245
247
  <ClickMenu x={vizEmbededMenu.position[0]} y={vizEmbededMenu.position[1]}>
@@ -1,7 +1,7 @@
1
1
  import { observer } from "mobx-react-lite";
2
2
  import React from "react";
3
3
  import { useTranslation } from "react-i18next";
4
-
4
+ import { toJS } from 'mobx';
5
5
  import Modal from "../../components/modal";
6
6
  import type { IFilterField, IFilterRule } from "../../interfaces";
7
7
  import { useGlobalStore } from "../../store";
@@ -45,7 +45,7 @@ const FilterEditDialog: React.FC = observer(() => {
45
45
 
46
46
  React.useEffect(() => {
47
47
  if (field !== ufRef.current) {
48
- setUncontrolledField(field);
48
+ setUncontrolledField(toJS(field) );
49
49
  }
50
50
  }, [field]);
51
51
 
package/src/main.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import { inject } from '@vercel/analytics';
2
2
  import { embedGraphicWalker } from './vanilla';
3
+ import './index.css';
3
4
 
4
5
  if (!import.meta.env.DEV) {
5
6
  inject();
@@ -1,6 +1,6 @@
1
1
  import { observer } from 'mobx-react-lite';
2
2
  import React, { useState, useEffect, forwardRef, useRef, useCallback } from 'react';
3
- import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig, IComputationFunction, IChannelScales } from '../interfaces';
3
+ import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig, IComputationFunction, IChannelScales, IViewField } from '../interfaces';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import SpecRenderer from './specRenderer';
6
6
  import { runInAction, toJS } from 'mobx';
@@ -8,10 +8,12 @@ import { useGlobalStore } from '../store';
8
8
  import { IReactVegaHandler } from '../vis/react-vega';
9
9
  import { unstable_batchedUpdates } from 'react-dom';
10
10
  import { useRenderer } from './hooks';
11
- import { initEncoding } from '../utils/save';
11
+ import { download, initEncoding } from '../utils/save';
12
12
  import { useChartIndexControl } from '../utils/chartIndexControl';
13
13
  import { LEAFLET_DEFAULT_HEIGHT, LEAFLET_DEFAULT_WIDTH } from '../components/leafletRenderer';
14
14
  import { initVisualConfig } from '../utils/save';
15
+ import { getMeaAggKey } from '../utils';
16
+ import { COUNT_FIELD_ID } from '../constants';
15
17
 
16
18
  interface RendererProps {
17
19
  themeKey?: IThemeKey;
@@ -19,13 +21,14 @@ interface RendererProps {
19
21
  dark?: IDarkMode;
20
22
  computationFunction: IComputationFunction;
21
23
  channelScales?: IChannelScales;
24
+ csvRef?: React.MutableRefObject<{ download: () => void }>;
22
25
  }
23
26
  /**
24
27
  * Renderer of GraphicWalker editor.
25
28
  * Depending on global store.
26
29
  */
27
30
  const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, ref) {
28
- const { themeKey, dark, computationFunction, themeConfig } = props;
31
+ const { themeKey, dark, computationFunction, themeConfig, csvRef, } = props;
29
32
  const { vizStore, commonStore } = useGlobalStore();
30
33
  const {
31
34
  allFields,
@@ -58,6 +61,32 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
58
61
  computationFunction,
59
62
  });
60
63
 
64
+ useEffect(() => {
65
+ if (csvRef) {
66
+ csvRef.current = {
67
+ download() {
68
+ const headers = viewDimensions.concat(viewMeasures).map(x => {
69
+ if (x.fid === COUNT_FIELD_ID) {
70
+ return {
71
+ name: 'Count',
72
+ fid: COUNT_FIELD_ID
73
+ }
74
+ }
75
+ if (viewConfig.defaultAggregated && x.analyticType === 'measure') {
76
+ return {
77
+ fid: getMeaAggKey(x.fid,x.aggName),
78
+ name: `${x.aggName}(${x.name})`
79
+ }
80
+ }
81
+ return {fid: x.fid, name: x.name};
82
+ });
83
+ const result = `${headers.map(x=>x.name).join(',')}\n${data.map(x => headers.map(f => x[f.fid]).join(',')).join('\n')}`;
84
+ download(result, `${chart.name}.csv`, 'text/plain');
85
+ },
86
+ }
87
+ }
88
+ }, [data,viewDimensions,viewMeasures,visualConfig.defaultAggregated]);
89
+
61
90
  // Dependencies that should not trigger effect individually
62
91
  const latestFromRef = useRef({
63
92
  data,
package/src/vanilla.tsx CHANGED
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import ReactDOM from 'react-dom';
3
3
  import { GraphicWalker } from './index';
4
4
 
5
- import './index.css';
6
5
  import { IGWProps } from './App';
7
6
 
8
7
  export function embedGraphicWalker (dom: HTMLElement | null, props: IGWProps | undefined = {}) {
@@ -69,6 +69,7 @@ const FormContainer = styled.div`
69
69
  interface IVisualSettings {
70
70
  darkModePreference: IDarkMode;
71
71
  rendererHandler?: React.RefObject<IReactVegaHandler>;
72
+ csvHandler?: React.MutableRefObject<{ download: () => void }>;
72
73
  exclude?: string[];
73
74
  extra?: ToolbarItemProps[];
74
75
  }
@@ -76,6 +77,7 @@ interface IVisualSettings {
76
77
  const VisualSettings: React.FC<IVisualSettings> = ({
77
78
  rendererHandler,
78
79
  darkModePreference,
80
+ csvHandler,
79
81
  extra = [],
80
82
  exclude = [],
81
83
  }) => {
@@ -109,6 +111,13 @@ const VisualSettings: React.FC<IVisualSettings> = ({
109
111
  [rendererHandler]
110
112
  );
111
113
 
114
+ const downloadCSV = useCallback(
115
+ throttle(() => {
116
+ csvHandler?.current?.download();
117
+ }, 200),
118
+ []
119
+ );
120
+
112
121
  const dark = useCurrentMediaTheme(darkModePreference) === 'dark';
113
122
 
114
123
  const items = useMemo<ToolbarItemProps[]>(() => {
@@ -524,6 +533,12 @@ const VisualSettings: React.FC<IVisualSettings> = ({
524
533
  </FormContainer>
525
534
  ),
526
535
  }]:[],
536
+ {
537
+ key: 'csv',
538
+ label: t('button.export_chart_as', { type: 'csv' }),
539
+ icon: TableCellsIcon,
540
+ onClick: downloadCSV,
541
+ },
527
542
  {
528
543
  key: 'config',
529
544
  label: t('button.config'),