@kanaries/graphic-walker 0.3.16 → 0.4.0

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.
Files changed (83) hide show
  1. package/dist/App.d.ts +9 -2
  2. package/dist/assets/filter.worker-f09fcd6f.js.map +1 -1
  3. package/dist/assets/sort.worker-f77540ac.js.map +1 -0
  4. package/dist/assets/transform.worker-bae8e910.js.map +1 -0
  5. package/dist/assets/{viewQuery.worker-03404216.js.map → viewQuery.worker-bdb6477c.js.map} +1 -1
  6. package/dist/components/askViz/index.d.ts +6 -0
  7. package/dist/components/askViz/schemaTransform.d.ts +2 -0
  8. package/dist/components/dataTable/index.d.ts +8 -5
  9. package/dist/components/pivotTable/store.d.ts +0 -2
  10. package/dist/components/spinner.d.ts +2 -0
  11. package/dist/computation/clientComputation.d.ts +3 -0
  12. package/dist/computation/serverComputation.d.ts +8 -0
  13. package/dist/config.d.ts +3 -1
  14. package/dist/fields/filterField/tabs.d.ts +2 -1
  15. package/dist/graphic-walker.es.js +22226 -21650
  16. package/dist/graphic-walker.es.js.map +1 -1
  17. package/dist/graphic-walker.umd.js +137 -137
  18. package/dist/graphic-walker.umd.js.map +1 -1
  19. package/dist/index.d.ts +2 -0
  20. package/dist/interfaces.d.ts +93 -4
  21. package/dist/lib/execExp.d.ts +4 -4
  22. package/dist/lib/interfaces.d.ts +1 -0
  23. package/dist/lib/viewQuery.d.ts +2 -2
  24. package/dist/renderer/hooks.d.ts +8 -4
  25. package/dist/renderer/index.d.ts +2 -1
  26. package/dist/renderer/pureRenderer.d.ts +17 -1
  27. package/dist/renderer/specRenderer.d.ts +1 -0
  28. package/dist/services.d.ts +8 -5
  29. package/dist/store/commonStore.d.ts +2 -2
  30. package/dist/store/visualSpecStore.d.ts +58 -42
  31. package/dist/utils/save.d.ts +10 -2
  32. package/dist/utils/workflow.d.ts +3 -0
  33. package/dist/vis/react-vega.d.ts +3 -1
  34. package/dist/workers/sort.d.ts +2 -2
  35. package/dist/workers/transform.d.ts +5 -2
  36. package/package.json +2 -2
  37. package/src/App.tsx +46 -7
  38. package/src/components/askViz/index.tsx +92 -0
  39. package/src/components/askViz/schemaTransform.ts +38 -0
  40. package/src/components/dataTable/index.tsx +51 -11
  41. package/src/components/pivotTable/index.tsx +0 -1
  42. package/src/components/pivotTable/store.tsx +0 -16
  43. package/src/components/spinner.tsx +14 -0
  44. package/src/components/toggle.tsx +2 -2
  45. package/src/components/visualConfig/index.tsx +78 -8
  46. package/src/computation/clientComputation.ts +55 -0
  47. package/src/computation/serverComputation.ts +153 -0
  48. package/src/config.ts +15 -2
  49. package/src/dataSource/datasetConfig/index.tsx +38 -6
  50. package/src/dataSource/table.tsx +11 -2
  51. package/src/fields/filterField/filterEditDialog.tsx +11 -10
  52. package/src/fields/filterField/tabs.tsx +178 -77
  53. package/src/index.tsx +2 -0
  54. package/src/interfaces.ts +108 -5
  55. package/src/lib/execExp.ts +20 -11
  56. package/src/lib/interfaces.ts +1 -0
  57. package/src/lib/op/aggregate.ts +1 -1
  58. package/src/lib/viewQuery.ts +2 -2
  59. package/src/locales/en-US.json +11 -2
  60. package/src/locales/ja-JP.json +11 -2
  61. package/src/locales/zh-CN.json +11 -2
  62. package/src/main.tsx +1 -1
  63. package/src/renderer/hooks.ts +100 -66
  64. package/src/renderer/index.tsx +10 -6
  65. package/src/renderer/pureRenderer.tsx +40 -14
  66. package/src/renderer/specRenderer.tsx +24 -7
  67. package/src/services.ts +7 -8
  68. package/src/store/commonStore.ts +7 -7
  69. package/src/store/visualSpecStore.ts +287 -192
  70. package/src/utils/save.ts +81 -3
  71. package/src/utils/workflow.ts +148 -0
  72. package/src/vis/react-vega.tsx +21 -6
  73. package/src/vis/spec/aggregate.ts +3 -2
  74. package/src/vis/spec/stack.ts +7 -6
  75. package/src/visualSettings/index.tsx +2 -3
  76. package/src/workers/filter.worker.js +1 -1
  77. package/src/workers/sort.ts +3 -4
  78. package/src/workers/sort.worker.ts +2 -2
  79. package/src/workers/transform.ts +7 -8
  80. package/src/workers/transform.worker.js +2 -2
  81. package/src/workers/viewQuery.worker.js +2 -2
  82. package/dist/assets/sort.worker-4299a6a0.js.map +0 -1
  83. package/dist/assets/transform.worker-a12fb3d8.js.map +0 -1
