Skip to content

Commit

Permalink
feat(state): 添加 setState 方法,通过条件设置三种状态的激活与否 (#1460)
Browse files Browse the repository at this point in the history
* feat(state): 添加 setState 方法,通过条件设置三种状态的激活与否

- [x] demo
- [ ] testcases
- [ ] docs

* feat(state): 添加 setState & getStates 方法,通过条件设置三种状态的激活与否

- [x] demo
- [ ] testcases
- [ ] docs

* refactor: 修改 cr 建议

* refactor: 修改 cr 建议 & 添加测试用例

* fix(pie-statistic): 修复中心文本类型定义 & 样式问题
  • Loading branch information
visiky authored Aug 19, 2020
1 parent 72d52b1 commit 5ba9e0a
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 37 deletions.
31 changes: 30 additions & 1 deletion __tests__/unit/core/index-spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Line, G2 } from '../../../src';
import { Line, G2, Pie } from '../../../src';
import { partySupport } from '../../data/party-support';
import { salesByArea } from '../../data/sales';
import { createDiv } from '../../utils/dom';

G2.registerTheme('new-theme', {
Expand Down Expand Up @@ -115,4 +116,32 @@ describe('core', () => {
_data: 1,
});
});

it('state', async () => {
const pie = new Pie(createDiv('饼图状态'), {
width: 400,
height: 400,
data: salesByArea,
angleField: 'sales',
colorField: 'area',
radius: 0.8,
autoFit: false,
interactions: [{ name: 'element-selected' }],
});

pie.render();

// 注意,如果 autoFit 会触发一次 render,导致 setState 的状态又还原了(实际场景,自己处理一个时机即可)
pie.setState('selected', (data) => (data as any).area === salesByArea[0].area);
expect(pie.getStates().length).toBe(1);

pie.chart.geometries[0].elements[0].setState('selected', false);
expect(pie.getStates().length).toBe(0);

pie.setState('selected', (data) => (data as any).area === salesByArea[2].area);
expect(pie.getStates().length).toBe(1);
// 取消 selected
pie.setState('selected', (data) => (data as any).area === salesByArea[2].area, false);
expect(pie.getStates().length).toBe(0);
});
});
82 changes: 82 additions & 0 deletions __tests__/unit/plots/pie/state-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Pie } from '../../../../src';
import { POSITIVE_NEGATIVE_DATA } from '../../../data/common';
import { createDiv } from '../../../utils/dom';

