Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Aug 19, 2020
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
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;
hustcc marked this conversation as resolved.
Show resolved Hide resolved
};
/** 自定义 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