Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/plots/src/core/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ export const CONFIG_SHAPE = [
type: 'line',
extend_keys: EXTEND_KEYS,
},
{
key: 'connector',
type: 'connector',
extend_keys: [],
},
{
key: 'point',
type: 'point',
Expand Down
27 changes: 25 additions & 2 deletions packages/plots/src/core/plots/waterfall/adaptor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { flow, transformOptions, fieldAdapter } from '../../utils';
import { flow, transformOptions, fieldAdapter, isObject, set } from '../../utils';
import { mark } from '../../adaptor';
import { START_KEY, END_KEY, WATERFALL_VALUE } from './constants';
import type { Adaptor } from '../../types';
Expand Down Expand Up @@ -69,5 +69,28 @@ export function adaptor(params: Params) {
return params;
};

return flow(transformData, link, mark, transformOptions)(params);
/**
* @description 连接线
*/
const connectorTransform = (params: Params) => {
const { options } = params;
const { data = [], connector } = options;
if (!connector) return params;
set(options, 'connector', {
xField: connector.reverse ? ['x2', 'x1'] : ['x1', 'x2'],
yField: connector.reverse ? ['y2', 'y1'] : ['y1', 'y2'],
data: [
{
x1: data[0].x,
y1: data[0][END_KEY],
x2: data[data.length - 1].x,
y2: data[data.length - 1][END_KEY],
},
],
...(isObject(connector) ? connector : {}),
})
return params;
};
Comment on lines +75 to +93
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

connectorTransform 函数实现需要改进

函数逻辑基本正确,但存在几个问题:

  1. 缺少数据长度检查:当 data 数组为空或只有一个元素时,data[data.length - 1] 可能导致问题
  2. 字段访问可能失败data[0].xdata[0][END_KEY] 没有进行安全检查

建议添加数据验证:

 const connectorTransform = (params: Params) => {
   const { options } = params;
   const { data = [], connector } = options;
   if (!connector) return params;
+  if (data.length < 2) return params;
+  
+  const firstItem = data[0];
+  const lastItem = data[data.length - 1];
+  if (!firstItem || !lastItem) return params;
+  
   set(options, 'connector', {
     xField: connector.reverse ? ['x2', 'x1'] : ['x1', 'x2'],
     yField: connector.reverse ? ['y2', 'y1'] : ['y1', 'y2'],
     data: [
       {
-        x1: data[0].x,
-        y1: data[0][END_KEY],
-        x2: data[data.length - 1].x,
-        y2: data[data.length - 1][END_KEY],
+        x1: firstItem.x,
+        y1: firstItem[END_KEY],
+        x2: lastItem.x,
+        y2: lastItem[END_KEY],
       },
     ],
     ...(isObject(connector) ? connector : {}),
   })
   return params;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const connectorTransform = (params: Params) => {
const { options } = params;
const { data = [], connector } = options;
if (!connector) return params;
set(options, 'connector', {
xField: connector.reverse ? ['x2', 'x1'] : ['x1', 'x2'],
yField: connector.reverse ? ['y2', 'y1'] : ['y1', 'y2'],
data: [
{
x1: data[0].x,
y1: data[0][END_KEY],
x2: data[data.length - 1].x,
y2: data[data.length - 1][END_KEY],
},
],
...(isObject(connector) ? connector : {}),
})
return params;
};
const connectorTransform = (params: Params) => {
const { options } = params;
const { data = [], connector } = options;
if (!connector) return params;
if (data.length < 2) return params;
const firstItem = data[0];
const lastItem = data[data.length - 1];
if (!firstItem || !lastItem) return params;
set(options, 'connector', {
xField: connector.reverse ? ['x2', 'x1'] : ['x1', 'x2'],
yField: connector.reverse ? ['y2', 'y1'] : ['y1', 'y2'],
data: [
{
x1: firstItem.x,
y1: firstItem[END_KEY],
x2: lastItem.x,
y2: lastItem[END_KEY],
},
],
...(isObject(connector) ? connector : {}),
})
return params;
};
🤖 Prompt for AI Agents
In packages/plots/src/core/plots/waterfall/adaptor.ts around lines 75 to 93, the
connectorTransform function lacks validation for the data array length and safe
access to its elements. To fix this, add checks to ensure data has at least two
elements before accessing data[0] and data[data.length - 1]. Also, verify that
data[0].x and data[0][END_KEY] exist before using them to prevent runtime
errors. If the data is insufficient or fields are missing, return params early
or handle the case gracefully.


return flow(transformData, link, mark, connectorTransform, transformOptions)(params);
}
7 changes: 2 additions & 5 deletions packages/plots/src/core/plots/waterfall/type.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import type { Options, AttrStyle } from '../../types/common';

export type WaterfallOptions = Omit<Options, 'xField' | 'children'> & {
export type WaterfallOptions = Omit<Options, 'children'> & {
/**
* @title 连线样式
*/
linkStyle?: AttrStyle;
/**
* @title x轴字段
*/
xField?: string | string[];
children?: Array<WaterfallOptions & { zIndex?: number }>;
connector?: Partial<Options> & { reverse?: boolean };
};
2 changes: 1 addition & 1 deletion packages/plots/src/core/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
LegendComponent,
} from '@antv/g2';

export type Primitive = number | string | boolean | Date;
export type Primitive = number | string | boolean | Date | string[];

export type FunctionEncodeSpec = (value: any, index?: number, array?: any[]) => Primitive;

Expand Down
2 changes: 1 addition & 1 deletion site/docs/components/plots/area.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ order: 2

## 简介

面积图用于展示数据的连续变化趋势,适用于时间序列分析、数据对比等场景
面积图(Area)是一种以折线图为基础,将折线与坐标轴之间的区域用颜色或纹理填充的统计图表。它通过面积大小直观展示数据的变化趋势、累计总量及部分与整体的关系,兼具折线图的趋势表达与柱状图的面积对比优势

## 代码演示

Expand Down
61 changes: 54 additions & 7 deletions site/docs/components/plots/waterfall.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,63 @@ link: /examples#statistics-waterfall
order: 14
---

## 特有
## 简介

### linkStyle
瀑布图(Waterfall)又称桥接图或飞瀑图,是一种用于展示数据从初始值到最终值的演变过程及各因素影响的可视化图表。其核心特征包括:

<description>**required** _attrStyle_</description>
- 阶梯式布局:以柱状图为基础,通过垂直条形的增减叠加,呈现数据的累积变化。
- 因果关系可视化:
- 初始值与最终值用完整柱形表示(通常居左、右两端)。
- 中间柱形表示各因素的影响(正值向上延伸,负值向下延伸)。
- 流向隐喻:形似瀑布的水流方向,直观展示数据 “增长 - 衰减” 的动态过程。

连线样式
## 代码演示

### xField
更多示例详见[Waterfall](/examples#statistics-waterfall)

<description>**required** _string | string[]_</description>
### 基础用法

x 轴字段
<Playground path="/statistics/waterfall/demo/basic.js" rid="waterfall-basic"></playground>

### 连接线标记

<Playground path="/statistics/waterfall/demo/connector.js" rid="waterfall-connector"></playground>

### 反向链接线标记

<Playground path="/statistics/waterfall/demo/reverse.js" rid="waterfall-reverse"></playground>

## 配置项

### 概览

|配置项|说明|类型|默认值|
|-----|---|----|-----|
| data | [数据](/options/plots/data/overview) | Array | [] |
| xField | 横轴字段 | string | - |
| yField | 纵轴字段 | string | - |
| colorField | 和 seriesField 类似,不过会加上颜色通道,详见[color](/options/plots/color) | string(可选) | - |
| linkStyle | 连接线样式,配置项详见[配置线的样式](/options/plots/style#配置线的样式) | object(可选) | - |
| connector | 连接线标记,配置和 [Line](/components/plots/line) 类似,默认处理了 `xField`、`yField`、`data`,可通过 `reverse` 字段让连线绘制方向颠倒,通过 `style` 指定[配置线的样式](/options/plots/style#配置线的样式)。 | object(可选) | - |
Comment thread
lxfu1 marked this conversation as resolved.
| title | 用于指定图表的标题内容,详见[标题](/options/plots/title) | object(可选) | - |
| axis | 用于建立数据与视觉位置的映射关系,详见[坐标轴](/options/plots/axis) | object(可选) | - |
| legend | 图表的辅助元素,使用颜色、大小、形状区分不同的数据类型,用于图表中数据的筛选,详见[图例](/options/plots/legend) | object(可选) | - |
| label | 数据标签是给图表添加标注的手段之一,详见[标签](/options/plots/label) | object(可选) | - |
| tooltip | 用于动态展示数据点的详细信息,详见[提示](/options/plots/tooltip) | object(可选) | - |
| style | 视觉样式,配置项详见[绘图属性](/options/plots/style#绘图属性) | object(可选) | - |
| theme | 用于控制图表的整体外观,包括颜色、字体、边距等视觉属性,详见[主题](/options/plots/theme/overview) | string \| object(可选) | `light` |
| onReady | 图表加载回调,用于后续的事件[事件](/options/plots/event)绑定 | Function(可选) | - |
| scale | 将抽象数据映射为视觉数据,详见[比例尺](/options/plots/scale/overview) | object(可选) | - |
| animate | 动画作为可视化的重要组成部分,能显著提高数据可视化的表现力,详见[动画](/options/plots/animate/overview) | object(可选) | - |
| interaction | 提供了按需探索数据的能力,详见[交互](/options/plots/interaction/overview) | object(可选) | - |
| state | 实现交互反馈、高亮、选中等效果,详见[状态](/options/plots/state),不同交互下图表样式 | object(可选) | - |
| annotations | 视图好比一个画板,`Waterfall` 组件默认在其上绘制了一个瀑布图,我们可以通过 annotations 在上面叠加更多的图层,详见[标注](/examples#statistics-annotation-shape) | Array(可选) | - |


## 事件

详见[选项-事件](/options/plots/event)。

## 方法

详见[图表概览-图表方法](/components/plots/overview#图表方法)。
51 changes: 51 additions & 0 deletions site/examples/statistics/waterfall/demo/connector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Waterfall } from '@ant-design/plots';
import React from 'react';
import { createRoot } from 'react-dom';

const DemoWaterfall = () => {
const config = {
data: [
{ x: 'Net Sales', value: 5085000 },
{ x: 'Cost of Sales', value: -1250450 },
{ x: 'Operating Expenses', value: -2350050 },
{ x: 'Other Income', value: 750000 },
{ x: 'Extraordinary Gain', value: -230050 },
{ x: 'Interest Expense', value: -500000 },
{ x: 'Taxes', value: 490000 },
{ x: 'Net Income', isTotal: true, value: 1994450 },
],
xField: 'x',
yField: 'value',
linkStyle: {
lineDash: [4, 2],
stroke: '#ccc',
},
style: {
maxWidth: 25,
stroke: '#ccc',
fill: (d, idx) => {
return idx === 0 || d.isTotal ? '#96a6a6' : d.value > 0 ? '#64b5f6' : '#ef6c00';
},
},
label: {
text: 'value',
formatter: '~s',
position: (d) => (d.value > 0 ? 'top' : 'bottom'),
textBaseline: (d) => (d.value > 0 ? 'bottom' : 'top'),
fontSize: 10,
dy: (d) => (d.value > 0 ? -4 : 4),
},
connector: {
label: {
text: (d) => `${d.y2 - d.y1}`,
formatter: '~s',
fontSize: 14,
dy: 2,
},
style: { stroke: '#697474', offset: 16 },
},
};
return <Waterfall {...config} />;
};

createRoot(document.getElementById('container')).render(<DemoWaterfall />);
24 changes: 16 additions & 8 deletions site/examples/statistics/waterfall/demo/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,28 @@
},
"demos": [
{
"filename": "revenue-flow-waterfall.js",
"filename": "basic.js",
"title": {
"zh": "线标记的瀑布图",
"en": "Revenue Flow Waterfall Chart"
"zh": "瀑布图 - 基础用法",
"en": "Waterfall plot - Basic Usage"
},
"screenshot": "https://mdn.alipayobjects.com/mdn/huamei_qa8qxu/afts/img/A*fG4DT7XWNzUAAAAAAAAAAAAADmJ7AQ"
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*xOfKT5qr9R4AAAAAAAAAAAAAARQnAQ"
},
{
"filename": "annotation.js",
"filename": "connector.js",
"title": {
"zh": "瀑布图 - 添加标注",
"en": "Waterfall plot with annotation"
"zh": "瀑布图 - 连接线",
"en": "Waterfall plot with connector"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*xOfKT5qr9R4AAAAAAAAAAAAAARQnAQ"
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*9BOSQozPCx4AAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "reverse.js",
"title": {
"zh": "瀑布图 - 反向连线",
"en": "Waterfall plot with reverse connector"
},
"screenshot": "https://mdn.alipayobjects.com/mdn/huamei_qa8qxu/afts/img/A*fG4DT7XWNzUAAAAAAAAAAAAADmJ7AQ"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const DemoWaterfall = () => {
fontSize: 10,
dy: (d) => (d.value > 0 ? -4 : 4),
},
connector: {
reverse: true,
style: { stroke: '#697474', offset: 16 },
},
};
return <Waterfall {...config} />;
};
Expand Down