describe('pie', () => {
const data = POSITIVE_NEGATIVE_DATA.filter((o) => o.value > 0).map((d, idx) =>
idx === 1 ? { ...d, type: 'item1' } : d
);

const options = {
width: 400,
height: 300,
data,
angleField: 'value',
colorField: 'type',
color: ['blue', 'red', 'yellow', 'lightgreen', 'lightblue', 'pink'],
radius: 0.8,
autoFit: false,
};

it('set statesStyle', () => {
const pie = new Pie(createDiv(), {
...options,
state: {
selected: {
style: {
lineWidth: 4,
fill: 'red',
},
},
inactive: {
style: {
fill: 'blue',
},
},
},
});

pie.render();

pie.setState('selected', (d: any) => d.type === data[0].type);
const shape = pie.chart.geometries[0].elements[0].shape;

expect(shape.attr('lineWidth')).toBe(4);
expect(shape.attr('fill')).toBe('red');

// // 取消 selected
pie.setState('selected', (d: any) => d.type === data[0].type, false);
pie.setState('inactive', (d: any) => d.type === data[0].type);
expect(shape.attr('fill')).toBe('blue');
expect(pie.getStates()[0].state).toBe('inactive');
});

it('set statesStyle by theme', () => {
const pie = new Pie(createDiv(), {
...options,
theme: {
geometries: {
interval: {
rect: {
active: {
style: {
fill: 'yellow',
fillOpacity: 0.65,
},
},
},
},
},
},
});

pie.render();

pie.setState('active', (d: any) => d.type === data[2].type);
const shape = pie.chart.geometries[0].elements[2].shape;

expect(shape.attr('fill')).toBe('yellow');
expect(shape.attr('fillOpacity')).toBe(0.65);
expect(pie.getStates()[0].state).toBe('active');
});
});
8 changes: 4 additions & 4 deletions examples/pie/basic/demo/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*ZztJQa4RLwoAAAAAAAAAAABkARQnAQ"
},
{
"filename": "spider-label.ts",
"filename": "pie-state.ts",
"title": {
"zh": "饼图-图形标签蜘蛛布局",
"en": "Pie chart - spider-layout label"
"zh": "饼图-设置条件状态",
"en": "Pie chart - set condition state"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*ahB1Qp7T-C8AAAAAAAAAAABkARQnAQ"
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*y8zjS5DZib8AAAAAAAAAAAAAARQnAQ"
},
{
"filename": "pie-texture.ts",
Expand Down
43 changes: 43 additions & 0 deletions examples/pie/basic/demo/pie-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Pie } from '@antv/g2plot';

const data = [
{ type: '分类一', value: 27 },
{ type: '分类二', value: 25 },
{ type: '分类三', value: 18 },
{ type: '分类四', value: 15 },
{ type: '分类五', value: 10 },
{ type: '其他', value: 5 },
];

const piePlot = new Pie('container', {
appendPadding: 10,
data,
angleField: 'value',
colorField: 'type',
radius: 0.8,
label: {
type: 'inner',
content: '{name} {percentage}',
style: {
fill: '#fff',
fontSize: 14,
},
},
// 设置 状态样式
state: {
active: {
style: {
lineWidth: 0,
fillOpacity: 0.65,
},
},
},
// 添加 element 选中和激活交互
interactions: [{ name: 'element-selected' }, { name: 'element-active' }],
});

piePlot.render();

// 初始化设置默认状态;状态可叠加,可通过回调设置
piePlot.setState('active', (data) => (data as any).type === '分类一');
piePlot.setState('selected', (d) => (data as any).type === '分类一' || (data as any).type === '分类二');
17 changes: 17 additions & 0 deletions src/adaptor/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,20 @@ export function theme<O extends Pick<Options, 'theme'>>(params: Params<O>): Para
}
return params;
}

/**
* 状态 state 配置
* @param params
*/
export function state(params: Params<Options>): Params<Options> {
const { chart, options } = params;
const { state } = options;

if (state) {
each(chart.geometries, (geometry: Geometry) => {
geometry.state(state);
});
}

return params;
}
41 changes: 38 additions & 3 deletions src/core/plot.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Chart, Event } from '@antv/g2';
import { deepMix } from '@antv/util';
import Element from '@antv/g2/lib/geometry/element';
import { deepMix, each } from '@antv/util';
import EE from '@antv/event-emitter';
import { bind } from 'size-sensor';
import { Adaptor } from './adaptor';
import { Options, Data, Size } from '../types';
import { getContainerSize } from '../utils';
import { Options, Data, StateName, StateCondition, Size, StateObject } from '../types';
import { getContainerSize, getAllElements } from '../utils';

/** 单独 pick 出来的用于基类的类型定义 */
type PickOptions = Pick<
Expand Down Expand Up @@ -151,6 +152,40 @@ export abstract class Plot<O extends PickOptions> extends EE {
this.render();
}

/**
* 设置状态
* @param type 状态类型,支持 'active' | 'inactive' | 'selected' 三种
* @param conditions 条件,支持数组
* @param status 是否激活,默认 true
*/
public setState(type: StateName, condition: StateCondition, status: boolean = true) {
const elements = getAllElements(this.chart);

each(elements, (ele: Element) => {
if (condition(ele.getData())) {
ele.setState(type, status);
}
});
}

/**
* 获取状态
*/
public getStates(): StateObject[] {
const elements = getAllElements(this.chart);

const stateObjects: StateObject[] = [];
each(elements, (element: Element) => {
const data = element.getData();
const states = element.getStates();
each(states, (state) => {
stateObjects.push({ data, state, geometry: element.geometry, element });
});
});

return stateObjects;
}

