@publishfx/publish-chart 2.1.43 → 2.1.44
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 +5 -0
- package/dist/components/g2/base/g2Helpers.d.ts +6 -0
- package/dist/components/g2/base/g2Helpers.js +131 -19
- package/dist/components/g2/base/g2bar.js +2 -1
- package/dist/components/g2/base/g2combine.d.ts +3 -3
- package/dist/components/g2/base/g2combine.js +10 -8
- package/dist/components/g2/base/g2line.js +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
**2.1.44** feat(chart): 更新图表组件及发布脚本
|
|
2
|
+
- 2026-07-08T11:17:13+08:00 [feat(chart): 更新图表组件及发布脚本](http://lf.git.oa.mt/publish_platform/web/publish/commit/c3efe54dd3e640b72ea027815efd84b5d817f472)
|
|
3
|
+
- 2026-07-07T14:20:55+08:00 [feat: 组合图副轴间距调整 f-6880297364](http://lf.git.oa.mt/publish_platform/web/publish/commit/b662f2234806c5b6e40c5ae8d2ed4a7cacc03bee)
|
|
4
|
+
- 2026-07-07T12:42:22+08:00 [feat: 辅助线延伸优化 f-6880297364](http://lf.git.oa.mt/publish_platform/web/publish/commit/41290b358961c2a1e7ff100addd11b23dc2d5990)
|
|
5
|
+
|
|
1
6
|
**2.1.41** feat(chart): 分组对比折线图自定义图例与tooltip交互优化 f-6719092744
|
|
2
7
|
- 2026-06-16T16:45:08+08:00 [feat(chart): 分组对比折线图自定义图例与tooltip交互优化 f-6719092744](http://lf.git.oa.mt/publish_platform/web/publish/commit/446f1dcea5fa50c1db96a395747bf26a5213a2f5)
|
|
3
8
|
|
|
@@ -285,6 +285,12 @@ export interface ApplyAuxiliaryLineYOptions {
|
|
|
285
285
|
stroke?: string;
|
|
286
286
|
strokeOpacity?: number;
|
|
287
287
|
labelMaxLength?: number;
|
|
288
|
+
/**
|
|
289
|
+
* 辅助线向 Y 轴刻度区域左侧延伸的像素距离。
|
|
290
|
+
* G2 lineY 默认只覆盖主绘图区,设置后会把起点往左平移指定像素,让线穿过左侧坐标轴区域。
|
|
291
|
+
* 传 'auto' 时,会根据坐标系左侧偏移(paddingLeft + marginLeft)自动填满 Y 轴刻度区域。
|
|
292
|
+
*/
|
|
293
|
+
extendLeft?: number | 'auto';
|
|
288
294
|
}
|
|
289
295
|
/** 在 view 上添加多条 lineY 辅助线 */
|
|
290
296
|
export declare function applyAuxiliaryLineY(view: any, lines: AuxiliaryLineItem[], options?: ApplyAuxiliaryLineYOptions): void;
|
|
@@ -146,28 +146,140 @@ function applyLegendColor(view, options = {}) {
|
|
|
146
146
|
}
|
|
147
147
|
function applyAuxiliaryLineY(view, lines, options = {}) {
|
|
148
148
|
if (!lines?.length) return;
|
|
149
|
-
const { labelMaxLength = 5, stroke = 'rgb(234, 54, 106)', maxLeft, maxRight } = options;
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
const { labelMaxLength = 5, stroke = 'rgb(234, 54, 106)', maxLeft, maxRight, extendLeft = 0 } = options;
|
|
150
|
+
const isAutoExtend = 'auto' === extendLeft;
|
|
151
|
+
lines.forEach((auxLine, index)=>{
|
|
152
|
+
const lineMark = view.lineY().data([
|
|
152
153
|
{
|
|
153
154
|
y: auxLine.value
|
|
154
155
|
}
|
|
155
156
|
]).encode('y', auxLine.value).style('stroke', stroke).style('strokeOpacity', 1).style('opacity', 1).style('lineWidth', 2).style('shadowColor', 'transparent').animate(false).scale('y', {
|
|
156
157
|
key: auxLine?.axis === 'right' ? 'line' : 'main'
|
|
157
158
|
}).axis('x', false).label({
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
text: splitTextToMultiline(auxLine.name, labelMaxLength, '<br />'),
|
|
162
|
-
position: auxLine?.axis === 'right' ? 'bottom-right' : 'bottom-left',
|
|
163
|
-
render: (text)=>{
|
|
164
|
-
let top = auxLine?.name?.length > 5 ? -38 : -24;
|
|
165
|
-
if ((auxLine?.axis === 'left' || auxLine?.axis === '') && maxLeft && auxLine.value > 0.92 * maxLeft || auxLine?.axis === 'right' && maxRight && auxLine.value > 0.92 * maxRight) top = 0;
|
|
166
|
-
top += 'px';
|
|
167
|
-
return `<div style="background-color: rgba(255, 0, 102, 0.15); padding: 5px; border-radius: 5px;font-size: 12px;position: relative;left: ${auxLine?.axis === 'right' ? '-75px' : '0px'}; top: ${top} ; display:inline-block;line-height: 14px; color: #FF0066;
|
|
168
|
-
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;">${text}</div>`;
|
|
169
|
-
}
|
|
159
|
+
text: ' ',
|
|
160
|
+
fill: 'transparent',
|
|
161
|
+
opacity: 0
|
|
170
162
|
});
|
|
163
|
+
if (isAutoExtend || 'number' == typeof extendLeft && extendLeft > 0) {
|
|
164
|
+
lineMark.style('strokeOpacity', 0).style('opacity', 0);
|
|
165
|
+
view.shape().data([
|
|
166
|
+
{
|
|
167
|
+
x: `_aux_${index}`,
|
|
168
|
+
y: auxLine.value
|
|
169
|
+
}
|
|
170
|
+
]).encode('x', 'x').encode('y', 'y').scale('x', {
|
|
171
|
+
type: 'band',
|
|
172
|
+
independent: true
|
|
173
|
+
}).scale('y', {
|
|
174
|
+
key: auxLine?.axis === 'right' ? 'line' : 'main'
|
|
175
|
+
}).axis('x', false).tooltip(false).style('clip', false).style('render', ({ y: py }, context)=>{
|
|
176
|
+
const { document } = context;
|
|
177
|
+
const coordinate = context?.coordinate;
|
|
178
|
+
const coorOptions = coordinate?.getOptions?.() || {};
|
|
179
|
+
const plotWidth = coorOptions.width ?? 0;
|
|
180
|
+
const plotHeight = coorOptions.height ?? 0;
|
|
181
|
+
const transformations = coorOptions.transformations ?? [];
|
|
182
|
+
const isTransposed = transformations.some((t)=>Array.isArray(t) ? t.includes('transpose') : 'transpose' === t);
|
|
183
|
+
const isRightAxis = auxLine?.axis === 'right';
|
|
184
|
+
const domainMax = isRightAxis ? maxRight ?? 1 : maxLeft ?? 1;
|
|
185
|
+
const normalized = domainMax > 0 ? auxLine.value / domainMax : 0;
|
|
186
|
+
let linePos;
|
|
187
|
+
if (isTransposed) {
|
|
188
|
+
const mapped = coordinate?.map?.([
|
|
189
|
+
0,
|
|
190
|
+
1 - normalized
|
|
191
|
+
]);
|
|
192
|
+
linePos = mapped?.[0] ?? py;
|
|
193
|
+
} else linePos = py;
|
|
194
|
+
const g = document.createElement('g', {});
|
|
195
|
+
const axisLeftWidth = (coorOptions.paddingLeft ?? 0) + (coorOptions.marginLeft ?? 0);
|
|
196
|
+
const axisRightWidth = (coorOptions.paddingRight ?? 0) + (coorOptions.marginRight ?? 0);
|
|
197
|
+
const axisTopHeight = (coorOptions.paddingTop ?? 0) + (coorOptions.marginTop ?? 0);
|
|
198
|
+
const leftExtend = isAutoExtend ? axisLeftWidth : extendLeft;
|
|
199
|
+
const rightExtend = isAutoExtend ? axisRightWidth : extendLeft;
|
|
200
|
+
const topExtend = isAutoExtend ? axisTopHeight : extendLeft;
|
|
201
|
+
let pathD;
|
|
202
|
+
pathD = isTransposed ? `M ${linePos} ${-topExtend} L ${linePos} ${plotHeight}` : isRightAxis ? `M 0 ${linePos} L ${plotWidth + rightExtend} ${linePos}` : `M ${-leftExtend} ${linePos} L ${plotWidth} ${linePos}`;
|
|
203
|
+
const path = document.createElement('path', {
|
|
204
|
+
style: {
|
|
205
|
+
d: pathD,
|
|
206
|
+
stroke,
|
|
207
|
+
lineWidth: 2,
|
|
208
|
+
strokeOpacity: 1,
|
|
209
|
+
opacity: 1
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
g.appendChild(path);
|
|
213
|
+
const labelText = splitTextToMultiline(auxLine.name, labelMaxLength, '\n');
|
|
214
|
+
const labelLines = labelText.split('\n');
|
|
215
|
+
const lineHeight = 14;
|
|
216
|
+
const fontSize = 12;
|
|
217
|
+
const padding = 5;
|
|
218
|
+
const maxLineWidth = Math.max(...labelLines.map((line)=>{
|
|
219
|
+
let width = 0;
|
|
220
|
+
for (const char of line)width += char.charCodeAt(0) > 127 ? fontSize : 0.6 * fontSize;
|
|
221
|
+
return width;
|
|
222
|
+
}));
|
|
223
|
+
const labelWidth = maxLineWidth + 2 * padding;
|
|
224
|
+
const labelHeight = labelLines.length * lineHeight + 2 * padding;
|
|
225
|
+
const isNearTop = (auxLine?.axis === 'left' || auxLine?.axis === '') && maxLeft && auxLine.value > 0.98 * maxLeft || isRightAxis && maxRight && auxLine.value > 0.98 * maxRight;
|
|
226
|
+
let top = auxLine?.name?.length > 5 ? -38 : -24;
|
|
227
|
+
if (isNearTop) top = 8;
|
|
228
|
+
const isNearRightEdge = isTransposed && maxLeft && auxLine.value > 0.98 * maxLeft;
|
|
229
|
+
let labelX;
|
|
230
|
+
let labelY;
|
|
231
|
+
let textX;
|
|
232
|
+
let textAnchor;
|
|
233
|
+
if (isTransposed) {
|
|
234
|
+
if (isNearRightEdge) {
|
|
235
|
+
labelX = linePos - labelWidth - 8;
|
|
236
|
+
textAnchor = 'start';
|
|
237
|
+
} else {
|
|
238
|
+
labelX = linePos + 8;
|
|
239
|
+
textAnchor = 'start';
|
|
240
|
+
}
|
|
241
|
+
labelY = plotHeight - labelHeight;
|
|
242
|
+
textX = labelX + padding;
|
|
243
|
+
} else if (isRightAxis) {
|
|
244
|
+
const leftShift = 0;
|
|
245
|
+
const rightEdge = plotWidth + rightExtend - leftShift;
|
|
246
|
+
labelY = linePos + top - padding;
|
|
247
|
+
labelX = rightEdge - labelWidth;
|
|
248
|
+
textX = labelX + padding;
|
|
249
|
+
textAnchor = 'start';
|
|
250
|
+
} else {
|
|
251
|
+
labelY = linePos + top - padding;
|
|
252
|
+
labelX = -leftExtend;
|
|
253
|
+
textX = labelX + padding;
|
|
254
|
+
textAnchor = 'start';
|
|
255
|
+
}
|
|
256
|
+
const labelBg = document.createElement('rect', {
|
|
257
|
+
style: {
|
|
258
|
+
x: labelX,
|
|
259
|
+
y: labelY,
|
|
260
|
+
width: labelWidth,
|
|
261
|
+
height: labelHeight,
|
|
262
|
+
fill: 'rgba(255, 0, 102, 0.15)',
|
|
263
|
+
radius: 5
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
g.appendChild(labelBg);
|
|
267
|
+
const labelEl = document.createElement('text', {
|
|
268
|
+
style: {
|
|
269
|
+
x: textX,
|
|
270
|
+
y: labelY + padding,
|
|
271
|
+
text: labelText,
|
|
272
|
+
fill: '#FF0066',
|
|
273
|
+
fontSize,
|
|
274
|
+
fontFamily: "'PingFang SC', 'Microsoft YaHei', sans-serif",
|
|
275
|
+
textBaseline: 'top',
|
|
276
|
+
textAnchor
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
g.appendChild(labelEl);
|
|
280
|
+
return g;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
171
283
|
});
|
|
172
284
|
}
|
|
173
285
|
function getColorByGroupType(groupType, options) {
|
|
@@ -330,15 +442,15 @@ function fillMissingIndicator(baseItems, safeTitle, isCompare, isGroup = false)
|
|
|
330
442
|
if (3 !== count) {
|
|
331
443
|
const color = baseItems.filter((item)=>'transparent' !== item.color).map((item)=>item.color)[0];
|
|
332
444
|
if (!baseItems.find((item)=>item.indicatorId === indicatorId && item.name === indicatorId)) baseItems.push({
|
|
333
|
-
indicatorId
|
|
445
|
+
indicatorId,
|
|
334
446
|
name: indicatorId,
|
|
335
447
|
value: '-',
|
|
336
448
|
x: safeTitle,
|
|
337
|
-
color
|
|
449
|
+
color,
|
|
338
450
|
dataType: 'manual'
|
|
339
451
|
});
|
|
340
452
|
if (!baseItems.find((item)=>item.indicatorId === indicatorId && item.name === indicatorId + '_compare')) baseItems.push({
|
|
341
|
-
indicatorId
|
|
453
|
+
indicatorId,
|
|
342
454
|
name: indicatorId + '_compare',
|
|
343
455
|
tipType: 'compareline',
|
|
344
456
|
value: '-',
|
|
@@ -346,7 +458,7 @@ function fillMissingIndicator(baseItems, safeTitle, isCompare, isGroup = false)
|
|
|
346
458
|
dataType: 'manual'
|
|
347
459
|
});
|
|
348
460
|
if (!baseItems.find((item)=>item.indicatorId === indicatorId && item.name === indicatorId + '_change')) baseItems.push({
|
|
349
|
-
indicatorId
|
|
461
|
+
indicatorId,
|
|
350
462
|
name: indicatorId + '_change',
|
|
351
463
|
isChange: true,
|
|
352
464
|
value: '-',
|
|
@@ -134,7 +134,8 @@ function renderG2BarChart(container, options) {
|
|
|
134
134
|
});
|
|
135
135
|
applyAuxiliaryLineY(view, auxiliaryLineData, {
|
|
136
136
|
labelMaxLength: 5,
|
|
137
|
-
maxLeft: maxY
|
|
137
|
+
maxLeft: maxY,
|
|
138
|
+
extendLeft: 'auto'
|
|
138
139
|
});
|
|
139
140
|
const nodeResult = applyNodeData(view, nodeData, x, y);
|
|
140
141
|
if (isDataTag && formatLabel) interval.label({
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* G2 柱状 + 折线组合图函数式 API
|
|
3
3
|
* 复用 G2BarChart / G2LineChart 的模式:React 负责数据与配置,G2 负责纯渲染
|
|
4
4
|
*/
|
|
5
|
-
import { Chart } from
|
|
6
|
-
import type { ChartTimeRange, IndicatorInfo } from
|
|
7
|
-
import { type AuxiliaryLineItem } from
|
|
5
|
+
import { Chart } from '@antv/g2';
|
|
6
|
+
import type { ChartTimeRange, IndicatorInfo } from '../../../core/ChartTypes';
|
|
7
|
+
import { type AuxiliaryLineItem } from './g2Helpers';
|
|
8
8
|
export interface RenderG2CombineChartOptions {
|
|
9
9
|
/** 已转换后的数据:约定包含 groupName、groupType、groupValue 等字段 */
|
|
10
10
|
data: any[];
|
|
@@ -18,7 +18,7 @@ function renderG2CombineChart(container, options) {
|
|
|
18
18
|
});
|
|
19
19
|
const minLabelWidth = data.some((d)=>d.groupName.length < 3) ? 30 : 90;
|
|
20
20
|
const view = getMainView(chart);
|
|
21
|
-
view.attr('marginLeft', 0).attr('marginRight',
|
|
21
|
+
view.attr('marginLeft', 0).attr('marginRight', 0).attr('marginTop', 16).attr('marginBottom', 0).animate(false);
|
|
22
22
|
view.data(data);
|
|
23
23
|
const xValueCount = new Set(data.map((d)=>d[x])).size;
|
|
24
24
|
applyAxisX(view, {
|
|
@@ -28,7 +28,7 @@ function renderG2CombineChart(container, options) {
|
|
|
28
28
|
containerWidth: container.clientWidth,
|
|
29
29
|
minLabelWidth
|
|
30
30
|
});
|
|
31
|
-
const barColor = themeColors[0] ??
|
|
31
|
+
const barColor = themeColors[0] ?? '#5B8FF9';
|
|
32
32
|
applyHighlightDate(view, x, data, highlightDate, isHighlight);
|
|
33
33
|
const intervalData = data.filter((item)=>!item.isCombine).map((item)=>({
|
|
34
34
|
...item,
|
|
@@ -39,7 +39,7 @@ function renderG2CombineChart(container, options) {
|
|
|
39
39
|
const auxRightValues = auxiliaryLineData?.length ? auxiliaryLineData.filter((item)=>'right' === item.axis).map((item)=>item.value) : [];
|
|
40
40
|
const maxAuxRight = auxRightValues.length ? Math.max(...auxRightValues) : 0;
|
|
41
41
|
const finalIntervalMax = Math.max(maxLeft, maxAuxLeft);
|
|
42
|
-
const interval = view.interval().data(intervalData).encode(
|
|
42
|
+
const interval = view.interval().data(intervalData).encode('x', x).encode('y', BAR_Y_FIELD).encode('color', 'groupType').animate(false).transform(isGroup ? {
|
|
43
43
|
type: 'stackY',
|
|
44
44
|
reverse: true
|
|
45
45
|
} : {
|
|
@@ -60,7 +60,7 @@ function renderG2CombineChart(container, options) {
|
|
|
60
60
|
fill: (datum)=>{
|
|
61
61
|
const groupType = String(datum.groupType ?? '');
|
|
62
62
|
const index = legendItems.findIndex((item)=>item.id === `${groupType}_${datum.isCombine}`);
|
|
63
|
-
if (isGroup) return themeColors[index % themeColors.length] ?? themeColors[0] ??
|
|
63
|
+
if (isGroup) return themeColors[index % themeColors.length] ?? themeColors[0] ?? '#5B8FF9';
|
|
64
64
|
if (groupType.includes('_compare')) return {
|
|
65
65
|
image: lines({
|
|
66
66
|
backgroundColor: '#fff',
|
|
@@ -110,7 +110,7 @@ function renderG2CombineChart(container, options) {
|
|
|
110
110
|
name: datum['groupType'],
|
|
111
111
|
indicatorId,
|
|
112
112
|
channel: Y_AXIS_FIELD,
|
|
113
|
-
color: themeColors[index % themeColors.length] ?? themeColors[0] ??
|
|
113
|
+
color: themeColors[index % themeColors.length] ?? themeColors[0] ?? '#5B8FF9',
|
|
114
114
|
percent: `${(100 * Number(datum.percent)).toFixed(2)}%`,
|
|
115
115
|
isCombine: false,
|
|
116
116
|
x: datum[x]
|
|
@@ -156,7 +156,7 @@ function renderG2CombineChart(container, options) {
|
|
|
156
156
|
[LINE_Y_FIELD]: item[Y_AXIS_FIELD]
|
|
157
157
|
}));
|
|
158
158
|
const finalLineMax = Math.max(maxRight, maxAuxRight);
|
|
159
|
-
const line = view.line().data(lineData).encode(
|
|
159
|
+
const line = view.line().data(lineData).encode('x', x).encode('y', LINE_Y_FIELD).encode('color', 'groupType').animate(false).style({
|
|
160
160
|
lineWidth: 2,
|
|
161
161
|
stroke: (datum)=>{
|
|
162
162
|
const currentDatum = datum[0];
|
|
@@ -261,7 +261,7 @@ function renderG2CombineChart(container, options) {
|
|
|
261
261
|
marker: false,
|
|
262
262
|
render: (_event, payload)=>{
|
|
263
263
|
const { title, items } = payload;
|
|
264
|
-
|
|
264
|
+
const safeTitle = title.replace(/,.*$/, '');
|
|
265
265
|
const baseItems = items.filter((item)=>item.hasOwnProperty('isCombine') && !item.isCombine);
|
|
266
266
|
const safeItems = fillMissingIndicator(baseItems, safeTitle, isCompare, isGroup);
|
|
267
267
|
const combineItems = items.filter((item)=>item.hasOwnProperty('isCombine') && item.isCombine);
|
|
@@ -302,7 +302,9 @@ function renderG2CombineChart(container, options) {
|
|
|
302
302
|
});
|
|
303
303
|
applyAuxiliaryLineY(view, auxiliaryLineData, {
|
|
304
304
|
maxLeft,
|
|
305
|
-
maxRight
|
|
305
|
+
maxRight,
|
|
306
|
+
extendLeft: 'auto',
|
|
307
|
+
labelMaxLength: 5
|
|
306
308
|
});
|
|
307
309
|
const nodeResult = applyNodeData(view, nodeData, x, 'groupValue');
|
|
308
310
|
if (formatLabel) {
|
|
@@ -120,7 +120,8 @@ function renderG2LineChart(container, options) {
|
|
|
120
120
|
tipBoxShadow: '0 3px 6px -4px rgba(0, 0, 0, 1)'
|
|
121
121
|
});
|
|
122
122
|
applyAuxiliaryLineY(view, auxiliaryLineData, {
|
|
123
|
-
maxLeft: maxY
|
|
123
|
+
maxLeft: maxY,
|
|
124
|
+
extendLeft: 'auto'
|
|
124
125
|
});
|
|
125
126
|
const nodeResult = applyNodeData(view, nodeData, x, y);
|
|
126
127
|
if (isDataTag && formatLabel) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@publishfx/publish-chart",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.44",
|
|
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": [
|