Skip to content

Commit bb4de33

Browse files
committed
1.expand browser support 2.add row color for table and point color for chart
1 parent 7cefa0c commit bb4de33

File tree

32 files changed

+649
-456
lines changed

32 files changed

+649
-456
lines changed

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

Lines changed: 55 additions & 261 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,17 @@ import {
55
CompActionTypes,
66
wrapChildAction,
77
} from "openblocks-core";
8-
import {
9-
AxisFormatterComp,
10-
calcXYConfig,
11-
EchartsAxisType,
12-
} from "./chartConfigs/cartesianAxisConfig";
13-
import { getPieRadiusAndCenter } from "./chartConfigs/pieChartConfig";
14-
import {
15-
CharOptionCompType,
16-
chartChildrenMap,
17-
ChartCompPropsType,
18-
ChartSize,
19-
getDataKeys,
20-
noDataAxisConfig,
21-
noDataPieChartConfig,
22-
} from "./chartConstants";
8+
import { AxisFormatterComp, EchartsAxisType } from "./chartConfigs/cartesianAxisConfig";
9+
import { chartChildrenMap, ChartSize, getDataKeys } from "./chartConstants";
2310
import { chartPropertyView } from "./chartPropertyView";
24-
import { EChartsOption } from "echarts";
2511
import _ from "lodash";
2612
import { useEffect, useMemo, useRef, useState } from "react";
2713
import ReactResizeDetector from "react-resize-detector";
2814
import ReactECharts from "./reactEcharts";
2915
import {
30-
chartColorPalette,
3116
childrenToProps,
3217
depsConfig,
3318
genRandomKey,
34-
isNumeric,
3519
JSONObject,
3620
JSONValue,
3721
NameConfig,
@@ -42,230 +26,12 @@ import {
4226
withViewFn,
4327
} from "openblocks-sdk";
4428
import { getEchartsLocale, trans } from "i18n/comps";
45-
46-
function transformData(originData: JSONObject[], xAxis: string, seriesColumnNames: string[]) {
47-
// aggregate data by x-axis
48-
const transformedData: JSONObject[] = [];
49-
originData.reduce((prev, cur) => {
50-
if (cur === null || cur === undefined) {
51-
return prev;
52-
}
53-
const groupValue = cur[xAxis] as string;
54-
if (!prev[groupValue]) {
55-
// init as 0
56-
const initValue: any = {};
57-
seriesColumnNames.forEach((name) => {
58-
initValue[name] = 0;
59-
});
60-
prev[groupValue] = initValue;
61-
transformedData.push(prev[groupValue]);
62-
}
63-
// remain the x-axis data
64-
prev[groupValue][xAxis] = groupValue;
65-
seriesColumnNames.forEach((key) => {
66-
if (key === xAxis) {
67-
return;
68-
} else if (isNumeric(cur[key])) {
69-
prev[groupValue][key] += Number(cur[key]);
70-
} else {
71-
prev[groupValue][key] += 1;
72-
}
73-
});
74-
return prev;
75-
}, {} as any);
76-
return transformedData;
77-
}
78-
79-
const notAxisChartSet: Set<CharOptionCompType> = new Set(["pie"] as const);
80-
const echartsConfigOmitChildren = ["hidden", "selectedPoints", "onEvent"] as const;
81-
type EchartsConfigProps = Omit<ChartCompPropsType, typeof echartsConfigOmitChildren[number]>;
82-
83-
function isAxisChart(type: CharOptionCompType) {
84-
return !notAxisChartSet.has(type);
85-
}
86-
87-
function getSeriesConfig(props: EchartsConfigProps) {
88-
const visibleSeries = props.series.filter((s) => !s.getView().hide);
89-
const seriesLength = visibleSeries.length;
90-
return visibleSeries.map((s, index) => {
91-
if (isAxisChart(props.chartConfig.type)) {
92-
let encodeX: string, encodeY: string;
93-
const horizontalX = props.xAxisDirection === "horizontal";
94-
let itemStyle = props.chartConfig.itemStyle;
95-
// FIXME: need refactor... chartConfig returns a function with paramters
96-
if (props.chartConfig.type === "bar") {
97-
// barChart's border radius, depend on x-axis direction and stack state
98-
const borderRadius = horizontalX ? [2, 2, 0, 0] : [0, 2, 2, 0];
99-
if (props.chartConfig.stack && index === visibleSeries.length - 1) {
100-
itemStyle = { ...itemStyle, borderRadius: borderRadius };
101-
} else if (!props.chartConfig.stack) {
102-
itemStyle = { ...itemStyle, borderRadius: borderRadius };
103-
}
104-
}
105-
if (horizontalX) {
106-
encodeX = props.xAxisKey;
107-
encodeY = s.getView().columnName;
108-
} else {
109-
encodeX = s.getView().columnName;
110-
encodeY = props.xAxisKey;
111-
}
112-
return {
113-
name: s.getView().seriesName,
114-
selectedMode: "single",
115-
select: {
116-
itemStyle: {
117-
borderColor: "#000",
118-
},
119-
},
120-
encode: {
121-
x: encodeX,
122-
y: encodeY,
123-
},
124-
// each type of chart's config
125-
...props.chartConfig,
126-
itemStyle: itemStyle,
127-
label: {
128-
...props.chartConfig.label,
129-
...(!horizontalX && { position: "outside" }),
130-
},
131-
};
132-
} else {
133-
// pie
134-
const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
135-
return {
136-
...props.chartConfig,
137-
radius: radiusAndCenter.radius,
138-
center: radiusAndCenter.center,
139-
name: s.getView().seriesName,
140-
selectedMode: "single",
141-
encode: {
142-
itemName: props.xAxisKey,
143-
value: s.getView().columnName,
144-
},
145-
};
146-
}
147-
});
148-
}
149-
150-
// https://echarts.apache.org/en/option.html
151-
function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSize): EChartsOption {
152-
if (props.mode === "json") {
153-
return props.echartsOption ? props.echartsOption : {};
154-
}
155-
// axisChart
156-
const axisChart = isAxisChart(props.chartConfig.type);
157-
const gridPos = {
158-
left: 20,
159-
right: props.legendConfig.left === "right" ? "10%" : 20,
160-
top: 50,
161-
bottom: 35,
162-
};
163-
let config: EChartsOption = {
164-
color: chartColorPalette,
165-
title: { text: props.title, left: "center" },
166-
backgroundColor: "#fff",
167-
tooltip: {
168-
confine: true,
169-
trigger: axisChart ? "axis" : "item",
170-
},
171-
legend: props.legendConfig,
172-
grid: {
173-
...gridPos,
174-
containLabel: true,
175-
},
176-
};
177-
if (props.data.length <= 0) {
178-
// no data
179-
return {
180-
...config,
181-
...(axisChart ? noDataAxisConfig : noDataPieChartConfig),
182-
};
183-
}
184-
const yAxisConfig = props.yConfig();
185-
const seriesColumnNames = props.series
186-
.filter((s) => !s.getView().hide)
187-
.map((s) => s.getView().columnName);
188-
// y-axis is category and time, data doesn't need to aggregate
189-
const transformedData =
190-
yAxisConfig.type === "category" || yAxisConfig.type === "time"
191-
? props.data
192-
: transformData(props.data, props.xAxisKey, seriesColumnNames);
193-
config = {
194-
...config,
195-
dataset: [
196-
{
197-
source: transformedData,
198-
sourceHeader: false,
199-
},
200-
],
201-
series: getSeriesConfig(props),
202-
};
203-
if (axisChart) {
204-
// pure chart's size except the margin around
205-
let chartRealSize;
206-
if (chartSize) {
207-
const rightSize =
208-
typeof gridPos.right === "number"
209-
? gridPos.right
210-
: (chartSize.w * parseFloat(gridPos.right)) / 100.0;
211-
chartRealSize = {
212-
// actually it's self-adaptive with the x-axis label on the left, not that accurate but work
213-
w: chartSize.w - gridPos.left - rightSize,
214-
// also self-adaptive on the bottom
215-
h: chartSize.h - gridPos.top - gridPos.bottom,
216-
right: rightSize,
217-
};
218-
}
219-
const finalXyConfig = calcXYConfig(
220-
props.xConfig,
221-
yAxisConfig,
222-
props.xAxisDirection,
223-
transformedData.map((d) => d[props.xAxisKey]),
224-
chartRealSize
225-
);
226-
config = {
227-
...config,
228-
// @ts-ignore
229-
xAxis: finalXyConfig.xConfig,
230-
// @ts-ignore
231-
yAxis: finalXyConfig.yConfig,
232-
};
233-
}
234-
// log.log("Echarts transformedData and config", transformedData, config);
235-
return config;
236-
}
237-
238-
function getSelectedPoints(param: any, option: any) {
239-
const series = option.series;
240-
const dataSource = _.isArray(option.dataset) && option.dataset[0]?.source;
241-
if (series && dataSource) {
242-
return param.selected.flatMap((selectInfo: any) => {
243-
const seriesInfo = series[selectInfo.seriesIndex];
244-
if (!seriesInfo || !seriesInfo.encode) {
245-
return [];
246-
}
247-
return selectInfo.dataIndex.map((index: any) => {
248-
const commonResult = {
249-
seriesName: seriesInfo.name,
250-
};
251-
if (seriesInfo.encode.itemName && seriesInfo.encode.value) {
252-
return {
253-
...commonResult,
254-
itemName: dataSource[index][seriesInfo.encode.itemName],
255-
value: dataSource[index][seriesInfo.encode.value],
256-
};
257-
} else {
258-
return {
259-
...commonResult,
260-
x: dataSource[index][seriesInfo.encode.x],
261-
y: dataSource[index][seriesInfo.encode.y],
262-
};
263-
}
264-
});
265-
});
266-
}
267-
return [];
268-
}
29+
import { ItemColorComp } from "comps/chartComp/chartConfigs/lineChartConfig";
30+
import {
31+
echartsConfigOmitChildren,
32+
getEchartsConfig,
33+
getSelectedPoints,
34+
} from "comps/chartComp/chartUtils";
26935