@@ -28,4 +28,5 @@ export interface IBinQuery {
28
28
 
29
29
  export interface IRawQuery {
30
30
  op: 'raw';
31
+ fields: string[];
31
32
  }
@@ -37,7 +37,7 @@ export function aggregate (data: IRow[], query: IAggQuery): IRow[] {
37
37
  aggRow[k] = subGroup[0][k];
38
38
  }
39
39
  for (let mea of measures) {
40
- const aggMeaKey = getMeaAggKey(mea.field, mea.agg);
40
+ const aggMeaKey = mea.asFieldKey || getMeaAggKey(mea.field, mea.agg);
41
41
  if (aggRow[aggMeaKey] === undefined) {
42
42
  aggRow[aggMeaKey] = 0;
43
43
  }
@@ -1,4 +1,4 @@
1
- import { IField, IRow } from "../interfaces";
1
+ import { IRow } from "../interfaces";
2
2
  import { aggregate } from "./op/aggregate";
3
3
  import { fold } from "./op/fold";
4
4
  import { IAggQuery, IBinQuery, IFoldQuery, IRawQuery } from "./interfaces";
@@ -6,7 +6,7 @@ import { bin } from "./op/bin";
6
6
 
7
7
  export type IViewQuery = IAggQuery | IFoldQuery | IBinQuery | IRawQuery;
8
8
 
9
- export function queryView (rawData: IRow[], metas: IField[], query: IViewQuery) {
9
+ export function queryView (rawData: IRow[], query: IViewQuery) {
10
10
  switch (query.op) {
11
11
  case 'aggregate':
12
12
  return aggregate(rawData, query);
@@ -3,7 +3,15 @@
3
3
  "format": "Format",
4
4
  "numberFormat": "Number format",
5
5
  "timeFormat": "Time format",
6
- "normalizedNumberFormat": "Normalized number format"
6
+ "normalizedNumberFormat": "Normalized number format",
7
+ "background": "Background",
8
+ "color":"Color",
9
+ "independence": "Independence",
10
+ "x": "x-Axis",
11
+ "y": "y-Axis",
12
+ "zeroScale": "Zero Scale",
13
+ "formatGuidesDocs": "Format guides docs",
14
+ "readHere": "read here"
7
15
  },
8
16
  "constant": {
9
17
  "row_count": "Row count",
@@ -37,7 +45,8 @@
37
45
  "__enum__": "Stack Mode",
38
46
  "none": "None",
39
47
  "stack": "Stack",
40
- "normalize": "Normalize"
48
+ "normalize": "Normalize",
49
+ "center": "Center"
41
50
  },
42
51
  "layout_type": {
43
52
  "__enum__": "Layout Mode",
@@ -3,7 +3,15 @@
3
3
  "format": "形式",
4
4
  "numberFormat": "数字の形式",
5
5
  "timeFormat": "時間の形式",
6
- "normalizedNumberFormat": "正規化された数字の形式"
6
+ "normalizedNumberFormat": "正規化された数字の形式",
7
+ "background": "背景",
8
+ "color":"色",
9
+ "independence": "独立性",
10
+ "x": "x軸",
11
+ "y": "y軸",
12
+ "zeroScale": "ゼロ目盛",
13
+ "formatGuidesDocs": "フォーマットガイド",
14
+ "readHere": "ここを読む"
7
15
  },
8
16
  "constant": {
9
17
  "row_count": "行数",
@@ -37,7 +45,8 @@
37
45
  "__enum__": "スタックモード",
38
46
  "none": "なし",
39
47
  "stack": "スタック",
40
- "normalize": "正規化"
48
+ "normalize": "正規化",
49
+ "center": "中央"
41
50
  },
42
51
  "layout_type": {
43
52
  "__enum__": "レイアウトタイプ",
@@ -3,7 +3,15 @@
3
3
  "format": "格式",
4
4
  "numberFormat": "数字格式",
5
5
  "timeFormat": "时间格式",
6
- "normalizedNumberFormat": "标准化数字格式"
6
+ "normalizedNumberFormat": "标准化数字格式",
7
+ "background": "背景",
8
+ "color":"颜色",
9
+ "independence": "独立性",
10
+ "x": "x轴",
11
+ "y": "y轴",
12
+ "zeroScale": "零刻度",
13
+ "formatGuidesDocs": "格式指南",
14
+ "readHere": "阅读此处"
7
15
  },
8
16
  "constant": {
9
17
  "row_count": "行数",
@@ -54,7 +62,8 @@
54
62
  "__enum__": "堆叠模式",
55
63
  "none": "关闭",
56
64
  "stack": "堆叠",
57
- "normalize": "归一化"
65
+ "normalize": "归一化",
66
+ "center": "中央"
58
67
  },
59
68
  "draggable_key": {
60
69
  "fields": "字段",
package/src/main.tsx CHANGED
@@ -5,4 +5,4 @@ if (!import.meta.env.DEV) {
5
5
  inject();
6
6
  }
7
7
 
8
- embedGraphicWalker(document.getElementById('root') as HTMLElement)
8
+ embedGraphicWalker(document.getElementById('root') as HTMLElement, {})
@@ -1,30 +1,39 @@
1
1
  import { useState, useEffect, useMemo, useRef } from 'react';
2
2
  import { unstable_batchedUpdates } from 'react-dom';
3
- import type { DeepReadonly, IFilterField, IRow, IViewField } from '../interfaces';
4
- import { applyFilter, applySort, applyViewQuery, transformDataService } from '../services';
5
- import { getMeaAggKey } from '../utils';
3
+ import type { DeepReadonly, IFilterField, IRow, IViewField, IDataQueryWorkflowStep, IComputationFunction } from '../interfaces';
4
+ import { useGlobalStore } from '../store';
6
5
  import { useAppRootContext } from '../components/appRoot';
6
+ import { toWorkflow } from '../utils/workflow';
7
+ import { dataQueryClient } from '../computation/clientComputation';
8
+ import { dataQueryServer } from '../computation/serverComputation';
7
9
  import { useDebounceValue } from '../hooks';
8
10
 
11
+ export const useComputationFunc = (): IComputationFunction => {
12
+ const { vizStore } = useGlobalStore();
13
+ return vizStore.computationFuction;
14
+ };
15
+
9
16
  interface UseRendererProps {
10
- data: IRow[];
11
17
  allFields: Omit<IViewField, 'dragId'>[];
12
- viewDimensions: IViewField[];
13
- viewMeasures: IViewField[];
18
+ viewDimensions: Omit<IViewField, 'dragId'>[];
19
+ viewMeasures: Omit<IViewField, 'dragId'>[];
14
20
  filters: readonly DeepReadonly<IFilterField>[];
15
21
  defaultAggregated: boolean;
16
22
  sort: 'none' | 'ascending' | 'descending';
17
23
  limit: number;
24
+ computationFunction: IComputationFunction;
18
25
  }
19
26
 
20
27
  interface UseRendererResult {
21
28
  viewData: IRow[];
22
29
  loading: boolean;
30
+ parsed: {
31
+ workflow: IDataQueryWorkflowStep[];
32
+ };
23
33
  }
24
34
 
25
35
  export const useRenderer = (props: UseRendererProps): UseRendererResult => {
26
36
  const {
27
- data,
28
37
  allFields,
29
38
  viewDimensions,
30
39
  viewMeasures,
@@ -32,86 +41,111 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
32
41
  defaultAggregated,
33
42
  sort,
34
43
  limit: storeLimit,
44
+ computationFunction,
35
45
  } = props;
36
46
  const [computing, setComputing] = useState(false);
37
47
  const taskIdRef = useRef(0);
38
48
 
39
49
  const limit = useDebounceValue(storeLimit);
40
50
 
51
+ const workflow = useMemo(() => {
52
+ return toWorkflow(
53
+ filters,
54
+ allFields,
55
+ viewDimensions,
56
+ viewMeasures,
57
+ defaultAggregated,
58
+ sort,
59
+ limit > 0 ? limit : undefined
60
+ );
61
+ }, [filters, allFields, viewDimensions, viewMeasures, defaultAggregated, sort, limit]);
62
+
41
63
  const [viewData, setViewData] = useState<IRow[]>([]);
64
+ const [parsedWorkflow, setParsedWorkflow] = useState<IDataQueryWorkflowStep[]>([]);
42
65
 
43
66
  const appRef = useAppRootContext();
44
67
 
68
+ // useEffect(() => {
69
+ // if (computationMode !== 'client') {
70
+ // return;
71
+ // }
72
+ // if (!data) {
73
+ // console.warn('useRenderer error: prop `data` is required for "client" mode, but none is found.');
74
+ // return;
75
+ // }
76
+ // if (!allFields) {
77
+ // console.warn('useRenderer error: prop `fields` is required for "client" mode, but none is found.');
78
+ // return;
79
+ // }
80
+ // const taskId = ++taskIdRef.current;
81
+ // appRef.current?.updateRenderStatus('computing');
82
+ // setComputing(true);
83
+ // dataQueryClient(data, allFields, workflow).then(data => {
84
+ // if (taskId !== taskIdRef.current) {
85
+ // return;
86
+ // }
87
+ // appRef.current?.updateRenderStatus('rendering');
88
+ // unstable_batchedUpdates(() => {
89
+ // setComputing(false);
90
+ // setViewData(data);
91
+ // setParsedWorkflow(workflow);
92
+ // });
93
+ // }).catch((err) => {
94
+ // if (taskId !== taskIdRef.current) {
95
+ // return;
96
+ // }
97
+ // appRef.current?.updateRenderStatus('error');
98
+ // console.error(err);
99
+ // unstable_batchedUpdates(() => {
100
+ // setComputing(false);
101
+ // setViewData([]);
102
+ // setParsedWorkflow([]);
103
+ // });
104
+ // });
105
+ // return () => {
106
+ // taskIdRef.current++;
107
+ // };
108
+ // }, [computationMode, data, allFields, workflow]);
109
+
45
110
  useEffect(() => {
46
111
  const taskId = ++taskIdRef.current;
47
112
  appRef.current?.updateRenderStatus('computing');
48
113
  setComputing(true);
49
- applyFilter(data, filters)
50
- .then((data) => {
51
- if (viewDimensions.length === 0 && viewMeasures.length === 0) {
52
- return data;
53
- }
54
- return transformDataService(data, allFields);
55
- })
56
- .then((d) => {
57
- if (viewDimensions.length === 0 && viewMeasures.length === 0) {
58
- return data;
59
- }
60
- // setViewData(d);
61
- const dims = viewDimensions;
62
- const meas = viewMeasures;
63
- return applyViewQuery(d, dims.concat(meas), {
64
- op: defaultAggregated ? 'aggregate' : 'raw',
65
- groupBy: dims.map((f) => f.fid),
66
- measures: meas.map((f) => ({
67
- field: f.fid,
68
- agg: f.aggName as any,
69
- asFieldKey: getMeaAggKey(f.fid, f.aggName!),
70
- })),
71
- });
72
- })
73
- .then((data) => {
74
- if (limit > 0 && sort !== 'none' && viewMeasures.length > 0) {
75
- return applySort(data, viewMeasures, sort);
76
- }
77
- return data;
78
- })
79
- .then((data) => {
80
- if (limit > 0) {
81
- return data.slice(0, limit);
82
- }
83
- return data;
84
- })
85
- .then((data) => {
86
- if (taskId !== taskIdRef.current) {
87
- return;
88
- }
89
- appRef.current?.updateRenderStatus('rendering');
90
- unstable_batchedUpdates(() => {
91
- setComputing(false);
92
- setViewData(data);
93
- });
94
- })
95
- .catch((err) => {
96
- if (taskId !== taskIdRef.current) {
97
- return;
98
- }
99
- appRef.current?.updateRenderStatus('error');
100
- console.error(err);
101
- unstable_batchedUpdates(() => {
102
- setComputing(false);
103
- setViewData([]);
104
- });
114
+ dataQueryServer(computationFunction, workflow, limit > 0 ? limit : undefined).then(data => {
115
+ if (taskId !== taskIdRef.current) {
116
+ return;
117
+ }
118
+ appRef.current?.updateRenderStatus('rendering');
119
+ unstable_batchedUpdates(() => {
120
+ setComputing(false);
121
+ setViewData(data);
122
+ setParsedWorkflow(workflow);
105
123
  });
106
- return () => {
107
- taskIdRef.current++;
124
+ }).catch((err) => {
125
+ if (taskId !== taskIdRef.current) {
126
+ return;
127
+ }
128
+ appRef.current?.updateRenderStatus('error');
129
+ console.error(err);
130
+ unstable_batchedUpdates(() => {
131
+ setComputing(false);
132
+ setViewData([]);
133
+ setParsedWorkflow([]);
134
+ });
135
+ });
136
+ }, [computationFunction, workflow]);
137
+
138
+ const parseResult = useMemo(() => {
139
+ return {
140
+ workflow: parsedWorkflow,
108
141
  };
109
- }, [data, filters, viewDimensions, viewMeasures, defaultAggregated, limit]);
142
+ }, [parsedWorkflow]);
110
143
 
111
144
  return useMemo(() => {
112
145
  return {
113
146
  viewData,
114
147
  loading: computing,
148
+ parsed: parseResult,
115
149
  };
116
150
  }, [viewData, computing]);
117
151
  };
@@ -1,25 +1,28 @@
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 } from '../interfaces';
3
+ import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig, IComputationFunction } from '../interfaces';
4
+ import { useTranslation } from 'react-i18next';
4
5
  import SpecRenderer from './specRenderer';
5
6
  import { runInAction, toJS } from 'mobx';
6
7
  import { useGlobalStore } from '../store';
7
8
  import { IReactVegaHandler } from '../vis/react-vega';
8
9
  import { unstable_batchedUpdates } from 'react-dom';
9
10
  import { useRenderer } from './hooks';
10
- import { initEncoding, initVisualConfig } from '../store/visualSpecStore';
11
+ import { initEncoding } from '../store/visualSpecStore';
11
12
  import { useChartIndexControl } from '../utils/chartIndexControl';
13
+ import { initVisualConfig } from '../utils/save';
12
14
 
13
15
  interface RendererProps {
14
16
  themeKey?: IThemeKey;
15
17
  dark?: IDarkMode;
18
+ computationFunction: IComputationFunction;
16
19
  }
17
20
  /**
18
21
  * Renderer of GraphicWalker editor.
19
22
  * Depending on global store.
20
23
  */
21
24
  const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, ref) {
22
- const { themeKey, dark } = props;
25
+ const { themeKey, dark, computationFunction } = props;
23
26
  const { vizStore, commonStore } = useGlobalStore();
24
27
  const {
25
28
  allFields,
@@ -34,15 +37,14 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
34
37
  limit,
35
38
  } = vizStore;
36
39
  const chart = visList[visIndex];
37
- const { currentDataset } = commonStore;
38
- const { dataSource } = currentDataset;
40
+
41
+ const { i18n } = useTranslation();
39
42
 
40
43
  const [viewConfig, setViewConfig] = useState<IVisualConfig>(initVisualConfig);
41
44
  const [encodings, setEncodings] = useState<DeepReadonly<DraggableFieldState>>(initEncoding);
42
45
  const [viewData, setViewData] = useState<IRow[]>([]);
43
46
 
44
47
  const { viewData: data, loading: waiting } = useRenderer({
45
- data: dataSource,
46
48
  allFields,
47
49
  viewDimensions,
48
50
  viewMeasures,
@@ -50,6 +52,7 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
50
52
  defaultAggregated: visualConfig.defaultAggregated,
51
53
  sort,
52
54
  limit: limit,
55
+ computationFunction,
53
56
  });
54
57
 
55
58
  // Dependencies that should not trigger effect individually
@@ -107,6 +110,7 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
107
110
  ref={ref}
108
111
  themeKey={themeKey}
109
112
  dark={dark}
113
+ locale={i18n.language}
110
114
  draggableFieldState={encodings}
111
115
  visualConfig={viewConfig}
112
116
  onGeomClick={handleGeomClick}
@@ -4,28 +4,53 @@ import { toJS } from 'mobx';
4
4
  import { observer } from 'mobx-react-lite';
5
5
  import { ShadowDom } from '../shadow-dom';
6
6
  import { withAppRoot } from '../components/appRoot';
7
- import type { IDarkMode, IViewField, IRow, IThemeKey, DraggableFieldState, IVisualConfig } from '../interfaces';
7
+ import type {
8
+ IDarkMode,
9
+ IViewField,
10
+ IRow,
11
+ IThemeKey,
12
+ DraggableFieldState,
13
+ IVisualConfig,
14
+ IComputationFunction,
15
+ } from '../interfaces';
8
16
  import type { IReactVegaHandler } from '../vis/react-vega';
9
17
  import SpecRenderer from './specRenderer';
10
18
  import { useRenderer } from './hooks';
19
+ import { getComputation } from '../computation/clientComputation';
11
20
 
12
- interface IPureRendererProps {
13
- name?: string;
14
- themeKey?: IThemeKey;
15
- dark?: IDarkMode;
16
- rawData?: IRow[];
17
- visualState: DraggableFieldState;
18
- visualConfig: IVisualConfig;
19
- sort?: 'none' | 'ascending' | 'descending';
20
- limit?: number;
21
- }
21
+ type IPureRendererProps =
22
+ | {
23
+ name?: string;
24
+ themeKey?: IThemeKey;
25
+ dark?: IDarkMode;
26
+ visualState: DraggableFieldState;
27
+ visualConfig: IVisualConfig;
28
+ sort?: 'none' | 'ascending' | 'descending';
29
+ limit?: number;
30
+ locale?: string;
31
+ } & (
32
+ | {
33
+ type: 'remote',
34
+ computation: IComputationFunction;
35
+ }
36
+ | {
37
+ type?: 'local'
38
+ rawData: IRow[];
39
+ }
40
+ );
22
41
 
23
42
  /**
24
43
  * Render a readonly chart with given visualization schema.
25
44
  * This is a pure component, which means it will not depend on any global state.
26
45
  */
27
46
  const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer(props, ref) {
28
- const { name, themeKey, dark, rawData, visualState, visualConfig, sort, limit } = props;
47
+ const { name, themeKey, dark, visualState, visualConfig, type, locale, sort, limit } = props;
48
+ const computation = useMemo(() => {
49
+ if (props.type === 'remote') {
50
+ return props.computation;
51
+ }
52
+ return getComputation(props.rawData);
53
+ }, [props.type, props.type === 'remote' ? props.computation: props.rawData])
29
54
  const defaultAggregated = visualConfig?.defaultAggregated ?? false;
30
55
 
31
56
  const [viewData, setViewData] = useState<IRow[]>([]);
@@ -52,7 +77,6 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
52
77
  }, [visualState]);
53
78
 
54
79
  const { viewData: data, loading: waiting } = useRenderer({
55
- data: rawData ?? [],
56
80
  allFields,
57
81
  viewDimensions,
58
82
  viewMeasures,
@@ -60,6 +84,7 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
60
84
  defaultAggregated,
61
85
  sort: sort ?? 'none',
62
86
  limit: limit ?? -1,
87
+ computationFunction: computation,
63
88
  });
64
89
 
65
90
  // Dependencies that should not trigger effect individually
@@ -86,10 +111,11 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
86
111
  dark={dark}
87
112
  draggableFieldState={visualState}
88
113
  visualConfig={visualConfig}
114
+ locale={locale ?? 'en-US'}
89
115
  />
90
116
  </div>
91
117
  </ShadowDom>
92
118
  );
93
119
  });
