@kanaries/graphic-walker 0.2.15 → 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/App.d.ts +4 -3
  2. package/dist/assets/transform.worker-5d54ff09.js.map +1 -0
  3. package/dist/assets/viewQuery.worker-ffefc111.js.map +1 -0
  4. package/dist/components/codeExport/index.d.ts +3 -0
  5. package/dist/components/loadingLayer.d.ts +2 -0
  6. package/dist/components/tabs/defaultTab.d.ts +1 -0
  7. package/dist/components/tabs/editableTab.d.ts +1 -2
  8. package/dist/dataSource/dataSelection/config.d.ts +1 -0
  9. package/dist/dataSource/dataSelection/utils.d.ts +2 -0
  10. package/dist/dataSource/index.d.ts +0 -1
  11. package/dist/datasets/tmp/test.json +1 -0
  12. package/dist/fields/encodeFields/singleEncodeEditor.d.ts +4 -4
  13. package/dist/graphic-walker.es.js +29228 -33650
  14. package/dist/graphic-walker.es.js.map +1 -1
  15. package/dist/graphic-walker.umd.js +218 -256
  16. package/dist/graphic-walker.umd.js.map +1 -1
  17. package/dist/index.d.ts +3 -3
  18. package/dist/interfaces.d.ts +52 -17
  19. package/dist/lib/execExp.d.ts +8 -0
  20. package/dist/lib/inferMeta.d.ts +2 -9
  21. package/dist/lib/insights/explainByChildren.d.ts +16 -0
  22. package/dist/lib/insights/explainBySelection.d.ts +5 -0
  23. package/dist/lib/insights/explainValue.d.ts +2 -0
  24. package/dist/lib/insights/utils.d.ts +11 -0
  25. package/dist/lib/interfaces.d.ts +23 -0
  26. package/dist/lib/op/aggregate.d.ts +3 -0
  27. package/dist/lib/op/bin.d.ts +3 -0
  28. package/dist/lib/op/fold.d.ts +3 -0
  29. package/dist/lib/op/stat.d.ts +8 -0
  30. package/dist/lib/viewQuery.d.ts +4 -0
  31. package/dist/models/visSpecHistory.d.ts +2 -0
  32. package/dist/renderer/index.d.ts +8 -7
  33. package/dist/renderer/specRenderer.d.ts +13 -0
  34. package/dist/services.d.ts +5 -31
  35. package/dist/store/commonStore.d.ts +6 -0
  36. package/dist/store/index.d.ts +3 -2
  37. package/dist/store/visualSpecStore.d.ts +11 -5
  38. package/dist/utils/autoMark.d.ts +1 -1
  39. package/dist/utils/dataPrep.d.ts +3 -2
  40. package/dist/utils/index.d.ts +3 -5
  41. package/dist/utils/save.d.ts +1 -2
  42. package/dist/vis/react-vega.d.ts +2 -22
  43. package/dist/vis/spec/aggregate.d.ts +4 -0
  44. package/dist/vis/spec/encode.d.ts +20 -0
  45. package/dist/vis/spec/field.d.ts +2 -0
  46. package/dist/vis/spec/mark.d.ts +7 -0
  47. package/dist/vis/spec/stack.d.ts +4 -0
  48. package/dist/vis/spec/tooltip.d.ts +4 -0
  49. package/dist/vis/spec/view.d.ts +73 -0
  50. package/dist/workers/transform.d.ts +2 -0
  51. package/package.json +5 -6
  52. package/src/App.tsx +56 -66
  53. package/src/components/codeExport/index.tsx +114 -0
  54. package/src/components/dataTable/index.tsx +10 -10
  55. package/src/components/loadingLayer.tsx +7 -0
  56. package/src/components/tabs/defaultTab.tsx +4 -2
  57. package/src/components/tabs/editableTab.tsx +74 -39
  58. package/src/dataSource/dataSelection/config.ts +11 -0
  59. package/src/dataSource/dataSelection/csvData.tsx +71 -39
  60. package/src/dataSource/dataSelection/gwFile.tsx +2 -2
  61. package/src/dataSource/dataSelection/utils.ts +28 -0
  62. package/src/dataSource/index.tsx +0 -17
  63. package/src/dataSource/utils.ts +8 -3
  64. package/src/fields/aestheticFields.tsx +1 -2
  65. package/src/fields/datasetFields/meaFields.tsx +12 -4
  66. package/src/fields/encodeFields/singleEncodeEditor.tsx +11 -12
  67. package/src/fields/fieldsContext.tsx +1 -0
  68. package/src/index.css +4 -4
  69. package/src/index.tsx +22 -22
  70. package/src/interfaces.ts +85 -49
  71. package/src/lib/execExp.ts +147 -0
  72. package/src/lib/inferMeta.ts +26 -29
  73. package/src/lib/insights/explainByChildren.ts +50 -0
  74. package/src/lib/insights/explainBySelection.ts +47 -0
  75. package/src/lib/insights/explainValue.ts +30 -0
  76. package/src/lib/insights/utils.ts +21 -0
  77. package/src/lib/interfaces.ts +33 -0
  78. package/src/lib/op/aggregate.ts +49 -0
  79. package/src/lib/op/bin.ts +25 -0
  80. package/src/lib/op/fold.ts +17 -0
  81. package/src/lib/op/stat.ts +46 -0
  82. package/src/lib/viewQuery.ts +22 -0
  83. package/src/locales/en-US.json +6 -3
  84. package/src/locales/i18n.ts +0 -1
  85. package/src/locales/ja-JP.json +4 -2
  86. package/src/locales/zh-CN.json +6 -3
  87. package/src/main.tsx +1 -1
  88. package/src/models/visSpecHistory.ts +14 -0
  89. package/src/renderer/index.tsx +58 -126
  90. package/src/renderer/specRenderer.tsx +121 -0
  91. package/src/segments/segmentNav.tsx +3 -16
  92. package/src/segments/visNav.tsx +17 -6
  93. package/src/services.ts +101 -67
  94. package/src/store/commonStore.ts +14 -9
  95. package/src/store/index.tsx +11 -4
  96. package/src/store/visualSpecStore.ts +89 -52
  97. package/src/utils/autoMark.ts +1 -1
  98. package/src/utils/dataPrep.ts +25 -2
  99. package/src/utils/index.ts +16 -17
  100. package/src/utils/normalization.ts +3 -1
  101. package/src/utils/save.ts +1 -2
  102. package/src/vis/react-vega.tsx +9 -340
  103. package/src/vis/spec/aggregate.ts +13 -0
  104. package/src/vis/spec/encode.ts +70 -0
  105. package/src/vis/spec/field.ts +10 -0
  106. package/src/vis/spec/mark.ts +30 -0
  107. package/src/vis/spec/stack.ts +11 -0
  108. package/src/vis/spec/tooltip.ts +16 -0
  109. package/src/vis/spec/view.ts +136 -0
  110. package/src/vis/theme.ts +12 -0
  111. package/src/visualSettings/index.tsx +10 -1
  112. package/src/workers/transform.ts +12 -0
  113. package/src/workers/transform.worker.js +13 -0
  114. package/src/workers/viewQuery.worker.js +16 -0
  115. package/dist/assets/explainer.worker-8428eb12.js.map +0 -1
  116. package/dist/dataSource/pannel.d.ts +0 -5
  117. package/dist/insightBoard/index.d.ts +0 -3
  118. package/dist/insightBoard/mainBoard.d.ts +0 -11
  119. package/dist/insightBoard/radioGroupButtons.d.ts +0 -12
  120. package/dist/insightBoard/selectionSpec.d.ts +0 -13
  121. package/dist/insightBoard/std2vegaSpec.d.ts +0 -12
  122. package/dist/insightBoard/utils.d.ts +0 -8
  123. package/dist/insights.d.ts +0 -61
  124. package/src/dataSource/pannel.tsx +0 -71
  125. package/src/insightBoard/index.tsx +0 -31
  126. package/src/insightBoard/mainBoard.tsx +0 -224
  127. package/src/insightBoard/radioGroupButtons.tsx +0 -57
  128. package/src/insightBoard/selectionSpec.ts +0 -113
  129. package/src/insightBoard/std2vegaSpec.ts +0 -184
  130. package/src/insightBoard/utils.ts +0 -32
  131. package/src/insights.ts +0 -408
  132. package/src/workers/explainer.worker.js +0 -76
