@kanaries/graphic-walker 0.3.15 → 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 (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 +8 -5
  9. package/dist/components/limitSetting.d.ts +5 -0
  10. package/dist/components/pivotTable/store.d.ts +0 -2
  11. package/dist/components/spinner.d.ts +2 -0
  12. package/dist/computation/clientComputation.d.ts +3 -0
  13. package/dist/computation/serverComputation.d.ts +8 -0
  14. package/dist/config.d.ts +3 -1
  15. package/dist/fields/filterField/tabs.d.ts +2 -1
  16. package/dist/graphic-walker.es.js +16181 -15523
  17. package/dist/graphic-walker.es.js.map +1 -1
  18. package/dist/graphic-walker.umd.js +144 -144
  19. package/dist/graphic-walker.umd.js.map +1 -1
  20. package/dist/hooks/index.d.ts +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/interfaces.d.ts +93 -4
  23. package/dist/lib/execExp.d.ts +4 -4
  24. package/dist/lib/interfaces.d.ts +1 -0
  25. package/dist/lib/viewQuery.d.ts +2 -2
  26. package/dist/renderer/hooks.d.ts +10 -4
  27. package/dist/renderer/index.d.ts +2 -1
  28. package/dist/renderer/pureRenderer.d.ts +17 -1
  29. package/dist/renderer/specRenderer.d.ts +1 -0
  30. package/dist/services.d.ts +8 -4
  31. package/dist/store/commonStore.d.ts +2 -2
  32. package/dist/store/visualSpecStore.d.ts +58 -40
  33. package/dist/utils/save.d.ts +10 -2
  34. package/dist/utils/workflow.d.ts +3 -0
  35. package/dist/vis/react-vega.d.ts +3 -1
  36. package/dist/workers/sort.d.ts +2 -0
  37. package/dist/workers/sort.worker.d.ts +1 -0
  38. package/dist/workers/transform.d.ts +5 -2
  39. package/package.json +2 -2
  40. package/src/App.tsx +46 -7
  41. package/src/components/askViz/index.tsx +92 -0
  42. package/src/components/askViz/schemaTransform.ts +38 -0
  43. package/src/components/dataTable/index.tsx +51 -11
  44. package/src/components/limitSetting.tsx +38 -0
  45. package/src/components/pivotTable/index.tsx +0 -1
  46. package/src/components/pivotTable/store.tsx +0 -16
  47. package/src/components/spinner.tsx +14 -0
  48. package/src/components/toggle.tsx +2 -2
  49. package/src/components/visualConfig/index.tsx +78 -8
  50. package/src/computation/clientComputation.ts +55 -0
  51. package/src/computation/serverComputation.ts +153 -0
  52. package/src/config.ts +15 -2
  53. package/src/dataSource/datasetConfig/index.tsx +38 -6
  54. package/src/dataSource/table.tsx +11 -2
  55. package/src/fields/filterField/filterEditDialog.tsx +11 -10
  56. package/src/fields/filterField/tabs.tsx +178 -77
  57. package/src/hooks/index.ts +10 -0
  58. package/src/index.tsx +2 -0
  59. package/src/interfaces.ts +108 -5
  60. package/src/lib/execExp.ts +20 -11
  61. package/src/lib/interfaces.ts +1 -0
  62. package/src/lib/op/aggregate.ts +1 -1
  63. package/src/lib/viewQuery.ts +2 -2
  64. package/src/locales/en-US.json +12 -2
  65. package/src/locales/ja-JP.json +12 -2
  66. package/src/locales/zh-CN.json +12 -2
  67. package/src/main.tsx +1 -1
  68. package/src/renderer/hooks.ts +113 -49
  69. package/src/renderer/index.tsx +32 -18
  70. package/src/renderer/pureRenderer.tsx +44 -22
  71. package/src/renderer/specRenderer.tsx +24 -7
  72. package/src/services.ts +30 -9
  73. package/src/store/commonStore.ts +7 -7
  74. package/src/store/visualSpecStore.ts +300 -193
  75. package/src/utils/save.ts +81 -3
  76. package/src/utils/workflow.ts +148 -0
  77. package/src/vis/react-vega.tsx +21 -6
  78. package/src/vis/spec/aggregate.ts +3 -2
  79. package/src/vis/spec/stack.ts +7 -6
  80. package/src/visualSettings/index.tsx +22 -1
  81. package/src/workers/filter.worker.js +1 -1
  82. package/src/workers/sort.ts +22 -0
  83. package/src/workers/sort.worker.ts +21 -0
  84. package/src/workers/transform.ts +7 -8
  85. package/src/workers/transform.worker.js +2 -2
  86. package/src/workers/viewQuery.worker.js +2 -2
  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",
@@ -154,6 +163,7 @@
154
163
  "export_chart_as": "Export as {{type}}",
155
164
  "export_code": "Export Code"
156
165
  },
