@kanaries/graphic-walker 0.4.1 → 0.4.3
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 -1
- package/dist/assets/buildMetricTable.worker-5555966a.js.map +1 -0
- package/dist/components/leafletRenderer/ChoroplethRenderer.d.ts +22 -0
- package/dist/components/leafletRenderer/POIRenderer.d.ts +20 -0
- package/dist/components/leafletRenderer/encodings.d.ts +7 -0
- package/dist/components/leafletRenderer/geoConfigPanel.d.ts +3 -0
- package/dist/components/leafletRenderer/index.d.ts +15 -0
- package/dist/components/leafletRenderer/tooltip.d.ts +9 -0
- package/dist/components/leafletRenderer/utils.d.ts +2 -0
- package/dist/components/pivotTable/index.d.ts +2 -1
- package/dist/components/pivotTable/inteface.d.ts +6 -2
- package/dist/components/pivotTable/leftTree.d.ts +1 -0
- package/dist/components/pivotTable/topTree.d.ts +2 -0
- package/dist/components/pivotTable/utils.d.ts +1 -2
- package/dist/config.d.ts +3 -2
- package/dist/graphic-walker.es.js +37811 -30386
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +145 -137
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/interfaces.d.ts +28 -0
- package/dist/renderer/specRenderer.d.ts +2 -1
- package/dist/services.d.ts +7 -1
- package/dist/store/commonStore.d.ts +6 -0
- package/dist/store/visualSpecStore.d.ts +180 -4
- package/dist/utils/save.d.ts +1 -0
- package/dist/workers/buildPivotTable.d.ts +7 -0
- package/package.json +14 -2
- package/src/App.tsx +18 -4
- package/src/components/leafletRenderer/ChoroplethRenderer.tsx +312 -0
- package/src/components/leafletRenderer/POIRenderer.tsx +189 -0
- package/src/components/leafletRenderer/encodings.ts +194 -0
- package/src/components/leafletRenderer/geoConfigPanel.tsx +197 -0
- package/src/components/leafletRenderer/index.tsx +70 -0
- package/src/components/leafletRenderer/tooltip.tsx +24 -0
- package/src/components/leafletRenderer/utils.ts +52 -0
- package/src/components/pivotTable/index.tsx +171 -67
- package/src/components/pivotTable/inteface.ts +6 -2
- package/src/components/pivotTable/leftTree.tsx +24 -11
- package/src/components/pivotTable/metricTable.tsx +15 -10
- package/src/components/pivotTable/topTree.tsx +50 -17
- package/src/components/pivotTable/utils.ts +70 -11
- package/src/components/visualConfig/index.tsx +17 -1
- package/src/config.ts +27 -16
- package/src/dataSource/table.tsx +7 -11
- package/src/fields/aestheticFields.tsx +4 -0
- package/src/fields/fieldsContext.tsx +3 -0
- package/src/fields/posFields/index.tsx +8 -2
- package/src/global.d.ts +4 -4
- package/src/index.tsx +11 -9
- package/src/interfaces.ts +35 -0
- package/src/locales/en-US.json +27 -2
- package/src/locales/ja-JP.json +27 -2
- package/src/locales/zh-CN.json +27 -2
- package/src/renderer/hooks.ts +1 -1
- package/src/renderer/index.tsx +24 -1
- package/src/renderer/pureRenderer.tsx +27 -13
- package/src/renderer/specRenderer.tsx +46 -30
- package/src/services.ts +32 -23
- package/src/shadow-dom.tsx +7 -0
- package/src/store/commonStore.ts +29 -1
- package/src/store/visualSpecStore.ts +38 -23
- package/src/utils/save.ts +28 -1
- package/src/utils/vegaApiExport.ts +3 -0
- package/src/visualSettings/index.tsx +58 -6
- package/src/workers/buildMetricTable.worker.js +27 -0
- package/src/workers/buildPivotTable.ts +27 -0
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { type ForwardedRef, forwardRef } from "react";
|
|
2
|
-
import {
|
|
1
|
+
import React, { type ForwardedRef, forwardRef, useState } from "react";
|
|
2
|
+
import { DOMProvider } from "@kanaries/react-beautiful-dnd";
|
|
3
3
|
import { observer } from "mobx-react-lite";
|
|
4
4
|
import App, { IGWProps } from "./App";
|
|
5
5
|
import { StoreWrapper } from "./store/index";
|
|
@@ -13,22 +13,24 @@ import "./empty_sheet.css";
|
|
|
13
13
|
export const GraphicWalker = observer(forwardRef<IGWHandler, IGWProps>((props, ref) => {
|
|
14
14
|
const { storeRef } = props;
|
|
15
15
|
|
|
16
|
+
const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);
|
|
17
|
+
|
|
16
18
|
const handleMount = (shadowRoot: ShadowRoot) => {
|
|
17
|
-
|
|
18
|
-
DOM.setHead(shadowRoot);
|
|
19
|
+
setShadowRoot(shadowRoot);
|
|
19
20
|
};
|
|
20
21
|
const handleUnmount = () => {
|
|
21
|
-
|
|
22
|
-
DOM.setHead(document.head);
|
|
22
|
+
setShadowRoot(null);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<StoreWrapper keepAlive={props.keepAlive} storeRef={storeRef}>
|
|
27
27
|
<AppRoot ref={ref as ForwardedRef<IGWHandlerInsider>}>
|
|
28
28
|
<ShadowDom onMount={handleMount} onUnmount={handleUnmount}>
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
29
|
+
<DOMProvider value={{ head: shadowRoot ?? document.head, body: shadowRoot ?? document.body }}>
|
|
30
|
+
<FieldsContextWrapper>
|
|
31
|
+
<App {...props} />
|
|
32
|
+
</FieldsContextWrapper>
|
|
33
|
+
</DOMProvider>
|
|
32
34
|
</ShadowDom>
|
|
33
35
|
</AppRoot>
|
|
34
36
|
</StoreWrapper>
|
package/src/interfaces.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {Config as VgConfig, View} from 'vega';
|
|
2
2
|
import {Config as VlConfig} from 'vega-lite';
|
|
3
|
+
import type { FeatureCollection } from 'geojson';
|
|
4
|
+
import type { feature } from 'topojson-client';
|
|
3
5
|
import type {IViewQuery} from "./lib/viewQuery";
|
|
4
6
|
|
|
5
7
|
export type DeepReadonly<T extends Record<keyof any, any>> = {
|
|
@@ -86,6 +88,8 @@ export interface IExpression {
|
|
|
86
88
|
as: string;
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
export type IGeoRole = 'longitude' | 'latitude' | 'none';
|
|
92
|
+
|
|
89
93
|
export interface IField {
|
|
90
94
|
/**
|
|
91
95
|
* fid: key in data record
|
|
@@ -101,6 +105,7 @@ export interface IField {
|
|
|
101
105
|
aggName?: string;
|
|
102
106
|
semanticType: ISemanticType;
|
|
103
107
|
analyticType: IAnalyticType;
|
|
108
|
+
geoRole?: IGeoRole;
|
|
104
109
|
cmp?: (a: any, b: any) => number;
|
|
105
110
|
computed?: boolean;
|
|
106
111
|
expression?: IExpression;
|
|
@@ -183,6 +188,9 @@ export interface DraggableFieldState {
|
|
|
183
188
|
shape: IViewField[];
|
|
184
189
|
theta: IViewField[];
|
|
185
190
|
radius: IViewField[];
|
|
191
|
+
longitude: IViewField[];
|
|
192
|
+
latitude: IViewField[];
|
|
193
|
+
geoId: IViewField[];
|
|
186
194
|
details: IViewField[];
|
|
187
195
|
filters: IFilterField[];
|
|
188
196
|
text: IViewField[];
|
|
@@ -209,14 +217,21 @@ export type IFilterRule =
|
|
|
209
217
|
|
|
210
218
|
export type IStackMode = 'none' | 'stack' | 'normalize' | 'zero' | 'center';
|
|
211
219
|
|
|
220
|
+
export type ICoordMode = 'generic' | 'geographic';
|
|
221
|
+
|
|
212
222
|
export interface IVisualConfig {
|
|
213
223
|
defaultAggregated: boolean;
|
|
214
224
|
geoms: string[];
|
|
225
|
+
showTableSummary: boolean;
|
|
226
|
+
/** @default "generic" */
|
|
227
|
+
coordSystem?: ICoordMode;
|
|
215
228
|
stack: IStackMode;
|
|
216
229
|
showActions: boolean;
|
|
217
230
|
interactiveScale: boolean;
|
|
218
231
|
sorted: ISortMode;
|
|
219
232
|
zeroScale: boolean;
|
|
233
|
+
/** @default false */
|
|
234
|
+
scaleIncludeUnmatchedChoropleth?: boolean;
|
|
220
235
|
background?: string;
|
|
221
236
|
format: {
|
|
222
237
|
numberFormat?: string;
|
|
@@ -236,6 +251,8 @@ export interface IVisualConfig {
|
|
|
236
251
|
width: number;
|
|
237
252
|
height: number;
|
|
238
253
|
};
|
|
254
|
+
geojson?: FeatureCollection;
|
|
255
|
+
geoKey?: string;
|
|
239
256
|
limit: number;
|
|
240
257
|
}
|
|
241
258
|
|
|
@@ -294,6 +311,7 @@ export interface IChartExportResult<T extends 'svg' | 'data-url' = 'svg' | 'data
|
|
|
294
311
|
canvas(): HTMLCanvasElement | null;
|
|
295
312
|
}[];
|
|
296
313
|
container(): HTMLDivElement | null;
|
|
314
|
+
chartType?: string;
|
|
297
315
|
}
|
|
298
316
|
|
|
299
317
|
interface IExportChart {
|
|
@@ -448,3 +466,20 @@ export type IResponse<T> = (
|
|
|
448
466
|
};
|
|
449
467
|
}
|
|
450
468
|
);
|
|
469
|
+
|
|
470
|
+
export type Topology = Parameters<typeof feature>[0];
|
|
471
|
+
|
|
472
|
+
export type IGeographicData = (
|
|
473
|
+
| {
|
|
474
|
+
type: 'GeoJSON';
|
|
475
|
+
data: FeatureCollection;
|
|
476
|
+
}
|
|
477
|
+
| {
|
|
478
|
+
type: 'TopoJSON';
|
|
479
|
+
data: Topology;
|
|
480
|
+
/**
|
|
481
|
+
* default to the first key of `objects` in Topology
|
|
482
|
+
*/
|
|
483
|
+
objectKey?: string;
|
|
484
|
+
}
|
|
485
|
+
);
|
package/src/locales/en-US.json
CHANGED
|
@@ -39,7 +39,14 @@
|
|
|
39
39
|
"arc": "Arc",
|
|
40
40
|
"boxplot": "Box (Box Plot)",
|
|
41
41
|
"table": "Table",
|
|
42
|
-
"text": "Text"
|
|
42
|
+
"text": "Text",
|
|
43
|
+
"poi": "POI",
|
|
44
|
+
"choropleth": "Choropleth"
|
|
45
|
+
},
|
|
46
|
+
"coord_system": {
|
|
47
|
+
"__enum__": "Coordinate System",
|
|
48
|
+
"generic": "Generic",
|
|
49
|
+
"geographic": "Geographic"
|
|
43
50
|
},
|
|
44
51
|
"stack_mode": {
|
|
45
52
|
"__enum__": "Stack Mode",
|
|
@@ -77,7 +84,10 @@
|
|
|
77
84
|
"radius": "Radius",
|
|
78
85
|
"filters": "Filters",
|
|
79
86
|
"details": "Details",
|
|
80
|
-
"text": "Text"
|
|
87
|
+
"text": "Text",
|
|
88
|
+
"longitude": "Longitude",
|
|
89
|
+
"latitude": "Latitude",
|
|
90
|
+
"geoId": "Geometry ID"
|
|
81
91
|
},
|
|
82
92
|
"aggregator": {
|
|
83
93
|
"sum": "Sum",
|
|
@@ -168,6 +178,21 @@
|
|
|
168
178
|
"size_setting": {
|
|
169
179
|
"width": "Width",
|
|
170
180
|
"height": "Height"
|
|
181
|
+
},
|
|
182
|
+
"table": {
|
|
183
|
+
"summary": "Show summary"
|
|
184
|
+
},
|
|
185
|
+
"geography": "Geography Configuration",
|
|
186
|
+
"geography_settings": {
|
|
187
|
+
"geoKey": "Feature ID",
|
|
188
|
+
"format": "Format",
|
|
189
|
+
"geojson": "GeoJSON",
|
|
190
|
+
"topojson": "TopoJSON",
|
|
191
|
+
"objectKey": "Extract Feature Key",
|
|
192
|
+
"jsonInputPlaceholder": "Paste {{format}} here",
|
|
193
|
+
"href": "{{format}} URL",
|
|
194
|
+
"hrefPlaceholder": "Enter {{format}} URL",
|
|
195
|
+
"load": "Load"
|
|
171
196
|
}
|
|
172
197
|
},
|
|
173
198
|
"DatasetFields": {
|
package/src/locales/ja-JP.json
CHANGED
|
@@ -39,7 +39,14 @@
|
|
|
39
39
|
"arc": "アーク",
|
|
40
40
|
"boxplot": "ボックスプロット",
|
|
41
41
|
"table": "表",
|
|
42
|
-
"text": "
|
|
42
|
+
"text": "テキスト",
|
|
43
|
+
"poi": "POI",
|
|
44
|
+
"choropleth": "コロプレス"
|
|
45
|
+
},
|
|
46
|
+
"coord_system": {
|
|
47
|
+
"__enum__": "座標系",
|
|
48
|
+
"generic": "ジェネリック",
|
|
49
|
+
"geographic": "地理"
|
|
43
50
|
},
|
|
44
51
|
"stack_mode": {
|
|
45
52
|
"__enum__": "スタックモード",
|
|
@@ -76,7 +83,10 @@
|
|
|
76
83
|
"theta": "角度",
|
|
77
84
|
"radius": "半径",
|
|
78
85
|
"filters": "フィルター",
|
|
79
|
-
"text": "本文"
|
|
86
|
+
"text": "本文",
|
|
87
|
+
"longitude": "経度",
|
|
88
|
+
"latitude": "緯度",
|
|
89
|
+
"geoId": "地理ID"
|
|
80
90
|
},
|
|
81
91
|
"aggregator": {
|
|
82
92
|
"sum": "合計",
|
|
@@ -167,6 +177,21 @@
|
|
|
167
177
|
"size_setting": {
|
|
168
178
|
"width": "幅",
|
|
169
179
|
"height": "高さ"
|
|
180
|
+
},
|
|
181
|
+
"table": {
|
|
182
|
+
"summary": "サマリを表示"
|
|
183
|
+
},
|
|
184
|
+
"geography": "地理情報設定",
|
|
185
|
+
"geography_settings": {
|
|
186
|
+
"geoKey": "フィーチャーID",
|
|
187
|
+
"format": "データ形式",
|
|
188
|
+
"geojson": "GeoJSON",
|
|
189
|
+
"topojson": "TopoJSON",
|
|
190
|
+
"objectKey": "Feature のキー",
|
|
191
|
+
"jsonInputPlaceholder": "{{format}}をここに貼り付けてください",
|
|
192
|
+
"href": "{{format}} URL",
|
|
193
|
+
"hrefPlaceholder": "{{format}} URLを入力",
|
|
194
|
+
"load": "ロード"
|
|
170
195
|
}
|
|
171
196
|
},
|
|
172
197
|
"DatasetFields": {
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -39,7 +39,14 @@
|
|
|
39
39
|
"arc": "弧形",
|
|
40
40
|
"boxplot": "统计箱",
|
|
41
41
|
"table": "表格",
|
|
42
|
-
"text": "文本"
|
|
42
|
+
"text": "文本",
|
|
43
|
+
"poi": "兴趣点",
|
|
44
|
+
"choropleth": "区域图"
|
|
45
|
+
},
|
|
46
|
+
"coord_system": {
|
|
47
|
+
"__enum__": "坐标系统",
|
|
48
|
+
"generic": "通用",
|
|
49
|
+
"geographic": "地理"
|
|
43
50
|
},
|
|
44
51
|
"layout_type": {
|
|
45
52
|
"__enum__": "尺寸模式",
|
|
@@ -77,7 +84,10 @@
|
|
|
77
84
|
"radius": "半径",
|
|
78
85
|
"filters": "筛选器",
|
|
79
86
|
"details": "信息",
|
|
80
|
-
"text": "文本"
|
|
87
|
+
"text": "文本",
|
|
88
|
+
"longitude": "经度",
|
|
89
|
+
"latitude": "纬度",
|
|
90
|
+
"geoId": "地理 ID"
|
|
81
91
|
},
|
|
82
92
|
"aggregator": {
|
|
83
93
|
"sum": "求和",
|
|
@@ -168,6 +178,21 @@
|
|
|
168
178
|
"size_setting": {
|
|
169
179
|
"width": "宽度",
|
|
170
180
|
"height": "高度"
|
|
181
|
+
},
|
|
182
|
+
"table": {
|
|
183
|
+
"summary": "显示摘要"
|
|
184
|
+
},
|
|
185
|
+
"geography": "地理信息配置",
|
|
186
|
+
"geography_settings": {
|
|
187
|
+
"geoKey": "地理 ID",
|
|
188
|
+
"format": "数据格式",
|
|
189
|
+
"geojson": "GeoJSON",
|
|
190
|
+
"topojson": "TopoJSON",
|
|
191
|
+
"objectKey": "提取要素键",
|
|
192
|
+
"jsonInputPlaceholder": "在此粘贴 {{format}}",
|
|
193
|
+
"href": "{{format}} URL",
|
|
194
|
+
"hrefPlaceholder": "输入在线资源地址",
|
|
195
|
+
"load": "加载"
|
|
171
196
|
}
|
|
172
197
|
},
|
|
173
198
|
"DatasetFields": {
|
package/src/renderer/hooks.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { dataQueryServer } from '../computation/serverComputation';
|
|
|
8
8
|
|
|
9
9
|
export const useComputationFunc = (): IComputationFunction => {
|
|
10
10
|
const { vizStore } = useGlobalStore();
|
|
11
|
-
return vizStore.
|
|
11
|
+
return vizStore.computationFunction;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
interface UseRendererProps {
|
package/src/renderer/index.tsx
CHANGED
|
@@ -8,8 +8,9 @@ import { useGlobalStore } from '../store';
|
|
|
8
8
|
import { IReactVegaHandler } from '../vis/react-vega';
|
|
9
9
|
import { unstable_batchedUpdates } from 'react-dom';
|
|
10
10
|
import { useRenderer } from './hooks';
|
|
11
|
-
import { initEncoding } from '../
|
|
11
|
+
import { initEncoding } from '../utils/save';
|
|
12
12
|
import { useChartIndexControl } from '../utils/chartIndexControl';
|
|
13
|
+
import { LEAFLET_DEFAULT_HEIGHT, LEAFLET_DEFAULT_WIDTH } from '../components/leafletRenderer';
|
|
13
14
|
import { initVisualConfig } from '../utils/save';
|
|
14
15
|
|
|
15
16
|
interface RendererProps {
|
|
@@ -102,6 +103,27 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
|
|
|
102
103
|
[vizStore]
|
|
103
104
|
);
|
|
104
105
|
|
|
106
|
+
const isSpatial = viewConfig.coordSystem === 'geographic';
|
|
107
|
+
|
|
108
|
+
const sizeRef = useRef(viewConfig.size);
|
|
109
|
+
sizeRef.current = viewConfig.size;
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (isSpatial) {
|
|
113
|
+
const prevSizeConfig = sizeRef.current;
|
|
114
|
+
if (sizeRef.current.width < LEAFLET_DEFAULT_WIDTH || sizeRef.current.height < LEAFLET_DEFAULT_HEIGHT) {
|
|
115
|
+
vizStore.setChartLayout({
|
|
116
|
+
mode: sizeRef.current.mode,
|
|
117
|
+
width: Math.max(prevSizeConfig.width, LEAFLET_DEFAULT_WIDTH),
|
|
118
|
+
height: Math.max(prevSizeConfig.height, LEAFLET_DEFAULT_HEIGHT),
|
|
119
|
+
});
|
|
120
|
+
return () => {
|
|
121
|
+
vizStore.setChartLayout(prevSizeConfig);
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}, [isSpatial, vizStore]);
|
|
126
|
+
|
|
105
127
|
return (
|
|
106
128
|
<SpecRenderer
|
|
107
129
|
name={chart?.name}
|
|
@@ -115,6 +137,7 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
|
|
|
115
137
|
visualConfig={viewConfig}
|
|
116
138
|
onGeomClick={handleGeomClick}
|
|
117
139
|
onChartResize={handleChartResize}
|
|
140
|
+
computationFunction={computationFunction}
|
|
118
141
|
/>
|
|
119
142
|
);
|
|
120
143
|
});
|
|
@@ -3,6 +3,7 @@ import { unstable_batchedUpdates } from 'react-dom';
|
|
|
3
3
|
import { toJS } from 'mobx';
|
|
4
4
|
import { observer } from 'mobx-react-lite';
|
|
5
5
|
import { ShadowDom } from '../shadow-dom';
|
|
6
|
+
import LeafletRenderer from '../components/leafletRenderer';
|
|
6
7
|
import { withAppRoot } from '../components/appRoot';
|
|
7
8
|
import type {
|
|
8
9
|
IDarkMode,
|
|
@@ -54,7 +55,6 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
|
|
|
54
55
|
const defaultAggregated = visualConfig?.defaultAggregated ?? false;
|
|
55
56
|
|
|
56
57
|
const [viewData, setViewData] = useState<IRow[]>([]);
|
|
57
|
-
|
|
58
58
|
const { allFields, viewDimensions, viewMeasures, filters } = useMemo(() => {
|
|
59
59
|
const viewDimensions: IViewField[] = [];
|
|
60
60
|
const viewMeasures: IViewField[] = [];
|
|
@@ -86,7 +86,7 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
|
|
|
86
86
|
limit: limit ?? -1,
|
|
87
87
|
computationFunction: computation,
|
|
88
88
|
});
|
|
89
|
-
|
|
89
|
+
console.log(computation)
|
|
90
90
|
// Dependencies that should not trigger effect individually
|
|
91
91
|
const latestFromRef = useRef({ data });
|
|
92
92
|
latestFromRef.current = { data };
|
|
@@ -99,20 +99,34 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
|
|
|
99
99
|
}
|
|
100
100
|
}, [waiting]);
|
|
101
101
|
|
|
102
|
+
const { coordSystem = 'generic' } = visualConfig;
|
|
103
|
+
const isSpatial = coordSystem === 'geographic';
|
|
104
|
+
|
|
102
105
|
return (
|
|
103
106
|
<ShadowDom>
|
|
104
107
|
<div className="relative">
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
{isSpatial && (
|
|
109
|
+
<LeafletRenderer
|
|
110
|
+
name={name}
|
|
111
|
+
data={data}
|
|
112
|
+
draggableFieldState={visualState}
|
|
113
|
+
visualConfig={visualConfig}
|
|
114
|
+
/>
|
|
115
|
+
)}
|
|
116
|
+
{isSpatial || (
|
|
117
|
+
<SpecRenderer
|
|
118
|
+
name={name}
|
|
119
|
+
loading={waiting}
|
|
120
|
+
data={viewData}
|
|
121
|
+
ref={ref}
|
|
122
|
+
themeKey={themeKey}
|
|
123
|
+
dark={dark}
|
|
124
|
+
draggableFieldState={visualState}
|
|
125
|
+
visualConfig={visualConfig}
|
|
126
|
+
locale={locale ?? 'en-US'}
|
|
127
|
+
computationFunction={computation}
|
|
128
|
+
/>
|
|
129
|
+
)}
|
|
116
130
|
</div>
|
|
117
131
|
</ShadowDom>
|
|
118
132
|
);
|
|
@@ -3,8 +3,9 @@ import { Resizable } from 're-resizable';
|
|
|
3
3
|
import React, { forwardRef, useMemo } from 'react';
|
|
4
4
|
|
|
5
5
|
import PivotTable from '../components/pivotTable';
|
|
6
|
+
import LeafletRenderer from '../components/leafletRenderer';
|
|
6
7
|
import ReactVega, { IReactVegaHandler } from '../vis/react-vega';
|
|
7
|
-
import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig, VegaGlobalConfig } from '../interfaces';
|
|
8
|
+
import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig, VegaGlobalConfig, IComputationFunction } from '../interfaces';
|
|
8
9
|
import LoadingLayer from '../components/loadingLayer';
|
|
9
10
|
import { useCurrentMediaTheme } from '../utils/media';
|
|
10
11
|
import { builtInThemes } from '../vis/theme';
|
|
@@ -20,17 +21,18 @@ interface SpecRendererProps {
|
|
|
20
21
|
onGeomClick?: ((values: any, e: any) => void) | undefined;
|
|
21
22
|
onChartResize?: ((width: number, height: number) => void) | undefined;
|
|
22
23
|
locale?: string;
|
|
24
|
+
computationFunction: IComputationFunction;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Sans-store renderer of GraphicWalker.
|
|
26
28
|
* This is a pure component, which means it will not depend on any global state.
|
|
27
29
|
*/
|
|
28
30
|
const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
29
|
-
{ name, themeKey, dark, data, loading, draggableFieldState, visualConfig, onGeomClick, onChartResize, locale },
|
|
31
|
+
{ name, themeKey, dark, data, loading, draggableFieldState, visualConfig, onGeomClick, onChartResize, locale, computationFunction },
|
|
30
32
|
ref
|
|
31
33
|
) {
|
|
32
34
|
// const { draggableFieldState, visualConfig } = vizStore;
|
|
33
|
-
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, format: _format, background, zeroScale, resolve } = visualConfig;
|
|
35
|
+
const { geoms, coordSystem = 'generic', interactiveScale, defaultAggregated, stack, showActions, size, format: _format, background, zeroScale, resolve } = visualConfig;
|
|
34
36
|
|
|
35
37
|
const rows = draggableFieldState.rows;
|
|
36
38
|
const columns = draggableFieldState.columns;
|
|
@@ -108,10 +110,13 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
108
110
|
loading={loading}
|
|
109
111
|
themeKey={themeKey}
|
|
110
112
|
dark={dark}
|
|
113
|
+
computationFunction={computationFunction}
|
|
111
114
|
/>
|
|
112
115
|
);
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
const isSpatial = coordSystem === 'geographic';
|
|
119
|
+
|
|
115
120
|
return (
|
|
116
121
|
<Resizable
|
|
117
122
|
className={enableResize ? 'border-blue-400 border-2 overflow-hidden' : ''}
|
|
@@ -139,33 +144,44 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
139
144
|
}}
|
|
140
145
|
>
|
|
141
146
|
{loading && <LoadingLayer />}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
147
|
+
{isSpatial && (
|
|
148
|
+
<LeafletRenderer
|
|
149
|
+
name={name}
|
|
150
|
+
data={data}
|
|
151
|
+
draggableFieldState={draggableFieldState}
|
|
152
|
+
visualConfig={visualConfig}
|
|
153
|
+
vegaConfig={vegaConfig}
|
|
154
|
+
/>
|
|
155
|
+
)}
|
|
156
|
+
{isSpatial || (
|
|
157
|
+
<ReactVega
|
|
158
|
+
name={name}
|
|
159
|
+
vegaConfig={vegaConfig}
|
|
160
|
+
// format={format}
|
|
161
|
+
layoutMode={size.mode}
|
|
162
|
+
interactiveScale={interactiveScale}
|
|
163
|
+
geomType={geoms[0]}
|
|
164
|
+
defaultAggregate={defaultAggregated}
|
|
165
|
+
stack={stack}
|
|
166
|
+
dataSource={data}
|
|
167
|
+
rows={rows}
|
|
168
|
+
columns={columns}
|
|
169
|
+
color={color[0]}
|
|
170
|
+
theta={theta[0]}
|
|
171
|
+
radius={radius[0]}
|
|
172
|
+
shape={shape[0]}
|
|
173
|
+
opacity={opacity[0]}
|
|
174
|
+
size={sizeChannel[0]}
|
|
175
|
+
details={details}
|
|
176
|
+
text={text[0]}
|
|
177
|
+
showActions={showActions}
|
|
178
|
+
width={size.width - 12 * 4}
|
|
179
|
+
height={size.height - 12 * 4}
|
|
180
|
+
ref={ref}
|
|
181
|
+
onGeomClick={onGeomClick}
|
|
182
|
+
locale={locale}
|
|
183
|
+
/>
|
|
184
|
+
)}
|
|
169
185
|
</Resizable>
|
|
170
186
|
);
|
|
171
187
|
});
|
package/src/services.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { toJS } from 'mobx';
|
|
2
|
-
import { IRow, IMutField, Specification, IFilterFiledSimple, IExpression } from './interfaces';
|
|
2
|
+
import { IRow, IMutField, IViewField, Specification, IFilterFiledSimple, IExpression } from './interfaces';
|
|
3
|
+
import { INestNode } from "./components/pivotTable/inteface";
|
|
3
4
|
/* eslint import/no-webpack-loader-syntax:0 */
|
|
4
5
|
// @ts-ignore
|
|
5
6
|
// eslint-disable-next-line
|
|
@@ -11,6 +12,7 @@ import { IRow, IMutField, Specification, IFilterFiledSimple, IExpression } from
|
|
|
11
12
|
import FilterWorker from './workers/filter.worker?worker&inline';
|
|
12
13
|
import TransformDataWorker from './workers/transform.worker?worker&inline';
|
|
13
14
|
import ViewQueryWorker from './workers/viewQuery.worker?worker&inline';
|
|
15
|
+
import BuildMetricTableWorker from './workers/buildMetricTable.worker?worker&inline';
|
|
14
16
|
import SortWorker from './workers/sort.worker?worker&inline';
|
|
15
17
|
|
|
16
18
|
import { IViewQuery } from './lib/viewQuery';
|
|
@@ -105,22 +107,12 @@ interface PreAnalysisParams {
|
|
|
105
107
|
// }
|
|
106
108
|
// }
|
|
107
109
|
|
|
108
|
-
let filterWorker: Worker | null = null;
|
|
109
|
-
let filterWorkerAutoTerminator: NodeJS.Timeout | null = null;
|
|
110
110
|
|
|
111
111
|
export const applyFilter = async (data: IRow[], filters: readonly IFilterFiledSimple[]): Promise<IRow[]> => {
|
|
112
112
|
if (filters.length === 0) return data;
|
|
113
|
-
|
|
114
|
-
clearTimeout(filterWorkerAutoTerminator);
|
|
115
|
-
filterWorkerAutoTerminator = null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (filterWorker === null) {
|
|
119
|
-
filterWorker = new FilterWorker();
|
|
120
|
-
}
|
|
121
|
-
|
|
113
|
+
const worker = new FilterWorker();
|
|
122
114
|
try {
|
|
123
|
-
const res: IRow[] = await workerService(
|
|
115
|
+
const res: IRow[] = await workerService(worker, {
|
|
124
116
|
dataSource: data,
|
|
125
117
|
filters: toJS(filters),
|
|
126
118
|
});
|
|
@@ -130,15 +122,7 @@ export const applyFilter = async (data: IRow[], filters: readonly IFilterFiledSi
|
|
|
130
122
|
// @ts-ignore @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
|
|
131
123
|
throw new Error('Uncaught error in FilterWorker', { cause: error });
|
|
132
124
|
} finally {
|
|
133
|
-
|
|
134
|
-
clearTimeout(filterWorkerAutoTerminator);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
filterWorkerAutoTerminator = setTimeout(() => {
|
|
138
|
-
filterWorker?.terminate();
|
|
139
|
-
filterWorker = null;
|
|
140
|
-
filterWorkerAutoTerminator = null;
|
|
141
|
-
}, 60_000); // Destroy the worker when no request is received for 60 secs
|
|
125
|
+
worker.terminate();
|
|
142
126
|
}
|
|
143
127
|
};
|
|
144
128
|
|
|
@@ -171,7 +155,32 @@ export const applyViewQuery = async (data: IRow[], query: IViewQuery): Promise<I
|
|
|
171
155
|
} finally {
|
|
172
156
|
worker.terminate();
|
|
173
157
|
}
|
|
174
|
-
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const buildPivotTableService = async (dimsInRow: IViewField[],
|
|
161
|
+
dimsInColumn: IViewField[],
|
|
162
|
+
allData: IRow[],
|
|
163
|
+
aggData: IRow[],
|
|
164
|
+
collapsedKeyList: string[],
|
|
165
|
+
showTableSummary: boolean
|
|
166
|
+
): Promise<{lt: INestNode, tt: INestNode, metric: (IRow | null)[][]}> => {
|
|
167
|
+
const worker = new BuildMetricTableWorker();
|
|
168
|
+
try {
|
|
169
|
+
const res: {lt: INestNode, tt: INestNode, metric: (IRow | null)[][]} = await workerService(worker, {
|
|
170
|
+
dimsInRow,
|
|
171
|
+
dimsInColumn,
|
|
172
|
+
allData,
|
|
173
|
+
aggData,
|
|
174
|
+
collapsedKeyList,
|
|
175
|
+
showTableSummary
|
|
176
|
+
});
|
|
177
|
+
return res;
|
|
178
|
+
} catch (error) {
|
|
179
|
+
throw new Error('Uncaught error in TableBuilderDataWorker', { cause: error });
|
|
180
|
+
} finally {
|
|
181
|
+
worker.terminate();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
175
184
|
|
|
176
185
|
export const applySort = async (
|
|
177
186
|
data: IRow[],
|
package/src/shadow-dom.tsx
CHANGED
|
@@ -36,6 +36,13 @@ export const ShadowDom: React.FC<IShadowDomProps> = function ShadowDom ({ onMoun
|
|
|
36
36
|
<root.div {...attrs} mode="open" ref={rootRef}>
|
|
37
37
|
<style>{tailwindStyle}</style>
|
|
38
38
|
<style>{style}</style>
|
|
39
|
+
{/* Leaflet CSS file */}
|
|
40
|
+
<link
|
|
41
|
+
rel="stylesheet"
|
|
42
|
+
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|
43
|
+
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
|
44
|
+
crossOrigin=""
|
|
45
|
+
/>
|
|
39
46
|
{shadowRoot && (
|
|
40
47
|
<StyleSheetManager target={shadowRoot}>
|
|
41
48
|
<ShadowDomContext.Provider value={{ root: shadowRoot }}>
|