27036
let ChartTmpComp = (function () {
27137
return new UICompBuilder(chartChildrenMap, () => null)
@@ -353,6 +119,52 @@ function getYAxisFormatContextValue(
353119

354120
ChartTmpComp = class extends ChartTmpComp {
355121
private lastYAxisFormatContextVal?: JSONValue;
122+
private lastColorContext?: JSONObject;
123+
124+
updateContext(comp: this) {
125+
// the context value of axis format
126+
let resultComp = comp;
127+
const data = comp.children.data.getView();
128+
const sampleSeries = comp.children.series.getView().find((s) => !s.getView().hide);
129+
const yAxisContextValue = getYAxisFormatContextValue(
130+
data,
131+
comp.children.yConfig.children.yAxisType.getView(),
132+
sampleSeries?.children.columnName.getView()
133+
);
134+
if (yAxisContextValue !== comp.lastYAxisFormatContextVal) {
135+
comp.lastYAxisFormatContextVal = yAxisContextValue;
136+
resultComp = comp.setChild(
137+
"yConfig",
138+
comp.children.yConfig.reduce(
139+
wrapChildAction(
140+
"formatter",
141+
AxisFormatterComp.changeContextDataAction({ value: yAxisContextValue })
142+
)
143+
)
144+
);
145+
}
146+
// item color context
147+
const colorContextVal = {
148+
seriesName: sampleSeries?.children.seriesName.getView(),
149+
value: yAxisContextValue,
150+
};
151+
if (
152+
comp.children.chartConfig.children.comp.children.hasOwnProperty("itemColor") &&
153+
!_.isEqual(colorContextVal, comp.lastColorContext)
154+
) {
155+
comp.lastColorContext = colorContextVal;
156+
resultComp = resultComp.setChild(
157+
"chartConfig",
158+
comp.children.chartConfig.reduce(
159+
wrapChildAction(
160+
"comp",
161+
wrapChildAction("itemColor", ItemColorComp.changeContextDataAction(colorContextVal))
162+
)
163+
)
164+
);
165+
}
166+
return resultComp;
167+
}
356168

357169
override reduce(action: CompAction): this {
358170
const comp = super.reduce(action);
@@ -370,25 +182,7 @@ ChartTmpComp = class extends ChartTmpComp {
370182
comp.children.series.dispatchDataChanged(newData);
371183
}, 0);
372184
}
373-
// the context value of axis format
374-
const sampleSeries = comp.children.series.getView().find((s) => !s.getView().hide);
375-
const contextValue = getYAxisFormatContextValue(
376-
newData,
377-
comp.children.yConfig.children.yAxisType.getView(),
378-
sampleSeries?.children.columnName.getView()
379-
);
380-
if (contextValue !== comp.lastYAxisFormatContextVal) {
381-
comp.lastYAxisFormatContextVal = contextValue;
382-
return comp.setChild(
383-
"yConfig",
384-
comp.children.yConfig.reduce(
385-
wrapChildAction(
386-
"formatter",
387-
AxisFormatterComp.changeContextDataAction({ value: contextValue })
388-
)
389-
)
390-
);
391-
}
185+
return this.updateContext(comp);
392186
}
393187
return comp;
394188
}

client/packages/openblocks-comps/src/comps/chartComp/chartConfigs/legendConfig.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
import { dropdownControl, MultiCompBuilder } from "openblocks-sdk";
1+
import {
2+
AlignBottom,
3+
AlignClose,
4+
AlignRight,
5+
dropdownControl,
6+
MultiCompBuilder,
7+
} from "openblocks-sdk";
28
import { LegendComponentOption } from "echarts";
39
import { trans } from "i18n/comps";
410

511
const LegendPositionOptions = [
612
{
7-
label: trans("chart.bottom"),
13+
label: <AlignBottom />,
814
value: "bottom",
915
},
1016
{
11-
label: trans("chart.right"),
17+
label: <AlignRight />,
1218
value: "right",
1319
},
1420
{
15-
label: trans("chart.close"),
21+
label: <AlignClose />,
1622
value: "close",
1723
},
1824
] as const;

0 commit comments

Comments
 (0)