Skip to content

Commit

Permalink
Merge pull request #3138 from VisActor/refactor/tooltip-pattern-parser
Browse files Browse the repository at this point in the history
Refactor/tooltip pattern parser
  • Loading branch information
xile611 authored Sep 3, 2024
2 parents 16aa476 + e2a0971 commit 64c7210
Show file tree
Hide file tree
Showing 23 changed files with 476 additions and 569 deletions.
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),
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

0 comments on commit 64c7210

Please sign in to comment.