166
+ "limit": "Limit",
157
167
  "size": "Resize",
158
168
  "size_setting": {
159
169
  "width": "Width",
@@ -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__": "レイアウトタイプ",
@@ -153,6 +162,7 @@
153
162
  "export_chart_as": "{{type}}としてエクスポート",
154
163
  "export_code": "コードをエクスポート"
155
164
  },
165
+ "limit": "上限",
156
166
  "size": "サイズ変更",
157
167
  "size_setting": {
158
168
  "width": "幅",
@@ -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": "字段",
@@ -154,6 +163,7 @@
154
163
  "export_chart_as": "导出 {{type}}",
155
164
  "export_code": "导出代码"
156
165
  },
166
+ "limit": "上限",
157
167
  "size": "调整尺寸",
158
168
  "size_setting": {
159
169
  "width": "宽度",
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,87 +1,151 @@
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, 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';
9
+ import { useDebounceValue } from '../hooks';
7
10
 
11
+ export const useComputationFunc = (): IComputationFunction => {
12
+ const { vizStore } = useGlobalStore();
13
+ return vizStore.computationFuction;
14
+ };
8
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;
22
+ sort: 'none' | 'ascending' | 'descending';
23
+ limit: number;
24
+ computationFunction: IComputationFunction;
16
25
  }
17
26
 
18
27
  interface UseRendererResult {
19
28
  viewData: IRow[];
20
29
  loading: boolean;
30
+ parsed: {
31
+ workflow: IDataQueryWorkflowStep[];
32
+ };
21
33
  }
22
34
 
23
35
  export const useRenderer = (props: UseRendererProps): UseRendererResult => {
24
- const { data, allFields, viewDimensions, viewMeasures, filters, defaultAggregated } = props;
36
+ const {
37
+ allFields,
38
+ viewDimensions,
39
+ viewMeasures,
40
+ filters,
41
+ defaultAggregated,
42
+ sort,
43
+ limit: storeLimit,
44
+ computationFunction,
45
+ } = props;
25
46
  const [computing, setComputing] = useState(false);
26
47
  const taskIdRef = useRef(0);
27
48
 
49
+ const limit = useDebounceValue(storeLimit);
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
+
28
63
  const [viewData, setViewData] = useState<IRow[]>([]);
64
+ const [parsedWorkflow, setParsedWorkflow] = useState<IDataQueryWorkflowStep[]>([]);
29
65
 
30
66
  const appRef = useAppRootContext();
31
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
+
32
110
  useEffect(() => {
33
111
  const taskId = ++taskIdRef.current;
34
112
  appRef.current?.updateRenderStatus('computing');
35
113
  setComputing(true);
36
- applyFilter(data, filters)
37
- .then((data) => {
38
- if (viewDimensions.length === 0 && viewMeasures.length === 0) {
39
- return data;
40
- }
41
- return transformDataService(data, allFields);
42
- })
43
- .then((d) => {
44
- if (viewDimensions.length === 0 && viewMeasures.length === 0) {
45
- return data;
46
- }
47
- // setViewData(d);
48
- const dims = viewDimensions;
49
- const meas = viewMeasures;
50
- return applyViewQuery(d, dims.concat(meas), {
51
- op: defaultAggregated ? 'aggregate' : 'raw',
52
- groupBy: dims.map((f) => f.fid),
53
- measures: meas.map((f) => ({ field: f.fid, agg: f.aggName as any, asFieldKey: getMeaAggKey(f.fid, f.aggName!) })),
54
- });
55
- })
56
- .then(data => {
57
- if (taskId !== taskIdRef.current) {
58
- return;
59
- }
60
- appRef.current?.updateRenderStatus('rendering');
61
- unstable_batchedUpdates(() => {
62
- setComputing(false);
63
- setViewData(data);
64
- });
65
- }).catch((err) => {
66
- if (taskId !== taskIdRef.current) {
67
- return;
68
- }
69
- appRef.current?.updateRenderStatus('error');
70
- console.error(err);
71
- unstable_batchedUpdates(() => {
72
- setComputing(false);
73
- setViewData([]);
74
- });
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);
75
123
  });
76
- return () => {
77
- 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,
78
141
  };
79
- }, [data, filters, viewDimensions, viewMeasures, defaultAggregated]);
142
+ }, [parsedWorkflow]);
80
143
 
81
144
  return useMemo(() => {
82
145
  return {
83
146
  viewData,
84
147
  loading: computing,
148
+ parsed: parseResult,
85
149
  };
86
150
  }, [viewData, computing]);
87
151
  };
