@kanaries/graphic-walker 0.3.16 → 0.4.1

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 (87) 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 +9 -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 +22268 -21682
  16. package/dist/graphic-walker.es.js.map +1 -1
  17. package/dist/graphic-walker.umd.js +139 -139
  18. package/dist/graphic-walker.umd.js.map +1 -1
  19. package/dist/hooks/index.d.ts +1 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/interfaces.d.ts +93 -4
  22. package/dist/lib/execExp.d.ts +4 -4
  23. package/dist/lib/interfaces.d.ts +1 -0
  24. package/dist/lib/viewQuery.d.ts +2 -2
  25. package/dist/renderer/hooks.d.ts +8 -4
  26. package/dist/renderer/index.d.ts +2 -1
  27. package/dist/renderer/pureRenderer.d.ts +17 -1
  28. package/dist/renderer/specRenderer.d.ts +1 -0
  29. package/dist/services.d.ts +8 -5
  30. package/dist/store/commonStore.d.ts +2 -2
  31. package/dist/store/visualSpecStore.d.ts +58 -42
  32. package/dist/utils/save.d.ts +10 -2
  33. package/dist/utils/workflow.d.ts +3 -0
  34. package/dist/vis/react-vega.d.ts +3 -1
  35. package/dist/workers/sort.d.ts +2 -2
  36. package/dist/workers/transform.d.ts +5 -2
  37. package/package.json +2 -2
  38. package/src/App.tsx +46 -7
  39. package/src/components/askViz/index.tsx +93 -0
  40. package/src/components/askViz/schemaTransform.ts +38 -0
  41. package/src/components/dataTable/index.tsx +53 -11
  42. package/src/components/limitSetting.tsx +8 -6
  43. package/src/components/pivotTable/index.tsx +0 -1
  44. package/src/components/pivotTable/store.tsx +0 -16
  45. package/src/components/sizeSetting.tsx +9 -7
  46. package/src/components/spinner.tsx +14 -0
  47. package/src/components/toggle.tsx +2 -2
  48. package/src/components/visualConfig/index.tsx +78 -8
  49. package/src/computation/clientComputation.ts +55 -0
  50. package/src/computation/serverComputation.ts +158 -0
  51. package/src/config.ts +15 -2
  52. package/src/dataSource/datasetConfig/index.tsx +38 -6
  53. package/src/dataSource/table.tsx +15 -2
  54. package/src/fields/filterField/filterEditDialog.tsx +11 -10
  55. package/src/fields/filterField/tabs.tsx +178 -77
  56. package/src/hooks/index.ts +8 -0
  57. package/src/index.tsx +2 -0
  58. package/src/interfaces.ts +108 -5
  59. package/src/lib/execExp.ts +20 -11
  60. package/src/lib/interfaces.ts +1 -0
  61. package/src/lib/op/aggregate.ts +1 -1
  62. package/src/lib/viewQuery.ts +2 -2
  63. package/src/locales/en-US.json +11 -2
  64. package/src/locales/ja-JP.json +11 -2
  65. package/src/locales/zh-CN.json +11 -2
  66. package/src/main.tsx +1 -1
  67. package/src/renderer/hooks.ts +57 -69
  68. package/src/renderer/index.tsx +10 -6
  69. package/src/renderer/pureRenderer.tsx +40 -14
  70. package/src/renderer/specRenderer.tsx +24 -7
  71. package/src/services.ts +7 -8
  72. package/src/store/commonStore.ts +7 -7
  73. package/src/store/visualSpecStore.ts +288 -192
  74. package/src/utils/save.ts +81 -3
  75. package/src/utils/workflow.ts +148 -0
  76. package/src/vis/react-vega.tsx +21 -6
  77. package/src/vis/spec/aggregate.ts +3 -2
  78. package/src/vis/spec/stack.ts +7 -6
  79. package/src/visualSettings/index.tsx +2 -4
  80. package/src/workers/filter.worker.js +1 -1
  81. package/src/workers/sort.ts +3 -4
  82. package/src/workers/sort.worker.ts +2 -2
  83. package/src/workers/transform.ts +7 -8
  84. package/src/workers/transform.worker.js +2 -2
  85. package/src/workers/viewQuery.worker.js +2 -2
  86. package/dist/assets/sort.worker-4299a6a0.js.map +0 -1
  87. package/dist/assets/transform.worker-a12fb3d8.js.map +0 -1
