Skip to content

Commit

Permalink
fix: funnel
Browse files Browse the repository at this point in the history
  • Loading branch information
lxfu1 committed Oct 25, 2023
1 parent 18523bc commit 1ccafba
Show file tree
Hide file tree
Showing 28 changed files with 241 additions and 1,217 deletions.
2 changes: 1 addition & 1 deletion packages/plots/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
},
"dependencies": {
"@antv/event-emitter": "^0.1.3",
"@antv/g2": "^5.1.3",
"@antv/g2": "^5.1.6",
"size-sensor": "^1.0.1",
"@ant-design/charts-util": "workspace:*"
},
Expand Down
3 changes: 1 addition & 2 deletions packages/plots/src/components/funnel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import React from 'react';
import { FunnelOptions } from '../../core';
import { CommonConfig } from '../../interface';
import { BaseChart } from '../base';
import { Funnel } from '../../core/plots/funnel';

export type FunnelConfig = CommonConfig<FunnelOptions>;

const FunnelChart = (props: FunnelConfig) => <BaseChart {...props} chartType="Funnel" />;

export default Object.assign(FunnelChart, Funnel.getFields());
export default FunnelChart;
15 changes: 0 additions & 15 deletions packages/plots/src/constants/index.ts

This file was deleted.

2 changes: 2 additions & 0 deletions packages/plots/src/core/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ export abstract class Plot<O extends Options> extends EE {
if (this.type !== 'base') {
this.execAdaptor();
}
// @ts-ignore
console.log(this.getSpecOptions());

// options 转换
this.chart.options(this.getSpecOptions());
Expand Down
2 changes: 1 addition & 1 deletion packages/plots/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export type { AreaOptions } from './plots/area';
export type { BarOptions } from './plots/bar';
export type { ColumnOptions } from './plots/column';
export type { DualAxesOptions } from './plots/dual-axes';
export type { FunnelOptions } from './plots/funnel';
export type { FunnelOptions } from './plots/funnel-o';
export type { LineOptions } from './plots/line';
export type { PieOptions } from './plots/pie';
export type { ScatterOptions } from './plots/scatter';
Expand Down
219 changes: 56 additions & 163 deletions packages/plots/src/core/plots/funnel/adaptor.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,7 @@
import { flow, transformOptions, set, groupBy } from '../../utils';
import { mark } from '../../components';
import type { Adaptor } from '../../types';
import {
deepAssign,
flow,
map,
transformOptions,
maxBy,
get,
groupBy,
conversionTagFormatter,
isNumber,
omit,
isFunction,
} from '../../utils';
import type { FunnelOptions } from './type';
import { FUNNEL_CONVERSATION, FUNNEL_PERCENT, FUNNEL_MAPPING_VALUE, CUSTOM_COMVERSION_TAG_CONFIG } from './constant';
import { Datum } from '../../../interface';
import { compareFunnel } from './compare';
import { facetFunnel } from './facet';

type Params = Adaptor<FunnelOptions>;

Expand All @@ -26,168 +11,76 @@ type Params = Adaptor<FunnelOptions>;
*/
export function adaptor(params: Params) {
/**
* @description 数据转换
* 图表差异化处理
*/
const _transformData = (params: Params, extraMaxValue?: number, customData?: any[]) => {
const { yField, maxSize, minSize, data: originData } = params.options;
const maxYFieldValue = extraMaxValue ?? get(maxBy(originData, yField), [yField]);
const max = isNumber(maxSize) ? maxSize : 1;
const min = isNumber(minSize) ? minSize : 0;

const curData = customData || originData;
return map(curData, (row, index) => {
const percent = row[yField] === 253 ? 1 : (row[yField] || 0) / maxYFieldValue;
row[FUNNEL_PERCENT] = percent;
row[FUNNEL_MAPPING_VALUE] = (max - min) * percent + min;
// 转化率数据存储前后数据
row[FUNNEL_CONVERSATION] = [get(curData, [index - 1, yField]), row[yField]];
return row;
});
const init = (params: Params) => {
const { options } = params;
const { xField, colorField, labels } = options;
if (!colorField) {
set(options, 'colorField', xField);
}
if (labels) {
set(options, 'label', false);
}
return params;
};

const transformData = (params: Params) => {
const { yField, data, compareField, seriesField } = params.options;

if (compareField || seriesField) {
const maxCache = {};
const groupByField = compareField || seriesField;

const groups = groupBy(data, (d) => () => {
const curKey = d[groupByField];
const curMax = maxCache[curKey] ?? Number.MIN_SAFE_INTEGER;
maxCache[curKey] = Math.max(curMax, d[yField]);
return curKey;
});

const formatData = Object.keys(groups).reduce((res, curKey) => {
return res.concat(_transformData(params, maxCache[curKey], groups[curKey]));
}, []);

params.options.data = formatData;
} else {
params.options.data = _transformData(params);
const transform = (params: Params) => {
const { options } = params;
const { compareField, transform, isTransposed = true, coordinate } = options;
if (!transform) {
if (compareField) {
set(options, 'transform', []);
} else {
set(options, 'transform', [{ type: 'symmetryY' }]);
}
}
if (!coordinate && isTransposed) {
set(options, 'coordinate', { transform: [{ type: 'transpose' }] });
}

return params;
};

/**
* 图表差异化处理
*/
const init = (params: Params) => {
const { xField, yField, shape, isTransposed, compareField, seriesField, funnelStyle, label } = params.options;

if (compareField) {
compareFunnel(params);
} else if (seriesField) {
facetFunnel(params);
} else {
const conversionTag = get(params.options, CUSTOM_COMVERSION_TAG_CONFIG);

const basicLabel = [
{
text: (d) => `${d[xField]} ${d[yField]}`,
position: 'inside',
fillOpacity: 1,
...label,
},
];

const rateLabel = [
// coversion 内容,
{
textAlign: 'left',
textBaseline: 'middle',
fill: '#aaa',
fillOpacity: 1,
connector: true,
...conversionTag,
text: (...args: [d: Datum, index: number]) =>
args[1] !== 0
? isFunction(conversionTag?.text)
? `${conversionTag.text(...args)}`
: `Rate: ${conversionTagFormatter(...(args[0][FUNNEL_CONVERSATION] as [number, number]))}`
: '',
...(!isTransposed
? {
position: 'top-right',
dx: 20,
backgroundPadding: [0, 8],
}
: {
position: 'top-left',
dy: -20,
dx: 8, // 与connector 间隙
backgroundPadding: [-8, 8],
}),
},
];

const labels = [...(label === false ? [] : basicLabel), ...(conversionTag === false ? [] : rateLabel)];
const compare = (params: Params) => {
const { options } = params;
const { compareField, seriesField, data, children, yField, isTransposed = true } = options;

const basicFunnel = {
if (compareField || seriesField) {
const groupedData = Object.values(groupBy(data, (item) => item[compareField || seriesField]));
children[0].data = groupedData[0];
children.push({
type: 'interval',
axis: false,
coordinate: !isTransposed
? {
transform: [{ type: 'transpose' }],
}
: undefined,
scale: {
x: {
padding: 0,
},
},
style: funnelStyle,
encode: {
x: xField,
y: FUNNEL_MAPPING_VALUE,
color: xField,
shape: shape || 'funnel',
},
animate: { enter: { type: 'fadeIn' } },
tooltip: {
title: false,
items: [
(d) => ({
name: d[xField],
value: d[yField],
}),
],
},
// labels 对应 xField
labels,
};

params.options.children = map(params.options.children, (child) => {
return deepAssign(child, basicFunnel);
data: groupedData[1],
yField: (item) => -item[yField],
});
delete options['compareField'];
delete options.data;
}
if (seriesField) {
set(options, 'type', 'spaceFlex');
set(options, 'ratio', [1, 1]);
set(options, 'direction', isTransposed ? 'row' : 'col');
delete options['seriesField'];
}

// 漏斗图 label、conversionTag 不可被通用处理
params.options = omit(params.options, ['label', CUSTOM_COMVERSION_TAG_CONFIG, 'yField', 'xField', 'seriesField']);

return params;
};

/**
* legend 配置
* @param params
*/
const legend = (params: Params): Params => {
const { legend } = params.options;

params.options.legend = legend ?? {
color: {
position: 'bottom',
layout: {
justifyContent: 'center',
},
},
};

const tooltip = (params) => {
const { options } = params;
const { tooltip, xField, yField } = options;
if (!tooltip) {
set(options, 'tooltip', {
title: false,
items: [
(d) => {
return { name: d[xField], value: d[yField] };
},
],
});
}
return params;
};

return flow(transformData, init, legend)(params);
return flow(init, transform, compare, tooltip, transformOptions, mark)(params);
}
Loading

0 comments on commit 1ccafba

Please sign in to comment.