@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.
- package/dist/App.d.ts +9 -2
- package/dist/assets/filter.worker-f09fcd6f.js.map +1 -1
- package/dist/assets/sort.worker-f77540ac.js.map +1 -0
- package/dist/assets/transform.worker-bae8e910.js.map +1 -0
- package/dist/assets/{viewQuery.worker-03404216.js.map → viewQuery.worker-bdb6477c.js.map} +1 -1
- package/dist/components/askViz/index.d.ts +6 -0
- package/dist/components/askViz/schemaTransform.d.ts +2 -0
- package/dist/components/dataTable/index.d.ts +8 -5
- package/dist/components/limitSetting.d.ts +5 -0
- package/dist/components/pivotTable/store.d.ts +0 -2
- package/dist/components/spinner.d.ts +2 -0
- package/dist/computation/clientComputation.d.ts +3 -0
- package/dist/computation/serverComputation.d.ts +8 -0
- package/dist/config.d.ts +3 -1
- package/dist/fields/filterField/tabs.d.ts +2 -1
- package/dist/graphic-walker.es.js +16181 -15523
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +144 -144
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/interfaces.d.ts +93 -4
- package/dist/lib/execExp.d.ts +4 -4
- package/dist/lib/interfaces.d.ts +1 -0
- package/dist/lib/viewQuery.d.ts +2 -2
- package/dist/renderer/hooks.d.ts +10 -4
- package/dist/renderer/index.d.ts +2 -1
- package/dist/renderer/pureRenderer.d.ts +17 -1
- package/dist/renderer/specRenderer.d.ts +1 -0
- package/dist/services.d.ts +8 -4
- package/dist/store/commonStore.d.ts +2 -2
- package/dist/store/visualSpecStore.d.ts +58 -40
- package/dist/utils/save.d.ts +10 -2
- package/dist/utils/workflow.d.ts +3 -0
- package/dist/vis/react-vega.d.ts +3 -1
- package/dist/workers/sort.d.ts +2 -0
- package/dist/workers/sort.worker.d.ts +1 -0
- package/dist/workers/transform.d.ts +5 -2
- package/package.json +2 -2
- package/src/App.tsx +46 -7
- package/src/components/askViz/index.tsx +92 -0
- package/src/components/askViz/schemaTransform.ts +38 -0
- package/src/components/dataTable/index.tsx +51 -11
- package/src/components/limitSetting.tsx +38 -0
- package/src/components/pivotTable/index.tsx +0 -1
- package/src/components/pivotTable/store.tsx +0 -16
- package/src/components/spinner.tsx +14 -0
- package/src/components/toggle.tsx +2 -2
- package/src/components/visualConfig/index.tsx +78 -8
- package/src/computation/clientComputation.ts +55 -0
- package/src/computation/serverComputation.ts +153 -0
- package/src/config.ts +15 -2
- package/src/dataSource/datasetConfig/index.tsx +38 -6
- package/src/dataSource/table.tsx +11 -2
- package/src/fields/filterField/filterEditDialog.tsx +11 -10
- package/src/fields/filterField/tabs.tsx +178 -77
- package/src/hooks/index.ts +10 -0
- package/src/index.tsx +2 -0
- package/src/interfaces.ts +108 -5
- package/src/lib/execExp.ts +20 -11
- package/src/lib/interfaces.ts +1 -0
- package/src/lib/op/aggregate.ts +1 -1
- package/src/lib/viewQuery.ts +2 -2
- package/src/locales/en-US.json +12 -2
- package/src/locales/ja-JP.json +12 -2
- package/src/locales/zh-CN.json +12 -2
- package/src/main.tsx +1 -1
- package/src/renderer/hooks.ts +113 -49
- package/src/renderer/index.tsx +32 -18
- package/src/renderer/pureRenderer.tsx +44 -22
- package/src/renderer/specRenderer.tsx +24 -7
- package/src/services.ts +30 -9
- package/src/store/commonStore.ts +7 -7
- package/src/store/visualSpecStore.ts +300 -193
- package/src/utils/save.ts +81 -3
- package/src/utils/workflow.ts +148 -0
- package/src/vis/react-vega.tsx +21 -6
- package/src/vis/spec/aggregate.ts +3 -2
- package/src/vis/spec/stack.ts +7 -6
- package/src/visualSettings/index.tsx +22 -1
- package/src/workers/filter.worker.js +1 -1
- package/src/workers/sort.ts +22 -0
- package/src/workers/sort.worker.ts +21 -0
- package/src/workers/transform.ts +7 -8
- package/src/workers/transform.worker.js +2 -2
- package/src/workers/viewQuery.worker.js +2 -2
- package/dist/assets/transform.worker-a12fb3d8.js.map +0 -1
package/src/lib/execExp.ts
CHANGED
|
@@ -4,7 +4,7 @@ interface IDataFrame {
|
|
|
4
4
|
[key: string]: any[];
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export function execExpression (exp: IExpression, dataFrame: 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
|
|
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
|
-
|
|
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
|
-
|
|
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[]
|
|
134
|
+
export function dataset2DataFrame(dataset: IRow[]): IDataFrame {
|
|
128
135
|
const dataFrame: IDataFrame = {};
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
136
|
-
|
|
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
|
-
|
|
142
|
-
row[
|
|
150
|
+
cols.forEach((k) => {
|
|
151
|
+
row[k] = dataFrame[k][i];
|
|
143
152
|
});
|
|
144
153
|
dataset.push(row);
|
|
145
154
|
}
|
package/src/lib/interfaces.ts
CHANGED
package/src/lib/op/aggregate.ts
CHANGED
|
@@ -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
|
}
|
package/src/lib/viewQuery.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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[],
|
|
9
|
+
export function queryView (rawData: IRow[], query: IViewQuery) {
|
|
10
10
|
switch (query.op) {
|
|
11
11
|
case 'aggregate':
|
|
12
12
|
return aggregate(rawData, query);
|
package/src/locales/en-US.json
CHANGED
|
@@ -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",
|
package/src/locales/ja-JP.json
CHANGED
|
@@ -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": "幅",
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -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
package/src/renderer/hooks.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
-
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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
|
};
|
package/src/renderer/index.tsx
CHANGED
|
@@ -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
|
|
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 {
|
|
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
|
-
|
|
27
|
-
const {
|
|
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
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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 {
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
27
|
-
const {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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));
|