@@ -4,7 +4,7 @@ interface IDataFrame {
4
4
  [key: string]: any[];
5
5
  }
6
6
 
7
- export function execExpression (exp: IExpression, dataFrame: IDataFrame, columns: IField[]): IDataFrame {
7
+ export function execExpression (exp: IExpression, dataFrame: IDataFrame): IDataFrame {
8
8
  const { op, params } = exp;
9
9
  const subFrame: IDataFrame = { ...dataFrame };
10
10
  const len = dataFrame[Object.keys(dataFrame)[0]].length;
@@ -17,7 +17,7 @@ export function execExpression (exp: IExpression, dataFrame: IDataFrame, columns
17
17
  subFrame[param.value] = new Array(len).fill(param.value);
18
18
  break;
19
19
  case 'expression':
20
- let f = execExpression(param.value, dataFrame, columns);
20
+ let f = execExpression(param.value, dataFrame);
21
21
  Object.keys(f).forEach(key => {
22
22
  subFrame[key] = f[key];
23
23
  })
@@ -54,11 +54,18 @@ function bin(resKey: string, params: IExpParamter[], data: IDataFrame, binSize:
54
54
  if (val < _min) _min = val;
55
55
  }
56
56
  const step = (_max - _min) / binSize;
57
- const beaStep = Math.max(-Math.round(Math.log10(_max - _min)) + 2, 0)
57
+ // prevent (_max - _min) to be 0
58
+ const safeWidth = Math.min(Number.MAX_SAFE_INTEGER, Math.max(_max - _min, Number.MIN_VALUE));
59
+ const beaStep = Math.max(-Math.round(Math.log10(safeWidth)) + 2, 0)
60
+ // toFix() accepts 0-100
61
+ const safeBeaStep = Math.min(100, Math.max(0, Math.max(Number.isFinite(beaStep) ? beaStep : 0, 0)));
58
62
  const newValues = fieldValues.map((v: number) => {
59
63
  let bIndex = Math.floor((v - _min) / step);
60
64
  if (bIndex === binSize) bIndex = binSize - 1;
61
- return Number(((bIndex * step + _min)).toFixed(beaStep))
65
+ if (Number.isNaN(bIndex)) {
66
+ bIndex = 0;
67
+ }
68
+ return Number(((bIndex * step + _min)).toFixed(safeBeaStep))
62
69
  });
63
70
  return {
64
71
  ...data,
@@ -124,22 +131,24 @@ function one(resKey: string, params: IExpParamter[], data: IDataFrame): IDataFra
124
131
  }
125
132
  }
126
133
 
127
- export function dataset2DataFrame(dataset: IRow[], columns: IField[]): IDataFrame {
134
+ export function dataset2DataFrame(dataset: IRow[]): IDataFrame {
128
135
  const dataFrame: IDataFrame = {};
129
- columns.forEach((col) => {
130
- dataFrame[col.fid] = dataset.map((row) => row[col.fid]);
136
+ if (dataset.length === 0) return dataFrame;
137
+ Object.keys(dataset[0]).forEach((k) => {
138
+ dataFrame[k] = dataset.map((row) => row[k]);
131
139
  });
132
140
  return dataFrame;
133
141
  }
134
142
 
135
- export function dataframe2Dataset(dataFrame: IDataFrame, columns: IField[]): IRow[] {
136
- if (columns.length === 0) return [];
143
+ export function dataframe2Dataset(dataFrame: IDataFrame): IRow[] {
144
+ const cols = Object.keys(dataFrame);
145
+ if (cols.length === 0) return [];
137
146
  const dataset: IRow[] = [];
138
147
  const len = dataFrame[Object.keys(dataFrame)[0]].length;
139
148
  for (let i = 0; i < len; i++) {
140
149
  const row: IRow = {};
141
- columns.forEach((col) => {
142
- row[col.fid] = dataFrame[col.fid][i];
150
+ cols.forEach((k) => {
151
+ row[k] = dataFrame[k][i];
143
152
  });
144
153
  dataset.push(row);
145
154
  }
@@ -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,44 +1,63 @@
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';
7
- import { useDebounceValue } from '../hooks';
6
+ import { toWorkflow } from '../utils/workflow';
7
+ import { dataQueryServer } from '../computation/serverComputation';
8
+
9
+ export const useComputationFunc = (): IComputationFunction => {
10
+ const { vizStore } = useGlobalStore();
11
+ return vizStore.computationFuction;
12
+ };
8
13
 
9
14
  interface UseRendererProps {
10
- data: IRow[];
11
15
  allFields: Omit<IViewField, 'dragId'>[];
12
- viewDimensions: IViewField[];
13
- viewMeasures: IViewField[];
16
+ viewDimensions: Omit<IViewField, 'dragId'>[];
17
+ viewMeasures: Omit<IViewField, 'dragId'>[];
14
18
  filters: readonly DeepReadonly<IFilterField>[];
15
19
  defaultAggregated: boolean;
16
20
  sort: 'none' | 'ascending' | 'descending';
17
21
  limit: number;
22
+ computationFunction: IComputationFunction;
18
23
  }
19
24
 
20
25
  interface UseRendererResult {
21
26
  viewData: IRow[];
22
27
  loading: boolean;
28
+ parsed: {
29
+ workflow: IDataQueryWorkflowStep[];
30
+ };
23
31
  }
24
32
 
25
33
  export const useRenderer = (props: UseRendererProps): UseRendererResult => {
26
34
  const {
27
- data,
28
35
  allFields,
29
36
  viewDimensions,
30
37
  viewMeasures,
31
38
  filters,
32
39
  defaultAggregated,
33
40
  sort,
34
- limit: storeLimit,
41
+ limit,
42
+ computationFunction,
35
43
  } = props;
36
44
  const [computing, setComputing] = useState(false);
37
45
  const taskIdRef = useRef(0);
38
46
 
39
- const limit = useDebounceValue(storeLimit);
47
+ const workflow = useMemo(() => {
48
+ return toWorkflow(
49
+ filters,
50
+ allFields,
51
+ viewDimensions,
52
+ viewMeasures,
53
+ defaultAggregated,
54
+ sort,
55
+ limit > 0 ? limit : undefined
56
+ );
57
+ }, [filters, allFields, viewDimensions, viewMeasures, defaultAggregated, sort, limit]);
40
58
 
41
59
  const [viewData, setViewData] = useState<IRow[]>([]);
60
+ const [parsedWorkflow, setParsedWorkflow] = useState<IDataQueryWorkflowStep[]>([]);
42
61
 
43
62
  const appRef = useAppRootContext();
44
63
 
@@ -46,72 +65,41 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
46
65
  const taskId = ++taskIdRef.current;
47
66
  appRef.current?.updateRenderStatus('computing');
48
67
  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
- });
68
+ dataQueryServer(computationFunction, workflow, limit > 0 ? limit : undefined).then(data => {
69
+ if (taskId !== taskIdRef.current) {
70
+ return;
71
+ }
72
+ appRef.current?.updateRenderStatus('rendering');
73
+ unstable_batchedUpdates(() => {
74
+ setComputing(false);
75
+ setViewData(data);
76
+ setParsedWorkflow(workflow);
77
+ });
78
+ }).catch((err) => {
79
+ if (taskId !== taskIdRef.current) {
80
+ return;
81
+ }
82
+ appRef.current?.updateRenderStatus('error');
83
+ console.error(err);
84
+ unstable_batchedUpdates(() => {
85
+ setComputing(false);
86
+ setViewData([]);
87
+ setParsedWorkflow([]);
105
88
  });
106
- return () => {
107
- taskIdRef.current++;
89
+ });
90
+ }, [computationFunction, workflow]);
91
+
92
+ const parseResult = useMemo(() => {
93
+ return {
94
+ workflow: parsedWorkflow,
108
95
  };
109
- }, [data, filters, viewDimensions, viewMeasures, defaultAggregated, limit]);
96
+ }, [parsedWorkflow]);
110
97
 
111
98
  return useMemo(() => {
112
99
  return {
113
100
  viewData,
114
101
  loading: computing,
102
+ parsed: parseResult,
115
103
  };
116
104
  }, [viewData, computing]);
117
105
  };
@@ -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();