@@ -1,42 +1,58 @@
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
- const { allFields, viewFilters, viewDimensions, viewMeasures, visualConfig, draggableFieldState, visList, visIndex } = vizStore;
27
+ const {
28
+ allFields,
29
+ viewFilters,
30
+ viewDimensions,
31
+ viewMeasures,
32
+ visualConfig,
33
+ draggableFieldState,
34
+ visList,
35
+ visIndex,
36
+ sort,
37
+ limit,
38
+ } = vizStore;
25
39
  const chart = visList[visIndex];
26
- const { currentDataset } = commonStore;
27
- const { dataSource } = currentDataset;
40
+
41
+ const { i18n } = useTranslation();
28
42
 
29
43
  const [viewConfig, setViewConfig] = useState<IVisualConfig>(initVisualConfig);
30
44
  const [encodings, setEncodings] = useState<DeepReadonly<DraggableFieldState>>(initEncoding);
31
45
  const [viewData, setViewData] = useState<IRow[]>([]);
32
46
 
33
47
  const { viewData: data, loading: waiting } = useRenderer({
34
- data: dataSource,
35
48
  allFields,
36
49
  viewDimensions,
37
50
  viewMeasures,
38
51
  filters: viewFilters,
39
52
  defaultAggregated: visualConfig.defaultAggregated,
53
+ sort,
54
+ limit: limit,
55
+ computationFunction,
40
56
  });
41
57
 
42
58
  // Dependencies that should not trigger effect individually
@@ -64,19 +80,16 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
64
80
  useChartIndexControl({
65
81
  count: visList.length,
66
82
  index: visIndex,
67
- onChange: idx => vizStore.selectVisualization(idx),
83
+ onChange: (idx) => vizStore.selectVisualization(idx),
68
84
  });
69
85
 
70
- const handleGeomClick = useCallback(
71
- (values: any, e: any) => {
72
- e.stopPropagation();
73
- runInAction(() => {
74
- commonStore.showEmbededMenu([e.pageX, e.pageY]);
75
- commonStore.setFilters(values);
76
- });
77
- },
78
- []
79
- );
86
+ const handleGeomClick = useCallback((values: any, e: any) => {
87
+ e.stopPropagation();
88
+ runInAction(() => {
89
+ commonStore.showEmbededMenu([e.pageX, e.pageY]);
90
+ commonStore.setFilters(values);
91
+ });
92
+ }, []);
80
93
 
81
94
  const handleChartResize = useCallback(
82
95
  (width: number, height: number) => {
@@ -97,6 +110,7 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
97
110
  ref={ref}
98
111
  themeKey={themeKey}
99
112
  dark={dark}
113
+ locale={i18n.language}
100
114
  draggableFieldState={encodings}
101
115
  visualConfig={viewConfig}
102
116
  onGeomClick={handleGeomClick}
@@ -4,38 +4,57 @@ 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
-
13
- interface IPureRendererProps {
14
- name?: string;
15
- themeKey?: IThemeKey;
16
- dark?: IDarkMode;
17
- rawData?: IRow[];
18
- visualState: DraggableFieldState;
19
- visualConfig: IVisualConfig;
20
- }
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
+ );
21
41
 
22
42
  /**
23
43
  * Render a readonly chart with given visualization schema.
24
44
  * This is a pure component, which means it will not depend on any global state.
25
45
  */
26
- const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer (props, ref) {
27
- const {
28
- name,
29
- themeKey,
30
- dark,
31
- rawData,
32
- visualState,
33
- visualConfig,
34
- } = props;
46
+ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer(props, ref) {
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])
35
54
  const defaultAggregated = visualConfig?.defaultAggregated ?? false;
36
55
 
37
56
  const [viewData, setViewData] = useState<IRow[]>([]);
38
-
57
+
39
58
  const { allFields, viewDimensions, viewMeasures, filters } = useMemo(() => {
40
59
  const viewDimensions: IViewField[] = [];
41
60
  const viewMeasures: IViewField[] = [];
@@ -58,12 +77,14 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
58
77
  }, [visualState]);
59
78
 
60
79
  const { viewData: data, loading: waiting } = useRenderer({
61
- data: rawData ?? [],
62
80
  allFields,
63
81
  viewDimensions,
64
82
  viewMeasures,
65
83
  filters,
66
84
  defaultAggregated,
85
+ sort: sort ?? 'none',
86
+ limit: limit ?? -1,
87
+ computationFunction: computation,
67
88
  });
68
89
 
69
90
  // Dependencies that should not trigger effect individually
@@ -90,10 +111,11 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
90
111
  dark={dark}
91
112
  draggableFieldState={visualState}
92
113
  visualConfig={visualConfig}
114
+ locale={locale ?? 'en-US'}
93
115
  />
94
116
  </div>
95
117
  </ShadowDom>
96
118
  );
97
119
  });
98
120
 
99
- export default observer(withAppRoot(PureRenderer));
121
+ export default observer(withAppRoot<IPureRendererProps>(PureRenderer));