Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c80384b
feat(funnel): set prop to flatten the funnel exit #14863
pe-3 Aug 2, 2022
4605cfc
feat(funnel): add dynamic height new prop #16445
pe-3 Aug 6, 2022
cf4166c
feat(funnel): add dynamic height new prop #16445
pe-3 Aug 6, 2022
5d65776
feat(funnel): fix code styles error
pe-3 Aug 7, 2022
903f00e
feat(funnel): add exitWidth prop
pe-3 Aug 12, 2022
a67891c
feat(funnel):add transform rate between each data
pe-3 Aug 14, 2022
8703c20
feat(funnel):add transform rate between each data
pe-3 Aug 14, 2022
94ad84b
feat(funnel): add dynamic size mapping mode
pe-3 Sep 3, 2022
4e0842e
feat(funnel): fix funnel code style
pe-3 Sep 3, 2022
fa55aca
feat(funnel): change lastWidth as thickDegree which mean dynamicHeigh…
pe-3 Sep 5, 2022
5aaf7b4
feat(funnel): fix the bug thay exit width can greater than 100%
pe-3 Sep 5, 2022
18a0dea
feat(funnel):add auto test, and change dynamciSize into dynamciArea
pe-3 Sep 6, 2022
151de1d
chore(workflow): dump github context object for debug
plainheart Sep 19, 2022
06f5055
Merge branch 'master' into 'feat-funnel-newStyle'
plainheart Sep 19, 2022
a236499
chore(workflow): remove debug step
plainheart Sep 19, 2022
0ca2b9e
feat(funnel): use formatter to set rate label text
pe-3 Oct 21, 2022
d128b3f
Merge branch 'feat-funnel-newStyle' of github.com:pe-3/echarts-funnel…
pe-3 Oct 21, 2022
fe14b3b
feat(funnel): use formatter to set rate label text
pe-3 Oct 21, 2022
646736c
feat(funnel): run test visual of funnel new feat
pe-3 Oct 21, 2022
83d6a84
feat(funnle): visual test of all funnel new feat
pe-3 Oct 21, 2022
f5b1065
feat(funnel): rename test case file, delete dynamicArea
pe-3 Oct 21, 2022
b7a2e5c
feat(funnel): remove unnecessary data from label format params
pe-3 Oct 25, 2022
19d9b6b
feat(funnel): remove tpl from rate label fomatter
pe-3 Oct 27, 2022
f4d5681
feat(funnel): fix overall rate label set styles separately
pe-3 Oct 30, 2022
ebda31a
feat(funnel): give formatter in params
pe-3 Oct 30, 2022
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
20 changes: 20 additions & 0 deletions src/chart/funnel/FunnelSeries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ type FunnelLabelOption = Omit<SeriesLabelOption, 'position'> & {
| 'outer' | 'inner' | 'center' | 'rightTop' | 'rightBottom' | 'leftTop' | 'leftBottom'
};

type FunnelRateLabelOption = Omit<SeriesLabelOption, 'position'> & {
precision: number
};

interface FunnelStatesMixin {
emphasis?: DefaultStatesMixinEmphasis
}
Expand All @@ -59,6 +63,8 @@ export interface FunnelStateOption<TCbParams = never> {
itemStyle?: ItemStyleOption<TCbParams>
label?: FunnelLabelOption
labelLine?: LabelLineOption
rateLabel?: FunnelRateLabelOption
overallRateLabel?: FunnelRateLabelOption
}

export interface FunnelDataItemOption
Expand Down Expand Up @@ -95,6 +101,12 @@ export interface FunnelSeriesOption
funnelAlign?: HorizontalAlign | VerticalAlign

data?: (OptionDataValueNumeric | OptionDataValueNumeric[] | FunnelDataItemOption)[]

exitWidth?: string
Comment thread
pe-3 marked this conversation as resolved.

showRate?: boolean

dynamicHeight?: boolean
}

