@kanaries/graphic-walker 0.3.16 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +22226 -21650
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +137 -137
- package/dist/graphic-walker.umd.js.map +1 -1
- 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 +92 -0
- package/src/components/askViz/schemaTransform.ts +38 -0
- package/src/components/dataTable/index.tsx +51 -11
- 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/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 +100 -66
- 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 +287 -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 -3
- 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/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,30 +1,39 @@
|
|
|
1
1
|
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { unstable_batchedUpdates } from 'react-dom';
|
|
3
|
-
import type { DeepReadonly, IFilterField, IRow, IViewField } from '../interfaces';
|
|
4
|
-
import {
|
|
5
|
-
import { getMeaAggKey } from '../utils';
|
|
3
|
+
import type { DeepReadonly, IFilterField, IRow, IViewField, IDataQueryWorkflowStep, IComputationFunction } from '../interfaces';
|
|
4
|
+
import { useGlobalStore } from '../store';
|
|
6
5
|
import { useAppRootContext } from '../components/appRoot';
|
|
6
|
+
import { toWorkflow } from '../utils/workflow';
|
|
7
|
+
import { dataQueryClient } from '../computation/clientComputation';
|
|
8
|
+
import { dataQueryServer } from '../computation/serverComputation';
|
|
7
9
|
import { useDebounceValue } from '../hooks';
|
|
8
10
|
|
|
11
|
+
export const useComputationFunc = (): IComputationFunction => {
|
|
12
|
+
const { vizStore } = useGlobalStore();
|
|
13
|
+
return vizStore.computationFuction;
|
|
14
|
+
};
|
|
15
|
+
|
|
9
16
|
interface UseRendererProps {
|
|
10
|
-
data: IRow[];
|
|
11
17
|
allFields: Omit<IViewField, 'dragId'>[];
|
|
12
|
-
viewDimensions: IViewField[];
|
|
13
|
-
viewMeasures: IViewField[];
|
|
18
|
+
viewDimensions: Omit<IViewField, 'dragId'>[];
|
|
19
|
+
viewMeasures: Omit<IViewField, 'dragId'>[];
|
|
14
20
|
filters: readonly DeepReadonly<IFilterField>[];
|
|
15
21
|
defaultAggregated: boolean;
|
|
16
22
|
sort: 'none' | 'ascending' | 'descending';
|
|
17
23
|
limit: number;
|
|
24
|
+
computationFunction: IComputationFunction;
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
interface UseRendererResult {
|
|
21
28
|
viewData: IRow[];
|
|
22
29
|
loading: boolean;
|
|
30
|
+
parsed: {
|
|
31
|
+
workflow: IDataQueryWorkflowStep[];
|
|
32
|
+
};
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
26
36
|
const {
|
|
27
|
-
data,
|
|
28
37
|
allFields,
|
|
29
38
|
viewDimensions,
|
|
30
39
|
viewMeasures,
|
|
@@ -32,86 +41,111 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
|
32
41
|
defaultAggregated,
|
|
33
42
|
sort,
|
|
34
43
|
limit: storeLimit,
|
|
44
|
+
computationFunction,
|
|
35
45
|
} = props;
|
|
36
46
|
const [computing, setComputing] = useState(false);
|
|
37
47
|
const taskIdRef = useRef(0);
|
|
38
48
|
|
|
39
49
|
const limit = useDebounceValue(storeLimit);
|
|
40
50
|
|
|
51
|
+
const workflow = useMemo(() => {
|
|
52
|
+
return toWorkflow(
|
|
53
|
+
filters,
|
|
54
|
+
allFields,
|
|
55
|
+
viewDimensions,
|
|
56
|
+
viewMeasures,
|
|
57
|
+
defaultAggregated,
|
|
58
|
+
sort,
|
|
59
|
+
limit > 0 ? limit : undefined
|
|
60
|
+
);
|
|
61
|
+
}, [filters, allFields, viewDimensions, viewMeasures, defaultAggregated, sort, limit]);
|
|
62
|
+
|
|
41
63
|
const [viewData, setViewData] = useState<IRow[]>([]);
|
|
64
|
+
const [parsedWorkflow, setParsedWorkflow] = useState<IDataQueryWorkflowStep[]>([]);
|
|
42
65
|
|
|
43
66
|
const appRef = useAppRootContext();
|
|
44
67
|
|
|
68
|
+
// useEffect(() => {
|
|
69
|
+
// if (computationMode !== 'client') {
|
|
70
|
+
// return;
|
|
71
|
+
// }
|
|
72
|
+
// if (!data) {
|
|
73
|
+
// console.warn('useRenderer error: prop `data` is required for "client" mode, but none is found.');
|
|
74
|
+
// return;
|
|
75
|
+
// }
|
|
76
|
+
// if (!allFields) {
|
|
77
|
+
// console.warn('useRenderer error: prop `fields` is required for "client" mode, but none is found.');
|
|
78
|
+
// return;
|
|
79
|
+
// }
|
|
80
|
+
// const taskId = ++taskIdRef.current;
|
|
81
|
+
// appRef.current?.updateRenderStatus('computing');
|
|
82
|
+
// setComputing(true);
|
|
83
|
+
// dataQueryClient(data, allFields, workflow).then(data => {
|
|
84
|
+
// if (taskId !== taskIdRef.current) {
|
|
85
|
+
// return;
|
|
86
|
+
// }
|
|
87
|
+
// appRef.current?.updateRenderStatus('rendering');
|
|
88
|
+
// unstable_batchedUpdates(() => {
|
|
89
|
+
// setComputing(false);
|
|
90
|
+
// setViewData(data);
|
|
91
|
+
// setParsedWorkflow(workflow);
|
|
92
|
+
// });
|
|
93
|
+
// }).catch((err) => {
|
|
94
|
+
// if (taskId !== taskIdRef.current) {
|
|
95
|
+
// return;
|
|
96
|
+
// }
|
|
97
|
+
// appRef.current?.updateRenderStatus('error');
|
|
98
|
+
// console.error(err);
|
|
99
|
+
// unstable_batchedUpdates(() => {
|
|
100
|
+
// setComputing(false);
|
|
101
|
+
// setViewData([]);
|
|
102
|
+
// setParsedWorkflow([]);
|
|
103
|
+
// });
|
|
104
|
+
// });
|
|
105
|
+
// return () => {
|
|
106
|
+
// taskIdRef.current++;
|
|
107
|
+
// };
|
|
108
|
+
// }, [computationMode, data, allFields, workflow]);
|
|
109
|
+
|
|
45
110
|
useEffect(() => {
|
|
46
111
|
const taskId = ++taskIdRef.current;
|
|
47
112
|
appRef.current?.updateRenderStatus('computing');
|
|
48
113
|
setComputing(true);
|
|
49
|
-
|
|
50
|
-
.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return data;
|
|
59
|
-
}
|
|
60
|
-
// setViewData(d);
|
|
61
|
-
const dims = viewDimensions;
|
|
62
|
-
const meas = viewMeasures;
|
|
63
|
-
return applyViewQuery(d, dims.concat(meas), {
|
|
64
|
-
op: defaultAggregated ? 'aggregate' : 'raw',
|
|
65
|
-
groupBy: dims.map((f) => f.fid),
|
|
66
|
-
measures: meas.map((f) => ({
|
|
67
|
-
field: f.fid,
|
|
68
|
-
agg: f.aggName as any,
|
|
69
|
-
asFieldKey: getMeaAggKey(f.fid, f.aggName!),
|
|
70
|
-
})),
|
|
71
|
-
});
|
|
72
|
-
})
|
|
73
|
-
.then((data) => {
|
|
74
|
-
if (limit > 0 && sort !== 'none' && viewMeasures.length > 0) {
|
|
75
|
-
return applySort(data, viewMeasures, sort);
|
|
76
|
-
}
|
|
77
|
-
return data;
|
|
78
|
-
})
|
|
79
|
-
.then((data) => {
|
|
80
|
-
if (limit > 0) {
|
|
81
|
-
return data.slice(0, limit);
|
|
82
|
-
}
|
|
83
|
-
return data;
|
|
84
|
-
})
|
|
85
|
-
.then((data) => {
|
|
86
|
-
if (taskId !== taskIdRef.current) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
appRef.current?.updateRenderStatus('rendering');
|
|
90
|
-
unstable_batchedUpdates(() => {
|
|
91
|
-
setComputing(false);
|
|
92
|
-
setViewData(data);
|
|
93
|
-
});
|
|
94
|
-
})
|
|
95
|
-
.catch((err) => {
|
|
96
|
-
if (taskId !== taskIdRef.current) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
appRef.current?.updateRenderStatus('error');
|
|
100
|
-
console.error(err);
|
|
101
|
-
unstable_batchedUpdates(() => {
|
|
102
|
-
setComputing(false);
|
|
103
|
-
setViewData([]);
|
|
104
|
-
});
|
|
114
|
+
dataQueryServer(computationFunction, workflow, limit > 0 ? limit : undefined).then(data => {
|
|
115
|
+
if (taskId !== taskIdRef.current) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
appRef.current?.updateRenderStatus('rendering');
|
|
119
|
+
unstable_batchedUpdates(() => {
|
|
120
|
+
setComputing(false);
|
|
121
|
+
setViewData(data);
|
|
122
|
+
setParsedWorkflow(workflow);
|
|
105
123
|
});
|
|
106
|
-
|
|
107
|
-
taskIdRef.current
|
|
124
|
+
}).catch((err) => {
|
|
125
|
+
if (taskId !== taskIdRef.current) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
appRef.current?.updateRenderStatus('error');
|
|
129
|
+
console.error(err);
|
|
130
|
+
unstable_batchedUpdates(() => {
|
|
131
|
+
setComputing(false);
|
|
132
|
+
setViewData([]);
|
|
133
|
+
setParsedWorkflow([]);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}, [computationFunction, workflow]);
|
|
137
|
+
|
|
138
|
+
const parseResult = useMemo(() => {
|
|
139
|
+
return {
|
|
140
|
+
workflow: parsedWorkflow,
|
|
108
141
|
};
|
|
109
|
-
}, [
|
|
142
|
+
}, [parsedWorkflow]);
|
|
110
143
|
|
|
111
144
|
return useMemo(() => {
|
|
112
145
|
return {
|
|
113
146
|
viewData,
|
|
114
147
|
loading: computing,
|
|
148
|
+
parsed: parseResult,
|
|
115
149
|
};
|
|
116
150
|
}, [viewData, computing]);
|
|
117
151
|
};
|
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();
|
package/src/store/commonStore.ts
CHANGED
|
@@ -147,14 +147,14 @@ export class CommonStore {
|
|
|
147
147
|
this.initTempDS();
|
|
148
148
|
this.showDSPanel = true;
|
|
149
149
|
}
|
|
150
|
-
public addAndUseDS(dataset: IDataSetInfo) {
|
|
151
|
-
const
|
|
150
|
+
public addAndUseDS(dataset: IDataSetInfo, datasetId?: string | undefined) {
|
|
151
|
+
const id = this.addDS(dataset, datasetId);
|
|
152
152
|
this.dsIndex = this.datasets.length - 1;
|
|
153
|
-
return
|
|
153
|
+
return id
|
|
154
154
|
}
|
|
155
|
-
public addDS(dataset: IDataSetInfo) {
|
|
155
|
+
public addDS(dataset: IDataSetInfo, datasetId?: string | undefined) {
|
|
156
156
|
const timestamp = new Date().getTime();
|
|
157
|
-
const dataSetId = `dst-${timestamp}`
|
|
157
|
+
const dataSetId = datasetId || `dst-${timestamp}`
|
|
158
158
|
const dataSourceId = `dse-${timestamp}`;
|
|
159
159
|
this.dataSources.push({
|
|
160
160
|
id: dataSourceId,
|
|
@@ -166,12 +166,12 @@ export class CommonStore {
|
|
|
166
166
|
rawFields: dataset.rawFields,
|
|
167
167
|
dsId: dataSourceId
|
|
168
168
|
})
|
|
169
|
-
return
|
|
169
|
+
return dataSourceId;
|
|
170
170
|
}
|
|
171
171
|
public removeDS(datasetId: string) {
|
|
172
172
|
const datasetIndex = this.datasets.findIndex(d => d.id === datasetId);
|
|
173
173
|
if (datasetIndex > -1) {
|
|
174
|
-
const dataSourceId = this.datasets[datasetIndex].
|
|
174
|
+
const dataSourceId = this.datasets[datasetIndex].id;
|
|
175
175
|
const dataSourceIndex = this.dataSources.findIndex(d => d.id === dataSourceId);
|
|
176
176
|
this.dataSources.splice(dataSourceIndex, 1);
|
|
177
177
|
this.datasets.splice(datasetIndex, 1);
|