94
120
 
95
- export default observer(withAppRoot(PureRenderer));
121
+ export default observer(withAppRoot<IPureRendererProps>(PureRenderer));
@@ -19,17 +19,18 @@ interface SpecRendererProps {
19
19
  visualConfig: DeepReadonly<IVisualConfig>;
20
20
  onGeomClick?: ((values: any, e: any) => void) | undefined;
21
21
  onChartResize?: ((width: number, height: number) => void) | undefined;
22
+ locale?: string;
22
23
  }
23
24
  /**
24
25
  * Sans-store renderer of GraphicWalker.
25
26
  * This is a pure component, which means it will not depend on any global state.
26
27
  */
27
28
  const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
28
- { name, themeKey, dark, data, loading, draggableFieldState, visualConfig, onGeomClick, onChartResize },
29
+ { name, themeKey, dark, data, loading, draggableFieldState, visualConfig, onGeomClick, onChartResize, locale },
29
30
  ref
30
31
  ) {
31
32
  // const { draggableFieldState, visualConfig } = vizStore;
32
- const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, format: _format, zeroScale } = visualConfig;
33
+ const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, format: _format, background, zeroScale, resolve } = visualConfig;
33
34
 
34
35
  const rows = draggableFieldState.rows;
35
36
  const columns = draggableFieldState.columns;
