lowcoder-comps 0.0.29 → 0.0.31
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/README.md +120 -0
- package/index.html +26 -0
- package/index.tsx +21 -0
- package/jest.config.js +6 -0
- package/package.json +3 -4
- package/src/__test__/allComp.test.tsx +61 -0
- package/src/app-env.d.ts +3 -0
- package/src/comps/calendarComp/calendarComp.tsx +633 -0
- package/src/comps/calendarComp/calendarConstants.tsx +1048 -0
- package/src/comps/calendarComp/errorBoundary.tsx +30 -0
- package/src/comps/chartComp/chartComp.tsx +442 -0
- package/src/comps/chartComp/chartConfigs/barChartConfig.tsx +51 -0
- package/src/comps/chartComp/chartConfigs/cartesianAxisConfig.tsx +307 -0
- package/src/comps/chartComp/chartConfigs/chartUrls.tsx +9 -0
- package/src/comps/chartComp/chartConfigs/legendConfig.tsx +55 -0
- package/src/comps/chartComp/chartConfigs/lineChartConfig.tsx +96 -0
- package/src/comps/chartComp/chartConfigs/pieChartConfig.tsx +83 -0
- package/src/comps/chartComp/chartConfigs/scatterChartConfig.tsx +62 -0
- package/src/comps/chartComp/chartConstants.tsx +299 -0
- package/src/comps/chartComp/chartPropertyView.tsx +235 -0
- package/src/comps/chartComp/chartUtils.ts +291 -0
- package/src/comps/chartComp/reactEcharts/core.tsx +194 -0
- package/src/comps/chartComp/reactEcharts/index.ts +21 -0
- package/src/comps/chartComp/reactEcharts/types.ts +76 -0
- package/src/comps/chartComp/seriesComp.tsx +119 -0
- package/src/comps/imageEditorComp/imageEditorClass.tsx +52 -0
- package/src/comps/imageEditorComp/imageEditorConstants.tsx +109 -0
- package/src/comps/imageEditorComp/index.tsx +184 -0
- package/src/comps/mermaidComp/index.tsx +44 -0
- package/src/comps/mermaidComp/mermaid.tsx +29 -0
- package/src/global.ts +1 -0
- package/src/i18n/comps/index.tsx +29 -0
- package/src/i18n/comps/locales/en.ts +163 -0
- package/src/i18n/comps/locales/enObj.tsx +198 -0
- package/src/i18n/comps/locales/index.ts +7 -0
- package/src/i18n/comps/locales/types.tsx +10 -0
- package/src/i18n/comps/locales/zh.ts +156 -0
- package/src/i18n/comps/locales/zhObj.tsx +4 -0
- package/src/index.ts +11 -0
- package/tsconfig.json +22 -0
- package/vite.config.js +10 -0
- package/2085da13.js +0 -960
- package/250691b5.js +0 -5
- package/256b619e.js +0 -92
- package/274f545c.js +0 -881
- package/289305a1.js +0 -208
- package/2eae45c2.js +0 -34
- package/2ff2c7a6.js +0 -6
- package/2ff7471d.js +0 -9
- package/335b22a2.js +0 -220
- package/38c826fe.js +0 -1127
- package/44011c1d.js +0 -818
- package/4fc06812.js +0 -64
- package/56a787cf.js +0 -915
- package/590941ff.js +0 -86
- package/6341867f.js +0 -804
- package/657fd065.js +0 -8
- package/78a5e50d.js +0 -1579
- package/820c3641.js +0 -25
- package/88b4e75a.js +0 -2967
- package/8d999722.js +0 -1102
- package/92e85b65.js +0 -65
- package/989caea2.js +0 -505
- package/99b984d1.js +0 -237
- package/9e5f02d6.js +0 -19104
- package/a40faea7.js +0 -11624
- package/abac9104.js +0 -1536
- package/af2f19b3.js +0 -819
- package/af5ee3de.js +0 -268
- package/b24707c2.js +0 -48428
- package/b68f8b69.js +0 -1276
- package/ba68ba65.js +0 -391
- package/bafb8599.js +0 -319
- package/bba60c35.js +0 -2501
- package/bd7c2a8e.js +0 -1089
- package/c71dadea.js +0 -455
- package/d05c1762.js +0 -933
- package/d073ab24.js +0 -134353
- package/d838cd10.js +0 -769
- package/dc36a6eb.js +0 -796
- package/ed143450.js +0 -1284
- package/ee8ec8f2.js +0 -2284
- package/f6755210.js +0 -1269
- package/f9637058.js +0 -16
- package/fba4c8e4.js +0 -447
- package/index.js +0 -5
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CharOptionCompType,
|
|
3
|
+
ChartCompPropsType,
|
|
4
|
+
ChartSize,
|
|
5
|
+
noDataAxisConfig,
|
|
6
|
+
noDataPieChartConfig,
|
|
7
|
+
} from "comps/chartComp/chartConstants";
|
|
8
|
+
import { getPieRadiusAndCenter } from "comps/chartComp/chartConfigs/pieChartConfig";
|
|
9
|
+
import { EChartsOptionWithMap } from "./reactEcharts/types";
|
|
10
|
+
import _ from "lodash";
|
|
11
|
+
import { chartColorPalette, isNumeric, JSONObject, loadScript } from "lowcoder-sdk";
|
|
12
|
+
import { calcXYConfig } from "comps/chartComp/chartConfigs/cartesianAxisConfig";
|
|
13
|
+
import Big from "big.js";
|
|
14
|
+
import { googleMapsApiUrl } from "./chartConfigs/chartUrls";
|
|
15
|
+
|
|
16
|
+
export function transformData(
|
|
17
|
+
originData: JSONObject[],
|
|
18
|
+
xAxis: string,
|
|
19
|
+
seriesColumnNames: string[]
|
|
20
|
+
) {
|
|
21
|
+
// aggregate data by x-axis
|
|
22
|
+
const transformedData: JSONObject[] = [];
|
|
23
|
+
originData.reduce((prev, cur) => {
|
|
24
|
+
if (cur === null || cur === undefined) {
|
|
25
|
+
return prev;
|
|
26
|
+
}
|
|
27
|
+
const groupValue = cur[xAxis] as string;
|
|
28
|
+
if (!prev[groupValue]) {
|
|
29
|
+
// init as 0
|
|
30
|
+
const initValue: any = {};
|
|
31
|
+
seriesColumnNames.forEach((name) => {
|
|
32
|
+
initValue[name] = 0;
|
|
33
|
+
});
|
|
34
|
+
prev[groupValue] = initValue;
|
|
35
|
+
transformedData.push(prev[groupValue]);
|
|
36
|
+
}
|
|
37
|
+
// remain the x-axis data
|
|
38
|
+
prev[groupValue][xAxis] = groupValue;
|
|
39
|
+
seriesColumnNames.forEach((key) => {
|
|
40
|
+
if (key === xAxis) {
|
|
41
|
+
return;
|
|
42
|
+
} else if (isNumeric(cur[key])) {
|
|
43
|
+
const bigNum = Big(cur[key]);
|
|
44
|
+
prev[groupValue][key] = bigNum.add(prev[groupValue][key]).toNumber();
|
|
45
|
+
} else {
|
|
46
|
+
prev[groupValue][key] += 1;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return prev;
|
|
50
|
+
}, {} as any);
|
|
51
|
+
return transformedData;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const notAxisChartSet: Set<CharOptionCompType> = new Set(["pie"] as const);
|
|
55
|
+
export const echartsConfigOmitChildren = [
|
|
56
|
+
"hidden",
|
|
57
|
+
"selectedPoints",
|
|
58
|
+
"onUIEvent",
|
|
59
|
+
"mapInstance"
|
|
60
|
+
] as const;
|
|
61
|
+
type EchartsConfigProps = Omit<ChartCompPropsType, typeof echartsConfigOmitChildren[number]>;
|
|
62
|
+
|
|
63
|
+
export function isAxisChart(type: CharOptionCompType) {
|
|
64
|
+
return !notAxisChartSet.has(type);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getSeriesConfig(props: EchartsConfigProps) {
|
|
68
|
+
const visibleSeries = props.series.filter((s) => !s.getView().hide);
|
|
69
|
+
const seriesLength = visibleSeries.length;
|
|
70
|
+
return visibleSeries.map((s, index) => {
|
|
71
|
+
if (isAxisChart(props.chartConfig.type)) {
|
|
72
|
+
let encodeX: string, encodeY: string;
|
|
73
|
+
const horizontalX = props.xAxisDirection === "horizontal";
|
|
74
|
+
let itemStyle = props.chartConfig.itemStyle;
|
|
75
|
+
// FIXME: need refactor... chartConfig returns a function with paramters
|
|
76
|
+
if (props.chartConfig.type === "bar") {
|
|
77
|
+
// barChart's border radius, depend on x-axis direction and stack state
|
|
78
|
+
const borderRadius = horizontalX ? [2, 2, 0, 0] : [0, 2, 2, 0];
|
|
79
|
+
if (props.chartConfig.stack && index === visibleSeries.length - 1) {
|
|
80
|
+
itemStyle = { ...itemStyle, borderRadius: borderRadius };
|
|
81
|
+
} else if (!props.chartConfig.stack) {
|
|
82
|
+
itemStyle = { ...itemStyle, borderRadius: borderRadius };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (horizontalX) {
|
|
86
|
+
encodeX = props.xAxisKey;
|
|
87
|
+
encodeY = s.getView().columnName;
|
|
88
|
+
} else {
|
|
89
|
+
encodeX = s.getView().columnName;
|
|
90
|
+
encodeY = props.xAxisKey;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
name: s.getView().seriesName,
|
|
94
|
+
selectedMode: "single",
|
|
95
|
+
select: {
|
|
96
|
+
itemStyle: {
|
|
97
|
+
borderColor: "#000",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
encode: {
|
|
101
|
+
x: encodeX,
|
|
102
|
+
y: encodeY,
|
|
103
|
+
},
|
|
104
|
+
// each type of chart's config
|
|
105
|
+
...props.chartConfig,
|
|
106
|
+
itemStyle: itemStyle,
|
|
107
|
+
label: {
|
|
108
|
+
...props.chartConfig.label,
|
|
109
|
+
...(!horizontalX && { position: "outside" }),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
} else {
|
|
113
|
+
// pie
|
|
114
|
+
const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
|
|
115
|
+
return {
|
|
116
|
+
...props.chartConfig,
|
|
117
|
+
radius: radiusAndCenter.radius,
|
|
118
|
+
center: radiusAndCenter.center,
|
|
119
|
+
name: s.getView().seriesName,
|
|
120
|
+
selectedMode: "single",
|
|
121
|
+
encode: {
|
|
122
|
+
itemName: props.xAxisKey,
|
|
123
|
+
value: s.getView().columnName,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// https://echarts.apache.org/en/option.html
|
|
131
|
+
export function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSize): EChartsOptionWithMap {
|
|
132
|
+
if (props.mode === "json") {
|
|
133
|
+
return props.echartsOption ? props.echartsOption : {};
|
|
134
|
+
}
|
|
135
|
+
if(props.mode === "map") {
|
|
136
|
+
const {
|
|
137
|
+
mapZoomLevel,
|
|
138
|
+
mapCenterLat,
|
|
139
|
+
mapCenterLng,
|
|
140
|
+
mapOptions,
|
|
141
|
+
showCharts,
|
|
142
|
+
} = props;
|
|
143
|
+
|
|
144
|
+
const echartsOption = mapOptions && showCharts ? mapOptions : {};
|
|
145
|
+
return {
|
|
146
|
+
gmap: {
|
|
147
|
+
center: [mapCenterLng, mapCenterLat],
|
|
148
|
+
zoom: mapZoomLevel,
|
|
149
|
+
renderOnMoving: true,
|
|
150
|
+
echartsLayerZIndex: showCharts ? 2019 : 0,
|
|
151
|
+
roam: true
|
|
152
|
+
},
|
|
153
|
+
...echartsOption,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// axisChart
|
|
157
|
+
const axisChart = isAxisChart(props.chartConfig.type);
|
|
158
|
+
const gridPos = {
|
|
159
|
+
left: 20,
|
|
160
|
+
right: props.legendConfig.left === "right" ? "10%" : 20,
|
|
161
|
+
top: 50,
|
|
162
|
+
bottom: 35,
|
|
163
|
+
};
|
|
164
|
+
let config: EChartsOptionWithMap = {
|
|
165
|
+
title: { text: props.title, left: "center" },
|
|
166
|
+
tooltip: {
|
|
167
|
+
confine: true,
|
|
168
|
+
trigger: axisChart ? "axis" : "item",
|
|
169
|
+
},
|
|
170
|
+
legend: props.legendConfig,
|
|
171
|
+
grid: {
|
|
172
|
+
...gridPos,
|
|
173
|
+
containLabel: true,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
if (props.data.length <= 0) {
|
|
177
|
+
// no data
|
|
178
|
+
return {
|
|
179
|
+
...config,
|
|
180
|
+
...(axisChart ? noDataAxisConfig : noDataPieChartConfig),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const yAxisConfig = props.yConfig();
|
|
184
|
+
const seriesColumnNames = props.series
|
|
185
|
+
.filter((s) => !s.getView().hide)
|
|
186
|
+
.map((s) => s.getView().columnName);
|
|
187
|
+
// y-axis is category and time, data doesn't need to aggregate
|
|
188
|
+
const transformedData =
|
|
189
|
+
yAxisConfig.type === "category" || yAxisConfig.type === "time"
|
|
190
|
+
? props.data
|
|
191
|
+
: transformData(props.data, props.xAxisKey, seriesColumnNames);
|
|
192
|
+
config = {
|
|
193
|
+
...config,
|
|
194
|
+
dataset: [
|
|
195
|
+
{
|
|
196
|
+
source: transformedData,
|
|
197
|
+
sourceHeader: false,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
series: getSeriesConfig(props),
|
|
201
|
+
};
|
|
202
|
+
if (axisChart) {
|
|
203
|
+
// pure chart's size except the margin around
|
|
204
|
+
let chartRealSize;
|
|
205
|
+
if (chartSize) {
|
|
206
|
+
const rightSize =
|
|
207
|
+
typeof gridPos.right === "number"
|
|
208
|
+
? gridPos.right
|
|
209
|
+
: (chartSize.w * parseFloat(gridPos.right)) / 100.0;
|
|
210
|
+
chartRealSize = {
|
|
211
|
+
// actually it's self-adaptive with the x-axis label on the left, not that accurate but work
|
|
212
|
+
w: chartSize.w - gridPos.left - rightSize,
|
|
213
|
+
// also self-adaptive on the bottom
|
|
214
|
+
h: chartSize.h - gridPos.top - gridPos.bottom,
|
|
215
|
+
right: rightSize,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const finalXyConfig = calcXYConfig(
|
|
219
|
+
props.xConfig,
|
|
220
|
+
yAxisConfig,
|
|
221
|
+
props.xAxisDirection,
|
|
222
|
+
transformedData.map((d) => d[props.xAxisKey]),
|
|
223
|
+
chartRealSize
|
|
224
|
+
);
|
|
225
|
+
config = {
|
|
226
|
+
...config,
|
|
227
|
+
// @ts-ignore
|
|
228
|
+
xAxis: finalXyConfig.xConfig,
|
|
229
|
+
// @ts-ignore
|
|
230
|
+
yAxis: finalXyConfig.yConfig,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// log.log("Echarts transformedData and config", transformedData, config);
|
|
234
|
+
return config;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function getSelectedPoints(param: any, option: any) {
|
|
238
|
+
const series = option.series;
|
|
239
|
+
const dataSource = _.isArray(option.dataset) && option.dataset[0]?.source;
|
|
240
|
+
if (series && dataSource) {
|
|
241
|
+
return param.selected.flatMap((selectInfo: any) => {
|
|
242
|
+
const seriesInfo = series[selectInfo.seriesIndex];
|
|
243
|
+
if (!seriesInfo || !seriesInfo.encode) {
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
return selectInfo.dataIndex.map((index: any) => {
|
|
247
|
+
const commonResult = {
|
|
248
|
+
seriesName: seriesInfo.name,
|
|
249
|
+
};
|
|
250
|
+
if (seriesInfo.encode.itemName && seriesInfo.encode.value) {
|
|
251
|
+
return {
|
|
252
|
+
...commonResult,
|
|
253
|
+
itemName: dataSource[index][seriesInfo.encode.itemName],
|
|
254
|
+
value: dataSource[index][seriesInfo.encode.value],
|
|
255
|
+
};
|
|
256
|
+
} else {
|
|
257
|
+
return {
|
|
258
|
+
...commonResult,
|
|
259
|
+
x: dataSource[index][seriesInfo.encode.x],
|
|
260
|
+
y: dataSource[index][seriesInfo.encode.y],
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function loadGoogleMapsScript(apiKey: string) {
|
|
270
|
+
const mapsUrl = `${googleMapsApiUrl}?key=${apiKey}`;
|
|
271
|
+
const scripts = document.getElementsByTagName('script');
|
|
272
|
+
// is script already loaded
|
|
273
|
+
let scriptIndex = _.findIndex(scripts, (script) => script.src.endsWith(mapsUrl));
|
|
274
|
+
if(scriptIndex > -1) {
|
|
275
|
+
return scripts[scriptIndex];
|
|
276
|
+
}
|
|
277
|
+
// is script loaded with diff api_key, remove the script and load again
|
|
278
|
+
scriptIndex = _.findIndex(scripts, (script) => script.src.startsWith(googleMapsApiUrl));
|
|
279
|
+
if(scriptIndex > -1) {
|
|
280
|
+
scripts[scriptIndex].remove();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const script = document.createElement("script");
|
|
284
|
+
script.type = "text/javascript";
|
|
285
|
+
script.src = mapsUrl;
|
|
286
|
+
script.async = true;
|
|
287
|
+
script.defer = true;
|
|
288
|
+
window.document.body.appendChild(script);
|
|
289
|
+
|
|
290
|
+
return script;
|
|
291
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { ECharts } from "echarts";
|
|
2
|
+
import { PureComponent } from "react";
|
|
3
|
+
import isEqual from "fast-deep-equal";
|
|
4
|
+
import { EChartsReactProps, EChartsInstance } from "./types";
|
|
5
|
+
import _ from "lodash";
|
|
6
|
+
import log from "loglevel";
|
|
7
|
+
|
|
8
|
+
function isString(v: any): boolean {
|
|
9
|
+
return typeof v === "string";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isFunction(v: any): boolean {
|
|
13
|
+
return typeof v === "function";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* core component for echarts binding
|
|
18
|
+
*/
|
|
19
|
+
export default class EChartsReactCore extends PureComponent<EChartsReactProps> {
|
|
20
|
+
/**
|
|
21
|
+
* echarts render container
|
|
22
|
+
*/
|
|
23
|
+
public ele: HTMLElement | null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* echarts library entry
|
|
27
|
+
*/
|
|
28
|
+
protected echarts: any;
|
|
29
|
+
|
|
30
|
+
constructor(props: EChartsReactProps) {
|
|
31
|
+
super(props);
|
|
32
|
+
|
|
33
|
+
this.echarts = props.echarts;
|
|
34
|
+
this.ele = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
componentDidMount() {
|
|
38
|
+
this.renderNewEcharts();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// update
|
|
42
|
+
componentDidUpdate(prevProps: EChartsReactProps) {
|
|
43
|
+
/**
|
|
44
|
+
* if shouldSetOption return false, then return, not update echarts options
|
|
45
|
+
* default is true
|
|
46
|
+
*/
|
|
47
|
+
const { shouldSetOption } = this.props;
|
|
48
|
+
if (shouldSetOption && isFunction(shouldSetOption) && !shouldSetOption(prevProps, this.props)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* the props below need to dispose before re-render
|
|
54
|
+
* 1. when switching theme
|
|
55
|
+
* 2. when modifying opts
|
|
56
|
+
* 3. when modifying onEvents, thus the binded event issue #151 can be cancel
|
|
57
|
+
*/
|
|
58
|
+
if (
|
|
59
|
+
!isEqual(prevProps.theme, this.props.theme) ||
|
|
60
|
+
!isEqual(prevProps.opts, this.props.opts) ||
|
|
61
|
+
!isEqual(prevProps.onEvents, this.props.onEvents) ||
|
|
62
|
+
!isEqual(prevProps.mode, this.props.mode)
|
|
63
|
+
// this.props.option.gmap
|
|
64
|
+
) {
|
|
65
|
+
this.dispose();
|
|
66
|
+
|
|
67
|
+
this.renderNewEcharts(); // re-render
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if(this.props.mode === "json") {
|
|
72
|
+
this.updateEChartsOption();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// when these props are not isEqual, update echarts
|
|
77
|
+
const pickKeys = ["option", "notMerge", "lazyUpdate", "showLoading", "loadingOption"];
|
|
78
|
+
if (!isEqual(_.pick(this.props, pickKeys), _.pick(prevProps, pickKeys))) {
|
|
79
|
+
this.updateEChartsOption();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
componentWillUnmount() {
|
|
84
|
+
this.dispose();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* return the echart object
|
|
89
|
+
* 1. if exist, return the existed instance
|
|
90
|
+
* 2. or new one instance
|
|
91
|
+
*/
|
|
92
|
+
public getEchartsInstance(): ECharts {
|
|
93
|
+
return (
|
|
94
|
+
this.echarts.getInstanceByDom(this.ele) ||
|
|
95
|
+
this.echarts.init(this.ele, this.props.theme, this.props.opts)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* dispose echarts and clear size-sensor
|
|
101
|
+
*/
|
|
102
|
+
private dispose() {
|
|
103
|
+
if (this.ele) {
|
|
104
|
+
// dispose echarts instance
|
|
105
|
+
this.echarts.dispose(this.ele);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* render a new echarts instance
|
|
111
|
+
*/
|
|
112
|
+
private renderNewEcharts() {
|
|
113
|
+
const { onEvents, onChartReady } = this.props;
|
|
114
|
+
|
|
115
|
+
// 1. new echarts instance
|
|
116
|
+
const echartsInstance = this.updateEChartsOption();
|
|
117
|
+
|
|
118
|
+
// 2. bind events
|
|
119
|
+
this.bindEvents(echartsInstance, onEvents || {});
|
|
120
|
+
|
|
121
|
+
// 3. on chart ready
|
|
122
|
+
if (onChartReady && isFunction(onChartReady)) onChartReady(echartsInstance);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// bind the events
|
|
126
|
+
private bindEvents(instance: any, events: EChartsReactProps["onEvents"]) {
|
|
127
|
+
function _bindEvent(eventName: string, func: Function) {
|
|
128
|
+
// ignore the event config which not satisfy
|
|
129
|
+
if (isString(eventName) && isFunction(func)) {
|
|
130
|
+
// binding event
|
|
131
|
+
instance.on(eventName, (param: any) => {
|
|
132
|
+
func(param, instance);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// loop and bind
|
|
138
|
+
for (const eventName in events) {
|
|
139
|
+
if (Object.prototype.hasOwnProperty.call(events, eventName)) {
|
|
140
|
+
_bindEvent(eventName, events[eventName]);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* render the echarts
|
|
147
|
+
*/
|
|
148
|
+
private updateEChartsOption(): EChartsInstance {
|
|
149
|
+
const {
|
|
150
|
+
option,
|
|
151
|
+
notMerge = false,
|
|
152
|
+
lazyUpdate = false,
|
|
153
|
+
showLoading,
|
|
154
|
+
loadingOption = null,
|
|
155
|
+
} = this.props;
|
|
156
|
+
// 1. get or initial the echarts object
|
|
157
|
+
const echartInstance = this.getEchartsInstance();
|
|
158
|
+
// 2. set the echarts option
|
|
159
|
+
try {
|
|
160
|
+
// set option catch exception
|
|
161
|
+
echartInstance.setOption(option, {
|
|
162
|
+
notMerge: notMerge,
|
|
163
|
+
lazyUpdate: lazyUpdate,
|
|
164
|
+
silent: true,
|
|
165
|
+
});
|
|
166
|
+
} catch (e) {
|
|
167
|
+
// FIXME: if don't dispose, setOption again will call cause bugs
|
|
168
|
+
// https://github.com/apache/echarts/issues/16608
|
|
169
|
+
this.dispose();
|
|
170
|
+
log.warn("invalid echarts option:", e);
|
|
171
|
+
}
|
|
172
|
+
// 3. set loading mask
|
|
173
|
+
if (showLoading) echartInstance.showLoading(loadingOption);
|
|
174
|
+
else echartInstance.hideLoading();
|
|
175
|
+
|
|
176
|
+
return echartInstance;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
render(): JSX.Element {
|
|
180
|
+
const { style, className = "" } = this.props;
|
|
181
|
+
// default height = 300
|
|
182
|
+
const newStyle = { height: 300, ...style };
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<div
|
|
186
|
+
ref={(e: HTMLElement | null) => {
|
|
187
|
+
this.ele = e;
|
|
188
|
+
}}
|
|
189
|
+
style={newStyle}
|
|
190
|
+
className={`echarts-for-react ${className}`}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as echarts from "echarts";
|
|
2
|
+
import "echarts-wordcloud";
|
|
3
|
+
import { EChartsReactProps, EChartsInstance, EChartsOptionWithMap } from "./types";
|
|
4
|
+
import EChartsReactCore from "./core";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* reference: https://github.com/hustcc/echarts-for-react
|
|
8
|
+
* add exception-catch for setOption
|
|
9
|
+
* if query isn't successfully loaded, chart will fail to load and can't reload
|
|
10
|
+
*/
|
|
11
|
+
export type { EChartsReactProps, EChartsOptionWithMap, EChartsInstance };
|
|
12
|
+
|
|
13
|
+
// export the Component the echarts Object.
|
|
14
|
+
export default class EChartsReact extends EChartsReactCore {
|
|
15
|
+
constructor(props: EChartsReactProps) {
|
|
16
|
+
super(props);
|
|
17
|
+
|
|
18
|
+
// initialize as echarts package
|
|
19
|
+
this.echarts = echarts;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { CSSProperties } from "react";
|
|
2
|
+
import { EChartsOption } from "echarts";
|
|
3
|
+
import { GoogleMapComponentOption } from "echarts-extension-gmap";
|
|
4
|
+
|
|
5
|
+
export type EChartsOptionWithMap = EChartsOption & GoogleMapComponentOption<any>;
|
|
6
|
+
|
|
7
|
+
export type EChartsInstance = any;
|
|
8
|
+
|
|
9
|
+
export type Opts = {
|
|
10
|
+
readonly devicePixelRatio?: number;
|
|
11
|
+
readonly renderer?: "canvas" | "svg";
|
|
12
|
+
readonly width?: number | null | undefined | "auto";
|
|
13
|
+
readonly height?: number | null | undefined | "auto";
|
|
14
|
+
readonly locale?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type EChartsReactProps = {
|
|
18
|
+
/**
|
|
19
|
+
* echarts library entry, use it for import necessary.
|
|
20
|
+
*/
|
|
21
|
+
readonly echarts?: any;
|
|
22
|
+
/**
|
|
23
|
+
* `className` for container
|
|
24
|
+
*/
|
|
25
|
+
readonly className?: string;
|
|
26
|
+
/**
|
|
27
|
+
* `style` for container
|
|
28
|
+
*/
|
|
29
|
+
readonly style?: CSSProperties;
|
|
30
|
+
/**
|
|
31
|
+
* echarts option
|
|
32
|
+
*/
|
|
33
|
+
readonly option: EChartsOptionWithMap;
|
|
34
|
+
/**
|
|
35
|
+
* echarts theme config, can be:
|
|
36
|
+
* 1. theme name string
|
|
37
|
+
* 2. theme object
|
|
38
|
+
*/
|
|
39
|
+
readonly theme?: string | Record<string, any>;
|
|
40
|
+
/**
|
|
41
|
+
* notMerge config for echarts, default is `false`
|
|
42
|
+
*/
|
|
43
|
+
readonly notMerge?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* lazyUpdate config for echarts, default is `false`
|
|
46
|
+
*/
|
|
47
|
+
readonly lazyUpdate?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* showLoading config for echarts, default is `false`
|
|
50
|
+
*/
|
|
51
|
+
readonly showLoading?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* loadingOption config for echarts, default is `null`
|
|
54
|
+
*/
|
|
55
|
+
readonly loadingOption?: any;
|
|
56
|
+
/**
|
|
57
|
+
* echarts opts config, default is `{}`
|
|
58
|
+
*/
|
|
59
|
+
readonly opts?: Opts;
|
|
60
|
+
/**
|
|
61
|
+
* when after chart reander, do the callback widht echarts instance
|
|
62
|
+
*/
|
|
63
|
+
readonly onChartReady?: (instance: EChartsInstance) => void;
|
|
64
|
+
/**
|
|
65
|
+
* bind events, default is `{}`
|
|
66
|
+
*/
|
|
67
|
+
readonly onEvents?: Record<string, Function>;
|
|
68
|
+
/**
|
|
69
|
+
* should update echarts options
|
|
70
|
+
*/
|
|
71
|
+
readonly shouldSetOption?: (prevProps: EChartsReactProps, props: EChartsReactProps) => boolean;
|
|
72
|
+
/**
|
|
73
|
+
* echarts mode: ui | json | map
|
|
74
|
+
*/
|
|
75
|
+
readonly mode?: 'ui' | 'json' | 'map'
|
|
76
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BoolControl,
|
|
3
|
+
StringControl,
|
|
4
|
+
list,
|
|
5
|
+
JSONObject,
|
|
6
|
+
isNumeric,
|
|
7
|
+
genRandomKey,
|
|
8
|
+
Dropdown,
|
|
9
|
+
OptionsType,
|
|
10
|
+
MultiCompBuilder,
|
|
11
|
+
valueComp,
|
|
12
|
+
} from "lowcoder-sdk";
|
|
13
|
+
import { trans } from "i18n/comps";
|
|
14
|
+
|
|
15
|
+
import { ConstructorToComp, ConstructorToDataType, ConstructorToView } from "lowcoder-core";
|
|
16
|
+
import { CompAction, CustomAction, customAction, isMyCustomAction } from "lowcoder-core";
|
|
17
|
+
|
|
18
|
+
export type SeriesCompType = ConstructorToComp<typeof SeriesComp>;
|
|
19
|
+
export type RawSeriesCompType = ConstructorToView<typeof SeriesComp>;
|
|
20
|
+
type SeriesDataType = ConstructorToDataType<typeof SeriesComp>;
|
|
21
|
+
|
|
22
|
+
type ActionDataType = {
|
|
23
|
+
type: "chartDataChanged";
|
|
24
|
+
chartData: Array<JSONObject>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function newSeries(name: string, columnName: string): SeriesDataType {
|
|
28
|
+
return {
|
|
29
|
+
seriesName: name,
|
|
30
|
+
columnName: columnName,
|
|
31
|
+
dataIndex: genRandomKey(),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const seriesChildrenMap = {
|
|
36
|
+
columnName: StringControl,
|
|
37
|
+
seriesName: StringControl,
|
|
38
|
+
hide: BoolControl,
|
|
39
|
+
// unique key, for sort
|
|
40
|
+
dataIndex: valueComp<string>(""),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const SeriesTmpComp = new MultiCompBuilder(seriesChildrenMap, (props) => {
|
|
44
|
+
return props;
|
|
45
|
+
})
|
|
46
|
+
.setPropertyViewFn(() => {
|
|
47
|
+
return <></>;
|
|
48
|
+
})
|
|
49
|
+
.build();
|
|
50
|
+
|
|
51
|
+
class SeriesComp extends SeriesTmpComp {
|
|
52
|
+
getPropertyViewWithData(columnOptions: OptionsType): React.ReactNode {
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
{this.children.seriesName.propertyView({
|
|
56
|
+
label: trans("chart.seriesName"),
|
|
57
|
+
})}
|
|
58
|
+
<Dropdown
|
|
59
|
+
value={this.children.columnName.getView()}
|
|
60
|
+
options={columnOptions}
|
|
61
|
+
label={trans("chart.dataColumns")}
|
|
62
|
+
onChange={(value) => {
|
|
63
|
+
this.children.columnName.dispatchChangeValueAction(value);
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const SeriesListTmpComp = list(SeriesComp);
|
|
72
|
+
|
|
73
|
+
export class SeriesListComp extends SeriesListTmpComp {
|
|
74
|
+
override reduce(action: CompAction): this {
|
|
75
|
+
if (isMyCustomAction<ActionDataType>(action, "chartDataChanged")) {
|
|
76
|
+
// auto generate series
|
|
77
|
+
const actions = this.genExampleSeriesActions(action.value.chartData);
|
|
78
|
+
return this.reduce(this.multiAction(actions));
|
|
79
|
+
}
|
|
80
|
+
return super.reduce(action);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private genExampleSeriesActions(chartData: Array<JSONObject>) {
|
|
84
|
+
const actions: CustomAction[] = [];
|
|
85
|
+
if (!chartData || chartData.length <= 0 || !chartData[0]) {
|
|
86
|
+
return actions;
|
|
87
|
+
}
|
|
88
|
+
let delCnt = 0;
|
|
89
|
+
const existColumns = this.getView().map((s) => s.getView().columnName);
|
|
90
|
+
// delete series not in data
|
|
91
|
+
existColumns.forEach((columnName) => {
|
|
92
|
+
if (chartData[0]?.[columnName] === undefined) {
|
|
93
|
+
actions.push(this.deleteAction(0));
|
|
94
|
+
delCnt++;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
if (existColumns.length > delCnt) {
|
|
98
|
+
// don't generate example if exists
|
|
99
|
+
return actions;
|
|
100
|
+
}
|
|
101
|
+
// generate example series
|
|
102
|
+
const exampleKeys = Object.keys(chartData[0])
|
|
103
|
+
.filter((key) => {
|
|
104
|
+
return !existColumns.includes(key) && isNumeric(chartData[0][key]);
|
|
105
|
+
})
|
|
106
|
+
.slice(0, 3);
|
|
107
|
+
exampleKeys.forEach((key) => actions.push(this.pushAction(newSeries(key, key))));
|
|
108
|
+
return actions;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
dispatchDataChanged(chartData: Array<JSONObject>): void {
|
|
112
|
+
this.dispatch(
|
|
113
|
+
customAction<ActionDataType>({
|
|
114
|
+
type: "chartDataChanged",
|
|
115
|
+
chartData: chartData,
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|