package/src/main.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useEffect } from "react";
2
2
  import ReactDOM from "react-dom";
3
3
  import { GraphicWalker } from "./index";
4
4
 
@@ -27,6 +27,14 @@ export class VisSpecWithHistory {
27
27
 
28
28
  private batchFlag = false;
29
29
 
30
+ public updateLatest(snapshot: Partial<Readonly<VisSpecWithHistory['snapshots'][0]>>) {
31
+ this.snapshots[this.cursor] = {
32
+ ...this.snapshots[this.cursor],
33
+ ...snapshot,
34
+ }
35
+ }
36
+
37
+
30
38
  private commit(snapshot: Partial<Readonly<VisSpecWithHistory['snapshots'][0]>>): void {
31
39
  if (this.batchFlag) {
32
40
  // batch this commit
@@ -120,6 +128,12 @@ export class VisSpecWithHistory {
120
128
  });
121
129
  }
122
130
 
131
+ public clone () {
132
+ const nextVSWH = new VisSpecWithHistory(this.frame);
133
+ nextVSWH.cursor = this.cursor;
134
+ return nextVSWH;
135
+ }
136
+
123
137
  public exportGW (): IVisSpec {
124
138
  return {
125
139
  ...this.frame
@@ -1,138 +1,70 @@
1
- import { runInAction, toJS } from "mobx";
2
- import { observer } from "mobx-react-lite";
3
- import { Resizable } from "re-resizable";
4
- import React, { useState, useCallback, useEffect, useRef, forwardRef } from "react";
5
- import { applyFilter } from "../services";
6
- import { useGlobalStore } from "../store";
7
- import ReactVega, { IReactVegaHandler } from "../vis/react-vega";
8
- import { IDarkMode, IThemeKey } from "../interfaces";
1
+ import { observer } from 'mobx-react-lite';
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';
7
+ import { useGlobalStore } from '../store';
8
+ import { IReactVegaHandler } from '../vis/react-vega';
9
+ import { unstable_batchedUpdates } from 'react-dom';
10
+ import { initEncoding, initVisualConfig } from '../store/visualSpecStore';
9
11
 
10
- const ReactiveRenderer = forwardRef<IReactVegaHandler, { themeKey?: IThemeKey; dark?: IDarkMode }>(function ReactiveRenderer(
11
- { themeKey, dark },
12
- ref
13
- ) {
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);
14
19
  const { vizStore, commonStore } = useGlobalStore();
15
- const { draggableFieldState, visualConfig } = vizStore;
16
- const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, exploration } = visualConfig;
20
+ const { allFields, viewFilters, viewDimensions, viewMeasures } = vizStore;
17
21
  const { currentDataset } = commonStore;
18
- const { filters } = draggableFieldState;
19
-
20
- const rows = toJS(draggableFieldState.rows);
21
- const columns = toJS(draggableFieldState.columns);
22
- const color = toJS(draggableFieldState.color);
23
- const opacity = toJS(draggableFieldState.opacity);
24
- const shape = toJS(draggableFieldState.shape);
25
- const theta = toJS(draggableFieldState.theta);
26
- const radius = toJS(draggableFieldState.radius);
27
- const sizeChannel = toJS(draggableFieldState.size);
28
-
29
- const rowLeftFacetFields = rows.slice(0, -1).filter((f) => f.analyticType === "dimension");
30
- const colLeftFacetFields = columns.slice(0, -1).filter((f) => f.analyticType === "dimension");
31
-
32
- const hasFacet = rowLeftFacetFields.length > 0 || colLeftFacetFields.length > 0;
33
-
34
- const shouldTriggerMenu = exploration.mode === "none";
35
-
36
- const handleGeomClick = useCallback(
37
- (values: any, e: any) => {
38
- if (shouldTriggerMenu) {
39
- e.stopPropagation();
40
- runInAction(() => {
41
- commonStore.showEmbededMenu([e.pageX, e.pageY]);
42
- commonStore.setFilters(values);
43
- });
44
- }
45
- },
46
- [shouldTriggerMenu]
47
- );
48
-
49
- // apply filters
50
22
  const { dataSource } = currentDataset;
23
+ const [viewConfig, setViewConfig] = useState<IVisualConfig>(initVisualConfig);
24
+ const [encodings, setEncodings] = useState<DeepReadonly<DraggableFieldState>>(initEncoding);
51
25
 
52
- const [data, setData] = useState(dataSource);
53
- const pendingPromiseRef = useRef<Promise<typeof data> | null>(null);
26
+ const [viewData, setViewData] = useState<IRow[]>([]);
54
27
 
55
28
  useEffect(() => {
56
- setData(dataSource);
57
- }, [dataSource]);
58
-
59
- useEffect(() => {
60
- const p = filters.length === 0 ? Promise.resolve(dataSource) : applyFilter(dataSource, filters);
61
- pendingPromiseRef.current = p;
62
-
63
- p.then((d) => {
64
- if (p !== pendingPromiseRef.current) {
65
- // This promise is out-of-date
66
- return;
67
- }
68
-
69
- setData(d);
70
- }).catch((err) => {
71
- console.error(err);
72
- });
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]);
73
56
 
74
- return () => {
75
- pendingPromiseRef.current = null;
76
- };
77
- }, [dataSource, filters]);
78
- const enableResize = size.mode === "fixed" && !hasFacet;
79
57
  return (
80
- <Resizable
81
- className={enableResize ? "border-blue-400 border-2 overflow-hidden" : ""}
82
- style={{ padding: "12px" }}
83
- onResizeStop={(e, direction, ref, d) => {
84
- vizStore.setChartLayout({
85
- mode: "fixed",
86
- width: size.width + d.width,
87
- height: size.height + d.height,
88
- });
89
- }}
90
- enable={
91
- enableResize
92
- ? undefined
93
- : {
94
- top: false,
95
- right: false,
96
- bottom: false,
97
- left: false,
98
- topRight: false,
99
- bottomRight: false,
100
- bottomLeft: false,
101
- topLeft: false,
102
- }
103
- }
104
- size={{
105
- width: size.width + "px",
106
- height: size.height + "px",
107
- }}
108
- >
109
- <ReactVega
110
- layoutMode={size.mode}
111
- interactiveScale={interactiveScale}
112
- geomType={geoms[0]}
113
- defaultAggregate={defaultAggregated}
114
- stack={stack}
115
- dataSource={data}
116
- rows={rows}
117
- columns={columns}
118
- color={color[0]}
119
- theta={theta[0]}
120
- radius={radius[0]}
121
- shape={shape[0]}
122
- opacity={opacity[0]}
123
- size={sizeChannel[0]}
124
- showActions={showActions}
125
- width={size.width - 12 * 4}
126
- height={size.height - 12 * 4}
127
- ref={ref}
128
- brushEncoding={exploration.mode === "brush" ? exploration.brushDirection : "none"}
129
- selectEncoding={exploration.mode === "point" ? "default" : "none"}
130
- onGeomClick={handleGeomClick}
131
- themeKey={themeKey}
132
- dark={dark}
133
- />
134
- </Resizable>
58
+ <SpecRenderer
59
+ loading={waiting}
60
+ data={viewData}
61
+ ref={ref}
62
+ themeKey={themeKey}
63
+ dark={dark}
64
+ draggableFieldState={encodings}
65
+ visualConfig={viewConfig}
66
+ />
135
67
  );
136
68
  });
