@kanaries/graphic-walker 0.2.14 → 0.2.16
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 +5 -2
- package/dist/assets/explainer.worker-8428eb12.js.map +1 -1
- package/dist/assets/transform.worker-5d54ff09.js.map +1 -0
- package/dist/assets/viewQuery.worker-ffefc111.js.map +1 -0
- package/dist/components/callout.d.ts +2 -0
- package/dist/components/codeExport/index.d.ts +3 -0
- package/dist/components/loadingLayer.d.ts +2 -0
- package/dist/components/tabs/defaultTab.d.ts +1 -0
- package/dist/components/tabs/editableTab.d.ts +1 -2
- package/dist/components/toolbar/components.d.ts +4 -1
- package/dist/components/toolbar/index.d.ts +2 -0
- package/dist/components/toolbar/toolbar-item.d.ts +3 -0
- package/dist/components/tooltip.d.ts +2 -0
- package/dist/dataSource/dataSelection/config.d.ts +1 -0
- package/dist/dataSource/dataSelection/utils.d.ts +2 -0
- package/dist/datasets/tmp/test.json +1 -0
- package/dist/fields/components.d.ts +0 -1
- package/dist/fields/filterField/filterEditDialog.d.ts +1 -1
- package/dist/graphic-walker.es.js +23930 -23320
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +143 -273
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/interfaces.d.ts +23 -1
- package/dist/lib/execExp.d.ts +8 -0
- package/dist/lib/interfaces.d.ts +22 -0
- package/dist/lib/op/aggregate.d.ts +3 -0
- package/dist/lib/op/bin.d.ts +3 -0
- package/dist/lib/op/fold.d.ts +3 -0
- package/dist/lib/op/stat.d.ts +8 -0
- package/dist/lib/viewQuery.d.ts +5 -0
- package/dist/models/visSpecHistory.d.ts +2 -0
- package/dist/renderer/index.d.ts +6 -3
- package/dist/renderer/specRenderer.d.ts +13 -0
- package/dist/services.d.ts +4 -1
- package/dist/store/commonStore.d.ts +6 -0
- package/dist/store/index.d.ts +3 -2
- package/dist/store/visualSpecStore.d.ts +11 -4
- package/dist/utils/dataPrep.d.ts +2 -0
- package/dist/utils/index.d.ts +3 -5
- package/dist/utils/media.d.ts +2 -1
- package/dist/utils/save.d.ts +1 -2
- package/dist/vis/react-vega.d.ts +4 -23
- package/dist/vis/spec/aggregate.d.ts +4 -0
- package/dist/vis/spec/encode.d.ts +19 -0
- package/dist/vis/spec/field.d.ts +2 -0
- package/dist/vis/spec/mark.d.ts +7 -0
- package/dist/vis/spec/stack.d.ts +4 -0
- package/dist/vis/spec/view.d.ts +67 -0
- package/dist/vis/theme.d.ts +36 -20
- package/dist/visualSettings/index.d.ts +2 -1
- package/dist/workers/transform.d.ts +2 -0
- package/package.json +4 -3
- package/src/App.tsx +23 -15
- package/src/components/callout.tsx +9 -7
- package/src/components/clickMenu.tsx +1 -7
- package/src/components/codeExport/index.tsx +114 -0
- package/src/components/dataTable/index.tsx +10 -10
- package/src/components/loadingLayer.tsx +7 -0
- package/src/components/modal.tsx +1 -15
- package/src/components/sizeSetting.tsx +2 -2
- package/src/components/tabs/defaultTab.tsx +4 -2
- package/src/components/tabs/editableTab.tsx +75 -40
- package/src/components/toolbar/components.tsx +8 -23
- package/src/components/toolbar/index.tsx +11 -4
- package/src/components/toolbar/toolbar-button.tsx +2 -1
- package/src/components/toolbar/toolbar-item.tsx +17 -12
- package/src/components/toolbar/toolbar-select-button.tsx +9 -13
- package/src/components/toolbar/toolbar-toggle-button.tsx +2 -1
- package/src/components/tooltip.tsx +10 -6
- package/src/dataSource/dataSelection/config.ts +11 -0
- package/src/dataSource/dataSelection/csvData.tsx +72 -40
- package/src/dataSource/dataSelection/gwFile.tsx +2 -2
- package/src/dataSource/dataSelection/utils.ts +28 -0
- package/src/dataSource/index.tsx +2 -3
- package/src/dataSource/utils.ts +8 -3
- package/src/fields/components.tsx +13 -50
- package/src/fields/datasetFields/index.tsx +3 -4
- package/src/fields/datasetFields/meaFields.tsx +12 -4
- package/src/fields/encodeFields/singleEncodeEditor.tsx +1 -1
- package/src/fields/filterField/filterEditDialog.tsx +63 -99
- package/src/fields/filterField/slider.tsx +1 -1
- package/src/index.css +4 -4
- package/src/index.tsx +22 -22
- package/src/insightBoard/mainBoard.tsx +9 -2
- package/src/interfaces.ts +30 -3
- package/src/lib/execExp.ts +147 -0
- package/src/lib/interfaces.ts +39 -0
- package/src/lib/op/aggregate.ts +49 -0
- package/src/lib/op/bin.ts +25 -0
- package/src/lib/op/fold.ts +17 -0
- package/src/lib/op/stat.ts +46 -0
- package/src/lib/viewQuery.ts +23 -0
- package/src/locales/en-US.json +8 -3
- package/src/locales/i18n.ts +7 -1
- package/src/locales/ja-JP.json +197 -0
- package/src/locales/zh-CN.json +8 -3
- package/src/main.tsx +1 -1
- package/src/models/visSpecHistory.ts +14 -0
- package/src/renderer/index.tsx +58 -101
- package/src/renderer/specRenderer.tsx +119 -0
- package/src/segments/segmentNav.tsx +3 -16
- package/src/segments/visNav.tsx +17 -6
- package/src/services.ts +37 -1
- package/src/store/commonStore.ts +14 -9
- package/src/store/index.tsx +11 -4
- package/src/store/visualSpecStore.ts +89 -50
- package/src/utils/dataPrep.ts +24 -0
- package/src/utils/index.ts +16 -17
- package/src/utils/media.ts +16 -11
- package/src/utils/normalization.ts +3 -1
- package/src/utils/save.ts +1 -2
- package/src/vis/react-vega.tsx +11 -332
- package/src/vis/spec/aggregate.ts +13 -0
- package/src/vis/spec/encode.ts +69 -0
- package/src/vis/spec/field.ts +10 -0
- package/src/vis/spec/mark.ts +30 -0
- package/src/vis/spec/stack.ts +11 -0
- package/src/vis/spec/view.ts +138 -0
- package/src/vis/theme.ts +35 -25
- package/src/visualSettings/index.tsx +22 -33
- package/src/workers/transform.ts +12 -0
- package/src/workers/transform.worker.js +13 -0
- package/src/workers/viewQuery.worker.js +16 -0
- package/dist/components/container.d.ts +0 -2
- package/dist/dataSource/pannel.d.ts +0 -5
- package/src/components/container.tsx +0 -25
- package/src/dataSource/pannel.tsx +0 -71
package/src/renderer/index.tsx
CHANGED
|
@@ -1,113 +1,70 @@
|
|
|
1
|
-
import { runInAction, toJS } from 'mobx';
|
|
2
1
|
import { observer } from 'mobx-react-lite';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
2
|
+
import React, { useState, useEffect, forwardRef } from 'react';
|
|
3
|
+
import { applyFilter, applyViewQuery, transformDataService } from '../services';
|
|
4
|
+
import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig } from '../interfaces';
|
|
5
|
+
import SpecRenderer from './specRenderer';
|
|
6
|
+
import { toJS } from 'mobx';
|
|
6
7
|
import { useGlobalStore } from '../store';
|
|
7
|
-
import
|
|
8
|
+
import { IReactVegaHandler } from '../vis/react-vega';
|
|
9
|
+
import { unstable_batchedUpdates } from 'react-dom';
|
|
10
|
+
import { initEncoding, initVisualConfig } from '../store/visualSpecStore';
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
interface RendererProps {
|
|
13
|
+
themeKey?: IThemeKey;
|
|
14
|
+
dark?: IDarkMode;
|
|
15
|
+
}
|
|
16
|
+
const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, ref) {
|
|
17
|
+
const { themeKey, dark } = props;
|
|
18
|
+
const [waiting, setWaiting] = useState<boolean>(false);
|
|
11
19
|
const { vizStore, commonStore } = useGlobalStore();
|
|
12
|
-
const {
|
|
13
|
-
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, exploration } = visualConfig;
|
|
20
|
+
const { allFields, viewFilters, viewDimensions, viewMeasures } = vizStore;
|
|
14
21
|
const { currentDataset } = commonStore;
|
|
15
|
-
const { filters } = draggableFieldState;
|
|
16
|
-
|
|
17
|
-
const rows = toJS(draggableFieldState.rows)
|
|
18
|
-
const columns = toJS(draggableFieldState.columns)
|
|
19
|
-
const color = toJS(draggableFieldState.color)
|
|
20
|
-
const opacity = toJS(draggableFieldState.opacity)
|
|
21
|
-
const shape = toJS(draggableFieldState.shape)
|
|
22
|
-
const theta = toJS(draggableFieldState.theta)
|
|
23
|
-
const radius = toJS(draggableFieldState.radius)
|
|
24
|
-
const sizeChannel = toJS(draggableFieldState.size)
|
|
25
|
-
|
|
26
|
-
const rowLeftFacetFields = rows.slice(0, -1).filter(f => f.analyticType === 'dimension');
|
|
27
|
-
const colLeftFacetFields = columns.slice(0, -1).filter(f => f.analyticType === 'dimension');
|
|
28
|
-
|
|
29
|
-
const hasFacet = rowLeftFacetFields.length > 0 || colLeftFacetFields.length > 0;
|
|
30
|
-
|
|
31
|
-
const shouldTriggerMenu = exploration.mode === 'none';
|
|
32
|
-
|
|
33
|
-
const handleGeomClick = useCallback((values: any, e: any) => {
|
|
34
|
-
if (shouldTriggerMenu) {
|
|
35
|
-
e.stopPropagation();
|
|
36
|
-
runInAction(() => {
|
|
37
|
-
commonStore.showEmbededMenu([e.pageX, e.pageY])
|
|
38
|
-
commonStore.setFilters(values);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}, [shouldTriggerMenu]);
|
|
42
|
-
|
|
43
|
-
// apply filters
|
|
44
22
|
const { dataSource } = currentDataset;
|
|
23
|
+
const [viewConfig, setViewConfig] = useState<IVisualConfig>(initVisualConfig);
|
|
24
|
+
const [encodings, setEncodings] = useState<DeepReadonly<DraggableFieldState>>(initEncoding);
|
|
45
25
|
|
|
46
|
-
const [
|
|
47
|
-
const pendingPromiseRef = useRef<Promise<typeof data> | null>(null);
|
|
26
|
+
const [viewData, setViewData] = useState<IRow[]>([]);
|
|
48
27
|
|
|
49
28
|
useEffect(() => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
29
|
+
setWaiting(true);
|
|
30
|
+
applyFilter(dataSource, viewFilters)
|
|
31
|
+
.then((data) => transformDataService(data, allFields))
|
|
32
|
+
.then((d) => {
|
|
33
|
+
// setViewData(d);
|
|
34
|
+
const dims = viewDimensions;
|
|
35
|
+
const meas = viewMeasures;
|
|
36
|
+
const config = toJS(vizStore.visualConfig);
|
|
37
|
+
return applyViewQuery(d, dims.concat(meas), {
|
|
38
|
+
op: config.defaultAggregated ? 'aggregate' : 'raw',
|
|
39
|
+
groupBy: dims.map((f) => f.fid),
|
|
40
|
+
agg: Object.fromEntries(meas.map((f) => [f.fid, f.aggName as any])),
|
|
41
|
+
});
|
|
42
|
+
})
|
|
43
|
+
.then((data) => {
|
|
44
|
+
unstable_batchedUpdates(() => {
|
|
45
|
+
setViewData(data);
|
|
46
|
+
setWaiting(false);
|
|
47
|
+
setEncodings(toJS(vizStore.draggableFieldState));
|
|
48
|
+
setViewConfig(toJS(vizStore.visualConfig));
|
|
49
|
+
});
|
|
50
|
+
})
|
|
51
|
+
.catch((err) => {
|
|
52
|
+
console.error(err);
|
|
53
|
+
setWaiting(false);
|
|
54
|
+
});
|
|
55
|
+
}, [dataSource, viewFilters, allFields, viewDimensions, viewMeasures]);
|
|
72
56
|
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
height: size.height + 'px',
|
|
85
|
-
}}>
|
|
86
|
-
<ReactVega
|
|
87
|
-
layoutMode={size.mode}
|
|
88
|
-
interactiveScale={interactiveScale}
|
|
89
|
-
geomType={geoms[0]}
|
|
90
|
-
defaultAggregate={defaultAggregated}
|
|
91
|
-
stack={stack}
|
|
92
|
-
dataSource={data}
|
|
93
|
-
rows={rows}
|
|
94
|
-
columns={columns}
|
|
95
|
-
color={color[0]}
|
|
96
|
-
theta={theta[0]}
|
|
97
|
-
radius={radius[0]}
|
|
98
|
-
shape={shape[0]}
|
|
99
|
-
opacity={opacity[0]}
|
|
100
|
-
size={sizeChannel[0]}
|
|
101
|
-
showActions={showActions}
|
|
102
|
-
width={size.width - 12 * 4}
|
|
103
|
-
height={size.height - 12 * 4}
|
|
104
|
-
ref={ref}
|
|
105
|
-
brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
|
|
106
|
-
selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
|
|
107
|
-
onGeomClick={handleGeomClick}
|
|
108
|
-
themeKey={themeKey}
|
|
109
|
-
/>
|
|
110
|
-
</Resizable>
|
|
57
|
+
return (
|
|
58
|
+
<SpecRenderer
|
|
59
|
+
loading={waiting}
|
|
60
|
+
data={viewData}
|
|
61
|
+
ref={ref}
|
|
62
|
+
themeKey={themeKey}
|
|
63
|
+
dark={dark}
|
|
64
|
+
draggableFieldState={encodings}
|
|
65
|
+
visualConfig={viewConfig}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
111
68
|
});
|
|
112
69
|
|
|
113
|
-
export default observer(
|
|
70
|
+
export default observer(Renderer);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { runInAction } from 'mobx';
|
|
2
|
+
import { Resizable } from 're-resizable';
|
|
3
|
+
import React, { useCallback, forwardRef, useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useGlobalStore } from '../store';
|
|
6
|
+
import ReactVega, { IReactVegaHandler } from '../vis/react-vega';
|
|
7
|
+
import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig } from '../interfaces';
|
|
8
|
+
import LoadingLayer from '../components/loadingLayer';
|
|
9
|
+
|
|
10
|
+
interface SpecRendererProps {
|
|
11
|
+
themeKey?: IThemeKey;
|
|
12
|
+
dark?: IDarkMode;
|
|
13
|
+
data: IRow[];
|
|
14
|
+
loading: boolean;
|
|
15
|
+
draggableFieldState: DeepReadonly<DraggableFieldState>;
|
|
16
|
+
visualConfig: IVisualConfig;
|
|
17
|
+
}
|
|
18
|
+
const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
19
|
+
{ themeKey, dark, data, loading, draggableFieldState, visualConfig },
|
|
20
|
+
ref
|
|
21
|
+
) {
|
|
22
|
+
const { vizStore, commonStore } = useGlobalStore();
|
|
23
|
+
// const { draggableFieldState, visualConfig } = vizStore;
|
|
24
|
+
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, exploration } = visualConfig;
|
|
25
|
+
|
|
26
|
+
const rows = draggableFieldState.rows;
|
|
27
|
+
const columns = draggableFieldState.columns;
|
|
28
|
+
const color = draggableFieldState.color;
|
|
29
|
+
const opacity = draggableFieldState.opacity;
|
|
30
|
+
const shape = draggableFieldState.shape;
|
|
31
|
+
const theta = draggableFieldState.theta;
|
|
32
|
+
const radius = draggableFieldState.radius;
|
|
33
|
+
const sizeChannel = draggableFieldState.size;
|
|
34
|
+
|
|
35
|
+
const rowLeftFacetFields = useMemo(() => rows.slice(0, -1).filter((f) => f.analyticType === 'dimension'), [rows]);
|
|
36
|
+
const colLeftFacetFields = useMemo(
|
|
37
|
+
() => columns.slice(0, -1).filter((f) => f.analyticType === 'dimension'),
|
|
38
|
+
[columns]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const hasFacet = rowLeftFacetFields.length > 0 || colLeftFacetFields.length > 0;
|
|
42
|
+
|
|
43
|
+
const shouldTriggerMenu = exploration.mode === 'none';
|
|
44
|
+
|
|
45
|
+
const handleGeomClick = useCallback(
|
|
46
|
+
(values: any, e: any) => {
|
|
47
|
+
if (shouldTriggerMenu) {
|
|
48
|
+
e.stopPropagation();
|
|
49
|
+
runInAction(() => {
|
|
50
|
+
commonStore.showEmbededMenu([e.pageX, e.pageY]);
|
|
51
|
+
commonStore.setFilters(values);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
[shouldTriggerMenu]
|
|
56
|
+
);
|
|
57
|
+
const enableResize = size.mode === 'fixed' && !hasFacet;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Resizable
|
|
61
|
+
className={enableResize ? 'border-blue-400 border-2 overflow-hidden' : ''}
|
|
62
|
+
style={{ padding: '12px' }}
|
|
63
|
+
onResizeStop={(e, direction, ref, d) => {
|
|
64
|
+
vizStore.setChartLayout({
|
|
65
|
+
mode: 'fixed',
|
|
66
|
+
width: size.width + d.width,
|
|
67
|
+
height: size.height + d.height,
|
|
68
|
+
});
|
|
69
|
+
}}
|
|
70
|
+
enable={
|
|
71
|
+
enableResize
|
|
72
|
+
? undefined
|
|
73
|
+
: {
|
|
74
|
+
top: false,
|
|
75
|
+
right: false,
|
|
76
|
+
bottom: false,
|
|
77
|
+
left: false,
|
|
78
|
+
topRight: false,
|
|
79
|
+
bottomRight: false,
|
|
80
|
+
bottomLeft: false,
|
|
81
|
+
topLeft: false,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
size={{
|
|
85
|
+
width: size.width + 'px',
|
|
86
|
+
height: size.height + 'px',
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
{loading && <LoadingLayer />}
|
|
90
|
+
<ReactVega
|
|
91
|
+
layoutMode={size.mode}
|
|
92
|
+
interactiveScale={interactiveScale}
|
|
93
|
+
geomType={geoms[0]}
|
|
94
|
+
defaultAggregate={defaultAggregated}
|
|
95
|
+
stack={stack}
|
|
96
|
+
dataSource={data}
|
|
97
|
+
rows={rows}
|
|
98
|
+
columns={columns}
|
|
99
|
+
color={color[0]}
|
|
100
|
+
theta={theta[0]}
|
|
101
|
+
radius={radius[0]}
|
|
102
|
+
shape={shape[0]}
|
|
103
|
+
opacity={opacity[0]}
|
|
104
|
+
size={sizeChannel[0]}
|
|
105
|
+
showActions={showActions}
|
|
106
|
+
width={size.width - 12 * 4}
|
|
107
|
+
height={size.height - 12 * 4}
|
|
108
|
+
ref={ref}
|
|
109
|
+
brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
|
|
110
|
+
selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
|
|
111
|
+
onGeomClick={handleGeomClick}
|
|
112
|
+
themeKey={themeKey}
|
|
113
|
+
dark={dark}
|
|
114
|
+
/>
|
|
115
|
+
</Resizable>
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export default SpecRenderer;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
2
|
import { observer } from "mobx-react-lite";
|
|
3
3
|
import DefaultTab, { ITabOption } from "../components/tabs/defaultTab";
|
|
4
4
|
import { useGlobalStore } from "../store";
|
|
5
|
-
import {
|
|
5
|
+
import { ChartPieIcon, CircleStackIcon } from "@heroicons/react/24/outline";
|
|
6
6
|
import { ISegmentKey } from "../interfaces";
|
|
7
7
|
import { useTranslation } from "react-i18next";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
const ADD_KEY = '_add';
|
|
11
|
-
|
|
12
9
|
const SegmentNav: React.FC = (props) => {
|
|
13
10
|
const { vizStore, commonStore } = useGlobalStore();
|
|
14
|
-
const {
|
|
15
|
-
const { currentDataset, segmentKey } = commonStore;
|
|
11
|
+
const { segmentKey } = commonStore;
|
|
16
12
|
const { t } = useTranslation();
|
|
17
13
|
|
|
18
14
|
const tabs: ITabOption[] = [
|
|
@@ -30,15 +26,6 @@ const SegmentNav: React.FC = (props) => {
|
|
|
30
26
|
}
|
|
31
27
|
]
|
|
32
28
|
|
|
33
|
-
const visSelectionHandler = useCallback((tabKey: string, tabIndex: number) => {
|
|
34
|
-
if (tabKey === ADD_KEY) {
|
|
35
|
-
vizStore.addVisualization();
|
|
36
|
-
vizStore.initMetaState(currentDataset)
|
|
37
|
-
} else {
|
|
38
|
-
vizStore.selectVisualization(tabIndex);
|
|
39
|
-
}
|
|
40
|
-
}, [currentDataset, vizStore])
|
|
41
|
-
|
|
42
29
|
const editLabelHandler = useCallback((content: string, tabIndex: number) => {
|
|
43
30
|
vizStore.setVisName(tabIndex, content)
|
|
44
31
|
}, [])
|
package/src/segments/visNav.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useCallback } from "react";
|
|
1
|
+
import React, { useCallback, useEffect } from "react";
|
|
2
2
|
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
3
4
|
import EditableTabs, { ITabOption } from "../components/tabs/editableTab";
|
|
4
5
|
import { useGlobalStore } from "../store";
|
|
5
6
|
|
|
@@ -11,25 +12,35 @@ const VisNav: React.FC = (props) => {
|
|
|
11
12
|
const { visIndex, visList } = vizStore;
|
|
12
13
|
const { currentDataset } = commonStore;
|
|
13
14
|
|
|
15
|
+
const { t } = useTranslation();
|
|
16
|
+
|
|
14
17
|
const tabs: ITabOption[] = visList.map((v) => ({
|
|
15
18
|
key: v.visId,
|
|
16
|
-
label: v.name
|
|
17
|
-
|
|
19
|
+
label: v.name ?? 'vis',
|
|
20
|
+
editable: true
|
|
18
21
|
}));
|
|
19
22
|
|
|
20
23
|
tabs.push({
|
|
21
24
|
key: ADD_KEY,
|
|
22
|
-
label: 'main.tablist.new'
|
|
25
|
+
label: t('main.tablist.new')
|
|
23
26
|
});
|
|
24
27
|
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (visList.length === 1) {
|
|
30
|
+
// should set the first vis name when the component is mounted
|
|
31
|
+
vizStore.setVisName(0, t('main.tablist.auto_title', { idx: 1 }));
|
|
32
|
+
}
|
|
33
|
+
// no need to add deps here
|
|
34
|
+
}, [])
|
|
35
|
+
|
|
25
36
|
const visSelectionHandler = useCallback((tabKey: string, tabIndex: number) => {
|
|
26
37
|
if (tabKey === ADD_KEY) {
|
|
27
|
-
vizStore.addVisualization();
|
|
38
|
+
vizStore.addVisualization(t('main.tablist.auto_title', { idx: visList.length + 1 }));
|
|
28
39
|
vizStore.initMetaState(currentDataset)
|
|
29
40
|
} else {
|
|
30
41
|
vizStore.selectVisualization(tabIndex);
|
|
31
42
|
}
|
|
32
|
-
}, [currentDataset, vizStore])
|
|
43
|
+
}, [currentDataset, vizStore, visList.length])
|
|
33
44
|
|
|
34
45
|
const editLabelHandler = useCallback((content: string, tabIndex: number) => {
|
|
35
46
|
vizStore.setVisName(tabIndex, content)
|
package/src/services.ts
CHANGED
|
@@ -10,7 +10,10 @@ import { IRow, Filters, SemanticType, IMeasure, IMutField, IFilterField } from '
|
|
|
10
10
|
// eslint-disable-next-line
|
|
11
11
|
import ExplainerWorker from './workers/explainer.worker?worker&inline';
|
|
12
12
|
import FilterWorker from './workers/filter.worker?worker&inline';
|
|
13
|
+
import TransformDataWorker from './workers/transform.worker?worker&inline';
|
|
14
|
+
import ViewQueryWorker from './workers/viewQuery.worker?worker&inline';
|
|
13
15
|
import { IExplaination, IMeasureWithStat } from './insights';
|
|
16
|
+
import { IViewQuery, queryView } from './lib/viewQuery';
|
|
14
17
|
|
|
15
18
|
interface WorkerState {
|
|
16
19
|
eWorker: Worker | null;
|
|
@@ -105,7 +108,8 @@ export function destroyWorker() {
|
|
|
105
108
|
let filterWorker: Worker | null = null;
|
|
106
109
|
let filterWorkerAutoTerminator: NodeJS.Timeout | null = null;
|
|
107
110
|
|
|
108
|
-
export const applyFilter = async (data:
|
|
111
|
+
export const applyFilter = async (data: IRow[], filters: readonly IFilterField[]): Promise<IRow[]> => {
|
|
112
|
+
if (filters.length === 0) return data;
|
|
109
113
|
if (filterWorkerAutoTerminator !== null) {
|
|
110
114
|
clearTimeout(filterWorkerAutoTerminator);
|
|
111
115
|
filterWorkerAutoTerminator = null;
|
|
@@ -137,3 +141,35 @@ export const applyFilter = async (data: readonly IRow[], filters: readonly IFilt
|
|
|
137
141
|
}, 60_000); // Destroy the worker when no request is received for 60 secs
|
|
138
142
|
}
|
|
139
143
|
};
|
|
144
|
+
|
|
145
|
+
export const transformDataService = async (data: IRow[], columns: IMutField[]): Promise<IRow[]> => {
|
|
146
|
+
if (columns.length === 0 || data.length === 0) return data;
|
|
147
|
+
const worker = new TransformDataWorker();
|
|
148
|
+
try {
|
|
149
|
+
const res: IRow[] = await workerService(worker, {
|
|
150
|
+
dataSource: data,
|
|
151
|
+
columns: toJS(columns),
|
|
152
|
+
});
|
|
153
|
+
return res;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
throw new Error('Uncaught error in TransformDataWorker', { cause: error });
|
|
156
|
+
} finally {
|
|
157
|
+
worker.terminate();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const applyViewQuery = async (data: IRow[], metas: IMutField[], query: IViewQuery): Promise<IRow[]> => {
|
|
162
|
+
const worker = new ViewQueryWorker();
|
|
163
|
+
try {
|
|
164
|
+
const res: IRow[] = await workerService(worker, {
|
|
165
|
+
dataSource: data,
|
|
166
|
+
metas: toJS(metas),
|
|
167
|
+
query: toJS(query),
|
|
168
|
+
});
|
|
169
|
+
return res;
|
|
170
|
+
} catch (err) {
|
|
171
|
+
throw new Error('Uncaught error in ViewQueryWorker', { cause: err });
|
|
172
|
+
} finally {
|
|
173
|
+
worker.terminate();
|
|
174
|
+
}
|
|
175
|
+
}
|
package/src/store/commonStore.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { DataSet, Filters, IDataSet, IDataSetInfo, IDataSource, IMutField, IRow, ISegmentKey } from '../interfaces';
|
|
2
2
|
import { makeAutoObservable, observable, toJS } from 'mobx';
|
|
3
3
|
import { transData } from '../dataSource/utils';
|
|
4
|
-
import { extendCountField } from '../utils';
|
|
5
4
|
|
|
6
5
|
export class CommonStore {
|
|
7
6
|
public datasets: IDataSet[] = [];
|
|
@@ -14,6 +13,7 @@ export class CommonStore {
|
|
|
14
13
|
public showInsightBoard: boolean = false;
|
|
15
14
|
public vizEmbededMenu: { show: boolean; position: [number, number] } = { show: false, position: [0, 0] };
|
|
16
15
|
public showDataConfig: boolean = false;
|
|
16
|
+
public showCodeExportPanel: boolean = false;
|
|
17
17
|
public filters: Filters = {};
|
|
18
18
|
public segmentKey: ISegmentKey = ISegmentKey.vis;
|
|
19
19
|
constructor () {
|
|
@@ -30,12 +30,12 @@ export class CommonStore {
|
|
|
30
30
|
if (this.datasets.length > 0) {
|
|
31
31
|
const dataSourceId = this.datasets[datasetIndex].dsId;
|
|
32
32
|
const dataSource = this.dataSources.find(d => d.id === dataSourceId);
|
|
33
|
-
const rawFields = toJS(this.datasets[datasetIndex].rawFields)
|
|
34
|
-
const base = extendCountField((dataSource ? dataSource.data : []), rawFields)
|
|
33
|
+
const rawFields = toJS(this.datasets[datasetIndex].rawFields)//.concat(createCountField())
|
|
34
|
+
// const base = extendCountField((dataSource ? dataSource.data : []), rawFields)
|
|
35
35
|
return {
|
|
36
36
|
...this.datasets[datasetIndex],
|
|
37
|
-
dataSource:
|
|
38
|
-
rawFields
|
|
37
|
+
dataSource: dataSource?.data ?? [],
|
|
38
|
+
rawFields
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
return {
|
|
@@ -61,6 +61,9 @@ export class CommonStore {
|
|
|
61
61
|
this.vizEmbededMenu.show = true;
|
|
62
62
|
this.vizEmbededMenu.position = position;
|
|
63
63
|
}
|
|
64
|
+
public setShowCodeExportPanel (show: boolean) {
|
|
65
|
+
this.showCodeExportPanel = show;
|
|
66
|
+
}
|
|
64
67
|
public closeEmbededMenu () {
|
|
65
68
|
this.vizEmbededMenu.show = false;
|
|
66
69
|
}
|
|
@@ -112,11 +115,13 @@ export class CommonStore {
|
|
|
112
115
|
|
|
113
116
|
public updateTempDS (rawData: IRow[]) {
|
|
114
117
|
const result = transData(rawData);
|
|
115
|
-
|
|
116
|
-
this.tmpDataSource = result.dataSource.slice(0, -1);
|
|
118
|
+
this.tmpDataSource = result.dataSource;
|
|
117
119
|
this.tmpDSRawFields = result.fields;
|
|
118
120
|
}
|
|
119
|
-
|
|
121
|
+
/**
|
|
122
|
+
* update temp dataset (standard) with dataset info
|
|
123
|
+
* @param dataset
|
|
124
|
+
*/
|
|
120
125
|
public updateTempSTDDS (dataset: IDataSetInfo) {
|
|
121
126
|
this.tmpDataSource = dataset.dataSource;
|
|
122
127
|
this.tmpDSRawFields = dataset.rawFields;
|
|
@@ -176,7 +181,7 @@ export class CommonStore {
|
|
|
176
181
|
}
|
|
177
182
|
public createPlaceholderDS() {
|
|
178
183
|
this.addDS({
|
|
179
|
-
name: '
|
|
184
|
+
name: 'new dataset',
|
|
180
185
|
dataSource: [],
|
|
181
186
|
rawFields: []
|
|
182
187
|
})
|
package/src/store/index.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React, { useContext
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
2
|
import { CommonStore } from './commonStore'
|
|
3
3
|
import { VizSpecStore } from './visualSpecStore'
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
export interface IGlobalStore {
|
|
6
6
|
commonStore: CommonStore;
|
|
7
7
|
vizStore: VizSpecStore;
|
|
8
8
|
}
|
|
@@ -10,12 +10,12 @@ interface GlobalStore {
|
|
|
10
10
|
const commonStore = new CommonStore();
|
|
11
11
|
const vizStore = new VizSpecStore(commonStore);
|
|
12
12
|
|
|
13
|
-
const initStore:
|
|
13
|
+
const initStore: IGlobalStore = {
|
|
14
14
|
commonStore,
|
|
15
15
|
vizStore
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const StoreContext = React.createContext<
|
|
18
|
+
const StoreContext = React.createContext<IGlobalStore>(null!);
|
|
19
19
|
|
|
20
20
|
export function destroyGWStore() {
|
|
21
21
|
initStore.commonStore.destroy();
|
|
@@ -31,16 +31,23 @@ export function rebootGWStore() {
|
|
|
31
31
|
|
|
32
32
|
interface StoreWrapperProps {
|
|
33
33
|
keepAlive?: boolean;
|
|
34
|
+
storeRef?: React.MutableRefObject<IGlobalStore | null>;
|
|
34
35
|
}
|
|
35
36
|
export class StoreWrapper extends React.Component<StoreWrapperProps> {
|
|
36
37
|
constructor(props: StoreWrapperProps) {
|
|
37
38
|
super(props)
|
|
39
|
+
if (props.storeRef) {
|
|
40
|
+
props.storeRef.current = initStore;
|
|
41
|
+
}
|
|
38
42
|
if (props.keepAlive) {
|
|
39
43
|
rebootGWStore();
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
componentWillUnmount() {
|
|
43
47
|
if (!this.props.keepAlive) {
|
|
48
|
+
if (this.props.storeRef) {
|
|
49
|
+
this.props.storeRef.current = null;
|
|
50
|
+
}
|
|
44
51
|
destroyGWStore();
|
|
45
52
|
}
|
|
46
53
|
}
|