@@ -41,7 +42,7 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
41
42
  const sizeChannel = draggableFieldState.size;
42
43
  const details = draggableFieldState.details;
43
44
  const text = draggableFieldState.text;
44
- const format = toJS(_format)
45
+ const format = toJS(_format);
45
46
 
46
47
  const rowLeftFacetFields = useMemo(() => rows.slice(0, -1).filter((f) => f.analyticType === 'dimension'), [rows]);
47
48
  const colLeftFacetFields = useMemo(
@@ -59,8 +60,9 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
59
60
 
60
61
  const vegaConfig = useMemo<VegaGlobalConfig>(() => {
61
62
  const config: VegaGlobalConfig = {
62
- ...themeConfig,
63
- }
63
+ ...themeConfig,
64
+ background: mediaTheme === 'dark' ? '#18181f' : '#ffffff',
65
+ };
64
66
  if (format.normalizedNumberFormat && format.normalizedNumberFormat.length > 0) {
65
67
  // @ts-ignore
66
68
  config.normalizedNumberFormat = format.normalizedNumberFormat;
@@ -79,9 +81,23 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
79
81
  config.scale = {};
80
82
  }
81
83
  // @ts-ignore
82
- config.scale.zero = Boolean(zeroScale)
84
+ config.scale.zero = Boolean(zeroScale);
85
+ // @ts-ignore
86
+ config.resolve = resolve;
87
+ if (background) {
88
+ config.background = background;
89
+ }
90
+
83
91
  return config;
84
- }, [themeConfig, zeroScale, format.normalizedNumberFormat, format.numberFormat, format.timeFormat])
92
+ }, [
93
+ themeConfig,
94
+ zeroScale,
95
+ resolve,
96
+ background,
97
+ format.normalizedNumberFormat,
98
+ format.numberFormat,
99
+ format.timeFormat,
100
+ ]);
85
101
 