/**
* 更新数据
* @param options
Expand Down
30 changes: 18 additions & 12 deletions src/plots/pie/adaptor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { deepMix, each, every, filter, get, isFunction, isString, isNil } from '@antv/util';
import { Params } from '../../core/adaptor';
import { legend, tooltip, interaction, animation, theme } from '../../adaptor/common';
import { legend, tooltip, interaction, animation, theme, state } from '../../adaptor/common';
import { Data } from '../../types';
import { flow, LEVEL, log, template } from '../../utils';
import { PieOptions } from './types';
Expand Down Expand Up @@ -161,7 +161,8 @@ function style(params: Params<PieOptions>): Params<PieOptions> {

/**
* annotation 配置
* 1. 中心文本
* 内置标注:
* 1. 中心文本
* @param params
*/
function annotation(params: Params<PieOptions>): Params<PieOptions> {
Expand All @@ -188,7 +189,10 @@ function annotation(params: Params<PieOptions>): Params<PieOptions> {
content: '',
};

const contentFormatter = get(content, 'formatter');
const getStatisticData = (data: Data) => ({
title: '总计',
value: getTotalValue(data, angleField),
});

if (title !== false) {
let titleLineHeight = get(title, 'style.lineHeight');
Expand All @@ -201,10 +205,7 @@ function annotation(params: Params<PieOptions>): Params<PieOptions> {
type: 'text',
position: ['50%', '50%'],
content: (filterData: Data) => {
const statisticData = {
title: '总计',
value: getTotalValue(filterData, angleField),
};
const statisticData = getStatisticData(filterData);
return titleFormatter ? titleFormatter(statisticData, filterData) : statisticData.title;
},
...deepMix(
Expand All @@ -213,6 +214,9 @@ function annotation(params: Params<PieOptions>): Params<PieOptions> {
offsetY: content === false ? 0 : -titleLineHeight,
// append-info
key: 'statistic',
style: {
textAlign: 'center',
},
},
title
),
Expand All @@ -224,14 +228,13 @@ function annotation(params: Params<PieOptions>): Params<PieOptions> {
if (!valueLineHeight) {
valueLineHeight = get(content, 'style.fontSize', 20);
}
const contentFormatter = get(content, 'formatter');

statisticContent = {
type: 'text',
position: ['50%', '50%'],
content: (filterData: Data) => {
const statisticData = {
title: '总计',
value: getTotalValue(filterData, angleField),
};
const statisticData = getStatisticData(filterData);
return contentFormatter ? contentFormatter(statisticData, filterData) : statisticData.value;
},
...deepMix(
Expand All @@ -241,6 +244,9 @@ function annotation(params: Params<PieOptions>): Params<PieOptions> {
offsetY: title === false ? 0 : valueLineHeight,
// append-info
key: 'statistic',
style: {
textAlign: 'center',
},
},
content
),
Expand All @@ -266,5 +272,5 @@ function annotation(params: Params<PieOptions>): Params<PieOptions> {
*/
export function adaptor(params: Params<PieOptions>) {
// flow 的方式处理所有的配置到 G2 API
flow(field, meta, theme, coord, legend, tooltip, label, style, annotation, interaction, animation)(params);
flow(field, meta, theme, coord, legend, tooltip, label, state, style, annotation, interaction, animation)(params);
}
33 changes: 18 additions & 15 deletions src/plots/pie/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ export type StatisticData = {
*/
type Statistic = Readonly<{
/** 自定义 title 标签 */
title?: {
formatter?: (item: StatisticData, data: LooseObject | LooseObject[]) => string;
rotate?: number;
offsetX?: number;
offsetY?: number;
style?: ShapeStyle;
};
title?:
| boolean
| {
formatter?: (item: StatisticData, data: LooseObject | LooseObject[]) => string;
rotate?: number;
offsetX?: number;
offsetY?: number;
style?: ShapeStyle;
};
/** 自定义 content 内容 */
content?: {
formatter?: (item: StatisticData, data: LooseObject | LooseObject[]) => string;
rotate?: number;
offsetX?: number;
offsetY?: number;
style?: ShapeStyle;
};
// todo 提供 htmlContent 的方式,自由定制中心文本
content?:
| boolean
| {
formatter?: (item: StatisticData, data: LooseObject | LooseObject[]) => string;
rotate?: number;
offsetX?: number;
offsetY?: number;
style?: ShapeStyle;
};
}>;

export interface PieOptions extends Options {
Expand Down
Loading

0 comments on commit 5ba9e0a

Please sign in to comment.