class FunnelSeriesModel extends SeriesModel<FunnelSeriesOption> {
Expand Down Expand Up @@ -172,6 +184,14 @@ class FunnelSeriesModel extends SeriesModel<FunnelSeriesOption> {
position: 'outer'
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
},
rateLabel: {
show: true,
precision: 2
},
overallRateLabel: {
show: true,
precision: 2
},
labelLine: {
show: true,
length: 20,
Expand Down
201 changes: 170 additions & 31 deletions src/chart/funnel/FunnelView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,100 @@
* specific language governing permissions and limitations
* under the License.
*/

import * as zrUtil from 'zrender/src/core/util';
import * as graphic from '../../util/graphic';
import { setStatesStylesFromModel, toggleHoverEmphasis } from '../../util/states';
import ChartView from '../../view/Chart';
import FunnelSeriesModel, {FunnelDataItemOption} from './FunnelSeries';
import FunnelSeriesModel, { FunnelDataItemOption } from './FunnelSeries';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../core/ExtensionAPI';
import SeriesData from '../../data/SeriesData';
import { ColorString } from '../../util/types';
import {
ColorString,
DisplayState,
InterpolatableValue,
SeriesDataType
} from '../../util/types';
import { setLabelLineStyle, getLabelLineStatesModels } from '../../label/labelGuideHelper';
import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
import { saveOldStyle } from '../../animation/basicTransition';

const opacityAccessPath = ['itemStyle', 'opacity'] as const;

const rateLabelFetcher = {
getFormattedLabel(
// In MapDraw case it can be string (region name)
labelDataIndex: number,
status: DisplayState,
dataType?: SeriesDataType,
labelDimIndex?: number,
formatter?: string | ((params: object) => string),
// If provided, the implementation of `getFormattedLabel` can use it
// to generate the final label text.
extendParams?: {
interpolatedValue: InterpolatableValue
}
): string {
status = status || 'normal';
const { hostModel, layout } = this as unknown as { hostModel: FunnelSeriesModel, layout: any };
const data = hostModel.getData(dataType);

if (!formatter) {
Comment thread
pe-3 marked this conversation as resolved.
const itemModel = data.getItemModel(labelDataIndex);
// @ts-ignore
formatter = itemModel.get(status === 'normal'
? ['rateLabel', 'formatter']
: [status, 'rateLabel', 'formatter']
);
}

const { rate, isLastPiece, nextName, preName, preDataIndex, nextDataIndex } = layout;

if (isLastPiece) {
const itemModel = data.getItemModel(labelDataIndex);
// @ts-ignore
formatter = itemModel.get(status === 'normal'
? ['overallRateLabel', 'formatter']
: [status, 'overallRateLabel', 'formatter']
);
}

type RateParams = {
rate: string,
preName: string,
nextName: string,
preDataIndex: string,
nextDataIndex: string,
formatter: string | ((params: object) => string)
};

const params: RateParams = {
rate, // a
preName, // b
nextName, // c
preDataIndex, // d
nextDataIndex, // e
formatter
};

if (zrUtil.isFunction(formatter)) {
return formatter(params);
}

return '';
}
};

/**
* Piece of pie including Sector, Label, LabelLine
*/
class FunnelPiece extends graphic.Polygon {

constructor(data: SeriesData, idx: number) {
/**
* @param type judge is data blocks or conversion blocks
*/

constructor(data: SeriesData, idx: number, type: 'data' | 'rate') {
super();

const polygon = this;
Expand All @@ -45,10 +118,10 @@ class FunnelPiece extends graphic.Polygon {
polygon.setTextContent(text);
this.setTextGuideLine(labelLine);

this.updateData(data, idx, true);
this.updateData(data, idx, type, true);
}

updateData(data: SeriesData, idx: number, firstCreate?: boolean) {
updateData(data: SeriesData, idx: number, type: 'data' | 'rate', firstCreate?: boolean) {

const polygon = this;

Expand All @@ -58,17 +131,29 @@ class FunnelPiece extends graphic.Polygon {
const emphasisModel = itemModel.getModel('emphasis');
let opacity = itemModel.get(opacityAccessPath);
opacity = opacity == null ? 1 : opacity;
if (type === 'rate') {
// the opacity of rate piece is half of data
opacity /= 2;
}

if (!firstCreate) {
saveOldStyle(polygon);
}
// hide the last rate piece and when it not the last one show it again
polygon.invisible = false;
if (layout.isLastPiece && type === 'rate') {
// hide last rate piece
polygon.invisible = true;
}
// Update common style
polygon.useStyle(data.getItemVisual(idx, 'style'));
polygon.style.lineJoin = 'round';

const points = type === 'data' ? layout.points : layout.ratePoints;

if (firstCreate) {
polygon.setShape({
points: layout.points
points
});
polygon.style.opacity = 0;
graphic.initProps(polygon, {
Expand All @@ -83,49 +168,62 @@ class FunnelPiece extends graphic.Polygon {
opacity: opacity
},
shape: {
points: layout.points
points
}
}, seriesModel, idx);
}

setStatesStylesFromModel(polygon, itemModel);

this._updateLabel(data, idx);
this._updateLabel(data, idx, type);

toggleHoverEmphasis(
this,
emphasisModel.get('focus'),
emphasisModel.get('blurScope'),
emphasisModel.get('disabled')
);
setStatesStylesFromModel(polygon, itemModel);
if (type === 'data') {
toggleHoverEmphasis(
this,
emphasisModel.get('focus'),
emphasisModel.get('blurScope'),
emphasisModel.get('disabled')
);
}
}

_updateLabel(data: SeriesData, idx: number) {
_updateLabel(data: SeriesData, idx: number, type: 'data' | 'rate') {
const polygon = this;
const labelLine = this.getTextGuideLine();
const labelText = polygon.getTextContent();

const seriesModel = data.hostModel;
const seriesModel = data.hostModel as FunnelSeriesModel;
const itemModel = data.getItemModel<FunnelDataItemOption>(idx);
const layout = data.getItemLayout(idx);
const labelLayout = layout.label;
const labelLayout = layout[type === 'data' ? 'label' : 'rateLabel'];
const style = data.getItemVisual(idx, 'style');
const visualColor = style.fill as ColorString;
// bind this to data of rateLabelFetherFunc
let rateFetcher: any; // clone default fechter
if (type === 'rate') {
rateFetcher = {
getFormattedLabel: rateLabelFetcher.getFormattedLabel.bind(
{ hostModel: data.hostModel, layout }
)
};
}
const rateLabel = layout.isLastPiece ? 'overallRateLabel' : 'rateLabel';

setLabelStyle(
// position will not be used in setLabelStyle
labelText,
getLabelStatesModels(itemModel),
getLabelStatesModels(itemModel, type === 'data' ? undefined : rateLabel),
{
labelFetcher: data.hostModel as FunnelSeriesModel,
labelFetcher: type === 'data' ? data.hostModel as FunnelSeriesModel : rateFetcher,
labelDataIndex: idx,
defaultOpacity: style.opacity,
defaultText: data.getName(idx)
defaultText: type === 'data' ? data.getName(idx) : layout.rate
},
{ normal: {
align: labelLayout.textAlign,
verticalAlign: labelLayout.verticalAlign
} }
{
normal: {
align: labelLayout.textAlign,
verticalAlign: labelLayout.verticalAlign
}
}
);

polygon.setTextConfig({
Expand Down Expand Up @@ -167,6 +265,9 @@ class FunnelPiece extends graphic.Polygon {
stroke: visualColor
});
}

// relate ratePiece with dataPiece
ratePiece: FunnelPiece;
}

class FunnelView extends ChartView {
Expand All @@ -182,26 +283,64 @@ class FunnelView extends ChartView {
const oldData = this._data;

const group = this.group;
// rate in other two mode did not support yet
const showRate =
seriesModel.get('showRate')
&& !(
seriesModel.get('dynamicHeight')
|| seriesModel.get('sort') === 'none'
);

data.diff(oldData)
.add(function (idx) {
const funnelPiece = new FunnelPiece(data, idx);
const funnelPiece = new FunnelPiece(data, idx, 'data');

data.setItemGraphicEl(idx, funnelPiece);

group.add(funnelPiece);

if (showRate) {
const ratePiece = new FunnelPiece(data, idx, 'rate');
group.add(ratePiece);
funnelPiece.ratePiece = ratePiece;
}
})
.update(function (newIdx, oldIdx) {
const piece = oldData.getItemGraphicEl(oldIdx) as FunnelPiece;

piece.updateData(data, newIdx);
piece.updateData(data, newIdx, 'data');

group.add(piece);
data.setItemGraphicEl(newIdx, piece);

// rate funnel piece may remove in this mount func
const ratePiece = piece.ratePiece;
if (showRate) {
if (ratePiece) {
ratePiece.updateData(data, newIdx, 'rate');
group.add(ratePiece);
}
else {
const ratePiece = new FunnelPiece(data, newIdx, 'rate');
group.add(ratePiece);
piece.ratePiece = ratePiece;
}
}
else {
if (ratePiece) {
graphic.removeElementWithFadeOut(ratePiece, seriesModel, oldIdx);
piece.ratePiece = null;
}
}
})
.remove(function (idx) {
const piece = oldData.getItemGraphicEl(idx);
const piece = oldData.getItemGraphicEl(idx) as FunnelPiece;
graphic.removeElementWithFadeOut(piece, seriesModel, idx);

if (showRate) {
const ratePiece = piece.ratePiece;
graphic.removeElementWithFadeOut(ratePiece, seriesModel, idx);
}
})
.execute();

Expand All @@ -213,7 +352,7 @@ class FunnelView extends ChartView {
this._data = null;
}

dispose() {}
dispose() { }
}


Expand Down
Loading