Skip to content

fix: update VennSeries to handle empty keys and filter valid legend keys #4000

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

Open
wants to merge 5 commits into
base: dev/2.0.0
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions packages/vchart/src/chart/venn/venn-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class VennChartSpecTransformer<T extends IVennChartSpec = IVennChartSpec>
const series: any = super._getDefaultSeriesSpec(spec, [
'categoryField',
'valueField',
'emptySetKey',
'circle',
'overlap',
'overlapLabel'
Expand Down
5 changes: 4 additions & 1 deletion packages/vchart/src/series/venn/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ export interface IVennSeriesSpec extends ISeriesSpec, IAnimationSpec<VennMark, V
* 权重字段
*/
valueField: string;

/**
* 空集合标签
*/
emptySetKey?: string;
/**
* 圆形图元配置
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/vchart/src/series/venn/tooltip-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { getVennSeriesDataKey } from './util';

export class VennTooltipHelper extends BaseSeriesTooltipHelper {
dimensionTooltipTitleCallback = (datum: any) => {
return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]]);
return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]], this.series.getSpec().emptySetKey);
};

markTooltipKeyCallback = (datum: any) => {
return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]]);
return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]], this.series.getSpec().emptySetKey);
};
}
5 changes: 4 additions & 1 deletion packages/vchart/src/series/venn/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { array } from '@visactor/vutils';

export const getVennSeriesDataKey = (sets: string | string[]) => {
export const getVennSeriesDataKey = (sets: string | string[], emptysetKey?: string) => {
if (!sets || (Array.isArray(sets) && sets.length === 0)) {
return emptysetKey || 'others';
}
return array(sets).join(',');
};
81 changes: 55 additions & 26 deletions packages/vchart/src/series/venn/venn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export class VennSeries<T extends IVennSeriesSpec = IVennSeriesSpec> extends Bas

static readonly mark: SeriesMarkMap = vennSeriesMark;
static readonly builtInTheme = { venn };

static readonly transformerConstructor = VennSeriesSpecTransformer;
readonly transformerConstructor = VennSeriesSpecTransformer;

Expand Down Expand Up @@ -67,11 +66,21 @@ export class VennSeries<T extends IVennSeriesSpec = IVennSeriesSpec> extends Bas
return this._valueField;
}

protected _emptySetKey!: string;
getEmptySetKey() {
return this._emptySetKey;
}
setEmptySetKey(f: string): string {
this._emptySetKey = f;
return this._emptySetKey;
}

setAttrFromSpec(): void {
super.setAttrFromSpec();
this.setCategoryField(this._spec.categoryField ?? 'sets');
this.setValueField(this._spec.valueField ?? 'size');
this.setSeriesField(this._spec.seriesField ?? DEFAULT_DATA_KEY);
this.setEmptySetKey(this._spec.emptySetKey ?? 'others');
}

initData() {
Expand Down Expand Up @@ -210,7 +219,13 @@ export class VennSeries<T extends IVennSeriesSpec = IVennSeriesSpec> extends Bas
{
x: datum => (datum as IVennCircleDatum).labelX,
y: datum => (datum as IVennCircleDatum).labelY,
text: datum => getVennSeriesDataKey((datum as IVennCircleDatum).sets),
text: datum => {
const sets = (datum as IVennOverlapDatum).sets;
if (!sets || (Array.isArray(sets) && sets.length === 0)) {
return '';
}
return getVennSeriesDataKey(sets);
},
maxLineWidth: (datum: any) => {
const { x, radius, labelX } = datum as IVennCircleDatum;
const circleX0 = x - radius;
Expand All @@ -234,7 +249,10 @@ export class VennSeries<T extends IVennSeriesSpec = IVennSeriesSpec> extends Bas
{
x: datum => (datum as IVennOverlapDatum).labelX,
y: datum => (datum as IVennOverlapDatum).labelY,
text: datum => getVennSeriesDataKey((datum as IVennOverlapDatum).sets)
text: datum => {
const sets = (datum as IVennOverlapDatum).sets;
return getVennSeriesDataKey(sets);
}
},
STATE_VALUE_ENUM.STATE_NORMAL,
AttributeLevel.Series
Expand Down Expand Up @@ -310,7 +328,7 @@ export class VennSeries<T extends IVennSeriesSpec = IVennSeriesSpec> extends Bas
protected _getSeriesInfo(field: string, keys: string[]) {
const defaultShapeType = this.getDefaultShapeType();
return keys.map(originalKey => {
const dataKey = getVennSeriesDataKey(originalKey);
const dataKey = getVennSeriesDataKey(originalKey, this._emptySetKey);
return {
key: dataKey,
originalKey,
Expand All @@ -324,7 +342,7 @@ export class VennSeries<T extends IVennSeriesSpec = IVennSeriesSpec> extends Bas

getSeriesFieldValue(datum: Datum, seriesField?: string) {
const value = super.getSeriesFieldValue(datum, seriesField);
return getVennSeriesDataKey(value);
return getVennSeriesDataKey(value, this._emptySetKey);
}

legendSelectedFilter(component: ILegend, selectedKeys: StringOrNumber[]) {
Expand All @@ -339,27 +357,38 @@ export class VennSeries<T extends IVennSeriesSpec = IVennSeriesSpec> extends Bas
if (selectedKeys.length === originalLegendKeys.length) {
return selectedKeys;
}

// 找到缺失的项
const selectedFilter = {};
selectedKeys.forEach(s => {
selectedFilter[s] = true;
});
const disableKeys = originalLegendKeys.filter(key => !selectedFilter[getVennSeriesDataKey(key)]);

// 找到缺失的项的派生项(如 “A&B” 的派生项 “A&B&C”)
const derivedDisableKeys = originalLegendKeys.filter(key => {
if (disableKeys.includes(key)) {
return false;
}
return disableKeys.some(disableKey => array(disableKey).every(k => key.includes(k)));
});

// 将派生项从 selectedKeys 中移除
selectedKeys = selectedKeys.slice();
derivedDisableKeys.forEach(key => {
selectedKeys.splice(selectedKeys.indexOf(getVennSeriesDataKey(key)), 1);
});
const emptyKey = this._emptySetKey;

const hasEmpty = selectedKeys.includes(emptyKey);
const nonEmpty = selectedKeys.filter(key => key !== emptyKey);

if (nonEmpty.length > 0) {
// 过滤出非空的原始图例键
const validKeys = originalLegendKeys.filter(key => getVennSeriesDataKey(key, this._emptySetKey) !== emptyKey);
// 找到缺失的项
const selectedFilter: Record<StringOrNumber, boolean> = {};
selectedKeys.forEach(s => {
selectedFilter[s] = true;
});
const disableKeys = validKeys.filter(key => !selectedFilter[getVennSeriesDataKey(key, this._emptySetKey)]);

// 找到缺失的项的派生项(如 “A&B” 的派生项 “A&B&C”)
const derivedDisableKeys = validKeys.filter(key => {
if (disableKeys.includes(key)) {
return false;
}
return disableKeys.some(disableKey => array(disableKey).every(k => key.includes(k)));
});

// 将派生项从 nonEmpty 中移除
selectedKeys = nonEmpty.slice();
derivedDisableKeys.forEach(key => {
selectedKeys.splice(selectedKeys.indexOf(getVennSeriesDataKey(key, this._emptySetKey)), 1);
});
}
if (hasEmpty) {
selectedKeys.push(emptyKey);
}
}
return selectedKeys;
}
Expand Down
Loading