137
69
 
138
- export default observer(ReactiveRenderer);
70
+ export default observer(Renderer);
@@ -0,0 +1,121 @@
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
+ const details = draggableFieldState.details;
35
+
36
+ const rowLeftFacetFields = useMemo(() => rows.slice(0, -1).filter((f) => f.analyticType === 'dimension'), [rows]);
37
+ const colLeftFacetFields = useMemo(
38
+ () => columns.slice(0, -1).filter((f) => f.analyticType === 'dimension'),
39
+ [columns]
40
+ );
41
+
42
+ const hasFacet = rowLeftFacetFields.length > 0 || colLeftFacetFields.length > 0;
43
+
44
+ const shouldTriggerMenu = exploration.mode === 'none';
45
+
46
+ const handleGeomClick = useCallback(
47
+ (values: any, e: any) => {
48
+ if (shouldTriggerMenu) {
49
+ e.stopPropagation();
50
+ runInAction(() => {
51
+ commonStore.showEmbededMenu([e.pageX, e.pageY]);
52
+ commonStore.setFilters(values);
53
+ });
54
+ }
55
+ },
56
+ [shouldTriggerMenu]
57
+ );
58
+ const enableResize = size.mode === 'fixed' && !hasFacet;
59
+
60
+ return (
61
+ <Resizable
62
+ className={enableResize ? 'border-blue-400 border-2 overflow-hidden' : ''}
63
+ style={{ padding: '12px' }}
64
+ onResizeStop={(e, direction, ref, d) => {
65
+ vizStore.setChartLayout({
66
+ mode: 'fixed',
67
+ width: size.width + d.width,
68
+ height: size.height + d.height,
69
+ });
70
+ }}
71
+ enable={
72
+ enableResize
73
+ ? undefined
74
+ : {
75
+ top: false,
76
+ right: false,
77
+ bottom: false,
78
+ left: false,
79
+ topRight: false,
80
+ bottomRight: false,
81
+ bottomLeft: false,
82
+ topLeft: false,
83
+ }
84
+ }
85
+ size={{
86
+ width: size.width + 'px',
87
+ height: size.height + 'px',
88
+ }}
89
+ >
90
+ {loading && <LoadingLayer />}
91
+ <ReactVega
92
+ layoutMode={size.mode}
93
+ interactiveScale={interactiveScale}
94
+ geomType={geoms[0]}
95
+ defaultAggregate={defaultAggregated}
96
+ stack={stack}
97
+ dataSource={data}
98
+ rows={rows}
99
+ columns={columns}
100
+ color={color[0]}
101
+ theta={theta[0]}
102
+ radius={radius[0]}
103
+ shape={shape[0]}
104
+ opacity={opacity[0]}
105
+ size={sizeChannel[0]}
106
+ details={details}
107
+ showActions={showActions}
108
+ width={size.width - 12 * 4}
109
+ height={size.height - 12 * 4}
110
+ ref={ref}
111
+ brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
112
+ selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
113
+ onGeomClick={handleGeomClick}
114
+ themeKey={themeKey}
115
+ dark={dark}
116
+ />
117
+ </Resizable>
118
+ );
119
+ });
120
+
121
+ export default SpecRenderer;
@@ -1,18 +1,14 @@
1
- import React, { Fragment, useCallback } from "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 { ChartBarIcon, ChartPieIcon, CircleStackIcon } from "@heroicons/react/24/outline";
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 { visIndex, visList } = vizStore;
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
  }, [])