86
102
  if (isPivotTable) {
87
103
  return (
@@ -148,6 +164,7 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
148
164
  height={size.height - 12 * 4}
149
165
  ref={ref}
150
166
  onGeomClick={onGeomClick}
167
+ locale={locale}
151
168
  />
152
169
  </Resizable>
153
170
  );
package/src/services.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { toJS } from 'mobx';
2
- import { IRow, IMutField, IField, IFilterField, Specification, IViewField } from './interfaces';
2
+ import { IRow, IMutField, Specification, IFilterFiledSimple, IExpression } from './interfaces';
3
3
  /* eslint import/no-webpack-loader-syntax:0 */
4
4
  // @ts-ignore
5
5
  // eslint-disable-next-line
@@ -108,7 +108,7 @@ interface PreAnalysisParams {
108
108
  let filterWorker: Worker | null = null;
109
109
  let filterWorkerAutoTerminator: NodeJS.Timeout | null = null;
110
110
 
111
- export const applyFilter = async (data: IRow[], filters: readonly IFilterField[]): Promise<IRow[]> => {
111
+ export const applyFilter = async (data: IRow[], filters: readonly IFilterFiledSimple[]): Promise<IRow[]> => {
112
112
  if (filters.length === 0) return data;
113
113
  if (filterWorkerAutoTerminator !== null) {
114
114
  clearTimeout(filterWorkerAutoTerminator);
@@ -142,13 +142,13 @@ export const applyFilter = async (data: IRow[], filters: readonly IFilterField[]
142
142
  }
143
143
  };
144
144
 
145
- export const transformDataService = async (data: IRow[], columns: IField[]): Promise<IRow[]> => {
146
- if (columns.length === 0 || data.length === 0) return data;
145
+ export const transformDataService = async (data: IRow[], trans: { key: string, expression: IExpression }[]): Promise<IRow[]> => {
146
+ if (data.length === 0) return data;
147
147
  const worker = new TransformDataWorker();
148
148
  try {
149
149
  const res: IRow[] = await workerService(worker, {
150
150
  dataSource: data,
151
- columns: toJS(columns),
151
+ trans,
152
152
  });
153
153
  return res;
154
154
  } catch (error) {
@@ -158,12 +158,11 @@ export const transformDataService = async (data: IRow[], columns: IField[]): Pro
158
158
  }
159
159
  };
160
160
 
161
- export const applyViewQuery = async (data: IRow[], metas: IField[], query: IViewQuery): Promise<IRow[]> => {
161
+ export const applyViewQuery = async (data: IRow[], query: IViewQuery): Promise<IRow[]> => {
162
162
  const worker = new ViewQueryWorker();
163
163
  try {
164
164
  const res: IRow[] = await workerService(worker, {
165
165
  dataSource: data,
166
- metas: toJS(metas),
167
166
  query: toJS(query),
168
167
  });
169
168
  return res;
@@ -176,7 +175,7 @@ export const applyViewQuery = async (data: IRow[], metas: IField[], query: IView
176
175
 
177
176
  export const applySort = async (
178
177
  data: IRow[],
179
- viewMeasures: IField[],
178
+ viewMeasures: string[],
180
179
  sort: 'ascending' | 'descending'
181
180
  ): Promise<IRow[]> => {
182
181
  const worker = new SortWorker();
@@ -147,14 +147,14 @@ export class CommonStore {
147
147
  this.initTempDS();
148
148
  this.showDSPanel = true;
149
149
  }
150
- public addAndUseDS(dataset: IDataSetInfo) {
151
- const datasetId = this.addDS(dataset);
150
+ public addAndUseDS(dataset: IDataSetInfo, datasetId?: string | undefined) {
151
+ const id = this.addDS(dataset, datasetId);
152
152
  this.dsIndex = this.datasets.length - 1;
153
- return datasetId
153
+ return id
154
154
  }
155
- public addDS(dataset: IDataSetInfo) {
155
+ public addDS(dataset: IDataSetInfo, datasetId?: string | undefined) {
156
156
  const timestamp = new Date().getTime();
157
- const dataSetId = `dst-${timestamp}`
157
+ const dataSetId = datasetId || `dst-${timestamp}`
158
158
  const dataSourceId = `dse-${timestamp}`;
159
159
  this.dataSources.push({
160
160
  id: dataSourceId,
@@ -166,12 +166,12 @@ export class CommonStore {
166
166
  rawFields: dataset.rawFields,
167
167
  dsId: dataSourceId
168
168
  })
169
- return dataSetId;
169
+ return dataSourceId;
170
170
  }
171
171
  public removeDS(datasetId: string) {
172
172
  const datasetIndex = this.datasets.findIndex(d => d.id === datasetId);
173
173
  if (datasetIndex > -1) {
174
- const dataSourceId = this.datasets[datasetIndex].dsId;
174
+ const dataSourceId = this.datasets[datasetIndex].id;
175
175
  const dataSourceIndex = this.dataSources.findIndex(d => d.id === dataSourceId);
176
176
  this.dataSources.splice(dataSourceIndex, 1);
177
177
  this.datasets.splice(datasetIndex, 1);