未验证 提交 88d2ad7a 编写于 作者: S sushuang 提交者: GitHub

Merge pull request #14473 from apache/fix/component-tooltip

feature: [tooltip] correct component tooltip cascade and support manually trigger component tooltip.
...@@ -50,14 +50,6 @@ type AxisEventData = { ...@@ -50,14 +50,6 @@ type AxisEventData = {
[key in AxisIndexKey]?: number [key in AxisIndexKey]?: number
}; };
type LabelFormatterParams = {
componentType: string
name: string
$vars: ['name']
} & {
[key in AxisIndexKey]?: number
};
type AxisLabelText = graphic.Text & { type AxisLabelText = graphic.Text & {
__fullText: string __fullText: string
__truncatedText: string __truncatedText: string
...@@ -422,16 +414,6 @@ const builders: Record<'axisLine' | 'axisTickLabel' | 'axisName', AxisElementsBu ...@@ -422,16 +414,6 @@ const builders: Record<'axisLine' | 'axisTickLabel' | 'axisName', AxisElementsBu
opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth
); );
const tooltipOpt = axisModel.get('tooltip', true);
const mainType = axisModel.mainType;
const formatterParams: LabelFormatterParams = {
componentType: mainType,
name: name,
$vars: ['name']
};
formatterParams[mainType + 'Index' as AxisIndexKey] = axisModel.componentIndex;
const textEl = new graphic.Text({ const textEl = new graphic.Text({
x: pos[0], x: pos[0],
y: pos[1], y: pos[1],
...@@ -452,15 +434,13 @@ const builders: Record<'axisLine' | 'axisTickLabel' | 'axisName', AxisElementsBu ...@@ -452,15 +434,13 @@ const builders: Record<'axisLine' | 'axisTickLabel' | 'axisName', AxisElementsBu
}), }),
z2: 1 z2: 1
}) as AxisLabelText; }) as AxisLabelText;
textEl.tooltip = (tooltipOpt && tooltipOpt.show)
? extend({ graphic.setTooltipConfig({
content: name, el: textEl,
formatter() { componentModel: axisModel,
return name; itemName: name
}, });
formatterParams: formatterParams
}, tooltipOpt)
: null;
textEl.__fullText = name; textEl.__fullText = name;
// Id for animation // Id for animation
textEl.anid = 'name'; textEl.anid = 'name';
......
...@@ -63,6 +63,11 @@ export interface BrushAreaParam extends ModelFinderObject { ...@@ -63,6 +63,11 @@ export interface BrushAreaParam extends ModelFinderObject {
// coord ranges, used in multiple cartesian in one grid. // coord ranges, used in multiple cartesian in one grid.
// Only for output to users. // Only for output to users.
coordRanges?: BrushAreaRange[]; coordRanges?: BrushAreaRange[];
__rangeOffset?: {
offset: BrushDimensionMinMax[] | BrushDimensionMinMax,
xyMinMax: BrushDimensionMinMax[]
}
} }
/** /**
......
...@@ -30,7 +30,8 @@ import { ...@@ -30,7 +30,8 @@ import {
Dictionary, Dictionary,
ZRStyleProps, ZRStyleProps,
OptionId, OptionId,
OptionPreprocessor OptionPreprocessor,
CommonTooltipOption
} from '../../util/types'; } from '../../util/types';
import ComponentModel from '../../model/Component'; import ComponentModel from '../../model/Component';
import Element, { ElementTextConfig } from 'zrender/src/Element'; import Element, { ElementTextConfig } from 'zrender/src/Element';
...@@ -44,6 +45,7 @@ import { getECData } from '../../util/innerStore'; ...@@ -44,6 +45,7 @@ import { getECData } from '../../util/innerStore';
import { TextStyleProps } from 'zrender/src/graphic/Text'; import { TextStyleProps } from 'zrender/src/graphic/Text';
import { isEC4CompatibleStyle, convertFromEC4CompatibleStyle } from '../../util/styleCompat'; import { isEC4CompatibleStyle, convertFromEC4CompatibleStyle } from '../../util/styleCompat';
import { EChartsExtensionInstallRegisters } from '../../extension'; import { EChartsExtensionInstallRegisters } from '../../extension';
import { graphic } from '../../export/api';
const TRANSFORM_PROPS = { const TRANSFORM_PROPS = {
x: 1, x: 1,
...@@ -129,6 +131,8 @@ interface GraphicComponentBaseElementOption extends ...@@ -129,6 +131,8 @@ interface GraphicComponentBaseElementOption extends
textConfig?: ElementTextConfig; textConfig?: ElementTextConfig;
$action?: 'merge' | 'replace' | 'remove'; $action?: 'merge' | 'replace' | 'remove';
tooltip?: CommonTooltipOption<unknown>;
}; };
interface GraphicComponentDisplayableOption extends interface GraphicComponentDisplayableOption extends
GraphicComponentBaseElementOption, GraphicComponentBaseElementOption,
...@@ -461,7 +465,8 @@ class GraphicComponentView extends ComponentView { ...@@ -461,7 +465,8 @@ class GraphicComponentView extends ComponentView {
if (elOptionStyle if (elOptionStyle
&& isEC4CompatibleStyle(elOptionStyle, elType, !!textConfig, !!textContentOption) && isEC4CompatibleStyle(elOptionStyle, elType, !!textConfig, !!textContentOption)
) { ) {
const convertResult = convertFromEC4CompatibleStyle(elOptionStyle, elType, true) as GraphicComponentZRPathOption; const convertResult =
convertFromEC4CompatibleStyle(elOptionStyle, elType, true) as GraphicComponentZRPathOption;
if (!textConfig && convertResult.textConfig) { if (!textConfig && convertResult.textConfig) {
textConfig = (elOption as GraphicComponentZRPathOption).textConfig = convertResult.textConfig; textConfig = (elOption as GraphicComponentZRPathOption).textConfig = convertResult.textConfig;
} }
...@@ -514,6 +519,13 @@ class GraphicComponentView extends ComponentView { ...@@ -514,6 +519,13 @@ class GraphicComponentView extends ComponentView {
elInner.__ecGraphicWidthOption = (elOption as GraphicComponentGroupOption).width; elInner.__ecGraphicWidthOption = (elOption as GraphicComponentGroupOption).width;
elInner.__ecGraphicHeightOption = (elOption as GraphicComponentGroupOption).height; elInner.__ecGraphicHeightOption = (elOption as GraphicComponentGroupOption).height;
setEventData(el, graphicModel, elOption); setEventData(el, graphicModel, elOption);
graphicUtil.setTooltipConfig({
el: el,
componentModel: graphicModel,
itemName: el.name,
itemTooltipOption: elOption.tooltip
});
} }
}); });
} }
......
...@@ -33,7 +33,6 @@ import { ...@@ -33,7 +33,6 @@ import {
ZRColor, ZRColor,
ItemStyleOption, ItemStyleOption,
ZRRectLike, ZRRectLike,
ECElement,
CommonTooltipOption, CommonTooltipOption,
ColorString ColorString
} from '../../util/types'; } from '../../util/types';
...@@ -353,8 +352,6 @@ class LegendView extends ComponentView { ...@@ -353,8 +352,6 @@ class LegendView extends ComponentView {
const itemIcon = itemModel.get('icon'); const itemIcon = itemModel.get('icon');
const tooltipModel = itemModel.getModel('tooltip') as Model<CommonTooltipOption<LegendTooltipFormatterParams>>;
const legendGlobalTooltipModel = tooltipModel.parentModel;
// Use user given icon first // Use user given icon first
legendSymbolType = itemIcon || legendSymbolType; legendSymbolType = itemIcon || legendSymbolType;
...@@ -432,22 +429,15 @@ class LegendView extends ComponentView { ...@@ -432,22 +429,15 @@ class LegendView extends ComponentView {
shape: itemGroup.getBoundingRect(), shape: itemGroup.getBoundingRect(),
invisible: true invisible: true
}); });
const tooltipModel = itemModel.getModel('tooltip') as Model<CommonTooltipOption<LegendTooltipFormatterParams>>;
if (tooltipModel.get('show')) { if (tooltipModel.get('show')) {
const formatterParams: LegendTooltipFormatterParams = { graphic.setTooltipConfig({
componentType: 'legend', el: hitRect,
legendIndex: legendModel.componentIndex, componentModel: legendModel,
name: name, itemName: name,
$vars: ['name'] itemTooltipOption: tooltipModel.option
}; });
(hitRect as ECElement).tooltip = zrUtil.extend({
content: name,
// Defaul formatter
formatter: legendGlobalTooltipModel.get('formatter', true)
|| function (params: LegendTooltipFormatterParams) {
return params.name;
},
formatterParams: formatterParams
}, tooltipModel.option);
} }
itemGroup.add(hitRect); itemGroup.add(hitRect);
......
...@@ -148,7 +148,8 @@ class ToolboxModel extends ComponentModel<ToolboxOption> { ...@@ -148,7 +148,8 @@ class ToolboxModel extends ComponentModel<ToolboxOption> {
// feature // feature
tooltip: { tooltip: {
show: false show: false,
position: 'bottom'
} }
}; };
} }
......
...@@ -28,7 +28,7 @@ import ComponentView from '../../view/Component'; ...@@ -28,7 +28,7 @@ import ComponentView from '../../view/Component';
import ToolboxModel from './ToolboxModel'; import ToolboxModel from './ToolboxModel';
import GlobalModel from '../../model/Global'; import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../core/ExtensionAPI'; import ExtensionAPI from '../../core/ExtensionAPI';
import { DisplayState, Dictionary, ECElement, Payload } from '../../util/types'; import { DisplayState, Dictionary, Payload } from '../../util/types';
import { import {
ToolboxFeature, ToolboxFeature,
getFeature, getFeature,
...@@ -39,6 +39,7 @@ import { ...@@ -39,6 +39,7 @@ import {
import { getUID } from '../../util/component'; import { getUID } from '../../util/component';
import Displayable from 'zrender/src/graphic/Displayable'; import Displayable from 'zrender/src/graphic/Displayable';
import ZRText from 'zrender/src/graphic/Text'; import ZRText from 'zrender/src/graphic/Text';
import { getECData } from '../../util/innerStore';
type IconPath = ToolboxFeatureModel['iconPaths'][string]; type IconPath = ToolboxFeatureModel['iconPaths'][string];
...@@ -224,23 +225,14 @@ class ToolboxView extends ComponentView { ...@@ -224,23 +225,14 @@ class ToolboxView extends ComponentView {
}); });
path.setTextContent(textContent); path.setTextContent(textContent);
const tooltipModel = toolboxModel.getModel('tooltip'); graphic.setTooltipConfig({
if (tooltipModel && tooltipModel.get('show')) { el: path,
(path as ECElement).tooltip = zrUtil.extend({ componentModel: toolboxModel,
content: titlesMap[iconName], itemName: iconName,
formatter: tooltipModel.get('formatter', true) formatterParamsExtra: {
|| function () { title: titlesMap[iconName]
return titlesMap[iconName]; }
}, });
formatterParams: {
componentType: 'toolbox',
name: iconName,
title: titlesMap[iconName],
$vars: ['name', 'title']
},
position: tooltipModel.get('position', true) || 'bottom'
}, tooltipModel.option);
}
// graphic.enableHoverEmphasis(path); // graphic.enableHoverEmphasis(path);
......
...@@ -29,7 +29,7 @@ import Model from '../../model/Model'; ...@@ -29,7 +29,7 @@ import Model from '../../model/Model';
import * as globalListener from '../axisPointer/globalListener'; import * as globalListener from '../axisPointer/globalListener';
import * as axisHelper from '../../coord/axisHelper'; import * as axisHelper from '../../coord/axisHelper';
import * as axisPointerViewHelper from '../axisPointer/viewHelper'; import * as axisPointerViewHelper from '../axisPointer/viewHelper';
import { getTooltipRenderMode } from '../../util/model'; import { getTooltipRenderMode, preParseFinder, queryReferringComponents } from '../../util/model';
import ComponentView from '../../view/Component'; import ComponentView from '../../view/Component';
import { format as timeFormat } from '../../util/time'; import { format as timeFormat } from '../../util/time';
import { import {
...@@ -41,7 +41,9 @@ import { ...@@ -41,7 +41,9 @@ import {
TooltipRenderMode, TooltipRenderMode,
ECElement, ECElement,
CommonTooltipOption, CommonTooltipOption,
ZRColor ZRColor,
ComponentMainType,
ComponentItemTooltipOption
} from '../../util/types'; } from '../../util/types';
import GlobalModel from '../../model/Global'; import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../core/ExtensionAPI'; import ExtensionAPI from '../../core/ExtensionAPI';
...@@ -49,12 +51,13 @@ import TooltipModel, {TooltipOption} from './TooltipModel'; ...@@ -49,12 +51,13 @@ import TooltipModel, {TooltipOption} from './TooltipModel';
import Element from 'zrender/src/Element'; import Element from 'zrender/src/Element';
import { AxisBaseModel } from '../../coord/AxisBaseModel'; import { AxisBaseModel } from '../../coord/AxisBaseModel';
// import { isDimensionStacked } from '../../data/helper/dataStackHelper'; // import { isDimensionStacked } from '../../data/helper/dataStackHelper';
import { getECData } from '../../util/innerStore'; import { ECData, getECData } from '../../util/innerStore';
import { shouldTooltipConfine } from './helper'; import { shouldTooltipConfine } from './helper';
import { DataByCoordSys, DataByAxis } from '../axisPointer/axisTrigger'; import { DataByCoordSys, DataByAxis } from '../axisPointer/axisTrigger';
import { normalizeTooltipFormatResult } from '../../model/mixin/dataFormat'; import { normalizeTooltipFormatResult } from '../../model/mixin/dataFormat';
import { createTooltipMarkup, buildTooltipMarkup, TooltipMarkupStyleCreator } from './tooltipMarkup'; import { createTooltipMarkup, buildTooltipMarkup, TooltipMarkupStyleCreator } from './tooltipMarkup';
import { findEventDispatcher } from '../../util/event'; import { findEventDispatcher } from '../../util/event';
import ComponentModel from '../../model/Component';
const bind = zrUtil.bind; const bind = zrUtil.bind;
const each = zrUtil.each; const each = zrUtil.each;
...@@ -76,7 +79,7 @@ interface ShowTipPayload { ...@@ -76,7 +79,7 @@ interface ShowTipPayload {
from?: string from?: string
// Type 1 // Type 1
tooltip?: ECElement['tooltip'] tooltip?: ECData['tooltipConfig']['option']
// Type 2 // Type 2
dataByCoordSys?: DataByCoordSys[] dataByCoordSys?: DataByCoordSys[]
...@@ -86,6 +89,11 @@ interface ShowTipPayload { ...@@ -86,6 +89,11 @@ interface ShowTipPayload {
seriesIndex?: number seriesIndex?: number
dataIndex?: number dataIndex?: number
// Type 4
name?: string // target item name that enable tooltip.
// legendIndex: 0,
// toolboxId: 'some_id',
// geoName: 'some_name',
x?: number x?: number
y?: number y?: number
...@@ -112,7 +120,7 @@ interface TryShowParams { ...@@ -112,7 +120,7 @@ interface TryShowParams {
*/ */
dataByCoordSys?: DataByCoordSys[] dataByCoordSys?: DataByCoordSys[]
tooltipOption?: CommonTooltipOption<TooltipCallbackDataParams | TooltipCallbackDataParams[]> tooltipOption?: ComponentItemTooltipOption<TooltipCallbackDataParams | TooltipCallbackDataParams[]>
position?: TooltipOption['position'] position?: TooltipOption['position']
} }
...@@ -287,12 +295,29 @@ class TooltipView extends ComponentView { ...@@ -287,12 +295,29 @@ class TooltipView extends ComponentView {
// When triggered from axisPointer. // When triggered from axisPointer.
const dataByCoordSys = payload.dataByCoordSys; const dataByCoordSys = payload.dataByCoordSys;
if (payload.tooltip && payload.x != null && payload.y != null) { const cmptRef = findComponentReference(payload, ecModel, api);
if (cmptRef) {
const rect = cmptRef.el.getBoundingRect().clone();
rect.applyTransform(cmptRef.el.transform);
this._tryShow({
offsetX: rect.x + rect.width / 2,
offsetY: rect.y + rect.height / 2,
target: cmptRef.el,
position: payload.position || 'bottom'
}, dispatchAction);
}
else if (payload.tooltip && payload.x != null && payload.y != null) {
const el = proxyRect as unknown as ECElement; const el = proxyRect as unknown as ECElement;
el.x = payload.x; el.x = payload.x;
el.y = payload.y; el.y = payload.y;
el.update(); el.update();
el.tooltip = payload.tooltip; getECData(el).tooltipConfig = {
componentMainType: null,
componentIndex: null,
name: null,
option: payload.tooltip
};
// Manually show tooltip while view is not using zrender elements. // Manually show tooltip while view is not using zrender elements.
this._tryShow({ this._tryShow({
offsetX: payload.x, offsetX: payload.x,
...@@ -428,15 +453,33 @@ class TooltipView extends ComponentView { ...@@ -428,15 +453,33 @@ class TooltipView extends ComponentView {
if (dataByCoordSys && dataByCoordSys.length) { if (dataByCoordSys && dataByCoordSys.length) {
this._showAxisTooltip(dataByCoordSys, e); this._showAxisTooltip(dataByCoordSys, e);
} }
// Always show item tooltip if mouse is on the element with dataIndex else if (el) {
else if (el && findEventDispatcher(el, (target) => getECData(target).dataIndex != null, true)) {
this._lastDataByCoordSys = null;
this._showSeriesItemTooltip(e, el, dispatchAction);
}
// Tooltip provided directly. Like legend.
else if (el && el.tooltip) {
this._lastDataByCoordSys = null; this._lastDataByCoordSys = null;
this._showComponentItemTooltip(e, el, dispatchAction);
let seriesDispatcher: Element;
let cmptDispatcher: Element;
findEventDispatcher(el, (target) => {
// Always show item tooltip if mouse is on the element with dataIndex
if (getECData(target).dataIndex != null) {
seriesDispatcher = target;
return true;
}
// Tooltip provided directly. Like legend.
if (getECData(target).tooltipConfig != null) {
cmptDispatcher = target;
return true;
}
}, true);
if (seriesDispatcher) {
this._showSeriesItemTooltip(e, seriesDispatcher, dispatchAction);
}
else if (cmptDispatcher) {
this._showComponentItemTooltip(e, cmptDispatcher, dispatchAction);
}
else {
this._hide(dispatchAction);
}
} }
else { else {
this._lastDataByCoordSys = null; this._lastDataByCoordSys = null;
...@@ -573,10 +616,9 @@ class TooltipView extends ComponentView { ...@@ -573,10 +616,9 @@ class TooltipView extends ComponentView {
private _showSeriesItemTooltip( private _showSeriesItemTooltip(
e: TryShowParams, e: TryShowParams,
el: ECElement, dispatcher: ECElement,
dispatchAction: ExtensionAPI['dispatchAction'] dispatchAction: ExtensionAPI['dispatchAction']
) { ) {
const dispatcher = findEventDispatcher(el, (target) => getECData(target).dataIndex != null, true);
const ecModel = this._ecModel; const ecModel = this._ecModel;
const ecData = getECData(dispatcher); const ecData = getECData(dispatcher);
// Use dataModel in element if possible // Use dataModel in element if possible
...@@ -653,7 +695,8 @@ class TooltipView extends ComponentView { ...@@ -653,7 +695,8 @@ class TooltipView extends ComponentView {
el: ECElement, el: ECElement,
dispatchAction: ExtensionAPI['dispatchAction'] dispatchAction: ExtensionAPI['dispatchAction']
) { ) {
let tooltipOpt = el.tooltip; const tooltipConfig = getECData(el).tooltipConfig;
let tooltipOpt = tooltipConfig.option;
if (zrUtil.isString(tooltipOpt)) { if (zrUtil.isString(tooltipOpt)) {
const content = tooltipOpt; const content = tooltipOpt;
tooltipOpt = { tooltipOpt = {
...@@ -662,7 +705,14 @@ class TooltipView extends ComponentView { ...@@ -662,7 +705,14 @@ class TooltipView extends ComponentView {
formatter: content formatter: content
}; };
} }
const subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel);
const tooltipModelCascade = [tooltipOpt] as Parameters<typeof buildTooltipModel>[0];
const cmpt = this._ecModel.getComponent(tooltipConfig.componentMainType, tooltipConfig.componentIndex);
cmpt && tooltipModelCascade.push(cmpt);
tooltipModelCascade.push(this._tooltipModel);
const subTooltipModel = buildTooltipModel(tooltipModelCascade) as Model<ComponentItemTooltipOption<unknown>>;
const defaultHtml = subTooltipModel.get('content'); const defaultHtml = subTooltipModel.get('content');
const asyncTicket = Math.random() + ''; const asyncTicket = Math.random() + '';
// PENDING: this case do not support richText style yet. // PENDING: this case do not support richText style yet.
...@@ -673,9 +723,11 @@ class TooltipView extends ComponentView { ...@@ -673,9 +723,11 @@ class TooltipView extends ComponentView {
// that requires setting `trigger` nothing on component yet. // that requires setting `trigger` nothing on component yet.
this._showOrMove(subTooltipModel, function (this: TooltipView) { this._showOrMove(subTooltipModel, function (this: TooltipView) {
// Use formatterParams from element defined in component
// Avoid users modify it.
const formatterParams = zrUtil.clone(subTooltipModel.get('formatterParams') as any || {});
this._showTooltipContent( this._showTooltipContent(
// Use formatterParams from element defined in component subTooltipModel, defaultHtml, formatterParams,
subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') as any || {},
asyncTicket, e.offsetX, e.offsetY, e.position, el, markupStyleCreator asyncTicket, e.offsetX, e.offsetY, e.position, el, markupStyleCreator
); );
}); });
...@@ -908,13 +960,13 @@ class TooltipView extends ComponentView { ...@@ -908,13 +960,13 @@ class TooltipView extends ComponentView {
} }
type TooltipableOption = { type TooltipableOption = {
tooltip?: Omit<TooltipOption, 'mainType'> | string tooltip?: CommonTooltipOption<unknown>;
}; };
/** /**
* From top to bottom. (the last one should be globalTooltipModel); * From top to bottom. (the last one should be globalTooltipModel);
*/ */
function buildTooltipModel(modelCascade: ( function buildTooltipModel(modelCascade: (
TooltipModel | Model<TooltipableOption> | Omit<TooltipOption, 'mainType'> | string TooltipModel | Model<TooltipableOption> | CommonTooltipOption<unknown> | ComponentModel | string
)[]) { )[]) {
// Last is always tooltip model. // Last is always tooltip model.
let resultModel = modelCascade.pop() as Model<TooltipOption>; let resultModel = modelCascade.pop() as Model<TooltipOption>;
...@@ -1036,4 +1088,60 @@ function isCenterAlign(align: HorizontalAlign | VerticalAlign) { ...@@ -1036,4 +1088,60 @@ function isCenterAlign(align: HorizontalAlign | VerticalAlign) {
return align === 'center' || align === 'middle'; return align === 'center' || align === 'middle';
} }
/**
* Find target component by payload like:
* ```js
* { legendId: 'some_id', name: 'xxx' }
* { toolboxIndex: 1, name: 'xxx' }
* { geoName: 'some_name', name: 'xxx' }
* ```
* PENDING: at present only
*
* If not found, return null/undefined.
*/
function findComponentReference(
payload: ShowTipPayload,
ecModel: GlobalModel,
api: ExtensionAPI
): {
componentMainType: ComponentMainType;
componentIndex: number;
el: ECElement;
} {
const { queryOptionMap } = preParseFinder(payload);
const componentMainType = queryOptionMap.keys()[0];
if (!componentMainType || componentMainType === 'series') {
return;
}
const queryResult = queryReferringComponents(
ecModel,
componentMainType,
queryOptionMap.get(componentMainType),
{ useDefault: false, enableAll: false, enableNone: false }
);
const model = queryResult.models[0];
if (!model) {
return;
}
const view = api.getViewOfComponentModel(model);
let el: ECElement;
view.group.traverse((subEl: ECElement) => {
const tooltipConfig = getECData(subEl).tooltipConfig;
if (tooltipConfig && tooltipConfig.name === payload.name) {
el = subEl;
return true; // stop
}
});
if (el) {
return {
componentMainType,
componentIndex: model.componentIndex,
el
};
}
}
export default TooltipView; export default TooltipView;
...@@ -53,7 +53,9 @@ import { ...@@ -53,7 +53,9 @@ import {
ZRRectLike, ZRRectLike,
ZRStyleProps, ZRStyleProps,
PayloadAnimationPart, PayloadAnimationPart,
AnimationOption AnimationOption,
CommonTooltipOption,
ComponentItemTooltipLabelFormatterParams
} from './types'; } from './types';
import { import {
extend, extend,
...@@ -61,10 +63,15 @@ import { ...@@ -61,10 +63,15 @@ import {
map, map,
defaults, defaults,
isObject, isObject,
retrieve2 retrieve2,
isString,
keys,
each,
hasOwn
} from 'zrender/src/core/util'; } from 'zrender/src/core/util';
import { AnimationEasing } from 'zrender/src/animation/easing'; import { AnimationEasing } from 'zrender/src/animation/easing';
import { getECData } from './innerStore'; import { getECData } from './innerStore';
import ComponentModel from '../model/Component';
const mathMax = Math.max; const mathMax = Math.max;
...@@ -805,6 +812,51 @@ function nearZero(val: number) { ...@@ -805,6 +812,51 @@ function nearZero(val: number) {
} }
export function setTooltipConfig(opt: {
el: Element,
componentModel: ComponentModel,
itemName: string,
itemTooltipOption?: string | CommonTooltipOption<unknown>
formatterParamsExtra?: Dictionary<unknown>
}): void {
const itemTooltipOption = opt.itemTooltipOption;
const componentModel = opt.componentModel;
const itemName = opt.itemName;
const itemTooltipOptionObj = isString(itemTooltipOption)
? { formatter: itemTooltipOption }
: itemTooltipOption;
const mainType = componentModel.mainType;
const componentIndex = componentModel.componentIndex;
const formatterParams = {
componentType: mainType,
name: itemName,
$vars: ['name']
} as ComponentItemTooltipLabelFormatterParams;
(formatterParams as any)[mainType + 'Index'] = componentIndex;
const formatterParamsExtra = opt.formatterParamsExtra;
if (formatterParamsExtra) {
each(keys(formatterParamsExtra), key => {
if (!hasOwn(formatterParams, key)) {
formatterParams[key] = formatterParamsExtra[key];
formatterParams.$vars.push(key);
}
});
}
getECData(opt.el).tooltipConfig = {
componentMainType: mainType,
componentIndex: componentIndex,
name: itemName,
option: defaults({
content: itemName,
formatterParams: formatterParams
}, itemTooltipOptionObj)
};
}
// Register built-in shapes. These shapes might be overwirtten // Register built-in shapes. These shapes might be overwirtten
// by users, although we do not recommend that. // by users, although we do not recommend that.
registerShape('circle', Circle); registerShape('circle', Circle);
......
...@@ -18,7 +18,10 @@ ...@@ -18,7 +18,10 @@
*/ */
import Element from 'zrender/src/Element'; import Element from 'zrender/src/Element';
import { DataModel, ECEventData, BlurScope, InnerFocus, SeriesDataType } from './types'; import {
DataModel, ECEventData, BlurScope, InnerFocus, SeriesDataType,
ComponentMainType, ComponentItemTooltipOption
} from './types';
import { makeInner } from './model'; import { makeInner } from './model';
/** /**
* ECData stored on graphic element * ECData stored on graphic element
...@@ -31,5 +34,17 @@ export interface ECData { ...@@ -31,5 +34,17 @@ export interface ECData {
dataType?: SeriesDataType; dataType?: SeriesDataType;
focus?: InnerFocus; focus?: InnerFocus;
blurScope?: BlurScope; blurScope?: BlurScope;
tooltipConfig?: {
// To make a tooltipConfig, seach `setTooltipConfig`.
// Used to find component tooltip option, which is used as
// the parent of tooltipConfig.option for cascading.
// If not provided, do not use component as its parent.
// (Set manatary to make developers not to forget them).
componentMainType: ComponentMainType;
componentIndex: number;
// Target item name to locate tooltip.
name: string;
option: ComponentItemTooltipOption<unknown>;
};
} }
export const getECData = makeInner<ECData, Element>(); export const getECData = makeInner<ECData, Element>();
...@@ -774,8 +774,8 @@ export type ModelFinderObject = { ...@@ -774,8 +774,8 @@ export type ModelFinderObject = {
xAxisIndex?: ModelFinderIndexQuery, xAxisId?: ModelFinderIdQuery, xAxisName?: ModelFinderNameQuery xAxisIndex?: ModelFinderIndexQuery, xAxisId?: ModelFinderIdQuery, xAxisName?: ModelFinderNameQuery
yAxisIndex?: ModelFinderIndexQuery, yAxisId?: ModelFinderIdQuery, yAxisName?: ModelFinderNameQuery yAxisIndex?: ModelFinderIndexQuery, yAxisId?: ModelFinderIdQuery, yAxisName?: ModelFinderNameQuery
gridIndex?: ModelFinderIndexQuery, gridId?: ModelFinderIdQuery, gridName?: ModelFinderNameQuery gridIndex?: ModelFinderIndexQuery, gridId?: ModelFinderIdQuery, gridName?: ModelFinderNameQuery
// ... (can be extended) dataIndex?: number, dataIndexInside?: number
[key: string]: unknown // ... (can be extended)
}; };
/** /**
* { * {
...@@ -819,6 +819,43 @@ export function parseFinder( ...@@ -819,6 +819,43 @@ export function parseFinder(
enableNone?: boolean; enableNone?: boolean;
} }
): ParsedModelFinder { ): ParsedModelFinder {
const { mainTypeSpecified, queryOptionMap, others } = preParseFinder(finderInput, opt);
const result = others as ParsedModelFinderKnown;
const defaultMainType = opt ? opt.defaultMainType : null;
if (!mainTypeSpecified && defaultMainType) {
queryOptionMap.set(defaultMainType, {});
}
queryOptionMap.each(function (queryOption, mainType) {
const queryResult = queryReferringComponents(
ecModel,
mainType,
queryOption,
{
useDefault: defaultMainType === mainType,
enableAll: (opt && opt.enableAll != null) ? opt.enableAll : true,
enableNone: (opt && opt.enableNone != null) ? opt.enableNone : true
}
);
result[mainType + 'Models'] = queryResult.models;
result[mainType + 'Model'] = queryResult.models[0];
});
return result;
}
export function preParseFinder(
finderInput: ModelFinder,
opt?: {
// If pervided, types out of this list will be ignored.
includeMainTypes?: ComponentMainType[];
}
): {
mainTypeSpecified: boolean;
queryOptionMap: HashMap<QueryReferringUserOption, ComponentMainType>;
others: Partial<Pick<ParsedModelFinderKnown, 'dataIndex' | 'dataIndexInside'>>
} {
let finder: ModelFinderObject; let finder: ModelFinderObject;
if (isString(finderInput)) { if (isString(finderInput)) {
const obj = {}; const obj = {};
...@@ -830,13 +867,13 @@ export function parseFinder( ...@@ -830,13 +867,13 @@ export function parseFinder(
} }
const queryOptionMap = createHashMap<QueryReferringUserOption, ComponentMainType>(); const queryOptionMap = createHashMap<QueryReferringUserOption, ComponentMainType>();
const result = {} as ParsedModelFinderKnown; const others = {} as Partial<Pick<ParsedModelFinderKnown, 'dataIndex' | 'dataIndexInside'>>;
let mainTypeSpecified = false; let mainTypeSpecified = false;
each(finder, function (value, key) { each(finder, function (value, key) {
// Exclude 'dataIndex' and other illgal keys. // Exclude 'dataIndex' and other illgal keys.
if (key === 'dataIndex' || key === 'dataIndexInside') { if (key === 'dataIndex' || key === 'dataIndexInside') {
result[key] = value as number; others[key] = value as number;
return; return;
} }
...@@ -858,29 +895,10 @@ export function parseFinder( ...@@ -858,29 +895,10 @@ export function parseFinder(
queryOption[queryType] = value as any; queryOption[queryType] = value as any;
}); });
const defaultMainType = opt ? opt.defaultMainType : null; return { mainTypeSpecified, queryOptionMap, others };
if (!mainTypeSpecified && defaultMainType) {
queryOptionMap.set(defaultMainType, {});
}
queryOptionMap.each(function (queryOption, mainType) {
const queryResult = queryReferringComponents(
ecModel,
mainType,
queryOption,
{
useDefault: defaultMainType === mainType,
enableAll: (opt && opt.enableAll != null) ? opt.enableAll : true,
enableNone: (opt && opt.enableNone != null) ? opt.enableNone : true
}
);
result[mainType + 'Models'] = queryResult.models;
result[mainType + 'Model'] = queryResult.models[0];
});
return result;
} }
export type QueryReferringUserOption = { export type QueryReferringUserOption = {
index?: ModelFinderIndexQuery, index?: ModelFinderIndexQuery,
id?: ModelFinderIdQuery, id?: ModelFinderIdQuery,
......
...@@ -46,6 +46,7 @@ import { PathStyleProps } from 'zrender/src/graphic/Path'; ...@@ -46,6 +46,7 @@ import { PathStyleProps } from 'zrender/src/graphic/Path';
import { ImageStyleProps } from 'zrender/src/graphic/Image'; import { ImageStyleProps } from 'zrender/src/graphic/Image';
import ZRText, { TextStyleProps } from 'zrender/src/graphic/Text'; import ZRText, { TextStyleProps } from 'zrender/src/graphic/Text';
import { Source } from '../data/Source'; import { Source } from '../data/Source';
import Model from '../model/Model';
...@@ -101,10 +102,6 @@ export interface ComponentTypeInfo { ...@@ -101,10 +102,6 @@ export interface ComponentTypeInfo {
} }
export interface ECElement extends Element { export interface ECElement extends Element {
tooltip?: CommonTooltipOption<unknown> & {
content?: string;
formatterParams?: unknown;
};
highDownSilentOnTouch?: boolean; highDownSilentOnTouch?: boolean;
onHoverStateChange?: (toState: DisplayState) => void; onHoverStateChange?: (toState: DisplayState) => void;
...@@ -130,7 +127,7 @@ export interface DataHost { ...@@ -130,7 +127,7 @@ export interface DataHost {
getData(dataType?: SeriesDataType): List; getData(dataType?: SeriesDataType): List;
} }
export interface DataModel extends DataHost, DataFormatMixin {} export interface DataModel extends Model<unknown>, DataHost, DataFormatMixin {}
// Pick<DataHost, 'getData'>, // Pick<DataHost, 'getData'>,
// Pick<DataFormatMixin, 'getDataParams' | 'formatTooltip'> {} // Pick<DataFormatMixin, 'getDataParams' | 'formatTooltip'> {}
...@@ -1348,6 +1345,24 @@ export interface CommonTooltipOption<FormatterParams> { ...@@ -1348,6 +1345,24 @@ export interface CommonTooltipOption<FormatterParams> {
} }
} }
export type ComponentItemTooltipOption<T> = CommonTooltipOption<T> & {
// Default content HTML.
content?: string;
formatterParams?: ComponentItemTooltipLabelFormatterParams;
};
export type ComponentItemTooltipLabelFormatterParams = {
componentType: string
name: string
// properies key array like ['name']
$vars: string[]
} & {
[key in `${ComponentMainType}Index`]: number
} & {
// Other properties
[key in string]: unknown
};
/** /**
* Tooltip option configured on each series * Tooltip option configured on each series
*/ */
...@@ -1355,6 +1370,9 @@ export type SeriesTooltipOption = CommonTooltipOption<CallbackDataParams> & { ...@@ -1355,6 +1370,9 @@ export type SeriesTooltipOption = CommonTooltipOption<CallbackDataParams> & {
trigger?: 'item' | 'axis' | boolean | 'none' trigger?: 'item' | 'axis' | boolean | 'none'
}; };
type LabelFormatterParams = { type LabelFormatterParams = {
value: ScaleDataValue value: ScaleDataValue
axisDimension: string axisDimension: string
......
...@@ -151,6 +151,7 @@ ...@@ -151,6 +151,7 @@
"tooltip-axisPointer": 20, "tooltip-axisPointer": 20,
"tooltip-axisPointer2": 1, "tooltip-axisPointer2": 1,
"tooltip-cascade": 4, "tooltip-cascade": 4,
"tooltip-component": 5,
"tooltip-event": 1, "tooltip-event": 1,
"tooltip-link": 2, "tooltip-link": 2,
"tooltip-rich": 1, "tooltip-rich": 1,
......
<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="lib/esl.js"></script>
<script src="lib/config.js"></script>
<script src="lib/jquery.min.js"></script>
<script src="lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script>
<!-- <script src="ut/lib/canteen.js"></script> -->
<link rel="stylesheet" href="lib/reset.css" />
</head>
<body>
<style>
</style>
<div id="main0"></div>
<div id="main1"></div>
<div id="axis-and-toolbox"></div>
<div id="axis-and-toolbox-formatter"></div>
<div id="graphic-component-tooltip"></div>
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option;
option = {
tooltip: {
confine: true,
textStyle: {
fontSize: 5
}
},
legend: {
id: 'legend_id',
tooltip: {
show: true,
textStyle: {
color: 'red',
fontSize: 20
}
},
data: [{
name: 'tooltip fontSize: 40 red',
tooltip: {
textStyle: {
fontSize: 40
}
}
}, {
name: 'tooltip fontSize: 20 red'
// This item inherit legend.tootip.
}, {
name: 'tooltip fontSize: 20 red formatter JSON',
tooltip: {
formatter: function (params) {
return '<pre>' + JSON.stringify(params, null, 2) + '</pre>';
}
}
// This item inherit legend.tootip.
}, {
name: 'only show "stringstring"',
tooltip: 'stringstring'
// This item inherit legend.tootip.
}]
},
series: {
type: 'pie',
radius: '30%',
data: [{
name: 'tooltip fontSize: 40 red',
value: 100
}, {
name: 'tooltip fontSize: 20 red',
value: 200
}, {
name: 'tooltip fontSize: 20 red formatter JSON',
value: 200
}, {
name: 'only show "stringstring"',
value: 200
}]
}
};
var chart = testHelper.create(echarts, 'main0', {
title: [
'Hover legend should show tooltip',
'check each **fontSize** and **color**',
'**click button** to trigger tooltip'
],
option: option,
height: 200,
buttons: [{
text: 'trigger 1st legend tooltip',
onclick: function () {
chart.dispatchAction({
type: 'showTip',
legendIndex: 0,
name: 'tooltip fontSize: 40 red'
});
}
}, {
text: 'trigger 2nd legend tooltip',
onclick: function () {
chart.dispatchAction({
type: 'showTip',
legendId: 'legend_id',
name: 'tooltip fontSize: 20 red'
});
}
}]
});
});
</script>
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option;
option = {
tooltip: {
textStyle: {
fontSize: 5
}
},
legend: {
data: [{
name: 'tooltip fontSize: 40',
tooltip: {
show: true,
textStyle: {
fontSize: 40
}
}
}, {
tooltip: {
show: true
},
name: 'tooltip fontSize: 5'
}, {
name: 'no tooltip'
}]
},
series: {
type: 'pie',
radius: '30%',
data: [{
name: 'tooltip fontSize: 40',
value: 100
}, {
name: 'tooltip fontSize: 5',
value: 200
}, {
name: 'no tooltip',
value: 200
}]
}
};
var chart = testHelper.create(echarts, 'main1', {
title: [
'Hover legend should show tooltip',
'check each **fontSize** and **color**',
],
option: option,
height: 200
});
});
</script>
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option;
option = {
tooltip: {
textStyle: {
fontSize: 5,
color: 'red'
}
},
toolbox: {
tooltip: {
confine: true,
show: true,
textStyle: {
fontSize: 20
}
},
feature: {
dataZoom: {},
magicType: {
type: ['line', 'bar', 'stack', 'tiled']
}
}
},
xAxis: {
name: 'show tooltip',
tooltip: {
show: true,
textStyle: {
fontSize: 20
}
}
},
yAxis: {},
series: {
type: 'scatter',
data: [12, 22, 33]
}
};
var chart = testHelper.create(echarts, 'axis-and-toolbox', {
title: [
'Hover toolbox and axis name:',
'should show tooltip, fontSize: 20 red',
'check each **fontSize** and **color**',
],
option: option,
height: 200
});
});
</script>
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option;
option = {
tooltip: {
textStyle: {
fontSize: 5,
color: 'red'
}
},
toolbox: {
tooltip: {
confine: true,
show: true,
formatter: params => {
return '<pre>' + JSON.stringify(params, null, 4) + '</pre>';
},
textStyle: {
fontSize: 20
}
},
feature: {
dataZoom: {},
magicType: {
type: ['line', 'bar', 'stack', 'tiled']
}
}
},
xAxis: {
name: 'show tooltip',
tooltip: {
formatter: params => {
return '<pre>' + JSON.stringify(params, null, 4) + '</pre>';
},
show: true,
textStyle: {
fontSize: 20
}
}
},
yAxis: {},
series: {
type: 'scatter',
data: [12, 22, 33]
}
};
var chart = testHelper.create(echarts, 'axis-and-toolbox-formatter', {
title: [
'Hover toolbox and axis name:',
'should show tooltip, fontSize: 20 red, **params JSON**',
'check each **fontSize** and **color**',
],
option: option,
height: 200
});
});
</script>
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
var option;
option = {
tooltip: {
textStyle: {
fontSize: 10,
color: 'red'
}
},
graphic: [{
tooltip: {
formatter: 'asdf',
textStyle: {
fontSize: 20
}
},
type: 'rect',
name: '1st',
shape: {
y: 20,
x: 250,
width: 40,
height: 40
},
textContent: {
style: { text: 'show tooltip asdf\nin fontSize: 20 red' }
},
textConfig: {
position: 'bottom'
},
style: {
fill: 'blue'
}
}, {
type: 'rect',
shape: {
y: 20,
x: 350,
width: 40,
height: 40
},
textContent: {
style: {
text: 'show no tooltip'
}
},
textConfig: {
position: 'bottom'
},
style: {
fill: 'blue'
}
}, {
type: 'rect',
tooltip: {
formatter: function (param) {
return '<pre>' + JSON.stringify(param, null, 2) + '</pre>';
},
textStyle: {
fontSize: 20
}
},
name: '3rd',
shape: {
y: 20,
x: 450,
width: 40,
height: 40
},
textContent: {
style: { text: 'show tooltip JSON params\nin fontSize: 20 red' }
},
textConfig: {
position: 'bottom'
},
style: {
fill: 'blue'
}
}, {
tooltip: 'stringstring',
type: 'rect',
name: '4nd',
shape: {
y: 20,
x: 550,
width: 40,
height: 40
},
textContent: {
style: { text: 'show tooltip "stringstring"\nin fontSize: 10 red' }
},
textConfig: {
position: 'bottom'
},
style: {
fill: 'blue'
}
}]
};
var chart = testHelper.create(echarts, 'graphic-component-tooltip', {
title: [
'Hover the graphic should show tooltip',
'check each tooltip',
],
option: option,
height: 200,
buttons: [{
text: 'trigger 1st graphic tooltip',
onclick: function () {
chart.dispatchAction({
type: 'showTip',
graphicIndex: 0,
name: '1st'
});
}
}, {
text: 'trigger 3rd graphic tooltip',
onclick: function () {
chart.dispatchAction({
type: 'showTip',
graphicIndex: 0,
name: '3rd'
});
}
}]
});
});
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册