@@ -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?.[0] || 'vis',
17
- options: v.name?.[1],
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
@@ -1,6 +1,5 @@
1
1
  import { toJS } from 'mobx';
2
- import { View, Specification } from 'visual-insights';
3
- import { IRow, Filters, SemanticType, IMeasure, IMutField, IFilterField } from './interfaces';
2
+ import { IRow, IMutField, IFilterField, Specification } from './interfaces';
4
3
  /* eslint import/no-webpack-loader-syntax:0 */
5
4
  // @ts-ignore
6
5
  // eslint-disable-next-line
@@ -8,17 +7,19 @@ import { IRow, Filters, SemanticType, IMeasure, IMutField, IFilterField } from '
8
7
  /* eslint import/no-webpack-loader-syntax:0 */
9
8
  // @ts-ignore
10
9
  // eslint-disable-next-line
11
- import ExplainerWorker from './workers/explainer.worker?worker&inline';
10
+ // import ExplainerWorker from './workers/explainer.worker?worker&inline';
12
11
  import FilterWorker from './workers/filter.worker?worker&inline';
13
- import { IExplaination, IMeasureWithStat } from './insights';
12
+ import TransformDataWorker from './workers/transform.worker?worker&inline';
13
+ import ViewQueryWorker from './workers/viewQuery.worker?worker&inline';
14
+ import { IViewQuery } from './lib/viewQuery';
14
15
 
