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

Refactor/tooltip pattern parser #3138

Merged
merged 9 commits into from
Sep 3, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "refactor: refactor the parser of tooltip pattern\n\n",
"type": "none",
"packageName": "@visactor/vchart"
}
],
"packageName": "@visactor/vchart",
"email": "[email protected]"
}
4 changes: 3 additions & 1 deletion packages/vchart/src/component/tooltip/constant.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PREFIX } from '../../constant/base';
import type { ITooltipLineActual } from '../../typings';
import type { ITooltipLineActual, TooltipActiveType } from '../../typings';

export class TooltipHandlerType {
static dom = `${PREFIX}_TOOLTIP_HANDLER_DOM`; // 模拟 enum
Expand All @@ -15,3 +15,5 @@ export const TOOLTIP_OTHERS_LINE = {
key: '其他',
value: '...'
} as ITooltipLineActual;

export const TOOLTIP_TYPES: TooltipActiveType[] = ['group', 'mark', 'dimension'];
6 changes: 3 additions & 3 deletions packages/vchart/src/component/tooltip/interface/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export type TooltipHandlerParams = DimensionEventParams & {
};

export interface ITooltipActiveTypeAsKeys<T, K, U> {
mark: T;
dimension: K;
group: U;
mark?: T;
dimension?: K;
group?: U;
}

export type TotalMouseEventData = {
Expand Down
94 changes: 15 additions & 79 deletions packages/vchart/src/component/tooltip/processor/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ import type { ITooltipSpec, TooltipHandlerParams } from '../interface';
// eslint-disable-next-line no-duplicate-imports
import { TooltipResult } from '../interface/common';
import type { Tooltip } from '../tooltip';
import type { DimensionTooltipInfo, MouseEventData, TooltipInfo } from './interface';
import type { MouseEventData, TooltipInfo } from './interface';
import { ChartEvent } from '../../../constant/event';
import type { TooltipEventParams } from '../interface/event';
import type { IDimensionData, IDimensionInfo } from '../../../event/events/dimension';
import { getPolarDimensionInfo } from '../../../event/events/dimension/util/polar';
import { getCartesianDimensionInfo } from '../../../event/events/dimension/util/cartesian';
import { isDiscrete } from '@visactor/vscale';
import type { ICartesianSeries, ISeries } from '../../../series/interface';
import type { IDimensionInfo } from '../../../event/events/dimension';
import type { ISeries } from '../../../series/interface';
import { getTooltipSpecForShow } from '../utils/get-spec';
import { getShowContent } from '../utils/compose';
import { getTooltipPatternValue } from '../utils/get-value';
import { isActiveTypeVisible } from '../utils/common';

export abstract class BaseTooltipProcessor {
readonly component: Tooltip;
Expand All @@ -30,12 +28,8 @@ export abstract class BaseTooltipProcessor {

/** 触发对应类型的 tooltip */
abstract showTooltip(info: TooltipInfo, params: BaseEventParams, changePositionOnly: boolean): TooltipResult;

/** 判断是否应该触发 tooltip */
abstract shouldHandleTooltip(params: BaseEventParams, mouseEventData: Partial<MouseEventData>): boolean;

/** 获取触发 tooltip 需要的信息 */
abstract getMouseEventData(params: BaseEventParams, dimensionInfo?: DimensionTooltipInfo): MouseEventData;
abstract getMouseEventData(params: BaseEventParams): MouseEventData;

protected _showTooltipByHandler = (data: TooltipData | undefined, params: TooltipHandlerParams): TooltipResult => {
if (isNil(data)) {
Expand Down Expand Up @@ -107,74 +101,6 @@ export abstract class BaseTooltipProcessor {
return undefined;
}

protected _getDimensionInfo(params: BaseEventParams): IDimensionInfo[] {
let targetDimensionInfo: IDimensionInfo[] | undefined;
// 处理dimension info
const chart = this.component.getChart();

// compute layer offset
const layer = chart.getCompiler().getStage().getLayer(undefined);
const point = { x: params.event.viewX, y: params.event.viewY };
layer.globalTransMatrix.transformPoint({ x: params.event.viewX, y: params.event.viewY }, point);

targetDimensionInfo = [
...(getCartesianDimensionInfo(chart, point, true) ?? []),
...(getPolarDimensionInfo(chart, point) ?? [])
];
if (targetDimensionInfo.length === 0) {
targetDimensionInfo = undefined;
} else if (targetDimensionInfo.length > 1) {
// 只保留一个轴的dimension info
const dimensionAxisInfo = targetDimensionInfo.filter(info => {
const axis = info.axis;
if (axis.getSpec().hasDimensionTooltip) {
return true;
}

// 优先显示离散轴 tooltip
if (!isDiscrete(axis.getScale().type)) {
return false;
}
// 下面的逻辑用来判断当前的离散轴是不是维度轴
let firstSeries: ICartesianSeries | undefined;
for (const region of axis?.getRegions() ?? []) {
for (const series of region.getSeries()) {
if (series.coordinate === 'cartesian') {
firstSeries = series as ICartesianSeries;
break;
}
}
if (isValid(firstSeries)) {
break;
}
}
if (isValid(firstSeries) && firstSeries.getDimensionField()[0] === firstSeries.fieldY[0]) {
// 维度轴为Y轴时,选择只显示Y轴tooltip
return axis.getOrient() === 'left' || axis.getOrient() === 'right';
}
// 维度轴为X轴时,选择只显示X轴tooltip
return axis.getOrient() === 'bottom' || axis.getOrient() === 'top';
});
targetDimensionInfo = dimensionAxisInfo.length ? dimensionAxisInfo : targetDimensionInfo.slice(0, 1);

// datum 去重,保证每个系列的每个数据项只对应于一行 tooltip 内容项
if (targetDimensionInfo.length > 1) {
const dimensionDataKeySet = new Set<string>();
targetDimensionInfo.forEach(info => {
info.data = info.data.filter(({ key }: IDimensionData) => {
if (dimensionDataKeySet.has(key)) {
return false;
}
dimensionDataKeySet.add(key);
return true;
});
});
}
}

return targetDimensionInfo;
}

/**
* 合成实际显示的 tooltip spec
* @param params
Expand Down Expand Up @@ -222,6 +148,16 @@ export abstract class BaseTooltipProcessor {
}
}

/** 判断是否应该触发 tooltip */
shouldHandleTooltip(params: BaseEventParams, mouseEventData: Partial<MouseEventData>): boolean {
const { tooltipInfo: info } = mouseEventData;
if (isNil(info)) {
return false;
}

return isActiveTypeVisible(this.activeType, (params.model as ISeries)?.tooltipHelper?.spec);
}

clearCache() {
this._cacheViewSpec = undefined;
this._cacheActualTooltip = undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import type { TooltipActiveType } from '../../../typings';
import type { TooltipHandlerParams } from '../interface';
import type { DimensionTooltipInfo, MouseEventData } from './interface';
import { BaseTooltipProcessor } from './base';
import { isNil } from '@visactor/vutils';
import type { ISeries } from '../../../series/interface';
import { isValid } from '@visactor/vutils';
import type { ICartesianSeries } from '../../../series/interface';
import { getCartesianDimensionInfo } from '../../../event/events/dimension/util/cartesian';
import { getPolarDimensionInfo } from '../../../event/events/dimension/util/polar';
import type { IDimensionData, IDimensionInfo } from '../../../event/events/dimension/interface';
import { isDiscrete } from '@visactor/vscale';

export class DimensionTooltipProcessor extends BaseTooltipProcessor {
activeType: TooltipActiveType = 'dimension';
Expand All @@ -20,19 +24,72 @@ export class DimensionTooltipProcessor extends BaseTooltipProcessor {
return this._showTooltipByHandler(info, newParams);
}

/** 判断是否应该触发 tooltip */
shouldHandleTooltip(params: BaseEventParams, mouseEventData: Partial<MouseEventData>): boolean {
const { tooltipInfo: info } = mouseEventData;
if (isNil(info)) {
return false;
}
protected _getDimensionInfo(params: BaseEventParams): IDimensionInfo[] {
let targetDimensionInfo: IDimensionInfo[] | undefined;
// 处理dimension info
const chart = this.component.getChart();

// compute layer offset
const layer = chart.getCompiler().getStage().getLayer(undefined);
const point = { x: params.event.viewX, y: params.event.viewY };
layer.globalTransMatrix.transformPoint({ x: params.event.viewX, y: params.event.viewY }, point);

targetDimensionInfo = [
...(getCartesianDimensionInfo(chart, point, true) ?? []),
...(getPolarDimensionInfo(chart, point) ?? [])
];
if (targetDimensionInfo.length === 0) {
targetDimensionInfo = undefined;
} else if (targetDimensionInfo.length > 1) {
// 只保留一个轴的dimension info
const dimensionAxisInfo = targetDimensionInfo.filter(info => {
const axis = info.axis;
if (axis.getSpec().hasDimensionTooltip) {
return true;
}

const helper = (params.model as ISeries)?.tooltipHelper;
const activeType = helper?.activeType ?? this.component.getSpec().activeType;
if (!activeType.includes('dimension')) {
return false;
// 优先显示离散轴 tooltip
if (!isDiscrete(axis.getScale().type)) {
return false;
}
// 下面的逻辑用来判断当前的离散轴是不是维度轴
let firstSeries: ICartesianSeries | undefined;
for (const region of axis?.getRegions() ?? []) {
for (const series of region.getSeries()) {
if (series.coordinate === 'cartesian') {
firstSeries = series as ICartesianSeries;
break;
}
}
if (isValid(firstSeries)) {
break;
}
}
if (isValid(firstSeries) && firstSeries.getDimensionField()[0] === firstSeries.fieldY[0]) {
// 维度轴为Y轴时,选择只显示Y轴tooltip
return axis.getOrient() === 'left' || axis.getOrient() === 'right';
}
// 维度轴为X轴时,选择只显示X轴tooltip
return axis.getOrient() === 'bottom' || axis.getOrient() === 'top';
});
targetDimensionInfo = dimensionAxisInfo.length ? dimensionAxisInfo : targetDimensionInfo.slice(0, 1);

// datum 去重,保证每个系列的每个数据项只对应于一行 tooltip 内容项
if (targetDimensionInfo.length > 1) {
const dimensionDataKeySet = new Set<string>();
targetDimensionInfo.forEach(info => {
info.data = info.data.filter(({ key }: IDimensionData) => {
if (dimensionDataKeySet.has(key)) {
return false;
}
dimensionDataKeySet.add(key);
return true;
});
});
}
}
return true;

return targetDimensionInfo;
}

/** 获取触发 tooltip 需要的信息 */
Expand Down
26 changes: 5 additions & 21 deletions packages/vchart/src/component/tooltip/processor/group-tooltip.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,29 @@
import type { BaseEventParams } from '../../../event/interface';
import type { Datum, IGroupTooltipPattern, TooltipActiveType } from '../../../typings';
import type { ITooltipSpec, TooltipHandlerParams } from '../interface';
import type { DimensionTooltipInfo, GroupTooltipInfo, MouseEventData } from './interface';
import type { GroupTooltipInfo, MouseEventData } from './interface';
import { BaseTooltipProcessor } from './base';
import { array, isNil } from '@visactor/vutils';
import { array } from '@visactor/vutils';
import type { ISeries } from '../../../series/interface';

export class GroupTooltipProcessor extends BaseTooltipProcessor {
activeType: TooltipActiveType = 'group';

/** 触发对应类型的 tooltip */
showTooltip(info: GroupTooltipInfo, params: BaseEventParams, changePositionOnly: boolean) {
const { datum, series, dimensionInfo } = info;
const { datum, series } = info;
const tooltipData = [{ datum: array(datum), series }];
const newParams: TooltipHandlerParams = {
...(params as any),
groupDatum: this._getGroupDatum(params),
dimensionInfo: this._preprocessDimensionInfo(dimensionInfo),
changePositionOnly,
tooltip: this.component
};
return this._showTooltipByHandler(tooltipData, newParams);
}

/** 判断是否应该触发 tooltip */
shouldHandleTooltip(params: BaseEventParams, mouseEventData: Partial<MouseEventData>): boolean {
const { tooltipInfo: info } = mouseEventData;
if (isNil(info)) {
return false;
}

const helper = (params.model as ISeries)?.tooltipHelper;
if (!helper?.activeType.includes('group')) {
return false;
}
return true;
}

/** 获取触发 tooltip 需要的信息 */
getMouseEventData(params: BaseEventParams, dimensionInfo?: DimensionTooltipInfo): MouseEventData {
getMouseEventData(params: BaseEventParams): MouseEventData {
let info: GroupTooltipInfo | undefined;
let ignore: boolean | undefined;

Expand All @@ -56,8 +41,7 @@ export class GroupTooltipProcessor extends BaseTooltipProcessor {
info = {
mark: params.mark,
datum: params.datum,
series,
dimensionInfo
series
};
}
} else if (ignoreTriggers?.has(params.model) || ignoreTriggers?.has(params.mark)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export type MarkTooltipInfo<T = Datum> = {
datum: T;
mark: IMark;
series: ISeries;
dimensionInfo: DimensionTooltipInfo;
};

export type GroupTooltipInfo = MarkTooltipInfo<Datum | Datum[]>;
Expand Down
25 changes: 4 additions & 21 deletions packages/vchart/src/component/tooltip/processor/mark-tooltip.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,27 @@
import type { BaseEventParams } from '../../../event/interface';
import type { TooltipActiveType } from '../../../typings';
import type { TooltipHandlerParams } from '../interface';
import type { DimensionTooltipInfo, MarkTooltipInfo, MouseEventData } from './interface';
import type { MarkTooltipInfo, MouseEventData } from './interface';
import { BaseTooltipProcessor } from './base';
import { isNil } from '@visactor/vutils';
import type { ISeries } from '../../../series/interface';

export class MarkTooltipProcessor extends BaseTooltipProcessor {
activeType: TooltipActiveType = 'mark';

/** 触发对应类型的 tooltip */
showTooltip(info: MarkTooltipInfo, params: BaseEventParams, changePositionOnly: boolean) {
const { datum, series, dimensionInfo } = info;
const { datum, series } = info;
const tooltipData = [{ datum: [datum], series }];
const newParams: TooltipHandlerParams = {
...(params as any),
dimensionInfo: this._preprocessDimensionInfo(dimensionInfo),
xile611 marked this conversation as resolved.
Show resolved Hide resolved
changePositionOnly,
tooltip: this.component
};
return this._showTooltipByHandler(tooltipData, newParams);
}

/** 判断是否应该触发 tooltip */
shouldHandleTooltip(params: BaseEventParams, mouseEventData: Partial<MouseEventData>): boolean {
const { tooltipInfo: info } = mouseEventData;
if (isNil(info)) {
return false;
}

const helper = (params.model as ISeries)?.tooltipHelper;
if (!helper?.activeType.includes('mark')) {
return false;
}
return true;
}

/** 获取触发 tooltip 需要的信息 */
getMouseEventData(params: BaseEventParams, dimensionInfo?: DimensionTooltipInfo): MouseEventData {
getMouseEventData(params: BaseEventParams): MouseEventData {
let info: MarkTooltipInfo | undefined;
let ignore: boolean | undefined;

Expand All @@ -51,8 +35,7 @@ export class MarkTooltipProcessor extends BaseTooltipProcessor {
info = {
mark: params.mark,
datum: params.datum,
series,
dimensionInfo
series
};
} else if (ignoreTriggers?.has(params.model) || ignoreTriggers?.has(params.mark)) {
ignore = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { IChartSpecInfo } from '../../chart/interface';
import { domDocument, isMiniAppLikeMode, isString, isTrueBrowser, isValid } from '../../util';
import { BaseComponentSpecTransformer } from '../base';
import { TOOLTIP_EL_CLASS_NAME } from './constant';
import { getTooltipActualActiveType } from './utils';
import { getTooltipActualActiveType } from './utils/common';
import { mergeSpec } from '@visactor/vutils-extension';

export class TooltipSpecTransformer extends BaseComponentSpecTransformer<any> {
Expand Down
Loading
Loading