@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.
- 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 +9 -5
- 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 +22268 -21682
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +139 -139
- 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 +8 -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 -5
- package/dist/store/commonStore.d.ts +2 -2
- package/dist/store/visualSpecStore.d.ts +58 -42
- 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 -2
- 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 +93 -0
- package/src/components/askViz/schemaTransform.ts +38 -0
- package/src/components/dataTable/index.tsx +53 -11
- package/src/components/limitSetting.tsx +8 -6
- package/src/components/pivotTable/index.tsx +0 -1
- package/src/components/pivotTable/store.tsx +0 -16
- package/src/components/sizeSetting.tsx +9 -7
- 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 +158 -0
- package/src/config.ts +15 -2
- package/src/dataSource/datasetConfig/index.tsx +38 -6
- package/src/dataSource/table.tsx +15 -2
- package/src/fields/filterField/filterEditDialog.tsx +11 -10
- package/src/fields/filterField/tabs.tsx +178 -77
- package/src/hooks/index.ts +8 -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 +11 -2
- package/src/locales/ja-JP.json +11 -2
- package/src/locales/zh-CN.json +11 -2
- package/src/main.tsx +1 -1
- package/src/renderer/hooks.ts +57 -69
- package/src/renderer/index.tsx +10 -6
- package/src/renderer/pureRenderer.tsx +40 -14
- package/src/renderer/specRenderer.tsx +24 -7
- package/src/services.ts +7 -8
- package/src/store/commonStore.ts +7 -7
- package/src/store/visualSpecStore.ts +288 -192
- 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 +2 -4
- package/src/workers/filter.worker.js +1 -1
- package/src/workers/sort.ts +3 -4
- package/src/workers/sort.worker.ts +2 -2
- 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/sort.worker-4299a6a0.js.map +0 -1
- 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",
|
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__": "レイアウトタイプ",
|
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": "字段",
|
package/src/main.tsx
CHANGED
package/src/renderer/hooks.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
|
41
|
+
limit,
|
|
42
|
+
computationFunction,
|
|
35
43
|
} = props;
|
|
36
44
|
const [computing, setComputing] = useState(false);
|
|
37
45
|
const taskIdRef = useRef(0);
|
|
38
46
|
|
|
39
|
-
const
|
|
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
|
-
|
|
50
|
-
.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
107
|
-
|
|
89
|
+
});
|
|
90
|
+
}, [computationFunction, workflow]);
|
|
91
|
+
|
|
92
|
+
const parseResult = useMemo(() => {
|
|
93
|
+
return {
|
|
94
|
+
workflow: parsedWorkflow,
|
|
108
95
|
};
|
|
109
|
-
}, [
|
|
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
|
};
|
package/src/renderer/index.tsx
CHANGED
|
@@ -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
|
|
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
|
-
|
|
38
|
-
const {
|
|
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 {
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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[],
|
|
146
|
-
if (
|
|
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
|
-
|
|
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[],
|
|
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:
|
|
178
|
+
viewMeasures: string[],
|
|
180
179
|
sort: 'ascending' | 'descending'
|
|
181
180
|
): Promise<IRow[]> => {
|
|
182
181
|
const worker = new SortWorker();
|