@publishfx/publish-chart 2.1.4 → 2.1.6

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/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ **2.1.6** fix(chart): 解决ts错误
2
+ - 2026-03-03T06:38:38+08:00 [fix(chart): 解决ts错误](http://lf.git.oa.mt/publish_platform/web/publish/commit/a1a61c8cbfede6e0993a1101dd4f375ad3383302)
3
+ - 2026-03-03T06:30:33+08:00 [fix(chart): 修复所有遗留问题](http://lf.git.oa.mt/publish_platform/web/publish/commit/73456fbb6fe853fc9069e2c6712851375dd372da)
4
+
5
+ **2.1.5** fix(chart): 修正ts错误
6
+ - 2026-03-03T04:41:18+08:00 [fix(chart): 修正ts错误](http://lf.git.oa.mt/publish_platform/web/publish/commit/ed6bc3403d6c06d8c0cce6f0193a447d74b68a3a)
7
+ - 2026-03-03T04:38:12+08:00 [fix(chart): 组合图代码整理](http://lf.git.oa.mt/publish_platform/web/publish/commit/6a94afb106447e54143f4a29e7d8fa24c718b321)
8
+ - 2026-03-03T01:44:41+08:00 [fix(chart): 修复一些问题](http://lf.git.oa.mt/publish_platform/web/publish/commit/52b05bffc199010d0ed76c112f615937c33edc53)
9
+ - 2026-03-03T00:19:14+08:00 [fix(chart): 辅助线不显示y轴](http://lf.git.oa.mt/publish_platform/web/publish/commit/4a7de7122d0ec4f037b95927f93a2626e7a8837a)
10
+ - 2026-03-03T00:13:56+08:00 [fix: 修复一批组合图和折线图的BUG](http://lf.git.oa.mt/publish_platform/web/publish/commit/c9f3f2a758ae49c94dada96acb859e8e842877ea)
11
+ - 2026-03-02T21:49:02+08:00 [feat(chart): 组合图排序](http://lf.git.oa.mt/publish_platform/web/publish/commit/46000898aac62b20b9c4f1543a0437808e795021)
12
+ - 2026-03-02T20:35:15+08:00 [fix: [f-6849783906] 修复柱状图倒序问题及图例编辑时不展示问题](http://lf.git.oa.mt/publish_platform/web/publish/commit/5fc94ba48503403c2aee24d59fc9ad6e71ce3a2d)
13
+ - 2026-03-02T20:24:49+08:00 [feat(chart): 组合图数据标签](http://lf.git.oa.mt/publish_platform/web/publish/commit/ec86c8ef7479f60c19d9a817212b2b3290a9c00f)
14
+ - 2026-03-02T19:37:12+08:00 [feat: 组合图辅助线](http://lf.git.oa.mt/publish_platform/web/publish/commit/7ea103172eb0f2529bd067a4187c4f9d6387dd0f)
15
+ - 2026-03-02T19:06:57+08:00 [feat(chart): 组合图折线逻辑](http://lf.git.oa.mt/publish_platform/web/publish/commit/c13e0dbf072b4736742a84890394a923495a6c54)
16
+
1
17
  **2.1.4** feat(chart): 柱状、折线、组合三个图表支持onChartClick
2
18
  - 2026-03-02T15:27:33+08:00 [feat(chart): 柱状、折线、组合三个图表支持onChartClick](http://lf.git.oa.mt/publish_platform/web/publish/commit/0cbed83b537a5b0a08f676256b8e69bb9ae0741a)
3
19
  - 2026-03-02T14:38:28+08:00 [fix(chart): 组合图针对副轴数据全为0时,Y刻度的处理。](http://lf.git.oa.mt/publish_platform/web/publish/commit/2b262810e33d3660255831fb7ef0e345907841e9)
@@ -5,6 +5,7 @@ export interface TransformConfig {
5
5
  z?: string;
6
6
  isDescend?: boolean;
7
7
  isHorizontal?: boolean;
8
+ isGroup?: boolean;
8
9
  }
9
10
  /**
10
11
  * 数据适配器类
@@ -13,7 +14,7 @@ export declare class DataAdapter {
13
14
  /**
14
15
  * 统一的数据转换接口
15
16
  */
16
- static transform(data: any[], transformType: TransformConfig['type'], config: TransformConfig): any[];
17
+ static transform(data: any[], transformType: TransformConfig['type'], config: TransformConfig): any;
17
18
  /**
18
19
  * 柱状图数据转换
19
20
  */
@@ -30,10 +31,6 @@ export declare class DataAdapter {
30
31
  * 柱状折线组合图数据转换
31
32
  */
32
33
  private static transformForBarLine;
33
- /**
34
- * 分组柱状折线组合图数据转换
35
- */
36
- private static transformForGroupBarLine;
37
34
  /**
38
35
  * 分组对比图数据转换
39
36
  */
@@ -11,8 +11,6 @@ class DataAdapter {
11
11
  return this.transformForLine(data, config);
12
12
  case 'barLine':
13
13
  return this.transformForBarLine(data, config);
14
- case 'groupBarLine':
15
- return this.transformForGroupBarLine(data, config);
16
14
  case 'groupCompare':
17
15
  return this.transformForGroupCompare(data, config);
18
16
  default:
@@ -30,14 +28,16 @@ class DataAdapter {
30
28
  return row;
31
29
  }
32
30
  });
33
- if (config.isDescend) dv.transform({
34
- type: 'sort',
35
- callback: (a, b)=>{
31
+ if (config.isDescend) {
32
+ const compareData = dv.rows.filter((row)=>String(row.groupType ?? '').includes('_compare'));
33
+ const basicData = dv.rows.filter((row)=>!String(row.groupType ?? '').includes('_compare'));
34
+ basicData.sort((a, b)=>{
36
35
  if ('-' === a[config.y]) return config.isHorizontal ? -1 : 1;
37
36
  if ('-' === b[config.y]) return config.isHorizontal ? 1 : -1;
38
- return b[config.y] - a[config.y];
39
- }
40
- });
37
+ return config.isHorizontal ? a[config.y] - b[config.y] : b[config.y] - a[config.y];
38
+ });
39
+ dv.rows = basicData.concat(compareData);
40
+ }
41
41
  }
42
42
  return dv.rows;
43
43
  }
@@ -113,28 +113,10 @@ class DataAdapter {
113
113
  return dv.rows;
114
114
  }
115
115
  static transformForBarLine(data, config) {
116
- const ds = new lib();
117
- const dv = ds.createView().source(data);
118
- if (config.y) dv.transform({
119
- type: 'map',
120
- callback: (row)=>{
121
- row[config.y] = '' !== row[config.y] ? +row[config.y] : '-';
122
- return row;
123
- }
124
- });
125
- if (config.z) dv.transform({
126
- type: 'map',
127
- callback: (row)=>{
128
- row[config.z] = '' !== row[config.z] ? +row[config.z] : '-';
129
- return row;
130
- }
131
- });
132
- return dv.rows;
133
- }
134
- static transformForGroupBarLine(data, config) {
135
116
  const ds = new lib();
136
117
  const dv = ds.createView().source(data);
137
118
  const tdv = ds.createView().source(data);
119
+ const combinedv = ds.createView().source(data);
138
120
  if (config.y && config.x) {
139
121
  dv.transform({
140
122
  type: 'map',
@@ -156,28 +138,19 @@ class DataAdapter {
156
138
  return !row.isCombine;
157
139
  }
158
140
  });
159
- tdv.transform({
141
+ combinedv.transform({
160
142
  type: 'filter',
161
143
  callback (row) {
162
- return !row[config.y] || '-' !== row[config.y];
144
+ return row.isCombine;
163
145
  }
164
146
  });
165
147
  tdv.transform({
166
- type: 'aggregate',
167
- fields: [
168
- config.y
169
- ],
170
- groupBy: [
171
- config.x
172
- ],
173
- operations: [
174
- 'sum'
175
- ],
176
- as: [
177
- 'total'
178
- ]
148
+ type: 'filter',
149
+ callback (row) {
150
+ return !row[config.y] || '-' !== row[config.y];
151
+ }
179
152
  });
180
- tdv.transform({
153
+ if (config.isDescend) tdv.transform({
181
154
  type: 'sort',
182
155
  callback (a, b) {
183
156
  return b.total - a.total;
@@ -185,16 +158,15 @@ class DataAdapter {
185
158
  });
186
159
  const xKey = config.x ?? 'x';
187
160
  const sortData = tdv.rows.map((item)=>item[xKey]);
188
- if (config.isDescend) dv.transform({
189
- type: 'sort',
190
- callback (a, b) {
191
- if (-1 === sortData.indexOf(a[xKey])) return 1;
192
- if (-1 === sortData.indexOf(b[xKey])) return -1;
193
- return sortData.indexOf(a[xKey]) - sortData.indexOf(b[xKey]);
194
- }
195
- });
161
+ if (config.isDescend) combinedv.rows.sort((a, b)=>sortData.indexOf(a[xKey]) - sortData.indexOf(b[xKey]));
162
+ else combinedv.rows.sort((a, b)=>sortData.indexOf(a[xKey]) - sortData.indexOf(b[xKey]));
196
163
  }
197
- return dv.rows;
164
+ console.log('renderG2CombineChart transformedData:leftData:', tdv.rows, 'rightData:', combinedv.rows);
165
+ return {
166
+ transformedData: dv.rows,
167
+ leftData: tdv.rows,
168
+ rightData: combinedv.rows
169
+ };
198
170
  }
199
171
  static transformForGroupCompare(data, _config) {
200
172
  return data;
@@ -184,6 +184,7 @@ const G2BarChart = ({ height = 300, data, x = "", y = "", indicatorMap, onChartC
184
184
  isDataTag,
185
185
  isLegend,
186
186
  isHighlight,
187
+ isCompare,
187
188
  maxY,
188
189
  themeColors,
189
190
  safeIndicatorMap,
@@ -200,7 +201,7 @@ const G2BarChart = ({ height = 300, data, x = "", y = "", indicatorMap, onChartC
200
201
  position: "relative"
201
202
  },
202
203
  children: [
203
- isLegend && legendItems.length > 1 && 2 === nodeSetting.showType && /*#__PURE__*/ jsx(G2BarLegend, {
204
+ (isLegend || isCompare) && legendItems.length > 1 && 2 === nodeSetting.showType && /*#__PURE__*/ jsx(G2BarLegend, {
204
205
  items: legendItems,
205
206
  activeIds: activeIds,
206
207
  onChange: setActiveIds,
@@ -215,7 +216,7 @@ const G2BarChart = ({ height = 300, data, x = "", y = "", indicatorMap, onChartC
215
216
  height: `${height - canvasMarginBottom}px`
216
217
  }
217
218
  }),
218
- isLegend && legendItems.length > 1 && 2 !== nodeSetting.showType && /*#__PURE__*/ jsx(G2BarLegend, {
219
+ (isLegend || isCompare) && legendItems.length > 1 && 2 !== nodeSetting.showType && /*#__PURE__*/ jsx(G2BarLegend, {
219
220
  items: legendItems,
220
221
  activeIds: activeIds,
221
222
  onChange: setActiveIds,
@@ -17,14 +17,12 @@ const G2BarLegend = ({ items, activeIds, onChange, pageSize: pageSizeProp, maxLa
17
17
  const availableWidth = Math.max(0, width - pagerReserveWidth);
18
18
  const estimatedItemWidths = items.map((item)=>{
19
19
  const labelLength = item.label ? item.label.length : 0;
20
- console.log("computePageSize: estimatedItemWidths:", item.label, labelLength);
21
20
  const visibleChars = labelLength;
22
21
  return 16 + 8 * visibleChars;
23
22
  });
24
23
  const avgItemWidth = estimatedItemWidths.reduce((sum, w)=>sum + w, 0) / estimatedItemWidths.length;
25
24
  if (!avgItemWidth || !isFinite(avgItemWidth)) return void setAutoPageSize(items.length || 1);
26
25
  const computed = Math.max(1, Math.floor(availableWidth / avgItemWidth));
27
- console.log("computePageSize: computed:", computed, availableWidth, avgItemWidth, estimatedItemWidths);
28
26
  setAutoPageSize(computed);
29
27
  setPage((p)=>Math.min(p, Math.max(1, Math.ceil(items.length / computed))));
30
28
  };
@@ -51,6 +49,7 @@ const G2BarLegend = ({ items, activeIds, onChange, pageSize: pageSizeProp, maxLa
51
49
  ]);
52
50
  const handleToggle = (id)=>{
53
51
  const isActive = activeIds.includes(id);
52
+ if (isActive && activeIds.length <= 1) return;
54
53
  const nextSet = new Set(isActive ? activeIds.filter((x)=>x !== id) : [
55
54
  ...activeIds,
56
55
  id
@@ -63,68 +63,65 @@ const G2CombineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorM
63
63
  }, indicators = [], auxiliaryLineData, highlightDate, timeRange, isGroup })=>{
64
64
  const { formatter, dataTransform, config: contextConfig } = useChartContext();
65
65
  const tooltipContainerRef = useG2TooltipContainer();
66
+ const isGroupRef = useRef(isGroup);
67
+ const [isOldData, setIsOldData] = useState(false);
68
+ isGroupRef.current = isGroup;
66
69
  console.log("G2CombineChart props:", data, x, y, z, indicators, legend, config, nodeSetting);
67
70
  const themeColors = contextConfig?.theme?.colors || [];
68
71
  const safeIndicatorMap = indicatorMap || contextConfig.indicatorMap || {};
69
- const safeLegend = useMemo(()=>{
70
- if (isGroup) return legend || 'groupType';
71
- if ("groupType" === legend) return y;
72
- return legend || y;
73
- }, [
74
- isGroup,
75
- legend,
76
- y
77
- ]);
72
+ const safeLegend = legend || "groupType";
78
73
  const safeZ = z;
79
74
  const safeY = "groupValue";
80
75
  const isCompare = timeRange?.compareStartTime !== "";
81
76
  const chartConfig = config || {};
82
- const { isDataTag = false, isLegend = true, isDescend = false } = chartConfig;
77
+ const { isDataTag = true, isCombineDataTag = true, isLegend = true, isDescend = false, isHighlight = true } = chartConfig;
83
78
  const [xyList, setXYList] = useState([]);
84
79
  const [activeIds, setActiveIds] = useState([]);
85
80
  const [legendItems, setLegendItems] = useState([]);
86
- const transformedData = useMemo(()=>{
81
+ const { transformedData, leftData, rightData } = useMemo(()=>{
82
+ console.log("G2CombineChart left useMemo: legend:", legend, data, x, y, z, indicators);
87
83
  if (!legend) {
88
84
  let result = transformDataToGroupBarLineFormat(data, x, y, indicators);
89
85
  data = result;
86
+ setIsOldData(true);
90
87
  }
91
- console.log("G2CombineChart transformedData2:", data);
92
- const result = isGroup ? DataAdapter.transform(data, "groupBarLine", {
93
- type: "barLine",
94
- x,
95
- y: safeY,
96
- isDescend
97
- }) : DataAdapter.transform(data, "barLine", {
88
+ console.log("G2CombineChart renderG2CombineChart transformedData2:", data);
89
+ const { transformedData, leftData, rightData } = DataAdapter.transform(data, "barLine", {
98
90
  type: "barLine",
99
91
  x,
100
92
  y: safeY,
101
- z
93
+ z,
94
+ isDescend,
95
+ isGroup: isGroupRef.current
102
96
  });
103
- return result.map((item)=>dataTransform.processNodeInfo(item, contextConfig.nodeMap));
97
+ return {
98
+ transformedData: transformedData.map((item)=>dataTransform.processNodeInfo(item, contextConfig.nodeMap)),
99
+ leftData: leftData.map((item)=>dataTransform.processNodeInfo(item, contextConfig.nodeMap)),
100
+ rightData: rightData.map((item)=>dataTransform.processNodeInfo(item, contextConfig.nodeMap))
101
+ };
104
102
  }, [
105
- isGroup,
106
103
  data,
107
104
  x,
108
105
  safeY,
109
106
  z,
110
107
  dataTransform,
111
- contextConfig.nodeMap
108
+ contextConfig.nodeMap,
109
+ isDescend
112
110
  ]);
113
- console.log("G2CombineChart transformedData:", transformedData);
111
+ console.log("G2CombineChart transformedData:", transformedData, leftData, rightData);
114
112
  const { maxY } = useMemo(()=>{
115
- const key = isGroup ? "total" : safeY;
113
+ const key = isGroupRef.current ? "total" : safeY;
116
114
  const yValues = transformedData.filter((item)=>"-" !== item[key] && void 0 !== item[key] && null !== item[key]).map((item)=>Number(item[key]) || 0);
117
115
  return {
118
116
  maxY: yValues.length > 0 ? Math.max(...yValues) : 0
119
117
  };
120
118
  }, [
121
- isGroup,
122
119
  transformedData,
123
120
  safeY
124
121
  ]);
125
- const formatAxis = useCallback((val)=>getAxisFormat(val, safeIndicatorMap, safeLegend), [
122
+ const formatAxis = useCallback((val)=>getAxisFormat(val, safeIndicatorMap, y), [
126
123
  safeIndicatorMap,
127
- safeLegend
124
+ y
128
125
  ]);
129
126
  const formatLineAxis = useCallback((val)=>getAxisFormat(val, safeIndicatorMap, safeZ), [
130
127
  safeIndicatorMap,
@@ -143,31 +140,53 @@ const G2CombineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorM
143
140
  const indicatorId = id.split("_")[0];
144
141
  const isCombine = "true" === id.split("_")[1];
145
142
  const lineGroupTypeIndex = lineGroupTypeArr.indexOf(id);
146
- const color = isGroup ? isCombine ? lineColors[lineGroupTypeIndex] || lineColors[0] : themeColors[colorIndex] || themeColors[0] : 0 === colorIndex ? themeColors[colorIndex] : lineColors[colorIndex - 1];
143
+ const color = isGroupRef.current ? isCombine ? lineColors[lineGroupTypeIndex] || lineColors[0] : themeColors[colorIndex] || themeColors[0] : 0 === colorIndex ? themeColors[colorIndex] : lineColors[colorIndex - 1];
147
144
  return {
148
145
  id,
149
- label: isGroup ? isCombine ? getIndicatorCompareName(safeIndicatorMap, String(indicatorId)) : indicatorId : getIndicatorCompareName(safeIndicatorMap, String(indicatorId)) + (String(indicatorId).includes("_compare") ? `(对比时间:${timeRange?.compareStartTime}~${timeRange?.compareEndTime})` : ""),
146
+ label: isGroupRef.current ? isCombine ? getIndicatorCompareName(safeIndicatorMap, String(indicatorId)) : indicatorId : getIndicatorCompareName(safeIndicatorMap, String(indicatorId)) + (String(indicatorId).includes("_compare") ? `(对比时间:${timeRange?.compareStartTime}~${timeRange?.compareEndTime})` : ""),
150
147
  color,
151
148
  isCompare: String(indicatorId).includes("_compare"),
152
- symbol: isGroup ? isCombine ? 'line' : 'square' : 0 === colorIndex ? "square" : "line"
149
+ symbol: isGroupRef.current ? isCombine ? "line" : "square" : 0 === colorIndex ? "square" : "line"
153
150
  };
154
151
  });
155
152
  setLegendItems(items);
156
- if (!activeIds.length) setActiveIds(items.map((i)=>i.id));
153
+ const newIds = items.map((i)=>i.id);
154
+ setActiveIds((prev)=>{
155
+ if (!prev.length) return newIds;
156
+ const newSet = new Set(newIds);
157
+ if (prev.length !== newIds.length || prev.some((id)=>!newSet.has(id))) return newIds;
158
+ return prev;
159
+ });
157
160
  }, [
158
- isGroup,
159
161
  transformedData,
160
162
  safeIndicatorMap,
161
- themeColors,
162
- activeIds.length
163
+ themeColors
163
164
  ]);
164
- const filteredData = useMemo(()=>{
165
- if (!activeIds.length) return transformedData;
166
- return transformedData.filter((d)=>d.groupType ? activeIds.includes(String(d.groupType.replace("_compare", "") + "_" + d.isCombine)) : true);
165
+ const filterLeftData = useMemo(()=>{
166
+ if (!activeIds.length) return leftData;
167
+ const result = leftData.filter((d)=>d.groupType ? activeIds.includes(String(d.groupType.replace("_compare", "") + "_" + d.isCombine)) : true);
168
+ if (isGroupRef.current) result.forEach((item)=>{
169
+ item.total = result.filter((d)=>d.groupName === item.groupName).reduce((acc, d)=>acc + d.groupValue, 0);
170
+ });
171
+ return result;
167
172
  }, [
168
- transformedData,
173
+ leftData,
174
+ activeIds
175
+ ]);
176
+ const filterRightData = useMemo(()=>{
177
+ if (!activeIds.length) return rightData;
178
+ return rightData.filter((d)=>d.groupType ? activeIds.includes(String(d.groupType.replace("_compare", "") + "_" + d.isCombine)) : true);
179
+ }, [
180
+ rightData,
169
181
  activeIds
170
182
  ]);
183
+ const filteredData = useMemo(()=>[
184
+ ...filterLeftData,
185
+ ...filterRightData
186
+ ], [
187
+ filterLeftData,
188
+ filterRightData
189
+ ]);
171
190
  const canvasMarginBottom = legendItems.length > 1 ? 2 === nodeSetting.showType ? 32 : 16 : 0;
172
191
  const containerRef = useRef(null);
173
192
  const chartRef = useRef(null);
@@ -192,13 +211,17 @@ const G2CombineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorM
192
211
  formatAxis,
193
212
  formatLineAxis,
194
213
  isDataTag,
214
+ isCombineDataTag,
195
215
  isLegend: false,
196
216
  isCompare,
217
+ isHighlight,
197
218
  isClickable: !!onChartClick,
198
219
  height: height - canvasMarginBottom,
199
- formatLabel: isDataTag ? (d)=>{
220
+ formatLabel: (isDataTag || isCombineDataTag) && !isOldData ? (d)=>{
200
221
  const indicatorId = d.groupType.split("_")[0];
201
- return isGroup ? d.isCombine ? "-" === formatter.formatIndicator(d.groupValue, safeIndicatorMap[z]) ? "" : formatter.formatIndicator(d.groupValue, safeIndicatorMap[z]) : "-" === formatter.formatIndicator(d['total'], safeIndicatorMap[y]) ? "" : formatter.formatIndicator(d['total'], safeIndicatorMap[y]) : "-" === formatter.formatIndicator(d.groupValue, safeIndicatorMap[indicatorId]) ? "" : formatter.formatIndicator(d.groupValue, safeIndicatorMap[indicatorId]);
222
+ const key = isGroupRef.current && !d.isCombine ? "total" : indicatorId;
223
+ if ("total" === key) return formatter.formatIndicator(d.total, safeIndicatorMap[y]);
224
+ return formatter.formatIndicator(d.groupValue, safeIndicatorMap[key]);
202
225
  } : void 0,
203
226
  indicators,
204
227
  tooltipRender: (title, items)=>{
@@ -210,7 +233,7 @@ const G2CombineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorM
210
233
  }));
211
234
  safeItems = safeItems.filter((i)=>i.indicatorId);
212
235
  react_dom.render(/*#__PURE__*/ jsx(G2CompareTooltip, {
213
- isGroupBar: isGroup,
236
+ isGroupBar: isGroupRef.current,
214
237
  title: title,
215
238
  items: safeItems,
216
239
  safeIndicatorMap: safeIndicatorMap,
@@ -222,6 +245,7 @@ const G2CombineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorM
222
245
  },
223
246
  highlightDate: highlightDate ?? [],
224
247
  timeRange,
248
+ auxiliaryLineData: auxiliaryLineData ?? [],
225
249
  onNodeListChange: (nodes)=>{
226
250
  setXYList(nodes);
227
251
  },
@@ -233,9 +257,9 @@ const G2CombineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorM
233
257
  }
234
258
  },
235
259
  lineColors,
236
- isGroup,
237
260
  legendItems,
238
- indicatorId: y
261
+ indicatorId: y,
262
+ isGroup: isGroupRef.current
239
263
  });
240
264
  chartRef.current = chart;
241
265
  if (onChartClick) chart.on("element:click", (e)=>{
@@ -259,15 +283,22 @@ const G2CombineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorM
259
283
  z,
260
284
  maxY,
261
285
  isDataTag,
262
- isLegend,
286
+ isCombineDataTag,
287
+ isDescend,
263
288
  themeColors,
264
289
  safeIndicatorMap,
265
290
  formatter,
266
291
  formatAxis,
267
292
  onChartClick,
268
- isGroup,
269
- legendItems
293
+ isLegend,
294
+ isHighlight,
295
+ legendItems,
296
+ auxiliaryLineData,
297
+ leftData,
298
+ rightData,
299
+ isOldData
270
300
  ]);
301
+ console.log("G2BarLegend items: combie", legendItems, activeIds);
271
302
  return /*#__PURE__*/ jsxs("div", {
272
303
  style: {
273
304
  width: "100%",
@@ -16,7 +16,7 @@ const G2LineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorMap,
16
16
  showType: 0,
17
17
  type: []
18
18
  }, auxiliaryLineData, highlightDate, indicators = [] })=>{
19
- console.log("props data: G2LineChart", data, nodeSetting, config, "x, y, z", x, y, z, "highlightDate:", highlightDate);
19
+ console.log("props data: G2LineChart", data, nodeSetting, config, "x, y, z", x, y, z, "highlightDate:", highlightDate, "indicators:", indicators);
20
20
  const { formatter, dataTransform, config: contextConfig } = useChartContext();
21
21
  const tooltipContainerRef = useG2TooltipContainer();
22
22
  const themeColors = contextConfig?.theme?.colors || [];
@@ -94,12 +94,17 @@ const G2LineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorMap,
94
94
  };
95
95
  });
96
96
  setLegendItems(items);
97
- if (!activeIds.length) setActiveIds(items.map((i)=>i.id));
97
+ const newIds = items.map((i)=>i.id);
98
+ setActiveIds((prev)=>{
99
+ if (!prev.length) return newIds;
100
+ const newSet = new Set(newIds);
101
+ if (prev.length !== newIds.length || prev.some((id)=>!newSet.has(id))) return newIds;
102
+ return prev;
103
+ });
98
104
  }, [
99
105
  transformedData,
100
106
  safeIndicatorMap,
101
- themeColors,
102
- activeIds.length
107
+ themeColors
103
108
  ]);
104
109
  const filteredData = useMemo(()=>{
105
110
  if (!activeIds.length) return transformedData;
@@ -123,7 +128,7 @@ const G2LineChart = ({ height = 300, data, x = "", y = "", z = "", indicatorMap,
123
128
  minY,
124
129
  maxY,
125
130
  themeColors,
126
- indicators,
131
+ indicators: indicators || activeIds,
127
132
  indicatorMap: safeIndicatorMap,
128
133
  formatAxis,
129
134
  highlightDate: highlightDate ?? [],
@@ -216,14 +216,18 @@ export interface ScaleYLinearOptions {
216
216
  export declare function applyScaleYLinear(view: any, options?: ScaleYLinearOptions): void;
217
217
  export interface ApplyAxisXOptions {
218
218
  title?: boolean;
219
- labelAutoHide?: 'greedy' | 'equidistant' | boolean;
220
- /** 为 true 时刻度过多会自动旋转为垂直;为 false 时保持水平,依赖 labelAutoHide 避免重叠。默认 false */
221
- labelAutoRotate?: boolean;
219
+ isHorizontal?: boolean;
222
220
  grid?: boolean;
223
221
  gridStroke?: string;
224
222
  gridLineWidth?: number;
225
223
  gridLineDash?: number[];
226
224
  gridFilter?: (val: any) => boolean;
225
+ /** X 轴数据点数量,用于计算 label/tick 采样间隔 */
226
+ dataCount?: number;
227
+ /** 图表容器宽度(px),用于计算可容纳的最大标签数 */
228
+ containerWidth?: number;
229
+ /** 单个标签最大显示字符数,超出截断。默认 6 */
230
+ maxLabelChars?: number;
227
231
  }
228
232
  /** 配置 X 轴,挂载在 mark 上(如 line / interval) */
229
233
  export declare function applyAxisX(mark: any, options?: ApplyAxisXOptions): void;
@@ -271,6 +275,7 @@ export declare function applyLegendColor(view: any, options?: ApplyLegendColorOp
271
275
  export interface AuxiliaryLineItem {
272
276
  name: string;
273
277
  value: number;
278
+ axis?: 'left' | 'right';
274
279
  }
275
280
  export interface ApplyAuxiliaryLineYOptions {
276
281
  stroke?: string;
@@ -291,3 +296,4 @@ export interface GetColorByGroupTypeOptions {
291
296
  */
292
297
  export declare function getColorByGroupType(groupType: string, options: GetColorByGroupTypeOptions): string;
293
298
  export declare function applyHighlightDate(view: any, x: string, data: any, highlightDate: string[], isHighlight: boolean): void;
299
+ export declare function adjustDomainMax(domainMin: number, domainMax: number): number;
@@ -19,7 +19,7 @@ function applyScaleYLinear(view, options = {}) {
19
19
  [field]: {
20
20
  type: 'linear',
21
21
  ...null != domainMax && {
22
- domainMax
22
+ domainMax: adjustDomainMax(domainMin, domainMax)
23
23
  },
24
24
  domainMin,
25
25
  clamp,
@@ -37,10 +37,30 @@ function applyScaleYLinear(view, options = {}) {
37
37
  }
38
38
  });
39
39
  }
40
+ function calcAxisInterval(dataCount, containerWidth, maxLabelChars, fontSize) {
41
+ if (dataCount <= 1) return 1;
42
+ const charWidth = 0.55 * fontSize;
43
+ const ellipsisWidth = Math.min(maxLabelChars * charWidth, 90);
44
+ const slotWidth = ellipsisWidth + 8;
45
+ const plotWidth = 0.85 * containerWidth;
46
+ const maxLabels = Math.max(Math.floor(plotWidth / slotWidth), 2);
47
+ return Math.max(Math.ceil(dataCount / maxLabels), 1);
48
+ }
49
+ function createAxisFilter(type, interval, _dataCount) {
50
+ return (_datum, index)=>{
51
+ if (0 === index) return true;
52
+ return 'tick' === type ? index % interval === 0 : true;
53
+ };
54
+ }
40
55
  function applyAxisX(mark, options = {}) {
41
- const { title = false, labelAutoRotate = false, grid = true, gridStroke = '#333', gridLineWidth = 50, gridLineDash = [
56
+ const { title = false, isHorizontal = false, grid = true, gridStroke = '#333', gridLineWidth = 50, gridLineDash = [
42
57
  1000
43
- ], gridFilter } = options;
58
+ ], gridFilter, dataCount = 0, containerWidth = 0, maxLabelChars = 30 } = options;
59
+ const fontSize = 12;
60
+ const needSampling = dataCount > 0 && containerWidth > 0;
61
+ const interval = needSampling ? calcAxisInterval(dataCount, containerWidth, maxLabelChars, fontSize) : 1;
62
+ const axisFilter = needSampling && interval > 1 ? (type)=>createAxisFilter(type, interval, dataCount) : null;
63
+ console.log('axisFilter:', interval);
44
64
  mark.axis('x', {
45
65
  title,
46
66
  grid,
@@ -50,16 +70,20 @@ function applyAxisX(mark, options = {}) {
50
70
  ...gridFilter && {
51
71
  gridFilter
52
72
  },
53
- labelAutoHide: {
54
- type: 'hide',
55
- keepHeader: true,
56
- keepTail: true
57
- },
58
- labelAutoRotate,
59
- labelFontSize: 12,
73
+ labelAutoRotate: false,
74
+ labelFontSize: fontSize,
60
75
  labelFontColor: '#000',
61
- labelAutoEllipsis: true,
62
- labelFontOpacity: 1
76
+ size: isHorizontal ? Math.floor(containerWidth / 5) : 30,
77
+ labelAutoEllipsis: {
78
+ type: 'ellipsis',
79
+ maxLength: isHorizontal ? Math.floor(containerWidth / 5) : Math.min(maxLabelChars * fontSize * 0.55, 90)
80
+ },
81
+ labelAlign: 'horizontal',
82
+ labelDx: (_datum, _index, _data)=>0,
83
+ ...axisFilter && {
84
+ tickFilter: axisFilter('tick'),
85
+ labelFilter: axisFilter('label')
86
+ }
63
87
  });
64
88
  }
65
89
  function applyAxisY(mark, options = {}) {
@@ -123,11 +147,16 @@ function applyAuxiliaryLineY(view, lines, options = {}) {
123
147
  console.log('applyAuxiliaryLineY:', lines);
124
148
  const { stroke = '#F4664A', strokeOpacity = 1, labelMaxLength = 5 } = options;
125
149
  lines.forEach((auxLine)=>{
126
- view.lineY().encode('y', auxLine.value).style('stroke', stroke).style('strokeOpacity', strokeOpacity).style('lineWidth', 1).style('shadowColor', 'transparent').label({
150
+ view.lineY().encode('y', auxLine.value).style('stroke', stroke).style('strokeOpacity', strokeOpacity).style('lineWidth', 1).style('shadowColor', 'transparent').axis('y', {
151
+ title: false,
152
+ grid: false,
153
+ label: false,
154
+ tick: false
155
+ }).label({
127
156
  dx: 0,
128
157
  dy: -5,
129
158
  text: splitTextToMultiline(auxLine.name, labelMaxLength, '<br />'),
130
- position: 'bottom-left',
159
+ position: auxLine?.axis === 'right' ? 'bottom-right' : 'bottom-left',
131
160
  style: {},
132
161
  render: (text)=>`<div style="background-color: transparent; padding: 5px; border-radius: 5px;font-size: 12px;position: absolute;left: -4px; top: -24px; width: 100px;color: ${stroke}; font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;">${text}</div>`
133
162
  });
@@ -179,4 +208,7 @@ function applyHighlightDate(view, x, data, highlightDate, isHighlight) {
179
208
  strokeOpacity: 0
180
209
  });
181
210
  }
182
- export { applyAuxiliaryLineY, applyAxisX, applyAxisY, applyHighlightDate, applyLegendColor, applyScaleYLinear, createChart, getColorByGroupType, getMainView };
211
+ function adjustDomainMax(domainMin, domainMax) {
212
+ return domainMax === domainMin ? domainMin > 0 ? 1.1 * domainMax : domainMax + 1 : domainMax;
213
+ }
214
+ export { adjustDomainMax, applyAuxiliaryLineY, applyAxisX, applyAxisY, applyHighlightDate, applyLegendColor, applyScaleYLinear, createChart, getColorByGroupType, getMainView };
@@ -20,7 +20,7 @@ function renderG2BarChart(container, options) {
20
20
  });
21
21
  console.log('isLegend:', legend, isLegend);
22
22
  const view = getMainView(chart);
23
- view.attr('marginBottom', isLegend ? 0 : 16);
23
+ view.attr('marginLeft', 0).attr('marginRight', 0).attr('marginBottom', isLegend ? 0 : 16);
24
24
  if (isHorizontal) view.coordinate({
25
25
  transform: [
26
26
  {
@@ -46,13 +46,13 @@ function renderG2BarChart(container, options) {
46
46
  columnWidthRatio: 0.6,
47
47
  insetLeft: (_d, index, _data, _column)=>{
48
48
  if (isHorizontal) return 0;
49
- if (isCompare) return index % 2 === 0 ? 4 : 0;
50
- return 4;
49
+ if (isCompare) return index % 2 === 0 ? 0 : 0;
50
+ return 0;
51
51
  },
52
52
  insetRight: (_d, index, _data, _column)=>{
53
53
  if (isHorizontal) return 0;
54
- if (isCompare) return index % 2 === 1 ? 4 : 0;
55
- return 4;
54
+ if (isCompare) return index % 2 === 1 ? 0 : 0;
55
+ return 0;
56
56
  },
57
57
  fill: (datum)=>{
58
58
  const groupType = String(datum.groupType ?? '');
@@ -71,13 +71,27 @@ function renderG2BarChart(container, options) {
71
71
  },
72
72
  cursor: isClickable ? 'pointer' : 'default'
73
73
  });
74
+ const xValueCount = new Set(data.map((d)=>d[x])).size;
74
75
  applyAxisX(view, {
75
- grid: false
76
+ grid: false,
77
+ isHorizontal,
78
+ dataCount: xValueCount,
79
+ containerWidth: container.clientWidth
76
80
  });
77
81
  applyAxisY(view, {
78
82
  labelFormatter: formatAxis
79
83
  });
80
84
  view.legend(false);
85
+ view.interaction('poptip', {
86
+ offsetY: -20,
87
+ offsetX: 0,
88
+ tipBackgroundColor: '#fcfcfc',
89
+ tipColor: '#333',
90
+ tipBorderRadius: '6px',
91
+ tipPadding: '10px 12px',
92
+ tipFontSize: '12px',
93
+ tipBoxShadow: '0 3px 6px -4px rgba(0, 0, 0, 1)'
94
+ });
81
95
  interval.tooltip({
82
96
  items: [
83
97
  (datum)=>({
@@ -104,11 +118,18 @@ function renderG2BarChart(container, options) {
104
118
  }) : null
105
119
  ].filter(Boolean)
106
120
  });
121
+ const tooltipBounding = {
122
+ x: 0,
123
+ y: 0,
124
+ width: container.clientWidth || container.offsetWidth,
125
+ height: height
126
+ };
107
127
  if (tooltipRender) view.interaction('tooltip', {
108
128
  shared: true,
109
129
  crosshairsY: true,
110
130
  wait: 100,
111
131
  marker: false,
132
+ bounding: tooltipBounding,
112
133
  render: (_event, payload)=>{
113
134
  const { title, items } = payload;
114
135
  const lastIsChange = items.filter((item)=>item.isChange);
@@ -120,7 +141,9 @@ function renderG2BarChart(container, options) {
120
141
  return tooltipRender(title, safeItems ?? []) ?? null;
121
142
  }
122
143
  });
123
- else view.interaction('tooltip', true);
144
+ else view.interaction('tooltip', {
145
+ bounding: tooltipBounding
146
+ });
124
147
  applyAuxiliaryLineY(view, auxiliaryLineData);
125
148
  const nodeList = [];
126
149
  console.log('nodeList data:', data);
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { Chart } from "@antv/g2";
6
6
  import type { ChartTimeRange, IndicatorInfo } from "../../../core/ChartTypes";
7
+ import { type AuxiliaryLineItem } from "./g2Helpers";
7
8
  export interface RenderG2CombineChartOptions {
8
9
  /** 已转换后的数据:约定包含 groupName、groupType、groupValue 等字段 */
9
10
  data: any[];
@@ -27,6 +28,8 @@ export interface RenderG2CombineChartOptions {
27
28
  formatLineAxis: (val: any) => string;
28
29
  /** 是否显示数据标签 */
29
30
  isDataTag?: boolean;
31
+ /** 是否显示副轴数据标签 */
32
+ isCombineDataTag?: boolean;
30
33
  /** 是否显示自定义 Legend(仅影响 React 层,G2 内部不画 legend) */
31
34
  isLegend?: boolean;
32
35
  /** 是否启用图例高亮 */
@@ -50,7 +53,12 @@ export interface RenderG2CombineChartOptions {
50
53
  value: any;
51
54
  isChange?: boolean;
52
55
  compareTime?: string;
53
- }>) => HTMLElement | null;
56
+ }>, position?: {
57
+ left: number;
58
+ top: number;
59
+ }) => HTMLElement | null;
60
+ /** 辅助线 */
61
+ auxiliaryLineData?: AuxiliaryLineItem[];
54
62
  /** 将计算出的节点信息传递给外层(用于渲染 NodePopover 等) */
55
63
  onNodeListChange?: (nodes: Array<{
56
64
  pointP: {
@@ -1,11 +1,11 @@
1
1
  import { lines } from "@antv/g-pattern";
2
- import { applyAxisX, applyHighlightDate, createChart, getMainView } from "./g2Helpers.js";
2
+ import { applyAuxiliaryLineY, applyAxisX, applyHighlightDate, createChart, getMainView } from "./g2Helpers.js";
3
3
  import { getIndicatorCompareName } from "../../../utils/indicatorHelpers.js";
4
4
  const Y_AXIS_FIELD = 'groupValue';
5
5
  const BAR_Y_FIELD = 'barValue';
6
6
  const LINE_Y_FIELD = 'lineValue';
7
7
  function renderG2CombineChart(container, options) {
8
- const { data, x, y, z: _z, maxY, themeColors, indicatorMap: _indicatorMap, formatAxis, formatLineAxis, isDataTag = false, isLegend: _isLegend = false, isCompare = false, formatLabel, highlightDate = [], height = 300, isHighlight = true, isClickable = false, indicators = [], tooltipRender, onNodeListChange, lineColors, onChartRender, isGroup, legendItems = [], indicatorId } = options;
8
+ const { data, x, y, z: _z, maxY, themeColors, indicatorMap: _indicatorMap, formatAxis, formatLineAxis, isDataTag = true, isCombineDataTag = true, isLegend: _isLegend = false, isCompare = false, formatLabel, highlightDate = [], height = 300, isHighlight = true, isClickable = false, indicators = [], tooltipRender, auxiliaryLineData = [], onNodeListChange, lineColors, onChartRender, isGroup, legendItems = [], indicatorId } = options;
9
9
  const chart = createChart({
10
10
  container,
11
11
  height,
@@ -17,25 +17,32 @@ function renderG2CombineChart(container, options) {
17
17
  insetBottom: 0
18
18
  });
19
19
  const view = getMainView(chart);
20
- view.attr('marginBottom', 0);
20
+ view.attr('insetLeft', 0).attr('insetRight', 0).attr('marginLeft', 0).attr('marginRight', 0).attr('marginBottom', 0);
21
21
  console.log('renderG2CombineChart data:', data, 'maxY:', maxY, x, y, 'indicators:', indicators);
22
22
  view.data(data);
23
+ const xValueCount = new Set(data.map((d)=>d[x])).size;
23
24
  applyAxisX(view, {
24
25
  title: false,
25
- grid: false
26
+ grid: false,
27
+ dataCount: xValueCount,
28
+ containerWidth: container.clientWidth
26
29
  });
27
30
  const barColor = themeColors[0] ?? "#5B8FF9";
28
31
  console.log('tooltip: themeColors:', themeColors[0], barColor, isGroup);
29
- console.log('renderG2CombineChart interval data:', data.filter((item)=>!item.isCombine));
32
+ console.log('renderG2CombineChart highlightDate:', highlightDate, isHighlight);
30
33
  applyHighlightDate(view, x, data, highlightDate, isHighlight);
31
34
  const intervalData = data.filter((item)=>!item.isCombine).map((item)=>({
32
35
  ...item,
33
36
  [BAR_Y_FIELD]: item[Y_AXIS_FIELD]
34
37
  }));
38
+ console.log('renderG2CombineChart interval data:', intervalData);
35
39
  const intervalMaxY = intervalData.reduce((max, item)=>{
36
40
  const key = isGroup ? "total" : BAR_Y_FIELD;
37
41
  return Math.max(max, item[key]);
38
42
  }, 0);
43
+ const auxValues = auxiliaryLineData?.length ? auxiliaryLineData.map((item)=>item.value) : [];
44
+ const maxAux = auxValues.length ? Math.max(...auxValues) : 0;
45
+ const finalIntervalMax = Math.max(intervalMaxY, maxAux);
39
46
  const interval = view.interval().data(intervalData).encode("x", x).encode("y", BAR_Y_FIELD).encode("color", "groupType").transform(isGroup ? {
40
47
  type: 'stackY',
41
48
  reverse: true
@@ -77,7 +84,7 @@ function renderG2CombineChart(container, options) {
77
84
  interval.scale('y', {
78
85
  type: 'linear',
79
86
  domainMin: 0,
80
- domainMax: intervalMaxY,
87
+ domainMax: finalIntervalMax,
81
88
  nice: true,
82
89
  clamp: true,
83
90
  key: 'bar',
@@ -104,6 +111,7 @@ function renderG2CombineChart(container, options) {
104
111
  (datum)=>{
105
112
  const groupType = String(datum.groupType ?? '');
106
113
  const index = legendItems.findIndex((item)=>item.id === `${groupType}_${datum.isCombine}`);
114
+ console.log(datum);
107
115
  return {
108
116
  value: datum[Y_AXIS_FIELD],
109
117
  name: datum['groupType'],
@@ -142,11 +150,11 @@ function renderG2CombineChart(container, options) {
142
150
  } : false : false
143
151
  ].filter(Boolean)
144
152
  });
145
- console.log('renderG2CombineChart: line data:', data.filter((item)=>item.isCombine));
146
153
  const lineData = data.filter((item)=>item.isCombine && !item.groupType.includes('_compare')).map((item)=>({
147
154
  ...item,
148
155
  [LINE_Y_FIELD]: item[Y_AXIS_FIELD]
149
156
  }));
157
+ console.log('renderG2CombineChart: line data:', lineData);
150
158
  const compareLineData = data.filter((item)=>item.isCombine && item.groupType.includes('_compare')).map((item)=>({
151
159
  ...item,
152
160
  [LINE_Y_FIELD]: item[Y_AXIS_FIELD]
@@ -155,7 +163,7 @@ function renderG2CombineChart(container, options) {
155
163
  const lineMaxY = lineData.reduce((max, item)=>Math.max(max, item[LINE_Y_FIELD]), 0);
156
164
  const compareLineMaxY = compareLineData.reduce((max, item)=>Math.max(max, item[LINE_Y_FIELD]), 0);
157
165
  const lineFinalMaxY = Math.max(lineMaxY, compareLineMaxY) || 1;
158
- console.log('lineFinalMaxY:', lineFinalMaxY);
166
+ const finalLineMax = Math.max(lineFinalMaxY, maxAux);
159
167
  const line = view.line().data(lineData).encode("x", x).encode("y", LINE_Y_FIELD).encode('color', 'groupType').style({
160
168
  lineWidth: 2,
161
169
  stroke: (datum)=>{
@@ -165,7 +173,7 @@ function renderG2CombineChart(container, options) {
165
173
  ];
166
174
  const groupType = String(datum[0]?.groupType ?? '');
167
175
  const index = groupTypeArr.findIndex((item)=>item === groupType);
168
- const color = isGroup ? lineColors[index] || lineColors[0] : lineColors[indicators.slice(1).indexOf(String(groupType)) ?? 0];
176
+ const color = lineColors[index] || lineColors[0];
169
177
  return color;
170
178
  }
171
179
  });
@@ -176,14 +184,14 @@ function renderG2CombineChart(container, options) {
176
184
  const groupTypeArr = [
177
185
  ...groupTypeSet
178
186
  ];
179
- const groupType = String(datum[0]?.groupType ?? '');
187
+ const groupType = String(datum?.groupType ?? '');
180
188
  const index = groupTypeArr.findIndex((item)=>item === groupType);
181
189
  return {
182
190
  value: datum[LINE_Y_FIELD],
183
- name: isGroup ? getIndicatorCompareName(_indicatorMap, datum['groupType']) : datum['groupType'],
191
+ name: getIndicatorCompareName(_indicatorMap, datum['groupType']),
184
192
  indicatorId: datum['groupType'],
185
193
  channel: Y_AXIS_FIELD,
186
- color: isGroup ? lineColors[index] || lineColors[0] : lineColors[indicators.slice(1).indexOf(String(datum['groupType'])) ?? 0] || 'transparent',
194
+ color: lineColors[index] || lineColors[0],
187
195
  isCombine: true,
188
196
  tipType: 'line'
189
197
  };
@@ -193,7 +201,7 @@ function renderG2CombineChart(container, options) {
193
201
  line.scale('y', {
194
202
  type: 'linear',
195
203
  domainMin: 0,
196
- domainMax: lineFinalMaxY,
204
+ domainMax: finalLineMax,
197
205
  nice: true,
198
206
  clamp: true,
199
207
  key: 'line',
@@ -222,15 +230,23 @@ function renderG2CombineChart(container, options) {
222
230
  4
223
231
  ],
224
232
  lineWidth: 2,
225
- stroke: (datum, index)=>{
233
+ stroke: (datum)=>{
226
234
  const groupType = String(datum[0]?.groupType ?? '');
227
235
  const color = lineColors[indicators.slice(1).indexOf(String(groupType.replace('_compare', ''))) ?? 0];
228
- console.log('compareLine: 222', 'index:', index, 'indicators:', indicators, 'datum:', datum, 'groupType:', groupType, 'color:', color);
229
236
  return color ?? 'transparent';
230
237
  }
231
238
  });
232
239
  compareLine.scale('y', {
233
- key: 'line'
240
+ type: 'linear',
241
+ domainMin: 0,
242
+ domainMax: finalLineMax,
243
+ nice: true,
244
+ clamp: true,
245
+ key: 'line',
246
+ independent: true,
247
+ tickMethod: (min, max)=>Array.from({
248
+ length: 6
249
+ }, (_, i)=>min + (max - min) * i / 5)
234
250
  });
235
251
  compareLine.axis('y', false);
236
252
  const hasCompareData = data.some((item)=>item.groupType.includes('_compare'));
@@ -262,37 +278,88 @@ function renderG2CombineChart(container, options) {
262
278
  shared: true,
263
279
  crosshairsY: true,
264
280
  marker: false,
265
- render: (_event, payload)=>{
281
+ render: (event, payload)=>{
266
282
  const { title, items } = payload;
267
283
  const baseItems = items.filter((item)=>!item.isCombine);
268
284
  const combineItems = items.filter((item)=>item.isCombine);
269
- console.log('tooltipRender:', title, items, indicators);
270
- if (isGroup) return tooltipRender(title, [
271
- ...baseItems,
285
+ const indicatorOrder = new Map(indicators.map((id, i)=>[
286
+ id,
287
+ i
288
+ ]));
289
+ const sortedCombineItems = [
272
290
  ...combineItems
291
+ ].sort((a, b)=>{
292
+ const orderA = indicatorOrder.get(a.indicatorId) ?? 1 / 0;
293
+ const orderB = indicatorOrder.get(b.indicatorId) ?? 1 / 0;
294
+ if (orderA !== orderB) return orderA - orderB;
295
+ const typeRank = (item)=>{
296
+ if (item.isChange) return 2;
297
+ if ('compareline' === item.tipType) return 1;
298
+ return 0;
299
+ };
300
+ return typeRank(a) - typeRank(b);
301
+ });
302
+ const content = tooltipRender(title, [
303
+ ...baseItems,
304
+ ...sortedCombineItems
273
305
  ]) ?? null;
274
- if (indicators.length) {
275
- const groupedItems = indicators.map((indicator, index)=>{
276
- if (0 === index) return items.filter((i)=>!i.isCombine).filter((i)=>i.indicatorId === String(indicator));
277
- return items.filter((i)=>i.isCombine).filter((i)=>i.indicatorId === String(indicator));
278
- });
279
- console.log(groupedItems.flat(), 'tooltip groupedItems');
280
- const sortedItems = groupedItems.map((group)=>[
281
- group.filter((i)=>i.name === i.indicatorId),
282
- group.filter((i)=>i.compareTime),
283
- group.filter((i)=>i.name.includes('_change'))
284
- ].filter(Boolean).flat()).flat();
285
- console.log(sortedItems, 'tooltip sortedItems');
286
- const formatItems = sortedItems.map((item)=>({
287
- ...item,
288
- color: item.isCombine ? lineColors[indicators.slice(1).indexOf(String(item?.indicatorId)) ?? 0] || barColor || 'transparent' : barColor || 'transparent'
289
- }));
290
- console.log(formatItems, 'tooltip formatItems');
291
- return tooltipRender(title, formatItems ?? []) ?? null;
306
+ if (content) {
307
+ const domEvent = event?.event ?? event;
308
+ const clientX = domEvent?.clientX ?? domEvent?.x ?? 0;
309
+ const clientY = domEvent?.clientY ?? domEvent?.y ?? 0;
310
+ const rect = container.getBoundingClientRect();
311
+ const offsetX = 12;
312
+ const offsetY = 12;
313
+ const applyClamp = ()=>{
314
+ let el = content;
315
+ while(el && el.parentElement){
316
+ const p = el.parentElement;
317
+ if (p.classList?.contains('g2-tooltip')) {
318
+ el = p;
319
+ break;
320
+ }
321
+ el = p;
322
+ }
323
+ const wrapper = el && el.classList?.contains('g2-tooltip') ? el : null;
324
+ if (!wrapper) return;
325
+ const wRect = wrapper.getBoundingClientRect();
326
+ const tooltipW = wRect.width || 260;
327
+ const tooltipH = wRect.height || 300;
328
+ const minLeft = rect.left;
329
+ const maxLeft = rect.right - tooltipW;
330
+ const minTop = rect.top;
331
+ const maxTop = rect.bottom - tooltipH;
332
+ const desiredLeft = clientX + offsetX;
333
+ const desiredTop = clientY + offsetY;
334
+ const left = Math.min(Math.max(desiredLeft, minLeft), Math.max(maxLeft, minLeft));
335
+ const top = Math.min(Math.max(desiredTop, minTop), Math.max(maxTop, minTop));
336
+ wrapper.style.position = 'fixed';
337
+ wrapper.style.left = `${left}px`;
338
+ wrapper.style.top = `${top}px`;
339
+ wrapper.style.transform = 'translate(0px, 0px)';
340
+ wrapper.style.pointerEvents = 'none';
341
+ wrapper.style.zIndex = '9999';
342
+ };
343
+ try {
344
+ applyClamp();
345
+ requestAnimationFrame(applyClamp);
346
+ } catch {}
292
347
  }
348
+ return content;
293
349
  }
294
350
  });
295
351
  view.legend(false);
352
+ view.interaction('poptip', {
353
+ offsetY: -20,
354
+ offsetX: 0,
355
+ tipBackgroundColor: '#fcfcfc',
356
+ tipColor: '#333',
357
+ tipBorderRadius: '6px',
358
+ tipPadding: '10px 12px',
359
+ tipFontSize: '12px',
360
+ tipBoxShadow: '0 3px 6px -4px rgba(0, 0, 0, 1)'
361
+ });
362
+ applyAuxiliaryLineY(view, auxiliaryLineData);
296
363
  const nodeList = [];
297
364
  console.log('nodeList data:', data);
298
365
  data.forEach((item)=>{
@@ -355,35 +422,45 @@ function renderG2CombineChart(container, options) {
355
422
  }
356
423
  });
357
424
  });
358
- if (isDataTag && formatLabel) {
359
- const groupedByX = intervalData.reduce((acc, item)=>{
360
- const key = item[x];
361
- if (!acc[key]) acc[key] = [];
362
- acc[key].push(item);
363
- return acc;
364
- }, {});
365
- const lastItemsInEachStack = new Set();
366
- Object.values(groupedByX).forEach((group)=>{
367
- group.sort((a, b)=>a[y] - b[y]);
368
- const lastItem = group[group.length - 1];
369
- lastItemsInEachStack.add(lastItem);
370
- });
371
- interval.label({
372
- dy: isGroup ? -16 : -10,
373
- dx: -10,
374
- offset: 0,
375
- text: (d)=>{
376
- if (!isGroup) return formatLabel(d);
377
- if (lastItemsInEachStack.has(d)) return formatLabel(d);
378
- return '';
379
- }
380
- });
381
- line.label({
425
+ if (formatLabel) {
426
+ if (isDataTag) {
427
+ const groupedByX = intervalData.reduce((acc, item)=>{
428
+ const key = item[x];
429
+ if (!acc[key]) acc[key] = [];
430
+ acc[key].push(item);
431
+ return acc;
432
+ }, {});
433
+ const lastItemsInEachStack = new Set();
434
+ Object.values(groupedByX).forEach((group)=>{
435
+ const lastItem = group[0];
436
+ lastItemsInEachStack.add(lastItem);
437
+ });
438
+ interval.label({
439
+ dy: -16,
440
+ dx: -10,
441
+ offset: 0,
442
+ text: (d)=>{
443
+ if (!isGroup) return formatLabel(d);
444
+ if (lastItemsInEachStack.has(d)) return formatLabel(d);
445
+ return '';
446
+ }
447
+ });
448
+ }
449
+ if (isCombineDataTag) line.label({
382
450
  dy: -10,
383
- dx: -10,
451
+ dx: 10,
384
452
  offset: 0,
385
453
  text: (d)=>formatLabel(d)
386
454
  });
455
+ view.labelTransform([
456
+ {
457
+ type: 'exceedAdjust',
458
+ bounds: 'main'
459
+ },
460
+ {
461
+ type: 'overlapHide'
462
+ }
463
+ ]);
387
464
  }
388
465
  chart.render();
389
466
  let hasEmittedNodeList = false;
@@ -56,9 +56,12 @@ function renderG2GroupBarChart(container, options) {
56
56
  },
57
57
  stroke: 'transparent'
58
58
  });
59
+ const xValueCount = new Set(data.map((d)=>d[x])).size;
59
60
  applyAxisX(view, {
60
61
  title: false,
61
- grid: false
62
+ grid: false,
63
+ dataCount: xValueCount,
64
+ containerWidth: container.clientWidth
62
65
  });
63
66
  applyAxisY(view, {
64
67
  title: false,
@@ -70,6 +73,7 @@ function renderG2GroupBarChart(container, options) {
70
73
  labelFormatter: formatAxis
71
74
  });
72
75
  view.legend(false);
76
+ view.interaction('poptip', true);
73
77
  interval.tooltip({
74
78
  items: [
75
79
  (datum)=>{
@@ -86,20 +90,38 @@ function renderG2GroupBarChart(container, options) {
86
90
  }
87
91
  ].filter(Boolean)
88
92
  });
89
- if (tooltipRender) interval.interaction('tooltip', {
90
- shared: true,
91
- render: (_event, payload)=>{
92
- const { title, items } = payload;
93
- const lastIsChange = items.filter((item)=>item.isChange);
94
- let safeItems = items;
95
- if (lastIsChange.length > 0) safeItems = [
96
- ...items.filter((item)=>!item.isChange),
97
- lastIsChange[lastIsChange.length - 1]
98
- ];
99
- return tooltipRender(title, safeItems ?? []) ?? null;
100
- }
101
- });
102
- else view.interaction('tooltip', true);
93
+ if (tooltipRender) {
94
+ const tooltipBounding = {
95
+ x: 0,
96
+ y: 0,
97
+ width: container.clientWidth || container.offsetWidth,
98
+ height: height
99
+ };
100
+ interval.interaction('tooltip', {
101
+ shared: true,
102
+ bounding: tooltipBounding,
103
+ render: (_event, payload)=>{
104
+ const { title, items } = payload;
105
+ const lastIsChange = items.filter((item)=>item.isChange);
106
+ let safeItems = items;
107
+ if (lastIsChange.length > 0) safeItems = [
108
+ ...items.filter((item)=>!item.isChange),
109
+ lastIsChange[lastIsChange.length - 1]
110
+ ];
111
+ return tooltipRender(title, safeItems ?? []) ?? null;
112
+ }
113
+ });
114
+ } else {
115
+ const tooltipBounding = {
116
+ x: 0,
117
+ y: 0,
118
+ width: container.clientWidth || container.offsetWidth,
119
+ height: height
120
+ };
121
+ view.interaction('tooltip', {
122
+ bounding: tooltipBounding
123
+ });
124
+ }
103
125
  applyAuxiliaryLineY(view, auxiliaryLineData);
104
126
  const nodeList = [];
105
127
  console.log('nodeList data:', data);
@@ -19,7 +19,7 @@ function renderG2LineChart(container, options) {
19
19
  insetBottom: 0
20
20
  });
21
21
  const view = getMainView(chart);
22
- view.attr('marginBottom', isLegend ? 0 : 16);
22
+ view.attr('marginLeft', 0).attr('marginRight', 0).attr('marginBottom', isLegend ? 0 : 16);
23
23
  view.data(data);
24
24
  const domainMin = minY ?? 0;
25
25
  applyScaleYLinear(view, {
@@ -43,8 +43,11 @@ function renderG2LineChart(container, options) {
43
43
  lineWidth: 2,
44
44
  stroke: (datum)=>getColorByGroupType(datum[0]?.groupType ?? '', colorOpts)
45
45
  });
46
+ const xValueCount = new Set(data.map((d)=>d[x])).size;
46
47
  applyAxisX(view, {
47
- grid: false
48
+ grid: false,
49
+ dataCount: xValueCount,
50
+ containerWidth: container.clientWidth
48
51
  });
49
52
  applyAxisY(view, {
50
53
  labelFormatter: formatAxis
@@ -81,9 +84,16 @@ function renderG2LineChart(container, options) {
81
84
  })
82
85
  ]
83
86
  });
87
+ const tooltipBounding = {
88
+ x: 0,
89
+ y: 0,
90
+ width: container.clientWidth || container.offsetWidth,
91
+ height: height
92
+ };
84
93
  if (tooltipRender) view.interaction('tooltip', {
85
94
  shared: true,
86
95
  marker: false,
96
+ bounding: tooltipBounding,
87
97
  render: (_event, payload)=>{
88
98
  const { title, items } = payload;
89
99
  console.log('tooltipRender:', title, items, indicators);
@@ -112,6 +122,16 @@ function renderG2LineChart(container, options) {
112
122
  }
113
123
  });
114
124
  view.legend(false);
125
+ view.interaction('poptip', {
126
+ offsetY: -20,
127
+ offsetX: 0,
128
+ tipBackgroundColor: '#fcfcfc',
129
+ tipColor: '#333',
130
+ tipBorderRadius: '6px',
131
+ tipPadding: '10px 12px',
132
+ tipFontSize: '12px',
133
+ tipBoxShadow: '0 3px 6px -4px rgba(0, 0, 0, 1)'
134
+ });
115
135
  applyAuxiliaryLineY(view, auxiliaryLineData);
116
136
  const nodeList = [];
117
137
  console.log('nodeList data:', data);
@@ -174,7 +194,11 @@ function renderG2LineChart(container, options) {
174
194
  });
175
195
  view.labelTransform([
176
196
  {
177
- type: 'overlapDodgeY'
197
+ type: 'exceedAdjust',
198
+ bounds: 'main'
199
+ },
200
+ {
201
+ type: 'overlapHide'
178
202
  }
179
203
  ]);
180
204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@publishfx/publish-chart",
3
- "version": "2.1.4",
3
+ "version": "2.1.6",
4
4
  "description": "A React chart component library for the Publish platform, including BarChart, LineChart, BarLineChart and other visualization components",
5
5
  "type": "module",
6
6
  "keywords": [