Skip to content

Commit 9a3126a

Browse files
authored
Merge pull request #372 from raheeliftikhar5/echarts-extension
Echarts extension
2 parents 4968773 + b137622 commit 9a3126a

File tree

23 files changed

+427
-25
lines changed

23 files changed

+427
-25
lines changed

client/packages/lowcoder-comps/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lowcoder-comps",
3-
"version": "0.0.12",
3+
"version": "0.0.13",
44
"type": "module",
55
"license": "MIT",
66
"dependencies": {
@@ -14,6 +14,7 @@
1414
"@types/react": "17",
1515
"@types/react-dom": "17",
1616
"big.js": "^6.2.1",
17+
"echarts-extension-gmap": "^1.6.0",
1718
"lowcoder-cli": "workspace:^",
1819
"lowcoder-sdk": "workspace:^",
1920
"mermaid": "^10.2.4",

client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@ import {
2626
withViewFn,
2727
ThemeContext,
2828
chartColorPalette,
29+
loadScript,
2930
} from "lowcoder-sdk";
3031
import { getEchartsLocale, trans } from "i18n/comps";
3132
import { ItemColorComp } from "comps/chartComp/chartConfigs/lineChartConfig";
3233
import {
3334
echartsConfigOmitChildren,
3435
getEchartsConfig,
3536
getSelectedPoints,
37+
loadGoogleMapsScript,
3638
} from "comps/chartComp/chartUtils";
39+
import 'echarts-extension-gmap';
3740
import log from "loglevel";
3841

3942
let ChartTmpComp = (function () {
@@ -45,6 +48,7 @@ let ChartTmpComp = (function () {
4548
ChartTmpComp = withViewFn(ChartTmpComp, (comp) => {
4649
const echartsCompRef = useRef<ReactECharts | null>();
4750
const [chartSize, setChartSize] = useState<ChartSize>();
51+
const [mapScriptLoaded, setMapScriptLoaded] = useState(false);
4852
const firstResize = useRef(true);
4953
const theme = useContext(ThemeContext);
5054
const defaultChartTheme = {
@@ -87,6 +91,34 @@ ChartTmpComp = withViewFn(ChartTmpComp, (comp) => {
8791
);
8892
}, [chartSize, ...Object.values(echartsConfigChildren)]);
8993

94+
const isMapScriptLoaded = useMemo(() => {
95+
return mapScriptLoaded || window?.google;
96+
}, [mapScriptLoaded])
97+
98+
const loadGoogleMapsData = () => {
99+
const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
100+
if (!echartsCompInstance) {
101+
return _.noop;
102+
}
103+
echartsCompInstance.getModel().getComponent("gmap").getGoogleMap();
104+
}
105+
106+
const apiKey = comp.children.mapApiKey.getView();
107+
const mode = comp.children.mode.getView();
108+
useEffect(() => {
109+
if(mode === 'map') {
110+
const gMapScript = loadGoogleMapsScript('');
111+
if(isMapScriptLoaded) {
112+
loadGoogleMapsData();
113+
return;
114+
}
115+
gMapScript.addEventListener('load', function () {
116+
setMapScriptLoaded(true);
117+
loadGoogleMapsData();
118+
});
119+
}
120+
}, [mode, apiKey, option])
121+
90122
return (
91123
<ReactResizeDetector
92124
onResize={(w, h) => {
@@ -101,15 +133,17 @@ ChartTmpComp = withViewFn(ChartTmpComp, (comp) => {
101133
}
102134
}}
103135
>
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-
/>
136+
{(mode !== 'map' || (mode === 'map' && isMapScriptLoaded)) && (
137+
<ReactECharts
138+
ref={(e) => (echartsCompRef.current = e)}
139+
style={{ height: "100%" }}
140+
notMerge
141+
lazyUpdate
142+
opts={{ locale: getEchartsLocale() }}
143+
option={option}
144+
theme={mode !== 'map' ? themeConfig : undefined}
145+
/>
146+
)}
113147
</ReactResizeDetector>
114148
);
115149
});

client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ const echartsUrlLocale = language === "zh" ? "zh" : "en";
44
export const optionUrl = `https://echarts.apache.org/${echartsUrlLocale}/option.html`;
55
export const examplesUrl = `https://echarts.apache.org/examples/${echartsUrlLocale}/index.html`;
66
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`;

client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { jsonControl, JSONObject, stateComp, toJSONObjectArray, toObject } from "lowcoder-sdk";
2-
import { StringControl } from "lowcoder-sdk";
2+
import { withDefault, BooleanControl, StringControl, NumberControl, JSONObjectControl } from "lowcoder-sdk";
33
import { dropdownControl } from "lowcoder-sdk";
44
import { eventHandlerControl } from "lowcoder-sdk";
55
import { valueComp, withType } from "lowcoder-sdk";
@@ -15,7 +15,6 @@ import { ScatterChartConfig } from "./chartConfigs/scatterChartConfig";
1515
import { SeriesListComp } from "./seriesComp";
1616
import { EChartsOption } from "echarts";
1717
import { i18nObjs, trans } from "i18n/comps";
18-
import { JSONValue } from "lowcoder";
1918

2019
export const ChartTypeOptions = [
2120
{
@@ -45,6 +44,10 @@ const chartModeOptions = [
4544
label: "ECharts JSON",
4645
value: "json",
4746
},
47+
{
48+
label: "Map",
49+
value: "map",
50+
},
4851
] as const;
4952

5053
export const EventOptions = [
@@ -221,6 +224,14 @@ export const chartUiModeChildren = {
221224
chartConfig: ChartOptionComp,
222225
};
223226

227+
const chartMapModeChildren = {
228+
mapApiKey: withDefault(StringControl, ''),
229+
mapZoomLevel: withDefault(NumberControl, 3),
230+
mapCenterLng: withDefault(NumberControl, 15.932644),
231+
mapCenterLat: withDefault(NumberControl, 50.942063),
232+
mapOptions: jsonControl(toObject, i18nObjs.defaultMapJsonOption),
233+
}
234+
224235
export const chartChildrenMap = {
225236
mode: dropdownControl(chartModeOptions, "ui"),
226237
echartsOption: jsonControl(toObject, i18nObjs.defaultEchartsJsonOption),
@@ -236,6 +247,7 @@ export const chartChildrenMap = {
236247
}>
237248
>([]),
238249
...chartUiModeChildren,
250+
...chartMapModeChildren,
239251
};
240252

241253
const chartUiChildrenMap = uiChildren(chartChildrenMap);

client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import {
99
RedButton,
1010
Section,
1111
sectionNames,
12+
controlItem,
1213
} from "lowcoder-sdk";
1314
import { trans } from "i18n/comps";
14-
import { examplesUrl, optionUrl } from "./chartConfigs/chartUrls";
15+
import { examplesUrl, mapExamplesUrl, mapOptionUrl, optionUrl } from "./chartConfigs/chartUrls";
1516

1617
export function chartPropertyView(
1718
children: ChartCompChildrenType,
@@ -147,6 +148,58 @@ export function chartPropertyView(
147148
</>
148149
);
149150

151+
const mapModePropertyView = (
152+
<>
153+
<Section name={'Map Configuration'}>
154+
{children.mapApiKey.propertyView({
155+
label: "API Key"
156+
})}
157+
{children.mapZoomLevel.propertyView({
158+
label: "Zoom Level"
159+
})}
160+
{controlItem({}, (
161+
<b style={{marginTop: '8px'}}>
162+
{'Center Position'}
163+
</b>
164+
))}
165+
{children.mapCenterLng.propertyView({
166+
label: "Longitude"
167+
})}
168+
{children.mapCenterLat.propertyView({
169+
label: "Latitude"
170+
})}
171+
</Section>
172+
<Section name={'Map Data'}>
173+
{children.mapOptions.propertyView({
174+
label: trans("chart.echartsOptionLabel"),
175+
styleName: "higher",
176+
tooltip: (
177+
<div>
178+
<a href={mapOptionUrl} target="_blank" rel="noopener noreferrer">
179+
{trans("chart.echartsMapOptionTooltip")}
180+
</a>
181+
<br />
182+
<a href={mapExamplesUrl} target="_blank" rel="noopener noreferrer">
183+
{trans("chart.echartsMapOptionExamples")}
184+
</a>
185+
</div>
186+
),
187+
})}
188+
</Section>
189+
<Section name={sectionNames.layout}>{hiddenPropertyView(children)}</Section>
190+
</>
191+
);
192+
193+
const getChatConfigByMode = (mode: string) => {
194+
switch(mode) {
195+
case "ui":
196+
return uiModePropertyView;
197+
case "json":
198+
return jsonModePropertyView;
199+
case "map":
200+
return mapModePropertyView;
201+
}
202+
}
150203
return (
151204
<>
152205
<Section name={trans("chart.mode")}>
@@ -155,7 +208,7 @@ export function chartPropertyView(
155208
radioButton: true,
156209
})}
157210
</Section>
158-
{children.mode.getView() === "ui" ? uiModePropertyView : jsonModePropertyView}
211+
{getChatConfigByMode(children.mode.getView())}
159212
</>
160213
);
161214
}

client/packages/lowcoder-comps/src/comps/chartComp/chartUtils.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import {
66
noDataPieChartConfig,
77
} from "comps/chartComp/chartConstants";
88
import { getPieRadiusAndCenter } from "comps/chartComp/chartConfigs/pieChartConfig";
9-
import { EChartsOption } from "echarts";
9+
import { EChartsOptionWithMap } from "./reactEcharts/types";
1010
import _ from "lodash";
11-
import { chartColorPalette, isNumeric, JSONObject } from "lowcoder-sdk";
11+
import { chartColorPalette, isNumeric, JSONObject, loadScript } from "lowcoder-sdk";
1212
import { calcXYConfig } from "comps/chartComp/chartConfigs/cartesianAxisConfig";
1313
import Big from "big.js";
14+
import { googleMapsApiUrl } from "./chartConfigs/chartUrls";
1415

1516
export function transformData(
1617
originData: JSONObject[],
@@ -122,10 +123,30 @@ export function getSeriesConfig(props: EchartsConfigProps) {
122123
}
123124

124125
// https://echarts.apache.org/en/option.html
125-
export function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSize): EChartsOption {
126+
export function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSize): EChartsOptionWithMap {
126127
if (props.mode === "json") {
127128
return props.echartsOption ? props.echartsOption : {};
128129
}
130+
if(props.mode === "map") {
131+
const {
132+
mapZoomLevel,
133+
mapCenterLat,
134+
mapCenterLng,
135+
mapOptions,
136+
} = props;
137+
138+
const echartsOption = mapOptions ? mapOptions : {};
139+
return {
140+
gmap: {
141+
center: [mapCenterLng, mapCenterLat],
142+
zoom: mapZoomLevel,
143+
renderOnMoving: true,
144+
// echartsLayerZIndex: 2019,
145+
roam: true
146+
},
147+
...echartsOption,
148+
}
149+
}
129150
// axisChart
130151
const axisChart = isAxisChart(props.chartConfig.type);
131152
const gridPos = {
@@ -134,7 +155,7 @@ export function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSiz
134155
top: 50,
135156
bottom: 35,
136157
};
137-
let config: EChartsOption = {
158+
let config: EChartsOptionWithMap = {
138159
title: { text: props.title, left: "center" },
139160
tooltip: {
140161
confine: true,
@@ -238,3 +259,21 @@ export function getSelectedPoints(param: any, option: any) {
238259
}
239260
return [];
240261
}
262+
263+
export function loadGoogleMapsScript(apiKey?: string) {
264+
const mapsUrl = `${googleMapsApiUrl}?key=${apiKey}`;
265+
const scripts = document.getElementsByTagName('script');
266+
const scriptIndex = _.findIndex(scripts, (script) => script.src === mapsUrl);
267+
if(scriptIndex > -1) {
268+
return scripts[scriptIndex];
269+
}
270+
271+
const script = document.createElement("script");
272+
script.type = "text/javascript";
273+
script.src = mapsUrl;
274+
script.async = true;
275+
script.defer = true;
276+
window.document.body.appendChild(script);
277+
278+
return script;
279+
}

client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/core.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export default class EChartsReactCore extends PureComponent<EChartsReactProps> {
5858
if (
5959
!isEqual(prevProps.theme, this.props.theme) ||
6060
!isEqual(prevProps.opts, this.props.opts) ||
61-
!isEqual(prevProps.onEvents, this.props.onEvents)
61+
!isEqual(prevProps.onEvents, this.props.onEvents) ||
62+
this.props.option.gmap
6263
) {
6364
this.dispose();
6465

client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import * as echarts from "echarts";
2-
import { EChartsReactProps, EChartsOption, EChartsInstance } from "./types";
2+
import { EChartsReactProps, EChartsInstance, EChartsOptionWithMap } from "./types";
33
import EChartsReactCore from "./core";
44

55
/**
66
* reference: https://github.com/hustcc/echarts-for-react
77
* add exception-catch for setOption
88
* if query isn't successfully loaded, chart will fail to load and can't reload
99
*/
10-
export type { EChartsReactProps, EChartsOption, EChartsInstance };
10+
export type { EChartsReactProps, EChartsOptionWithMap, EChartsInstance };
1111

1212
// export the Component the echarts Object.
1313
export default class EChartsReact extends EChartsReactCore {

client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { CSSProperties } from "react";
2+
import { EChartsOption } from "echarts";
3+
import { GoogleMapComponentOption } from "echarts-extension-gmap";
24

3-
export type EChartsOption = any;
5+
export type EChartsOptionWithMap = EChartsOption & GoogleMapComponentOption<any>;
46

57
export type EChartsInstance = any;
68

@@ -28,7 +30,7 @@ export type EChartsReactProps = {
2830
/**
2931
* echarts option
3032
*/
31-
readonly option: EChartsOption;
33+
readonly option: EChartsOptionWithMap;
3234
/**
3335
* echarts theme config, can be:
3436
* 1. theme name string

client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export const en = {
5757
echartsOptionLabel: "Option",
5858
echartsOptionTooltip: "ECharts option",
5959
echartsOptionExamples: "ECharts examples",
60+
echartsMapOptionTooltip: "ECharts Map Option",
61+
echartsMapOptionExamples: "ECharts Map Examples",
6062
selectDesc: "Triggered when the user selects part of the data in the chart",
6163
unselectDesc: "Triggered when the user unselects part of the data in the chart",
6264
selectedPointsDesc: "Selected points",

0 commit comments

Comments
 (0)