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.
Files changed (123) hide show
  1. package/README.md +37 -0
  2. package/icons/demo-icon.svg +10 -0
  3. package/icons/hills.svg +17 -0
  4. package/index.css +27 -0
  5. package/index.html +35 -0
  6. package/index.tsx +20 -0
  7. package/loader.mjs +11 -0
  8. package/package.json +175 -0
  9. package/src/README.md +35 -0
  10. package/src/components/ChartPropertyView.tsx +961 -0
  11. package/src/components/SeriesComp.tsx +368 -0
  12. package/src/components/TabPropertyView.tsx +127 -0
  13. package/src/comps/Barcharts/comp.tsx +338 -0
  14. package/src/comps/Barcharts/constants.tsx +77 -0
  15. package/src/comps/Linecharts/comp.tsx +350 -0
  16. package/src/comps/Linecharts/constants.tsx +53 -0
  17. package/src/comps/Linechartsv2/comp.tsx +350 -0
  18. package/src/comps/Linechartsv2/constants.tsx +68 -0
  19. package/src/comps/Mapcharts/comp.tsx +381 -0
  20. package/src/comps/Mapcharts/constants.tsx +312 -0
  21. package/src/comps/Mapchartsv2/comp.tsx +393 -0
  22. package/src/comps/Mapchartsv2/constants.tsx +340 -0
  23. package/src/comps/MixedLineBarCharts/comp.tsx +353 -0
  24. package/src/comps/MixedLineBarCharts/constants.tsx +60 -0
  25. package/src/comps/MultiLineCharts/comp.tsx +362 -0
  26. package/src/comps/MultiLineCharts/constants.tsx +96 -0
  27. package/src/comps/PercentageCharts/comp.tsx +359 -0
  28. package/src/comps/PercentageCharts/constants.tsx +98 -0
  29. package/src/comps/Piecharts/comp.tsx +334 -0
  30. package/src/comps/Piecharts/constants.tsx +48 -0
  31. package/src/comps/Tablecharts/comp.tsx +429 -0
  32. package/src/comps/Tablecharts/constants.tsx +97 -0
  33. package/src/comps/Totalcharts/comp.tsx +463 -0
  34. package/src/comps/Totalcharts/constants.tsx +66 -0
  35. package/src/comps/TwoLineCharts/comp.tsx +350 -0
  36. package/src/comps/TwoLineCharts/constants.tsx +82 -0
  37. package/src/comps/mapComponent/comp.tsx +338 -0
  38. package/src/comps/mapComponent/constants.tsx +2149 -0
  39. package/src/comps/tab/comp.tsx +283 -0
  40. package/src/comps/tab/constants.tsx +79 -0
  41. package/src/configs/barChartConfig.tsx +153 -0
  42. package/src/configs/baseConfig.tsx +66 -0
  43. package/src/configs/candleStickChartConfig.tsx +35 -0
  44. package/src/configs/cartesianAxisConfig.tsx +314 -0
  45. package/src/configs/chartUrls.tsx +9 -0
  46. package/src/configs/echartConfig.tsx +260 -0
  47. package/src/configs/echartsLabelConfig.tsx +47 -0
  48. package/src/configs/echartsLegendConfig.tsx +29 -0
  49. package/src/configs/echartsTitleConfig.tsx +49 -0
  50. package/src/configs/echartsTitleVerticalConfig.tsx +50 -0
  51. package/src/configs/funnelChartConfig.tsx +35 -0
  52. package/src/configs/gaugeChartConfig.tsx +31 -0
  53. package/src/configs/graphChartConfig.tsx +31 -0
  54. package/src/configs/heatmapChartConfig.tsx +31 -0
  55. package/src/configs/legendConfig.tsx +55 -0
  56. package/src/configs/lineChartConfig.tsx +246 -0
  57. package/src/configs/lineChartConfigv2.tsx +246 -0
  58. package/src/configs/mapChartConfig.tsx +106 -0
  59. package/src/configs/mapChartConfigv2.tsx +106 -0
  60. package/src/configs/mixedChartConfig.tsx +21 -0
  61. package/src/configs/pieChartConfig.tsx +156 -0
  62. package/src/configs/radarChartConfig.tsx +31 -0
  63. package/src/configs/sankeyChartConfig.tsx +35 -0
  64. package/src/configs/scatterChartConfig.tsx +152 -0
  65. package/src/configs/sunburstChartConfig.tsx +31 -0
  66. package/src/configs/tabConfig.tsx +0 -0
  67. package/src/configs/tableChartConfig.tsx +81 -0
  68. package/src/configs/themeriverChartConfig.tsx +31 -0
  69. package/src/configs/totalChartConfig.tsx +670 -0
  70. package/src/configs/treeChartConfig.tsx +31 -0
  71. package/src/configs/treemapChartConfig.tsx +31 -0
  72. package/src/controls/AnimationsControls.tsx +3 -0
  73. package/src/controls/AutoHeightControl.tsx +2 -0
  74. package/src/controls/AxisControls.tsx +146 -0
  75. package/src/controls/AxisVisibilityControl.tsx +14 -0
  76. package/src/controls/ChartTypeControl.tsx +15 -0
  77. package/src/controls/ColorScaleControl.tsx +221 -0
  78. package/src/controls/ColumnControl.tsx +204 -0
  79. package/src/controls/ConfigControl.tsx +37 -0
  80. package/src/controls/DirectionControls.tsx +20 -0
  81. package/src/controls/IconControl.tsx +88 -0
  82. package/src/controls/LegendControl.tsx +8 -0
  83. package/src/controls/RowLimitControl.tsx +91 -0
  84. package/src/controls/StyleControls.tsx +22 -0
  85. package/src/controls/TimeControls.tsx +41 -0
  86. package/src/controls/TrendlineControl.tsx +89 -0
  87. package/src/controls/UIEventControl.tsx +21 -0
  88. package/src/controls/index.ts +16 -0
  89. package/src/controls/radioControl.tsx +88 -0
  90. package/src/exposing/index.ts +3 -0
  91. package/src/exposing/setPoint.ts +19 -0
  92. package/src/geo/vn.geo.json +369897 -0
  93. package/src/geo/world.geo.json +32127 -0
  94. package/src/i18n/comps/index.tsx +39 -0
  95. package/src/i18n/comps/locales/en.ts +558 -0
  96. package/src/i18n/comps/locales/enObj.tsx +7186 -0
  97. package/src/i18n/comps/locales/index.ts +7 -0
  98. package/src/i18n/comps/locales/pt.ts +37 -0
  99. package/src/i18n/comps/locales/ptObj.tsx +40 -0
  100. package/src/i18n/comps/locales/types.tsx +622 -0
  101. package/src/i18n/comps/locales/zh.ts +3 -0
  102. package/src/i18n/comps/locales/zhObj.tsx +4 -0
  103. package/src/index.ts +31 -0
  104. package/src/types/global.d.ts +9 -0
  105. package/src/types/lowcoder-sdk.d.ts +578 -0
  106. package/src/utils/chartStyle.util.ts +121 -0
  107. package/src/utils/columnExtractor.util.ts +41 -0
  108. package/src/utils/dataTransform.util.ts +37 -0
  109. package/src/utils/deepEqual.util.ts +29 -0
  110. package/src/utils/echarts.util.tsx +822 -0
  111. package/src/utils/getDataKey.util.ts +115 -0
  112. package/src/utils/getLinearRegression.util.ts +46 -0
  113. package/src/utils/googleMaps.util.ts +28 -0
  114. package/src/utils/isEmpty.util.ts +10 -0
  115. package/src/utils/move.util.ts +6 -0
  116. package/src/utils/selection.util.ts +73 -0
  117. package/src/utils/style.util.ts +315 -0
  118. package/src/utils/time.util.ts +221 -0
  119. package/src/utils/timeFormatter.util.ts +104 -0
  120. package/src/utils/timeProcessing.util.ts +38 -0
  121. package/src/utils/trendline.util.ts +342 -0
  122. package/tsconfig.json +25 -0
  123. 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
+ })();