15
- interface WorkerState {
16
- eWorker: Worker | null;
17
- }
16
+ // interface WorkerState {
17
+ // eWorker: Worker | null;
18
+ // }
18
19
 
19
- const workerState: WorkerState = {
20
- eWorker: null,
21
- }
20
+ // const workerState: WorkerState = {
21
+ // eWorker: null,
22
+ // }
22
23
 
23
24
  function workerService<T, R>(worker: Worker, data: R): Promise<T> {
24
25
  return new Promise<T>((resolve, reject) => {
@@ -35,77 +36,78 @@ function workerService<T, R>(worker: Worker, data: R): Promise<T> {
35
36
  });
36
37
  }
37
38
 
38
- interface ExplainParams {
39
- dimensions: string[];
40
- measures: string[];
41
- dataSource: IRow[];
42
- filters?: Filters;
43
- currentSpace: {
44
- dimensions: string[];
45
- measures: IMeasure[];
46
- };
47
- }
39
+ // interface ExplainParams {
40
+ // dimensions: string[];
41
+ // measures: string[];
42
+ // dataSource: IRow[];
43
+ // filters?: Filters;
44
+ // currentSpace: {
45
+ // dimensions: string[];
46
+ // measures: IMeasure[];
47
+ // };
48
+ // }
48
49
  export interface IVisSpace {
49
50
  dataView: IRow[];
50
51
  schema: Specification;
51
52
  }
52
- interface ExplainReturns {
53
- explainations: IExplaination[];
54
- valueExp: IMeasureWithStat[];
55
- visSpaces: IVisSpace[];
56
- fieldsWithSemanticType: Array<{ key: string; type: SemanticType }>;
57
- }
58
- export async function getExplaination(props: ExplainParams) {
59
- const worker = workerState.eWorker;
60
- if (worker === null) throw new Error('init worker first.')
61
- let result: ExplainReturns = {
62
- explainations: [],
63
- valueExp: [],
64
- visSpaces: [],
65
- fieldsWithSemanticType: [],
66
- };
67
- try {
68
- result = await workerService<ExplainReturns, { type: string; data: ExplainParams }>(
69
- worker,
70
- {
71
- type: 'getExplaination',
72
- data: props
73
- }
74
- );
75
- return result;
76
- } catch (error) {
77
- console.error(error);
78
- }
79
- return result;
80
- }
53
+ // interface ExplainReturns {
54
+ // explainations: IExplaination[];
55
+ // valueExp: IMeasureWithStat[];
56
+ // visSpaces: IVisSpace[];
57
+ // fieldsWithSemanticType: Array<{ key: string; type: SemanticType }>;
58
+ // }
59
+ // export async function getExplaination(props: ExplainParams) {
60
+ // const worker = workerState.eWorker;
61
+ // if (worker === null) throw new Error('init worker first.')
62
+ // let result: ExplainReturns = {
63
+ // explainations: [],
64
+ // valueExp: [],
65
+ // visSpaces: [],
66
+ // fieldsWithSemanticType: [],
67
+ // };
68
+ // try {
69
+ // result = await workerService<ExplainReturns, { type: string; data: ExplainParams }>(
70
+ // worker,
71
+ // {
72
+ // type: 'getExplaination',
73
+ // data: props
74
+ // }
75
+ // );
76
+ // return result;
77
+ // } catch (error) {
78
+ // console.error(error);
79
+ // }
80
+ // return result;
81
+ // }
81
82
 
82
83
  interface PreAnalysisParams {
83
84
  fields: IMutField[];
84
85
  dataSource: IRow[];
85
86
  }
86
- export async function preAnalysis(props: PreAnalysisParams) {
87
- if (workerState.eWorker !== null) {
88
- workerState.eWorker.terminate();
89
- }
90
- try {
91
- workerState.eWorker = new ExplainerWorker() as Worker;
92
- const tmp = await workerService<boolean, { type: string; data: PreAnalysisParams}>(workerState.eWorker, { type: 'preAnalysis', data: props });
93
- } catch (error) {
94
- console.error(error)
95
- }
96
- }
87
+ // export async function preAnalysis(props: PreAnalysisParams) {
88
+ // if (workerState.eWorker !== null) {
89
+ // workerState.eWorker.terminate();
90
+ // }
91
+ // try {
92
+ // workerState.eWorker = new ExplainerWorker() as Worker;
93
+ // const tmp = await workerService<boolean, { type: string; data: PreAnalysisParams}>(workerState.eWorker, { type: 'preAnalysis', data: props });
94
+ // } catch (error) {
95
+ // console.error(error)
96
+ // }
97
+ // }
97
98
 
98
- export function destroyWorker() {
99
- if (workerState.eWorker) {
100
- workerState.eWorker.terminate();
101
- workerState.eWorker = null;
102
- }
103
- }
99
+ // export function destroyWorker() {
100
+ // if (workerState.eWorker) {
101
+ // workerState.eWorker.terminate();
102
+ // workerState.eWorker = null;
103
+ // }
104
+ // }
104
105
 
105
106
  let filterWorker: Worker | null = null;
106
107
  let filterWorkerAutoTerminator: NodeJS.Timeout | null = null;
107
108
 
108
- export const applyFilter = async (data: readonly IRow[], filters: readonly IFilterField[]): Promise<IRow[]> => {
109
+ export const applyFilter = async (data: IRow[], filters: readonly IFilterField[]): Promise<IRow[]> => {
110
+ if (filters.length === 0) return data;
109
111
  if (filterWorkerAutoTerminator !== null) {
110
112
  clearTimeout(filterWorkerAutoTerminator);
111
113
  filterWorkerAutoTerminator = null;
@@ -137,3 +139,35 @@ export const applyFilter = async (data: readonly IRow[], filters: readonly IFilt
137
139
  }, 60_000); // Destroy the worker when no request is received for 60 secs
138
140
  }
139
141
  };
142
+
143
+ export const transformDataService = async (data: IRow[], columns: IMutField[]): Promise<IRow[]> => {
144
+ if (columns.length === 0 || data.length === 0) return data;
145
+ const worker = new TransformDataWorker();
146
+ try {
147
+ const res: IRow[] = await workerService(worker, {
148
+ dataSource: data,
149
+ columns: toJS(columns),
150
+ });
151
+ return res;
152
+ } catch (error) {
153
+ throw new Error('Uncaught error in TransformDataWorker', { cause: error });
154
+ } finally {
155
+ worker.terminate();
156
+ }
157
+ }
158
+
159
+ export const applyViewQuery = async (data: IRow[], metas: IMutField[], query: IViewQuery): Promise<IRow[]> => {
160
+ const worker = new ViewQueryWorker();
161
+ try {
162
+ const res: IRow[] = await workerService(worker, {
163
+ dataSource: data,
164
+ metas: toJS(metas),
165
+ query: toJS(query),
166
+ });
167
+ return res;
168
+ } catch (err) {
169
+ throw new Error('Uncaught error in ViewQueryWorker', { cause: err });
170
+ } finally {
171
+ worker.terminate();
172
+ }
173
+ }