lowcoder-map-component 0.1.1
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 +37 -0
- package/icons/demo-icon.svg +10 -0
- package/icons/hills.svg +17 -0
- package/index.css +27 -0
- package/index.html +35 -0
- package/index.tsx +20 -0
- package/loader.mjs +11 -0
- package/package.json +175 -0
- package/src/README.md +35 -0
- package/src/components/ChartPropertyView.tsx +961 -0
- package/src/components/SeriesComp.tsx +368 -0
- package/src/components/TabPropertyView.tsx +127 -0
- package/src/comps/Barcharts/comp.tsx +338 -0
- package/src/comps/Barcharts/constants.tsx +77 -0
- package/src/comps/Linecharts/comp.tsx +350 -0
- package/src/comps/Linecharts/constants.tsx +53 -0
- package/src/comps/Linechartsv2/comp.tsx +350 -0
- package/src/comps/Linechartsv2/constants.tsx +68 -0
- package/src/comps/Mapcharts/comp.tsx +381 -0
- package/src/comps/Mapcharts/constants.tsx +312 -0
- package/src/comps/Mapchartsv2/comp.tsx +393 -0
- package/src/comps/Mapchartsv2/constants.tsx +340 -0
- package/src/comps/MixedLineBarCharts/comp.tsx +353 -0
- package/src/comps/MixedLineBarCharts/constants.tsx +60 -0
- package/src/comps/MultiLineCharts/comp.tsx +362 -0
- package/src/comps/MultiLineCharts/constants.tsx +96 -0
- package/src/comps/PercentageCharts/comp.tsx +359 -0
- package/src/comps/PercentageCharts/constants.tsx +98 -0
- package/src/comps/Piecharts/comp.tsx +334 -0
- package/src/comps/Piecharts/constants.tsx +48 -0
- package/src/comps/Tablecharts/comp.tsx +429 -0
- package/src/comps/Tablecharts/constants.tsx +97 -0
- package/src/comps/Totalcharts/comp.tsx +463 -0
- package/src/comps/Totalcharts/constants.tsx +66 -0
- package/src/comps/TwoLineCharts/comp.tsx +350 -0
- package/src/comps/TwoLineCharts/constants.tsx +82 -0
- package/src/comps/mapComponent/comp.tsx +338 -0
- package/src/comps/mapComponent/constants.tsx +2149 -0
- package/src/comps/tab/comp.tsx +283 -0
- package/src/comps/tab/constants.tsx +79 -0
- package/src/configs/barChartConfig.tsx +153 -0
- package/src/configs/baseConfig.tsx +66 -0
- package/src/configs/candleStickChartConfig.tsx +35 -0
- package/src/configs/cartesianAxisConfig.tsx +314 -0
- package/src/configs/chartUrls.tsx +9 -0
- package/src/configs/echartConfig.tsx +260 -0
- package/src/configs/echartsLabelConfig.tsx +47 -0
- package/src/configs/echartsLegendConfig.tsx +29 -0
- package/src/configs/echartsTitleConfig.tsx +49 -0
- package/src/configs/echartsTitleVerticalConfig.tsx +50 -0
- package/src/configs/funnelChartConfig.tsx +35 -0
- package/src/configs/gaugeChartConfig.tsx +31 -0
- package/src/configs/graphChartConfig.tsx +31 -0
- package/src/configs/heatmapChartConfig.tsx +31 -0
- package/src/configs/legendConfig.tsx +55 -0
- package/src/configs/lineChartConfig.tsx +246 -0
- package/src/configs/lineChartConfigv2.tsx +246 -0
- package/src/configs/mapChartConfig.tsx +106 -0
- package/src/configs/mapChartConfigv2.tsx +106 -0
- package/src/configs/mixedChartConfig.tsx +21 -0
- package/src/configs/pieChartConfig.tsx +156 -0
- package/src/configs/radarChartConfig.tsx +31 -0
- package/src/configs/sankeyChartConfig.tsx +35 -0
- package/src/configs/scatterChartConfig.tsx +152 -0
- package/src/configs/sunburstChartConfig.tsx +31 -0
- package/src/configs/tabConfig.tsx +0 -0
- package/src/configs/tableChartConfig.tsx +81 -0
- package/src/configs/themeriverChartConfig.tsx +31 -0
- package/src/configs/totalChartConfig.tsx +670 -0
- package/src/configs/treeChartConfig.tsx +31 -0
- package/src/configs/treemapChartConfig.tsx +31 -0
- package/src/controls/AnimationsControls.tsx +3 -0
- package/src/controls/AutoHeightControl.tsx +2 -0
- package/src/controls/AxisControls.tsx +146 -0
- package/src/controls/AxisVisibilityControl.tsx +14 -0
- package/src/controls/ChartTypeControl.tsx +15 -0
- package/src/controls/ColorScaleControl.tsx +221 -0
- package/src/controls/ColumnControl.tsx +204 -0
- package/src/controls/ConfigControl.tsx +37 -0
- package/src/controls/DirectionControls.tsx +20 -0
- package/src/controls/IconControl.tsx +88 -0
- package/src/controls/LegendControl.tsx +8 -0
- package/src/controls/RowLimitControl.tsx +91 -0
- package/src/controls/StyleControls.tsx +22 -0
- package/src/controls/TimeControls.tsx +41 -0
- package/src/controls/TrendlineControl.tsx +89 -0
- package/src/controls/UIEventControl.tsx +21 -0
- package/src/controls/index.ts +16 -0
- package/src/controls/radioControl.tsx +88 -0
- package/src/exposing/index.ts +3 -0
- package/src/exposing/setPoint.ts +19 -0
- package/src/geo/vn.geo.json +369897 -0
- package/src/geo/world.geo.json +32127 -0
- package/src/i18n/comps/index.tsx +39 -0
- package/src/i18n/comps/locales/en.ts +558 -0
- package/src/i18n/comps/locales/enObj.tsx +7186 -0
- package/src/i18n/comps/locales/index.ts +7 -0
- package/src/i18n/comps/locales/pt.ts +37 -0
- package/src/i18n/comps/locales/ptObj.tsx +40 -0
- package/src/i18n/comps/locales/types.tsx +622 -0
- package/src/i18n/comps/locales/zh.ts +3 -0
- package/src/i18n/comps/locales/zhObj.tsx +4 -0
- package/src/index.ts +31 -0
- package/src/types/global.d.ts +9 -0
- package/src/types/lowcoder-sdk.d.ts +578 -0
- package/src/utils/chartStyle.util.ts +121 -0
- package/src/utils/columnExtractor.util.ts +41 -0
- package/src/utils/dataTransform.util.ts +37 -0
- package/src/utils/deepEqual.util.ts +29 -0
- package/src/utils/echarts.util.tsx +822 -0
- package/src/utils/getDataKey.util.ts +115 -0
- package/src/utils/getLinearRegression.util.ts +46 -0
- package/src/utils/googleMaps.util.ts +28 -0
- package/src/utils/isEmpty.util.ts +10 -0
- package/src/utils/move.util.ts +6 -0
- package/src/utils/selection.util.ts +73 -0
- package/src/utils/style.util.ts +315 -0
- package/src/utils/time.util.ts +221 -0
- package/src/utils/timeFormatter.util.ts +104 -0
- package/src/utils/timeProcessing.util.ts +38 -0
- package/src/utils/trendline.util.ts +342 -0
- package/tsconfig.json +25 -0
- package/vite.config.js +19 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { XAxisDirectionType } from "@/controls";
|
|
2
|
+
import { trans } from "@/i18n/comps";
|
|
3
|
+
import { ChartSize } from "@/utils/echarts.util";
|
|
4
|
+
import dayjs from "dayjs";
|
|
5
|
+
import { XAXisComponentOption, YAXisComponentOption } from "echarts";
|
|
6
|
+
import {
|
|
7
|
+
JSONValue,
|
|
8
|
+
MultiCompBuilder,
|
|
9
|
+
StringControl,
|
|
10
|
+
isNumeric,
|
|
11
|
+
withContext,
|
|
12
|
+
} from "lowcoder-sdk";
|
|
13
|
+
|
|
14
|
+
export type EchartsAxisType = "category" | "value" | "time" | "log";
|
|
15
|
+
|
|
16
|
+
export const AxisFormatterComp = withContext(
|
|
17
|
+
new MultiCompBuilder({ value: StringControl }, (props) => props.value)
|
|
18
|
+
.setPropertyViewFn((children) =>
|
|
19
|
+
children.value.propertyView({
|
|
20
|
+
label: trans("chart.yAxisDataFormat"),
|
|
21
|
+
placeholder: "{{value}}",
|
|
22
|
+
tooltip: trans("chart.yAxisDataFormatTooltip"),
|
|
23
|
+
})
|
|
24
|
+
)
|
|
25
|
+
.build(),
|
|
26
|
+
["value"] as const
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const timeFormat = [
|
|
30
|
+
"YYYY-MM-DD",
|
|
31
|
+
"DD-MM-YYYY",
|
|
32
|
+
"MM-DD-YYYY",
|
|
33
|
+
"YYYY/MM/DD",
|
|
34
|
+
"DD/MM/YYYY",
|
|
35
|
+
"MM/DD/YYYY",
|
|
36
|
+
"YYYY-MM",
|
|
37
|
+
"MM-YYYY",
|
|
38
|
+
"YYYY/MM",
|
|
39
|
+
"MM/YYYY",
|
|
40
|
+
"YYYY",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
function isDateLike(value: string) {
|
|
44
|
+
return timeFormat.some((fmt) => dayjs(value, fmt, true).isValid());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function calcXAxisType(
|
|
48
|
+
xAxisData: Array<JSONValue | undefined>
|
|
49
|
+
): EchartsAxisType {
|
|
50
|
+
if (!xAxisData || xAxisData.length <= 0) {
|
|
51
|
+
return "category";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const validData = xAxisData.filter(Boolean);
|
|
55
|
+
if (validData.length === 0) {
|
|
56
|
+
return "category";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sampleSize = Math.min(10, validData.length);
|
|
60
|
+
const samples = validData.slice(0, sampleSize);
|
|
61
|
+
|
|
62
|
+
let numericCount = 0;
|
|
63
|
+
let timeCount = 0;
|
|
64
|
+
let categoryCount = 0;
|
|
65
|
+
|
|
66
|
+
for (const sample of samples) {
|
|
67
|
+
if (isNumeric(sample)) {
|
|
68
|
+
numericCount++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof sample === "string") {
|
|
73
|
+
const trimmed = sample.trim();
|
|
74
|
+
|
|
75
|
+
if (/^\d{2}-\d{4}$/.test(trimmed)) {
|
|
76
|
+
const [month, year] = trimmed.split("-");
|
|
77
|
+
const monthNum = parseInt(month, 10);
|
|
78
|
+
const yearNum = parseInt(year, 10);
|
|
79
|
+
if (
|
|
80
|
+
monthNum >= 1 &&
|
|
81
|
+
monthNum <= 12 &&
|
|
82
|
+
yearNum > 1900 &&
|
|
83
|
+
yearNum < 2100
|
|
84
|
+
) {
|
|
85
|
+
timeCount++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (/^\d{2}-\d{2}-\d{4}$/.test(trimmed)) {
|
|
91
|
+
const [day, month, year] = trimmed.split("-");
|
|
92
|
+
const dayNum = parseInt(day, 10);
|
|
93
|
+
const monthNum = parseInt(month, 10);
|
|
94
|
+
const yearNum = parseInt(year, 10);
|
|
95
|
+
if (
|
|
96
|
+
dayNum >= 1 &&
|
|
97
|
+
dayNum <= 31 &&
|
|
98
|
+
monthNum >= 1 &&
|
|
99
|
+
monthNum <= 12 &&
|
|
100
|
+
yearNum > 1900 &&
|
|
101
|
+
yearNum < 2100
|
|
102
|
+
) {
|
|
103
|
+
timeCount++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isDateLike(trimmed)) {
|
|
109
|
+
timeCount++;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (sample instanceof Date && !isNaN(sample.getTime())) {
|
|
115
|
+
timeCount++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
categoryCount++;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const total = samples.length;
|
|
123
|
+
|
|
124
|
+
// if (timeCount / total >= 0.8) {
|
|
125
|
+
// return "time";
|
|
126
|
+
// }
|
|
127
|
+
|
|
128
|
+
if (numericCount / total >= 0.8) {
|
|
129
|
+
return "value";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return "category";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const dateInterval = {
|
|
136
|
+
year: 3600 * 24 * 1000 * 365,
|
|
137
|
+
month: 3600 * 24 * 1000 * 28,
|
|
138
|
+
day: 3600 * 24 * 1000,
|
|
139
|
+
hour: 3600 * 1000,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
function calcTimeInterval(xAxisData: Array<JSONValue | undefined>) {
|
|
143
|
+
if (!xAxisData || xAxisData.length === 0) {
|
|
144
|
+
return dateInterval.month;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const validData = xAxisData.filter(Boolean);
|
|
148
|
+
if (validData.length === 0) {
|
|
149
|
+
return dateInterval.month;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const sampleSize = Math.min(10, validData.length);
|
|
153
|
+
const samples = validData.slice(0, sampleSize);
|
|
154
|
+
|
|
155
|
+
const intervals: number[] = [];
|
|
156
|
+
|
|
157
|
+
for (const data of samples) {
|
|
158
|
+
let dataStr = "";
|
|
159
|
+
|
|
160
|
+
if (data instanceof Date && !isNaN(data.getTime())) {
|
|
161
|
+
dataStr = data.toISOString();
|
|
162
|
+
} else {
|
|
163
|
+
dataStr = String(data).trim();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Format: MM-YYYY
|
|
167
|
+
if (/^\d{2}-\d{4}$/.test(dataStr)) {
|
|
168
|
+
intervals.push(dateInterval.month);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Format: DD-MM-YYYY DD/MM/YYYY
|
|
173
|
+
if (/^\d{2}[-/]\d{2}[-/]\d{4}$/.test(dataStr)) {
|
|
174
|
+
intervals.push(dateInterval.day);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Format: YYYY-MM-DD YYYY/MM/DD
|
|
179
|
+
if (/^\d{4}[-/]\d{2}[-/]\d{2}$/.test(dataStr)) {
|
|
180
|
+
intervals.push(dateInterval.day);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Format: YYYY-MM YYYY/MM
|
|
185
|
+
if (/^\d{4}[-/]\d{2}$/.test(dataStr)) {
|
|
186
|
+
intervals.push(dateInterval.month);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Format: YYYY
|
|
191
|
+
if (/^\d{4}$/.test(dataStr)) {
|
|
192
|
+
intervals.push(dateInterval.year);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Format ISO
|
|
197
|
+
if (/\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}/.test(dataStr)) {
|
|
198
|
+
if (/\d{2}:\d{2}:\d{2}/.test(dataStr)) {
|
|
199
|
+
intervals.push(dateInterval.hour || dateInterval.day);
|
|
200
|
+
} else {
|
|
201
|
+
intervals.push(dateInterval.day);
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Format: DD/MM/YYYY HH:mm DD-MM-YYYY HH:mm
|
|
207
|
+
if (/^\d{2}[-/]\d{2}[-/]\d{4}\s+\d{2}:\d{2}/.test(dataStr)) {
|
|
208
|
+
intervals.push(dateInterval.hour || dateInterval.day);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const dataLen = dataStr.length;
|
|
213
|
+
if (dataLen === 4) {
|
|
214
|
+
intervals.push(dateInterval.year);
|
|
215
|
+
} else if (dataLen >= 6 && dataLen <= 8) {
|
|
216
|
+
intervals.push(dateInterval.month);
|
|
217
|
+
} else if (dataLen >= 10 && dataLen <= 19) {
|
|
218
|
+
intervals.push(dateInterval.day);
|
|
219
|
+
} else if (dataLen > 19) {
|
|
220
|
+
intervals.push(dateInterval.hour || dateInterval.day);
|
|
221
|
+
} else {
|
|
222
|
+
intervals.push(dateInterval.month);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return intervals.length > 0 ? Math.min(...intervals) : dateInterval.month;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let measureCanvas: HTMLCanvasElement;
|
|
230
|
+
|
|
231
|
+
// calculate x-axis text width
|
|
232
|
+
function getXAxisDataLength(xAxisData: Array<JSONValue | undefined>) {
|
|
233
|
+
const canvas =
|
|
234
|
+
measureCanvas || (measureCanvas = document.createElement("canvas"));
|
|
235
|
+
const context = canvas.getContext("2d");
|
|
236
|
+
if (!context) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
// echarts default font
|
|
240
|
+
context.font = "normal 12px sans-serif";
|
|
241
|
+
return xAxisData.map((d) =>
|
|
242
|
+
d ? context.measureText(d.toString()).width + 2 : 0
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function calcXYConfig(
|
|
247
|
+
xConfig: XAXisComponentOption,
|
|
248
|
+
yConfig: YAXisComponentOption,
|
|
249
|
+
xAxisDirection: XAxisDirectionType,
|
|
250
|
+
xAxisData: Array<JSONValue | undefined>,
|
|
251
|
+
chartSize?: ChartSize & { right: number }
|
|
252
|
+
) {
|
|
253
|
+
const resXConfig = { ...xConfig };
|
|
254
|
+
const resYConfig = { ...yConfig };
|
|
255
|
+
|
|
256
|
+
if (!resXConfig.type) {
|
|
257
|
+
// simple calculate x-axis type
|
|
258
|
+
resXConfig.type = calcXAxisType(xAxisData);
|
|
259
|
+
}
|
|
260
|
+
// x-axis label style adaptive
|
|
261
|
+
if (resXConfig.type === "category" && chartSize) {
|
|
262
|
+
const xAxisDataLenList = getXAxisDataLength(xAxisData);
|
|
263
|
+
const maxDataWidth =
|
|
264
|
+
xAxisDataLenList.length > 0 ? Math.max(...xAxisDataLenList) : 0;
|
|
265
|
+
const lastDataWidth = xAxisDataLenList[xAxisDataLenList.length - 1];
|
|
266
|
+
// grid width
|
|
267
|
+
let eachDataWidth = chartSize.w / xAxisData.length;
|
|
268
|
+
let rotate = 0;
|
|
269
|
+
let labelWidth = maxDataWidth;
|
|
270
|
+
// rotate when width is not enough
|
|
271
|
+
if (
|
|
272
|
+
maxDataWidth &&
|
|
273
|
+
eachDataWidth < maxDataWidth &&
|
|
274
|
+
xAxisDirection === "horizontal"
|
|
275
|
+
) {
|
|
276
|
+
labelWidth = Math.min(maxDataWidth, 150);
|
|
277
|
+
// vertical rotate 0.87 => sin(60) when exceeding the right boundary
|
|
278
|
+
const verticalRotate =
|
|
279
|
+
lastDataWidth &&
|
|
280
|
+
lastDataWidth * 0.87 > eachDataWidth / 2 + chartSize.right;
|
|
281
|
+
rotate = verticalRotate ? 270 : 330;
|
|
282
|
+
// to keep x-axis name under label, nameGap is related to label rotation angle
|
|
283
|
+
resXConfig.nameGap = verticalRotate
|
|
284
|
+
? labelWidth + 5
|
|
285
|
+
: labelWidth / 2 + 10;
|
|
286
|
+
} else if (xAxisDirection === "vertical" && maxDataWidth) {
|
|
287
|
+
// vertical direction
|
|
288
|
+
resXConfig.nameGap = maxDataWidth + 10;
|
|
289
|
+
}
|
|
290
|
+
resXConfig.axisLabel = {
|
|
291
|
+
interval: 0,
|
|
292
|
+
width: labelWidth,
|
|
293
|
+
// @ts-ignore
|
|
294
|
+
overflow: "truncate",
|
|
295
|
+
rotate: rotate,
|
|
296
|
+
};
|
|
297
|
+
} else if (resXConfig.type === "time") {
|
|
298
|
+
resXConfig.minInterval = calcTimeInterval(xAxisData);
|
|
299
|
+
}
|
|
300
|
+
if (xAxisDirection === "vertical") {
|
|
301
|
+
resYConfig.nameLocation = "middle";
|
|
302
|
+
resYConfig.nameGap = 25;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return xAxisDirection === "horizontal"
|
|
306
|
+
? {
|
|
307
|
+
xConfig: resXConfig,
|
|
308
|
+
yConfig: resYConfig,
|
|
309
|
+
}
|
|
310
|
+
: {
|
|
311
|
+
xConfig: resYConfig,
|
|
312
|
+
yConfig: resXConfig,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { language } from "@/i18n/comps";
|
|
2
|
+
|
|
3
|
+
const echartsUrlLocale = language === "zh" ? "zh" : "en";
|
|
4
|
+
export const optionUrl = `https://echarts.apache.org/${echartsUrlLocale}/option.html`;
|
|
5
|
+
export const examplesUrl = `https://echarts.apache.org/examples/${echartsUrlLocale}/index.html`;
|
|
6
|
+
export const xAxisTypeUrl = `${optionUrl}#xAxis.type`;
|
|
7
|
+
export const googleMapsApiUrl = `https://maps.googleapis.com/maps/api/js`;
|
|
8
|
+
export const mapOptionUrl = `https://github.com/plainheart/echarts-extension-gmap`;
|
|
9
|
+
export const mapExamplesUrl = `https://codepen.io/plainheart/pen/VweLGbR`;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import * as echarts from "echarts";
|
|
2
|
+
import { PieConfigViewType } from "@/configs/pieChartConfig";
|
|
3
|
+
import { trans } from "@/i18n/comps";
|
|
4
|
+
import { ChartSize, EChartPropsType, isAxisChart } from "../utils/echarts.util";
|
|
5
|
+
|
|
6
|
+
export function getAxisSeriesConfig(props: EChartPropsType, series: any) {
|
|
7
|
+
const horizontalX = props.xAxisDirection === "horizontal";
|
|
8
|
+
const encodeX = horizontalX ? props.xAxisKey : series.getView().columnName;
|
|
9
|
+
const encodeY = horizontalX ? series.getView().columnName : props.xAxisKey;
|
|
10
|
+
|
|
11
|
+
const markLineData = series.getView().markLines.map((line: any) => ({
|
|
12
|
+
type: line.getView().type,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const markAreaData = series.getView().markAreas.map((area: any) => [
|
|
16
|
+
{
|
|
17
|
+
name: area.getView().name,
|
|
18
|
+
[horizontalX ? "xAxis" : "yAxis"]: area.getView().from,
|
|
19
|
+
label: { position: horizontalX ? "top" : "right" },
|
|
20
|
+
},
|
|
21
|
+
{ [horizontalX ? "xAxis" : "yAxis"]: area.getView().to },
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
name: series.getView().seriesName,
|
|
26
|
+
columnName: series.getView().columnName,
|
|
27
|
+
selectedMode: "single",
|
|
28
|
+
select: { itemStyle: { borderColor: "#000" } },
|
|
29
|
+
step: series.getView().step,
|
|
30
|
+
encode: { x: encodeX, y: encodeY },
|
|
31
|
+
markLine: { data: markLineData },
|
|
32
|
+
markArea: {
|
|
33
|
+
itemStyle: { color: "rgba(255, 173, 177, 0.4)" },
|
|
34
|
+
data: markAreaData,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const pieRadiusConfig = [65, 35, 20];
|
|
40
|
+
|
|
41
|
+
export function getPieRadiusAndCenter(
|
|
42
|
+
seriesLength: number,
|
|
43
|
+
pieIndex: number,
|
|
44
|
+
_: PieConfigViewType
|
|
45
|
+
) {
|
|
46
|
+
const columnPieNum = Math.min(seriesLength, pieRadiusConfig.length);
|
|
47
|
+
const radiusNumber = pieRadiusConfig[columnPieNum - 1];
|
|
48
|
+
const radius = radiusNumber + "%";
|
|
49
|
+
|
|
50
|
+
/*** calculate center coordinates ***/
|
|
51
|
+
const pieDiameter = 100 / columnPieNum;
|
|
52
|
+
const xPosition =
|
|
53
|
+
(pieDiameter * (pieIndex % columnPieNum) + pieDiameter / 2).toFixed(2) +
|
|
54
|
+
"%";
|
|
55
|
+
const rowIndex = Math.floor(pieIndex / columnPieNum) + 1;
|
|
56
|
+
const yPosition =
|
|
57
|
+
(
|
|
58
|
+
(100 / Math.floor((columnPieNum * 2 + seriesLength - 1) / columnPieNum)) *
|
|
59
|
+
rowIndex
|
|
60
|
+
).toFixed(2) + "%";
|
|
61
|
+
return {
|
|
62
|
+
radius: radius,
|
|
63
|
+
center: [xPosition, yPosition],
|
|
64
|
+
} as const;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getPieSeriesConfig(
|
|
68
|
+
props: EChartPropsType,
|
|
69
|
+
series: any,
|
|
70
|
+
index: number,
|
|
71
|
+
seriesLength: number
|
|
72
|
+
) {
|
|
73
|
+
const radiusAndCenter = getPieRadiusAndCenter(
|
|
74
|
+
seriesLength,
|
|
75
|
+
index,
|
|
76
|
+
props.chartConfig
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
columnName: series.getView().columnName,
|
|
81
|
+
radius: radiusAndCenter.radius,
|
|
82
|
+
center: radiusAndCenter.center,
|
|
83
|
+
name: series.getView().seriesName,
|
|
84
|
+
selectedMode: "single",
|
|
85
|
+
encode: {
|
|
86
|
+
itemName: props.xAxisKey,
|
|
87
|
+
value: series.getView().columnName,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function getSeriesConfig(props: EChartPropsType) {
|
|
93
|
+
const visibleSeries =
|
|
94
|
+
props.chartConfig.subtype === "waterfall"
|
|
95
|
+
? [props.series[0], props.series[0]]
|
|
96
|
+
: props.series.filter((s: any) => !s.getView().hide);
|
|
97
|
+
|
|
98
|
+
const isAxis = isAxisChart(
|
|
99
|
+
props.chartConfig.type,
|
|
100
|
+
props.chartConfig.polarData?.polar
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return visibleSeries.map((series: any, index: number) => {
|
|
104
|
+
const seriesView = series.getView();
|
|
105
|
+
const base = isAxis
|
|
106
|
+
? getAxisSeriesConfig(props, series)
|
|
107
|
+
: getPieSeriesConfig(props, series, index, visibleSeries.length);
|
|
108
|
+
|
|
109
|
+
const numericCfg = (props.chartConfig as any)[index];
|
|
110
|
+
|
|
111
|
+
const seriesViewConfig = {
|
|
112
|
+
type:
|
|
113
|
+
(seriesView.chartType === "linev2" ? "line" : seriesView.chartType) ||
|
|
114
|
+
(props.chartConfig.type === "linev2" ? "line" : props.chartConfig.type),
|
|
115
|
+
name: seriesView.seriesName,
|
|
116
|
+
areaStyle: props.areaColor
|
|
117
|
+
? {
|
|
118
|
+
opacity: 0.5,
|
|
119
|
+
color: props.areaColor,
|
|
120
|
+
}
|
|
121
|
+
: seriesView.areaStyle || undefined,
|
|
122
|
+
columnName: seriesView.columnName || undefined,
|
|
123
|
+
markLines: seriesView.markLines || undefined,
|
|
124
|
+
markAreas: seriesView.markAreas || undefined,
|
|
125
|
+
markPoint: props.showMaxPoint
|
|
126
|
+
? {
|
|
127
|
+
data: [{ type: "max", name: "Max" }],
|
|
128
|
+
label: { show: true, position: "top" },
|
|
129
|
+
}
|
|
130
|
+
: undefined,
|
|
131
|
+
hide: seriesView.hide || undefined,
|
|
132
|
+
dataIndex: seriesView.dataIndex || undefined,
|
|
133
|
+
step: seriesView.step || undefined,
|
|
134
|
+
itemStyle: {
|
|
135
|
+
color: seriesView.color,
|
|
136
|
+
...(seriesView.itemStyle || {}),
|
|
137
|
+
},
|
|
138
|
+
stack: seriesView.stack || undefined,
|
|
139
|
+
barWidth: seriesView.barWidth || undefined,
|
|
140
|
+
smooth: seriesView.smooth || undefined,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const mergedConfig = {
|
|
144
|
+
...base,
|
|
145
|
+
...(numericCfg ?? props.chartConfig),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
...mergedConfig,
|
|
150
|
+
...seriesViewConfig,
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function getPolarConfig(props: EChartPropsType) {
|
|
156
|
+
const { polarData } = props.chartConfig;
|
|
157
|
+
|
|
158
|
+
const isTangent = polarData.polarIsTangent;
|
|
159
|
+
const hasLabels = polarData.labelData.length > 0;
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
polar: {
|
|
163
|
+
radius: [polarData.polarRadiusStart, polarData.polarRadiusEnd],
|
|
164
|
+
},
|
|
165
|
+
radiusAxis: {
|
|
166
|
+
type: isTangent ? "category" : undefined,
|
|
167
|
+
data: isTangent && hasLabels ? polarData.labelData : undefined,
|
|
168
|
+
max: isTangent ? undefined : polarData.radiusAxisMax || undefined,
|
|
169
|
+
},
|
|
170
|
+
angleAxis: {
|
|
171
|
+
type: isTangent ? undefined : "category",
|
|
172
|
+
data: !isTangent && hasLabels ? polarData.labelData : undefined,
|
|
173
|
+
max: isTangent ? polarData.radiusAxisMax || undefined : undefined,
|
|
174
|
+
startAngle: polarData.polarStartAngle,
|
|
175
|
+
endAngle: polarData.polarEndAngle,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function calculateChartRealSize(chartSize: ChartSize, grid: any) {
|
|
181
|
+
const rightSize =
|
|
182
|
+
typeof grid.right === "number"
|
|
183
|
+
? grid.right
|
|
184
|
+
: (chartSize.w * parseFloat(grid.right)) / 100.0;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
w: chartSize.w - grid.left - rightSize,
|
|
188
|
+
h: chartSize.h - grid.top - grid.bottom,
|
|
189
|
+
right: rightSize,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function getLegendConfig(seriesConfig: any[]) {
|
|
194
|
+
// Đảm bảo legend hiển thị đủ tất cả series
|
|
195
|
+
const legendData = seriesConfig.map((s) => s.name).filter(Boolean);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
legend: {
|
|
199
|
+
data: legendData,
|
|
200
|
+
type: "scroll", // Nếu nhiều series thì dùng scroll
|
|
201
|
+
orient: "vertical",
|
|
202
|
+
right: 10,
|
|
203
|
+
top: "center",
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function getMixedChartAxisConfig(seriesConfig: any[]) {
|
|
209
|
+
const isMixed = seriesConfig.some((s) => s.type !== seriesConfig[0].type);
|
|
210
|
+
|
|
211
|
+
if (!isMixed) return {};
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
yAxis: [
|
|
215
|
+
{ type: "value", position: "left" },
|
|
216
|
+
{ type: "value", position: "right" },
|
|
217
|
+
],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const MarkLineTypeOptions = [
|
|
222
|
+
{
|
|
223
|
+
label: trans("chart.max"),
|
|
224
|
+
value: "max",
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
label: trans("chart.average"),
|
|
228
|
+
value: "average",
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
label: trans("chart.min"),
|
|
232
|
+
value: "min",
|
|
233
|
+
},
|
|
234
|
+
] as const;
|
|
235
|
+
|
|
236
|
+
export const StepOptions = [
|
|
237
|
+
{
|
|
238
|
+
label: trans("chart.none"),
|
|
239
|
+
value: "",
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
label: trans("chart.start"),
|
|
243
|
+
value: "start",
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
label: trans("chart.middle"),
|
|
247
|
+
value: "middle",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
label: trans("chart.end"),
|
|
251
|
+
value: "end",
|
|
252
|
+
},
|
|
253
|
+
] as const;
|
|
254
|
+
|
|
255
|
+
export const eChartConfigOmitChildren = [
|
|
256
|
+
"hidden",
|
|
257
|
+
"selectedPoints",
|
|
258
|
+
"onUIEvent",
|
|
259
|
+
"mapInstance",
|
|
260
|
+
] as const;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { trans } from "@/i18n/comps";
|
|
2
|
+
import { LegendComponentOption } from "echarts";
|
|
3
|
+
import {
|
|
4
|
+
AlignClose,
|
|
5
|
+
AlignLeft,
|
|
6
|
+
AlignRight,
|
|
7
|
+
dropdownControl,
|
|
8
|
+
MultiCompBuilder,
|
|
9
|
+
} from "lowcoder-sdk";
|
|
10
|
+
export const PositionLabelOptions = [
|
|
11
|
+
{
|
|
12
|
+
label: <AlignLeft />,
|
|
13
|
+
value: "left",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: <AlignClose />,
|
|
17
|
+
value: "center",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
label: <AlignRight />,
|
|
21
|
+
value: "right",
|
|
22
|
+
},
|
|
23
|
+
] as const;
|
|
24
|
+
export const EchartsLabelConfig = (function () {
|
|
25
|
+
return new MultiCompBuilder(
|
|
26
|
+
{
|
|
27
|
+
position: dropdownControl(PositionLabelOptions, "inside"),
|
|
28
|
+
},
|
|
29
|
+
(props): LegendComponentOption => {
|
|
30
|
+
const config: LegendComponentOption = {
|
|
31
|
+
top: "inside",
|
|
32
|
+
type: "scroll",
|
|
33
|
+
};
|
|
34
|
+
config.top = props.position;
|
|
35
|
+
return config;
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
.setPropertyViewFn((children) => (
|
|
39
|
+
<>
|
|
40
|
+
{children.position.propertyView({
|
|
41
|
+
label: trans("chart.labelPosition"),
|
|
42
|
+
radioButton: true,
|
|
43
|
+
})}
|
|
44
|
+
</>
|
|
45
|
+
))
|
|
46
|
+
.build();
|
|
47
|
+
})();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { trans } from "@/i18n/comps";
|
|
2
|
+
import { LegendComponentOption } from "echarts";
|
|
3
|
+
import { dropdownControl, MultiCompBuilder } from "lowcoder-sdk";
|
|
4
|
+
import { legendPositionOptions } from "./legendConfig";
|
|
5
|
+
|
|
6
|
+
export const EchartsLegendConfig = (function () {
|
|
7
|
+
return new MultiCompBuilder(
|
|
8
|
+
{
|
|
9
|
+
position: dropdownControl(legendPositionOptions, "bottom"),
|
|
10
|
+
},
|
|
11
|
+
(props): LegendComponentOption => {
|
|
12
|
+
const config: LegendComponentOption = {
|
|
13
|
+
top: "bottom",
|
|
14
|
+
type: "scroll",
|
|
15
|
+
};
|
|
16
|
+
config.top = props.position;
|
|
17
|
+
return config;
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
.setPropertyViewFn((children) => (
|
|
21
|
+
<>
|
|
22
|
+
{children.position.propertyView({
|
|
23
|
+
label: trans("chart.legendPosition"),
|
|
24
|
+
radioButton: true,
|
|
25
|
+
})}
|
|
26
|
+
</>
|
|
27
|
+
))
|
|
28
|
+
.build();
|
|
29
|
+
})();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { trans } from "@/i18n/comps";
|
|
2
|
+
import { LegendComponentOption } from "echarts";
|
|
3
|
+
import {
|
|
4
|
+
AlignClose,
|
|
5
|
+
AlignLeft,
|
|
6
|
+
AlignRight,
|
|
7
|
+
dropdownControl,
|
|
8
|
+
MultiCompBuilder,
|
|
9
|
+
} from "lowcoder-sdk";
|
|
10
|
+
|
|
11
|
+
export const PositionTitleHorizontalOptions = [
|
|
12
|
+
{
|
|
13
|
+
label: <AlignLeft />,
|
|
14
|
+
value: "left",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: <AlignClose />,
|
|
18
|
+
value: "center",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: <AlignRight />,
|
|
22
|
+
value: "right",
|
|
23
|
+
},
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export const EchartsTitleConfig = (function () {
|
|
27
|
+
return new MultiCompBuilder(
|
|
28
|
+
{
|
|
29
|
+
position: dropdownControl(PositionTitleHorizontalOptions, "center"),
|
|
30
|
+
},
|
|
31
|
+
(props): LegendComponentOption => {
|
|
32
|
+
const config: LegendComponentOption = {
|
|
33
|
+
top: "center",
|
|
34
|
+
type: "scroll",
|
|
35
|
+
};
|
|
36
|
+
config.top = props.position;
|
|
37
|
+
return config;
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
.setPropertyViewFn((children) => (
|
|
41
|
+
<>
|
|
42
|
+
{children.position.propertyView({
|
|
43
|
+
label: trans("chart.titlePosition"),
|
|
44
|
+
radioButton: true,
|
|
45
|
+
})}
|
|
46
|
+
</>
|
|
47
|
+
))
|
|
48
|
+
.build();
|
|
49
|
+
})();
|