lowcoder-comps 0.0.7 → 0.0.8
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/icons/icon-chart.svg +15 -0
- package/icons/icon-comp-calendar.svg +22 -0
- package/index.html +26 -0
- package/index.tsx +19 -0
- package/jest.config.js +5 -0
- package/package/012a06bb.js +34 -0
- package/package/06481cb7.js +1060 -0
- package/package/0c8b29d5.js +6 -0
- package/package/114884df.js +805 -0
- package/package/1264ddb2.js +194 -0
- package/package/1cee31ec.js +1095 -0
- package/package/269c3ba3.js +46662 -0
- package/package/2a2c2e55.js +799 -0
- package/package/2b3ab1e4.js +98763 -0
- package/package/37590a81.js +967 -0
- package/package/3cd7980e.js +184 -0
- package/package/40112498.js +424 -0
- package/package/4b6e1d6f.js +24 -0
- package/package/4bc1082b.js +86 -0
- package/package/53e49f9b.js +1600 -0
- package/package/55bd5c26.js +266 -0
- package/package/591684c8.js +2071 -0
- package/package/6c000872.js +16 -0
- package/package/756addec.js +775 -0
- package/package/7668124e.js +8 -0
- package/package/79e8eeeb.js +949 -0
- package/package/926fbfec.js +91 -0
- package/package/945eefb3.js +2665 -0
- package/package/a35f148e.js +1228 -0
- package/package/a4d12a43.js +175 -0
- package/package/a60de5d1.js +804 -0
- package/package/a8dde1ea.js +589 -0
- package/package/b4148642.js +7 -0
- package/package/b7dd22a2.js +847 -0
- package/package/d4f3a2c4.js +18046 -0
- package/package/e0f479f3.js +91 -0
- package/package/eaa160c9.js +341 -0
- package/package/f1bfd8b5.js +311 -0
- package/package/f1f9ba79.js +900 -0
- package/package/fba61f63.js +2868 -0
- package/package/icons/icon-chart.svg +15 -0
- package/package/icons/icon-comp-calendar.svg +22 -0
- package/package/index.js +5 -0
- package/package/package.json +73 -0
- package/package.json +1 -4
- package/src/__test__/allComp.test.tsx +61 -0
- package/src/app-env.d.ts +3 -0
- package/src/comps/calendarComp/calendarComp.tsx +442 -0
- package/src/comps/calendarComp/calendarConstants.tsx +897 -0
- package/src/comps/chartComp/chartComp.tsx +249 -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 +6 -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 +243 -0
- package/src/comps/chartComp/chartPropertyView.tsx +161 -0
- package/src/comps/chartComp/chartUtils.ts +240 -0
- package/src/comps/chartComp/reactEcharts/core.tsx +187 -0
- package/src/comps/chartComp/reactEcharts/index.ts +20 -0
- package/src/comps/chartComp/reactEcharts/types.ts +70 -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 +142 -0
- package/src/i18n/comps/locales/enObj.tsx +120 -0
- package/src/i18n/comps/locales/index.ts +7 -0
- package/src/i18n/comps/locales/types.tsx +9 -0
- package/src/i18n/comps/locales/zh.ts +4 -0
- package/src/i18n/comps/locales/zhObj.tsx +4 -0
- package/src/index.ts +11 -0
- package/vite.config.js +10 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
changeChildAction,
|
|
3
|
+
changeValueAction,
|
|
4
|
+
CompAction,
|
|
5
|
+
CompActionTypes,
|
|
6
|
+
wrapChildAction,
|
|
7
|
+
} from "lowcoder-core";
|
|
8
|
+
import { AxisFormatterComp, EchartsAxisType } from "./chartConfigs/cartesianAxisConfig";
|
|
9
|
+
import { chartChildrenMap, ChartSize, getDataKeys } from "./chartConstants";
|
|
10
|
+
import { chartPropertyView } from "./chartPropertyView";
|
|
11
|
+
import _ from "lodash";
|
|
12
|
+
import { useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
13
|
+
import ReactResizeDetector from "react-resize-detector";
|
|
14
|
+
import ReactECharts from "./reactEcharts";
|
|
15
|
+
import {
|
|
16
|
+
childrenToProps,
|
|
17
|
+
depsConfig,
|
|
18
|
+
genRandomKey,
|
|
19
|
+
JSONObject,
|
|
20
|
+
JSONValue,
|
|
21
|
+
NameConfig,
|
|
22
|
+
ToViewReturn,
|
|
23
|
+
UICompBuilder,
|
|
24
|
+
withDefault,
|
|
25
|
+
withExposingConfigs,
|
|
26
|
+
withViewFn,
|
|
27
|
+
ThemeContext,
|
|
28
|
+
chartColorPalette,
|
|
29
|
+
} from "lowcoder-sdk";
|
|
30
|
+
import { getEchartsLocale, trans } from "i18n/comps";
|
|
31
|
+
import { ItemColorComp } from "comps/chartComp/chartConfigs/lineChartConfig";
|
|
32
|
+
import {
|
|
33
|
+
echartsConfigOmitChildren,
|
|
34
|
+
getEchartsConfig,
|
|
35
|
+
getSelectedPoints,
|
|
36
|
+
} from "comps/chartComp/chartUtils";
|
|
37
|
+
import log from "loglevel";
|
|
38
|
+
|
|
39
|
+
let ChartTmpComp = (function () {
|
|
40
|
+
return new UICompBuilder(chartChildrenMap, () => null)
|
|
41
|
+
.setPropertyViewFn(chartPropertyView)
|
|
42
|
+
.build();
|
|
43
|
+
})();
|
|
44
|
+
|
|
45
|
+
ChartTmpComp = withViewFn(ChartTmpComp, (comp) => {
|
|
46
|
+
const echartsCompRef = useRef<ReactECharts | null>();
|
|
47
|
+
const [chartSize, setChartSize] = useState<ChartSize>();
|
|
48
|
+
const firstResize = useRef(true);
|
|
49
|
+
const theme = useContext(ThemeContext);
|
|
50
|
+
const defaultChartTheme = {
|
|
51
|
+
color: chartColorPalette,
|
|
52
|
+
backgroundColor: "#fff",
|
|
53
|
+
};
|
|
54
|
+
let themeConfig = defaultChartTheme;
|
|
55
|
+
try {
|
|
56
|
+
themeConfig = theme?.theme.chart ? JSON.parse(theme?.theme.chart) : defaultChartTheme;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
log.error('theme chart error: ', error);
|
|
59
|
+
}
|
|
60
|
+
const onEvent = comp.children.onEvent.getView();
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
// bind events
|
|
63
|
+
const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
|
|
64
|
+
if (!echartsCompInstance) {
|
|
65
|
+
return _.noop;
|
|
66
|
+
}
|
|
67
|
+
echartsCompInstance.on("selectchanged", (param: any) => {
|
|
68
|
+
const option: any = echartsCompInstance.getOption();
|
|
69
|
+
//log.log("chart select change", param);
|
|
70
|
+
if (param.fromAction === "select") {
|
|
71
|
+
comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option)));
|
|
72
|
+
onEvent("select");
|
|
73
|
+
} else if (param.fromAction === "unselect") {
|
|
74
|
+
comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option)));
|
|
75
|
+
onEvent("unselect");
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
// unbind
|
|
79
|
+
return () => echartsCompInstance.off("selectchanged");
|
|
80
|
+
}, [onEvent]);
|
|
81
|
+
|
|
82
|
+
const echartsConfigChildren = _.omit(comp.children, echartsConfigOmitChildren);
|
|
83
|
+
const option = useMemo(() => {
|
|
84
|
+
return getEchartsConfig(
|
|
85
|
+
childrenToProps(echartsConfigChildren) as ToViewReturn<typeof echartsConfigChildren>,
|
|
86
|
+
chartSize
|
|
87
|
+
);
|
|
88
|
+
}, [chartSize, ...Object.values(echartsConfigChildren)]);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<ReactResizeDetector
|
|
92
|
+
onResize={(w, h) => {
|
|
93
|
+
if (w && h) {
|
|
94
|
+
setChartSize({ w: w, h: h });
|
|
95
|
+
}
|
|
96
|
+
if (!firstResize.current) {
|
|
97
|
+
// ignore the first resize, which will impact the loading animation
|
|
98
|
+
echartsCompRef.current?.getEchartsInstance().resize();
|
|
99
|
+
} else {
|
|
100
|
+
firstResize.current = false;
|
|
101
|
+
}
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
<ReactECharts
|
|
105
|
+
ref={(e) => (echartsCompRef.current = e)}
|
|
106
|
+
style={{ height: "100%" }}
|
|
107
|
+
notMerge
|
|
108
|
+
lazyUpdate
|
|
109
|
+
opts={{ locale: getEchartsLocale() }}
|
|
110
|
+
option={option}
|
|
111
|
+
theme={themeConfig}
|
|
112
|
+
/>
|
|
113
|
+
</ReactResizeDetector>
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function getYAxisFormatContextValue(
|
|
118
|
+
data: Array<JSONObject>,
|
|
119
|
+
yAxisType: EchartsAxisType,
|
|
120
|
+
yAxisName?: string
|
|
121
|
+
) {
|
|
122
|
+
const dataSample = yAxisName && data.length > 0 && data[0][yAxisName];
|
|
123
|
+
let contextValue = dataSample;
|
|
124
|
+
if (yAxisType === "time") {
|
|
125
|
+
// to timestamp
|
|
126
|
+
const time =
|
|
127
|
+
typeof dataSample === "number" || typeof dataSample === "string"
|
|
128
|
+
? new Date(dataSample).getTime()
|
|
129
|
+
: null;
|
|
130
|
+
if (time) contextValue = time;
|
|
131
|
+
}
|
|
132
|
+
return contextValue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
ChartTmpComp = class extends ChartTmpComp {
|
|
136
|
+
private lastYAxisFormatContextVal?: JSONValue;
|
|
137
|
+
private lastColorContext?: JSONObject;
|
|
138
|
+
|
|
139
|
+
updateContext(comp: this) {
|
|
140
|
+
// the context value of axis format
|
|
141
|
+
let resultComp = comp;
|
|
142
|
+
const data = comp.children.data.getView();
|
|
143
|
+
const sampleSeries = comp.children.series.getView().find((s) => !s.getView().hide);
|
|
144
|
+
const yAxisContextValue = getYAxisFormatContextValue(
|
|
145
|
+
data,
|
|
146
|
+
comp.children.yConfig.children.yAxisType.getView(),
|
|
147
|
+
sampleSeries?.children.columnName.getView()
|
|
148
|
+
);
|
|
149
|
+
if (yAxisContextValue !== comp.lastYAxisFormatContextVal) {
|
|
150
|
+
comp.lastYAxisFormatContextVal = yAxisContextValue;
|
|
151
|
+
resultComp = comp.setChild(
|
|
152
|
+
"yConfig",
|
|
153
|
+
comp.children.yConfig.reduce(
|
|
154
|
+
wrapChildAction(
|
|
155
|
+
"formatter",
|
|
156
|
+
AxisFormatterComp.changeContextDataAction({ value: yAxisContextValue })
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
// item color context
|
|
162
|
+
const colorContextVal = {
|
|
163
|
+
seriesName: sampleSeries?.children.seriesName.getView(),
|
|
164
|
+
value: yAxisContextValue,
|
|
165
|
+
};
|
|
166
|
+
if (
|
|
167
|
+
comp.children.chartConfig.children.comp.children.hasOwnProperty("itemColor") &&
|
|
168
|
+
!_.isEqual(colorContextVal, comp.lastColorContext)
|
|
169
|
+
) {
|
|
170
|
+
comp.lastColorContext = colorContextVal;
|
|
171
|
+
resultComp = resultComp.setChild(
|
|
172
|
+
"chartConfig",
|
|
173
|
+
comp.children.chartConfig.reduce(
|
|
174
|
+
wrapChildAction(
|
|
175
|
+
"comp",
|
|
176
|
+
wrapChildAction("itemColor", ItemColorComp.changeContextDataAction(colorContextVal))
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return resultComp;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
override reduce(action: CompAction): this {
|
|
185
|
+
const comp = super.reduce(action);
|
|
186
|
+
if (action.type === CompActionTypes.UPDATE_NODES_V2) {
|
|
187
|
+
const newData = comp.children.data.getView();
|
|
188
|
+
// data changes
|
|
189
|
+
if (comp.children.data !== this.children.data) {
|
|
190
|
+
setTimeout(() => {
|
|
191
|
+
// update x-axis value
|
|
192
|
+
const keys = getDataKeys(newData);
|
|
193
|
+
if (keys.length > 0 && !keys.includes(comp.children.xAxisKey.getView())) {
|
|
194
|
+
comp.children.xAxisKey.dispatch(changeValueAction(keys[0] || ""));
|
|
195
|
+
}
|
|
196
|
+
// pass to child series comp
|
|
197
|
+
comp.children.series.dispatchDataChanged(newData);
|
|
198
|
+
}, 0);
|
|
199
|
+
}
|
|
200
|
+
return this.updateContext(comp);
|
|
201
|
+
}
|
|
202
|
+
return comp;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
override autoHeight(): boolean {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const ChartComp = withExposingConfigs(ChartTmpComp, [
|
|
211
|
+
depsConfig({
|
|
212
|
+
name: "selectedPoints",
|
|
213
|
+
desc: trans("chart.selectedPointsDesc"),
|
|
214
|
+
depKeys: ["selectedPoints"],
|
|
215
|
+
func: (input) => {
|
|
216
|
+
return input.selectedPoints;
|
|
217
|
+
},
|
|
218
|
+
}),
|
|
219
|
+
depsConfig({
|
|
220
|
+
name: "data",
|
|
221
|
+
desc: trans("chart.dataDesc"),
|
|
222
|
+
depKeys: ["data", "mode"],
|
|
223
|
+
func: (input) => {
|
|
224
|
+
if (input.mode === "ui") {
|
|
225
|
+
return input.data;
|
|
226
|
+
} else {
|
|
227
|
+
// no data in json mode
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
}),
|
|
232
|
+
new NameConfig("title", trans("chart.titleDesc")),
|
|
233
|
+
]);
|
|
234
|
+
|
|
235
|
+
export const ChartCompWithDefault = withDefault(ChartComp, {
|
|
236
|
+
xAxisKey: "date",
|
|
237
|
+
series: [
|
|
238
|
+
{
|
|
239
|
+
dataIndex: genRandomKey(),
|
|
240
|
+
seriesName: trans("chart.spending"),
|
|
241
|
+
columnName: "spending",
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
dataIndex: genRandomKey(),
|
|
245
|
+
seriesName: trans("chart.budget"),
|
|
246
|
+
columnName: "budget",
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BoolControl,
|
|
3
|
+
dropdownControl,
|
|
4
|
+
MultiCompBuilder,
|
|
5
|
+
showLabelPropertyView,
|
|
6
|
+
} from "lowcoder-sdk";
|
|
7
|
+
import { BarSeriesOption } from "echarts";
|
|
8
|
+
import { trans } from "i18n/comps";
|
|
9
|
+
|
|
10
|
+
const BarTypeOptions = [
|
|
11
|
+
{
|
|
12
|
+
label: trans("chart.basicBar"),
|
|
13
|
+
value: "basicBar",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: trans("chart.stackedBar"),
|
|
17
|
+
value: "stackedBar",
|
|
18
|
+
},
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
export const BarChartConfig = (function () {
|
|
22
|
+
return new MultiCompBuilder(
|
|
23
|
+
{
|
|
24
|
+
showLabel: BoolControl,
|
|
25
|
+
type: dropdownControl(BarTypeOptions, "basicBar"),
|
|
26
|
+
},
|
|
27
|
+
(props): BarSeriesOption => {
|
|
28
|
+
const config: BarSeriesOption = {
|
|
29
|
+
type: "bar",
|
|
30
|
+
label: {
|
|
31
|
+
show: props.showLabel,
|
|
32
|
+
position: "top",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
if (props.type === "stackedBar") {
|
|
36
|
+
config.stack = "stackValue";
|
|
37
|
+
}
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
.setPropertyViewFn((children) => (
|
|
42
|
+
<>
|
|
43
|
+
{showLabelPropertyView(children)}
|
|
44
|
+
{children.type.propertyView({
|
|
45
|
+
label: trans("chart.barType"),
|
|
46
|
+
radioButton: true,
|
|
47
|
+
})}
|
|
48
|
+
</>
|
|
49
|
+
))
|
|
50
|
+
.build();
|
|
51
|
+
})();
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { XAXisComponentOption, YAXisComponentOption } from "echarts";
|
|
2
|
+
import { ChartSize, XAxisDirectionType } from "../chartConstants";
|
|
3
|
+
import { i18n } from "lowcoder-core";
|
|
4
|
+
import {
|
|
5
|
+
MultiCompBuilder,
|
|
6
|
+
withContext,
|
|
7
|
+
NumberControl,
|
|
8
|
+
StringControl,
|
|
9
|
+
dropdownControl,
|
|
10
|
+
JSONValue,
|
|
11
|
+
isNumeric,
|
|
12
|
+
} from "lowcoder-sdk";
|
|
13
|
+
import { i18nObjs, trans } from "i18n/comps";
|
|
14
|
+
import _, { isNil } from "lodash";
|
|
15
|
+
import { xAxisTypeUrl } from "./chartUrls";
|
|
16
|
+
|
|
17
|
+
const XAxisTypeOptions = [
|
|
18
|
+
{
|
|
19
|
+
label: trans("chart.auto"),
|
|
20
|
+
value: "default",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: trans("chart.categoryAxis"),
|
|
24
|
+
value: "category",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: trans("chart.valueAxis"),
|
|
28
|
+
value: "value",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: trans("chart.timeAxis"),
|
|
32
|
+
value: "time",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: trans("chart.logAxis"),
|
|
36
|
+
value: "log",
|
|
37
|
+
},
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
const YAxisTypeOptions = [
|
|
41
|
+
{
|
|
42
|
+
label: trans("chart.valueAxis"),
|
|
43
|
+
value: "value",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: trans("chart.categoryAxis"),
|
|
47
|
+
value: "category",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
label: trans("chart.timeAxis"),
|
|
51
|
+
value: "time",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: trans("chart.logAxis"),
|
|
55
|
+
value: "log",
|
|
56
|
+
},
|
|
57
|
+
] as const;
|
|
58
|
+
|
|
59
|
+
export type EchartsAxisType = "category" | "value" | "time" | "log";
|
|
60
|
+
|
|
61
|
+
const axisCommonMap = {
|
|
62
|
+
axisName: StringControl,
|
|
63
|
+
logBase: NumberControl,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const AxisFormatterComp = withContext(
|
|
67
|
+
new MultiCompBuilder({ value: StringControl }, (props) => props.value)
|
|
68
|
+
.setPropertyViewFn((children) =>
|
|
69
|
+
children.value.propertyView({
|
|
70
|
+
label: trans("chart.yAxisDataFormat"),
|
|
71
|
+
placeholder: "{{value}}",
|
|
72
|
+
tooltip: trans("chart.yAxisDataFormatTooltip"),
|
|
73
|
+
})
|
|
74
|
+
)
|
|
75
|
+
.build(),
|
|
76
|
+
["value"] as const
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
export const XAxisConfig = (function () {
|
|
80
|
+
return new MultiCompBuilder(
|
|
81
|
+
{
|
|
82
|
+
...axisCommonMap,
|
|
83
|
+
type: dropdownControl(XAxisTypeOptions, "default"),
|
|
84
|
+
},
|
|
85
|
+
(props): XAXisComponentOption => {
|
|
86
|
+
const config: XAXisComponentOption = {
|
|
87
|
+
name: props.axisName,
|
|
88
|
+
nameGap: 22,
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
nameLocation: "middle",
|
|
91
|
+
};
|
|
92
|
+
if (props.type !== "default") {
|
|
93
|
+
// don't assign value for default value, compute it in the end
|
|
94
|
+
config.type = props.type;
|
|
95
|
+
}
|
|
96
|
+
return config;
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
.setPropertyViewFn((children) => (
|
|
100
|
+
<>
|
|
101
|
+
{children.axisName.propertyView({
|
|
102
|
+
label: trans("chart.xAxisName"),
|
|
103
|
+
})}
|
|
104
|
+
{children.type.propertyView({
|
|
105
|
+
label: trans("chart.xAxisType"),
|
|
106
|
+
tooltip: (
|
|
107
|
+
<>
|
|
108
|
+
{trans("chart.xAxisTypeTooltip")}
|
|
109
|
+
<a href={xAxisTypeUrl} target="_blank" rel="noreferrer">
|
|
110
|
+
{trans("chart.xAxisType")}
|
|
111
|
+
</a>
|
|
112
|
+
</>
|
|
113
|
+
),
|
|
114
|
+
})}
|
|
115
|
+
{children.type.getView() === "log" &&
|
|
116
|
+
children.logBase.propertyView({
|
|
117
|
+
label: trans("chart.logBase"),
|
|
118
|
+
})}
|
|
119
|
+
</>
|
|
120
|
+
))
|
|
121
|
+
.build();
|
|
122
|
+
})();
|
|
123
|
+
|
|
124
|
+
export const YAxisConfig = (function () {
|
|
125
|
+
return new MultiCompBuilder(
|
|
126
|
+
{
|
|
127
|
+
...axisCommonMap,
|
|
128
|
+
// the old data has "type" field with default value "category". change field name to "yAxisType" for compatibility
|
|
129
|
+
yAxisType: dropdownControl(YAxisTypeOptions, "value"),
|
|
130
|
+
formatter: AxisFormatterComp,
|
|
131
|
+
},
|
|
132
|
+
(props) => () => {
|
|
133
|
+
const config: YAXisComponentOption = {
|
|
134
|
+
name: props.axisName,
|
|
135
|
+
type: props.yAxisType,
|
|
136
|
+
nameTextStyle: {
|
|
137
|
+
align: "left",
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
const numberFormat = new Intl.NumberFormat(i18n.locales, {
|
|
141
|
+
notation: "compact",
|
|
142
|
+
});
|
|
143
|
+
(config.axisLabel as any) = {
|
|
144
|
+
formatter: (value: string | number) => {
|
|
145
|
+
const res = (props.formatter as any)({ value: value });
|
|
146
|
+
if (!isNil(res) && res !== "") {
|
|
147
|
+
return res;
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
(props.yAxisType === "value" || props.yAxisType === "log") &&
|
|
151
|
+
typeof value === "number"
|
|
152
|
+
) {
|
|
153
|
+
return numberFormat.format(value);
|
|
154
|
+
}
|
|
155
|
+
return value + "";
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
if (props.yAxisType === "log") {
|
|
159
|
+
(config as any).logBase = props.logBase || 10;
|
|
160
|
+
}
|
|
161
|
+
return config;
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
.setPropertyViewFn((children) => (
|
|
165
|
+
<>
|
|
166
|
+
{children.axisName.propertyView({
|
|
167
|
+
label: trans("chart.yAxisName"),
|
|
168
|
+
})}
|
|
169
|
+
{children.yAxisType.propertyView({
|
|
170
|
+
label: trans("chart.yAxisType"),
|
|
171
|
+
})}
|
|
172
|
+
{children.yAxisType.getView() === "log" &&
|
|
173
|
+
children.logBase.propertyView({
|
|
174
|
+
label: trans("chart.logBase"),
|
|
175
|
+
})}
|
|
176
|
+
{children.formatter.getPropertyView()}
|
|
177
|
+
</>
|
|
178
|
+
))
|
|
179
|
+
.build();
|
|
180
|
+
})();
|
|
181
|
+
|
|
182
|
+
function calcXAxisType(xAxisData: Array<JSONValue | undefined>): EchartsAxisType {
|
|
183
|
+
if (!xAxisData || xAxisData.length <= 0) {
|
|
184
|
+
return "category";
|
|
185
|
+
}
|
|
186
|
+
const sampleData = xAxisData[0];
|
|
187
|
+
if (!sampleData) {
|
|
188
|
+
return "category";
|
|
189
|
+
}
|
|
190
|
+
if (isNumeric(sampleData)) {
|
|
191
|
+
return "value";
|
|
192
|
+
} else if (!isNaN(new Date(sampleData.toString()).getDate())) {
|
|
193
|
+
return "time";
|
|
194
|
+
} else {
|
|
195
|
+
return "category";
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const dateInterval = {
|
|
200
|
+
year: 3600 * 24 * 1000 * 365,
|
|
201
|
+
month: 3600 * 24 * 1000 * 28,
|
|
202
|
+
day: 3600 * 24 * 1000,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
function calcTimeInterval(xAxisData: Array<JSONValue | undefined>) {
|
|
206
|
+
const minIntervals = xAxisData.map((data) => {
|
|
207
|
+
if (!data) {
|
|
208
|
+
// 1 is echarts default value, to make sure axis tick is integer
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
const dataLen = data.toString().length;
|
|
212
|
+
if (dataLen === 4) {
|
|
213
|
+
// year 2022
|
|
214
|
+
return dateInterval.year;
|
|
215
|
+
} else if (dataLen === 6 || dataLen === 7) {
|
|
216
|
+
// month 2022-01 222201
|
|
217
|
+
return dateInterval.month;
|
|
218
|
+
} else if (dataLen === 10 || dataLen === 8) {
|
|
219
|
+
// day 2022-01-01 20220101
|
|
220
|
+
return dateInterval.day;
|
|
221
|
+
} else {
|
|
222
|
+
return 1;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
return _.min(minIntervals);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let measureCanvas: HTMLCanvasElement;
|
|
229
|
+
|
|
230
|
+
// calculate x-axis text width
|
|
231
|
+
function getXAxisDataLength(xAxisData: Array<JSONValue | undefined>) {
|
|
232
|
+
const canvas = measureCanvas || (measureCanvas = document.createElement("canvas"));
|
|
233
|
+
const context = canvas.getContext("2d");
|
|
234
|
+
if (!context) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
// echarts default font
|
|
238
|
+
context.font = "normal 12px sans-serif";
|
|
239
|
+
return xAxisData.map((d) => (d ? context.measureText(d.toString()).width + 2 : 0));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function calcXYConfig(
|
|
243
|
+
xConfig: XAXisComponentOption,
|
|
244
|
+
yConfig: YAXisComponentOption,
|
|
245
|
+
xAxisDirection: XAxisDirectionType,
|
|
246
|
+
xAxisData: Array<JSONValue | undefined>,
|
|
247
|
+
chartSize?: ChartSize & { right: number }
|
|
248
|
+
) {
|
|
249
|
+
const resXConfig = { ...xConfig };
|
|
250
|
+
const resYConfig = { ...yConfig };
|
|
251
|
+
|
|
252
|
+
if (!resXConfig.type) {
|
|
253
|
+
// simple calculate x-axis type
|
|
254
|
+
resXConfig.type = calcXAxisType(xAxisData);
|
|
255
|
+
}
|
|
256
|
+
// x-axis label style adaptive
|
|
257
|
+
if (resXConfig.type === "category" && chartSize) {
|
|
258
|
+
const xAxisDataLenList = getXAxisDataLength(xAxisData);
|
|
259
|
+
// get x-axis single data's max width
|
|
260
|
+
const maxDataWidth = _.max(xAxisDataLenList);
|
|
261
|
+
const lastDataWidth = xAxisDataLenList[xAxisDataLenList.length - 1];
|
|
262
|
+
// grid width
|
|
263
|
+
let eachDataWidth = chartSize.w / xAxisData.length;
|
|
264
|
+
let rotate = 0;
|
|
265
|
+
let labelWidth = maxDataWidth;
|
|
266
|
+
// rotate when width is not enough
|
|
267
|
+
if (maxDataWidth && eachDataWidth < maxDataWidth && xAxisDirection === "horizontal") {
|
|
268
|
+
labelWidth = Math.min(maxDataWidth, 150);
|
|
269
|
+
// vertical rotate 0.87 => sin(60) when exceeding the right boundary
|
|
270
|
+
const verticalRotate =
|
|
271
|
+
lastDataWidth && lastDataWidth * 0.87 > eachDataWidth / 2 + chartSize.right;
|
|
272
|
+
rotate = verticalRotate ? 270 : 330;
|
|
273
|
+
// to keep x-axis name under label, nameGap is related to label rotation angle
|
|
274
|
+
resXConfig.nameGap = verticalRotate ? labelWidth + 5 : labelWidth / 2 + 10;
|
|
275
|
+
} else if (xAxisDirection === "vertical" && maxDataWidth) {
|
|
276
|
+
// vertical direction
|
|
277
|
+
resXConfig.nameGap = maxDataWidth + 10;
|
|
278
|
+
}
|
|
279
|
+
resXConfig.axisLabel = {
|
|
280
|
+
interval: 0,
|
|
281
|
+
width: labelWidth,
|
|
282
|
+
// @ts-ignore
|
|
283
|
+
overflow: "truncate",
|
|
284
|
+
rotate: rotate,
|
|
285
|
+
};
|
|
286
|
+
} else if (resXConfig.type === "time") {
|
|
287
|
+
(resXConfig as any).minInterval = calcTimeInterval(xAxisData);
|
|
288
|
+
const timeXAxisLabel = i18nObjs.timeXAxisLabel;
|
|
289
|
+
if (timeXAxisLabel) {
|
|
290
|
+
resXConfig.axisLabel = timeXAxisLabel;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (xAxisDirection === "vertical") {
|
|
294
|
+
resYConfig.nameLocation = "middle";
|
|
295
|
+
resYConfig.nameGap = 25;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return xAxisDirection === "horizontal"
|
|
299
|
+
? {
|
|
300
|
+
xConfig: resXConfig,
|
|
301
|
+
yConfig: resYConfig,
|
|
302
|
+
}
|
|
303
|
+
: {
|
|
304
|
+
xConfig: resYConfig,
|
|
305
|
+
yConfig: resXConfig,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
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`;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AlignBottom,
|
|
3
|
+
AlignClose,
|
|
4
|
+
AlignRight,
|
|
5
|
+
dropdownControl,
|
|
6
|
+
MultiCompBuilder,
|
|
7
|
+
} from "lowcoder-sdk";
|
|
8
|
+
import { LegendComponentOption } from "echarts";
|
|
9
|
+
import { trans } from "i18n/comps";
|
|
10
|
+
|
|
11
|
+
const LegendPositionOptions = [
|
|
12
|
+
{
|
|
13
|
+
label: <AlignBottom />,
|
|
14
|
+
value: "bottom",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: <AlignRight />,
|
|
18
|
+
value: "right",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: <AlignClose />,
|
|
22
|
+
value: "close",
|
|
23
|
+
},
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export const LegendConfig = (function () {
|
|
27
|
+
return new MultiCompBuilder(
|
|
28
|
+
{
|
|
29
|
+
position: dropdownControl(LegendPositionOptions, "bottom"),
|
|
30
|
+
},
|
|
31
|
+
(props): LegendComponentOption => {
|
|
32
|
+
const config: LegendComponentOption = {
|
|
33
|
+
top: "bottom",
|
|
34
|
+
type: "scroll",
|
|
35
|
+
};
|
|
36
|
+
if (props.position === "right") {
|
|
37
|
+
config.top = "center";
|
|
38
|
+
config.left = "right";
|
|
39
|
+
config.orient = "vertical";
|
|
40
|
+
} else if (props.position === "close") {
|
|
41
|
+
config.show = false;
|
|
42
|
+
}
|
|
43
|
+
return config;
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
.setPropertyViewFn((children) => (
|
|
47
|
+
<>
|
|
48
|
+
{children.position.propertyView({
|
|
49
|
+
label: trans("chart.legendPosition"),
|
|
50
|
+
radioButton: true,
|
|
51
|
+
})}
|
|
52
|
+
</>
|
|
53
|
+
))
|
|
54
|
+
.build();
|
|
55
|
+
})();
|