@visactor/vchart
Version:
charts lib based @visactor/VGrammar
303 lines (285 loc) • 18.1 kB
JavaScript
import { ComponentTypeEnum } from "../interface/type";
import { BaseComponent } from "../base/base-component";
import { isMobileLikeMode, isTrueBrowser, isMiniAppLikeMode } from "../../util/env";
import { showTooltip } from "./utils/show-tooltip";
import { isEmptyPos } from "./utils/common";
import { isSameDimensionInfo } from "../../event/events/dimension/util/common";
import { ChartEvent, Event_Source_Type } from "../../constant/event";
import { isDimensionInfo, isMarkInfo } from "./processor/util";
import { isValid, isNil, array, isNumber, throttle, isObject } from "@visactor/vutils";
import { VChart } from "../../core/vchart";
import { Factory } from "../../core/factory";
import { TooltipSpecTransformer } from "./tooltip-transformer";
import { error } from "../../util";
import { DEFAULT_SHOW_DELAY, TooltipHandlerType } from "./constant";
import { tooltip } from "../../theme/builtin/common/component/tooltip";
export class Tooltip extends BaseComponent {
constructor() {
super(...arguments), this.layoutZIndex = 1, this.type = ComponentTypeEnum.tooltip,
this.name = ComponentTypeEnum.tooltip, this.transformerConstructor = TooltipSpecTransformer,
this.specKey = "tooltip", this.layoutType = "none", this._isReleased = !1, this._alwaysShow = !1,
this._eventList = [], this._isTooltipShown = !1, this._clickLock = !1, this._mountEvent = (eType, query, callback) => {
this.event.on(eType, query, callback), this._eventList.push({
eventType: eType,
handler: callback
});
}, this._handleClickToLock = params => {
var _a, _b;
if (this._clickLock) this._handleChartMouseOut(params), this._clickLock = !1; else {
if (!this._isTooltipShown && !(null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a))) return;
this._clickLock = !0;
}
}, this._getMouseOutHandler = needPointerDetection => params => {
var _a, _b, _c, _d, _e;
if (this._isReleased) return;
if (this._alwaysShow || this._clickLock) return;
if (!this._isTooltipShown && !(null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a))) return;
const browserEnv = isTrueBrowser(null === (_c = this._option) || void 0 === _c ? void 0 : _c.mode), {clientX: clientX, clientY: clientY} = params.event;
browserEnv && needPointerDetection && this._isPointerInChart({
x: clientX,
y: clientY
}) || (this._enterable ? this._outTimer = setTimeout((() => {
this._handleChartMouseOut(params);
}), null !== (_e = null === (_d = this._spec) || void 0 === _d ? void 0 : _d.showDelay) && void 0 !== _e ? _e : DEFAULT_SHOW_DELAY) : this._handleChartMouseOut(params));
}, this._handleChartMouseOut = params => {
this._alwaysShow || this._isReleased || this._isEnterTooltip || "none" !== this._spec.triggerOff && (this._hideTooltipByHandler(Object.assign(Object.assign({}, params), {
tooltip: this
})), this._handleMouseMove && this._handleMouseMove.cancel && this._handleMouseMove.cancel(),
this._cacheEnterableRect = null, this._cacheInfo = void 0, this._cacheParams = void 0,
this._cacheActiveType = void 0);
}, this._getMouseMoveHandler = isClick => params => {
var _a, _b, _c, _d;
this._isReleased || this._isEnterTooltip || (this._outTimer && (clearTimeout(this._outTimer),
this._outTimer = null), this.tooltipHandler || this._initHandler(), this.processor || this._initProcessor(),
this._alwaysShow || (this._clickLock ? isClick && (this._handleChartMouseOut(params),
this._clickLock = !1) : !isClick && this._enterable && (null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a)) ? (this._showTimer && clearTimeout(this._showTimer),
this._showTimer = setTimeout((() => {
this._handleChartMouseMove(params, isClick);
}), null !== (_d = null === (_c = this._spec) || void 0 === _c ? void 0 : _c.showDelay) && void 0 !== _d ? _d : DEFAULT_SHOW_DELAY)) : this._handleChartMouseMove(params, isClick)));
}, this._handleChartMouseMove = (params, isClick) => {
if (this._isReleased) return;
const mouseEventData = this._getMouseEventData(params), {tooltipInfo: {dimension: dimensionInfo}, ignore: {mark: ignoreMark}} = mouseEventData, success = {
mark: !1,
dimension: !1,
group: !1
}, supportedTooltip = [ "group", "mark", "dimension" ];
for (let i = 0, len = supportedTooltip.length; i < len; i++) {
const type = supportedTooltip[i];
if (!!this.processor[type] && this._showTooltipByMouseEvent(type, mouseEventData, params, isClick)) {
success[type] = !0;
break;
}
}
Object.values(success).every((val => !val)) && !isEmptyPos(params) && (ignoreMark && isMarkInfo(this._cacheInfo) ? success.mark = this._showTooltipByMouseEvent("mark", mouseEventData, params, isClick, !0) : isValid(dimensionInfo) && (success.dimension = this._showTooltipByMouseEvent("dimension", mouseEventData, params, isClick))),
success.mark || success.group || success.dimension && !isNil(dimensionInfo) ? this._initEventOfTooltipContent() : this._handleChartMouseOut(params);
}, this._showTooltipByMouseEvent = (activeType, mouseEventData, params, isClick, useCache) => {
var _a;
const processor = this.processor[activeType];
if (!processor.shouldHandleTooltip(params, mouseEventData.tooltipInfo[activeType])) return !1;
let success;
if (this._hideTimer && clearTimeout(this._hideTimer), useCache) success = !processor.showTooltip(this._cacheInfo, params, !0); else {
const tooltipInfo = mouseEventData.tooltipInfo[activeType], isSameAsCache = this._isSameAsCache(tooltipInfo, params, activeType);
success = !processor.showTooltip(tooltipInfo, params, isSameAsCache), success && (this._cacheInfo = tooltipInfo,
this._cacheParams = params, this._cacheActiveType = activeType);
}
success && (this._isTooltipShown = !0, isClick && this._spec.lockAfterClick && !this._clickLock ? this._clickLock = !0 : Number.isFinite(this._spec.hideTimer) && (this._hideTimer = setTimeout((() => {
this._handleChartMouseOut();
}), this._spec.hideTimer)));
const vchart = null === (_a = this._option) || void 0 === _a ? void 0 : _a.globalInstance;
return success && VChart.globalConfig.uniqueTooltip && vchart && VChart.hideTooltip(vchart.id),
success;
}, this._getMouseEventData = params => {
const result = {
tooltipInfo: {},
ignore: {}
};
return Object.keys(this.processor).forEach((activeType => {
const {tooltipInfo: tooltipInfo, ignore: ignore} = this.processor[activeType].getMouseEventData(params);
result.tooltipInfo[activeType] = tooltipInfo, result.ignore[activeType] = ignore;
})), result;
}, this._hideTooltipByHandler = params => {
var _a, _b, _c;
if (!this._isTooltipShown && !(null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.isTooltipShown) || void 0 === _b ? void 0 : _b.call(_a))) return 0;
this.event.emit(ChartEvent.tooltipHide, Object.assign(Object.assign({}, params), {
source: Event_Source_Type.chart,
tooltip: this
})), Object.values(this.processor).forEach((processor => {
processor.clearCache();
}));
const handler = null !== (_c = this._spec.handler) && void 0 !== _c ? _c : this.tooltipHandler;
if (handler.hideTooltip) {
const result = handler.hideTooltip.call(handler, params);
return result || (this._isTooltipShown = !1), result;
}
return 1;
}, this.hideTooltip = () => {
if (this._isReleased) return !1;
const params = {
changePositionOnly: !1,
tooltip: this,
item: void 0,
datum: void 0,
source: Event_Source_Type.chart
};
return this._alwaysShow = !1, !this._hideTooltipByHandler(params);
};
}
isTooltipShown() {
return this._isTooltipShown;
}
_registerEvent() {}
_releaseEvent() {}
onLayoutEnd() {}
created() {
super.created(), this._regions = this._option.getAllRegions(), this._initEvent();
}
release() {
var _a, _b;
super.release(), this._isReleased = !0, this._hideTimer && clearTimeout(this._hideTimer),
this._eventList.forEach((({eventType: eventType, handler: handler}) => {
this.event.off(eventType, handler);
})), this._eventList = [], null === (_b = null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.release) || void 0 === _b || _b.call(_a),
this._isTooltipShown = !1;
}
beforeRelease() {
this.event.emit(ChartEvent.tooltipHide, {
tooltip: this,
chart: this.getChart()
}), this.event.emit(ChartEvent.tooltipRelease, {
tooltip: this,
chart: this.getChart()
});
}
_initHandler() {
var _a, _b, _c;
const renderMode = null !== (_a = this._spec.renderMode) && void 0 !== _a ? _a : "html", userTooltipHandler = this._option.globalInstance.getTooltipHandlerByUser();
if (userTooltipHandler) this.tooltipHandler = userTooltipHandler, this._enterable = !1; else {
const type = "canvas" === renderMode ? TooltipHandlerType.canvas : TooltipHandlerType.dom, handlerConstructor = Factory.getComponentPluginInType(type);
handlerConstructor || error("Can not find tooltip handler: " + type);
const handler = new handlerConstructor;
handler.name = `${this._spec.className}-${null !== (_b = this._option.globalInstance.id) && void 0 !== _b ? _b : 0}-${this.getSpecIndex()}`,
null === (_c = this.pluginService) || void 0 === _c || _c.load([ handler ]), this.tooltipHandler = handler,
this._spec.enterable && "html" === renderMode && this.tooltipHandler ? (this._enterable = !0,
this._needInitEventOfTooltip = !0) : this._enterable = !1;
}
}
_initEventOfTooltipContent() {
var _a, _b;
if (!this._needInitEventOfTooltip) return;
const container = null === (_b = (_a = this.tooltipHandler).getRootDom) || void 0 === _b ? void 0 : _b.call(_a);
container && (container.addEventListener("pointerenter", (() => {
var _a;
if (!this._enterable) return;
this._isEnterTooltip = !0;
const rect = null === (_a = container.getBoundingClientRect) || void 0 === _a ? void 0 : _a.call(container);
rect && (this._cacheEnterableRect = {
width: rect.width,
height: rect.height
}), this._outTimer && (clearTimeout(this._outTimer), this._outTimer = null), this._showTimer && (clearTimeout(this._showTimer),
this._showTimer = null);
})), container.addEventListener("pointerleave", (() => {
var _a, _b, _c;
if (this._enterable && (this._isEnterTooltip = !1, "none" !== this._spec.triggerOff && this._cacheEnterableRect)) {
const newRect = null === (_a = container.getBoundingClientRect) || void 0 === _a ? void 0 : _a.call(container);
newRect && Object.keys(this._cacheEnterableRect).every((k => this._cacheEnterableRect[k] === newRect[k])) && (this._cacheEnterableRect = null,
this._outTimer = setTimeout(this.hideTooltip, null !== (_c = null === (_b = this._spec) || void 0 === _b ? void 0 : _b.showDelay) && void 0 !== _c ? _c : DEFAULT_SHOW_DELAY));
}
})), this._needInitEventOfTooltip = !1);
}
_initProcessor() {
const activeType = this._spec.activeType;
this.processor = {}, activeType.forEach((type => {
const instance = Factory.createTooltipProcessor(type, this);
instance && (this.processor[type] = instance);
}));
}
_initEvent() {
var _a;
if (this._option.disableTriggerEvent) return;
const trigger = array(null !== (_a = this._spec.trigger) && void 0 !== _a ? _a : "hover"), triggerOff = array(this._spec.triggerOff), mode = this._option.mode;
trigger.forEach((triggerType => {
var _a;
"hover" === triggerType ? (this._handleMouseMove = this._throttle(this._getMouseMoveHandler(!1)),
this._mountEvent("pointermove", {
source: "chart"
}, this._handleMouseMove), (isMobileLikeMode(mode) || isMiniAppLikeMode(mode)) && (this._mountEvent("pointerdown", {
source: "chart"
}, this._getMouseMoveHandler(!1)), this._mountEvent("pointerup", {
source: "window"
}, this._getMouseOutHandler(!0))), this._mountEvent("pointerleave", {
source: "chart"
}, this._getMouseOutHandler(!1))) : "click" === triggerType ? (this._mountEvent("pointertap", {
source: "chart"
}, this._getMouseMoveHandler(!0)), this._mountEvent("pointerup", {
source: "window"
}, this._getMouseOutHandler(!0))) : isObject(triggerType) && this._mountEvent(triggerType.eventType, {
source: null !== (_a = triggerType.source) && void 0 !== _a ? _a : "chart",
consume: triggerType.consume
}, this._getMouseMoveHandler(!0));
}));
triggerOff.filter((entry => isObject(entry))).forEach((entry => {
var _a, _b;
this._mountEvent(entry.eventType, {
source: null !== (_a = entry.source) && void 0 !== _a ? _a : "chart",
consume: entry.consume
}, this._getMouseOutHandler(null !== (_b = entry.checkOutside) && void 0 !== _b && _b));
})), !trigger.includes("click") && this._spec.lockAfterClick && this._mountEvent("pointertap", {
source: "chart"
}, this._handleClickToLock);
}
_throttle(callback) {
let wait;
return wait = isNumber(this._spec.throttleInterval) ? this._spec.throttleInterval : "html" === this._spec.renderMode && this._spec.transitionDuration ? 50 : 10,
throttle(callback, wait);
}
reInit(spec) {
var _a, _b, _c;
if (super.reInit(spec), this.tooltipHandler) {
const renderMode = null !== (_a = this._spec.renderMode) && void 0 !== _a ? _a : "html", newEnterable = this._spec.enterable && "html" === renderMode;
newEnterable && !this._enterable && (this._needInitEventOfTooltip = !0), this._enterable = newEnterable,
null === (_c = (_b = this.tooltipHandler).reInit) || void 0 === _c || _c.call(_b);
} else this._initHandler();
}
showTooltip(datum, options) {
var _a;
if (this.tooltipHandler || this._initHandler(), this.processor || this._initProcessor(),
!(null === (_a = this.tooltipHandler) || void 0 === _a ? void 0 : _a.showTooltip)) return !1;
const result = showTooltip(datum, options, this);
return "none" !== result && (this._alwaysShow = !!(null == options ? void 0 : options.alwaysShow)),
result;
}
_isSameAsCache(nextInfo, nextParams, nextActiveType) {
if (nextActiveType !== this._cacheActiveType) return !1;
if (nextInfo === this._cacheInfo) return !0;
if (isNil(this._cacheInfo) || isNil(nextInfo)) return !1;
if (isDimensionInfo(nextInfo)) {
if (isMarkInfo(this._cacheInfo)) return !1;
const prevInfo = this._cacheInfo;
return prevInfo.length === nextInfo.length && nextInfo.every(((info, i) => isSameDimensionInfo(info, prevInfo[i])));
}
if (isDimensionInfo(this._cacheInfo)) return !1;
const prevInfo = this._cacheInfo;
if (!((null == nextInfo ? void 0 : nextInfo.datum) === prevInfo.datum && (null == nextInfo ? void 0 : nextInfo.mark) === prevInfo.mark && (null == nextInfo ? void 0 : nextInfo.series) === prevInfo.series)) return !1;
const prevParams = this._cacheParams;
return !isNil(prevParams) && !isNil(nextParams) && (prevParams.mark === nextParams.mark && prevParams.model === nextParams.model && prevParams.datum === nextParams.datum);
}
_isPointerInChart(point) {
var _a;
const globalInstance = null === (_a = this._option) || void 0 === _a ? void 0 : _a.globalInstance;
if (!globalInstance) return !1;
if (!globalInstance.getChart()) return !1;
const {x: x, y: y} = point, canvas = globalInstance.getCanvas(), {x: chartX, y: chartY, width: chartWidth, height: chartHeight} = canvas.getBoundingClientRect();
return x >= chartX && x <= chartX + chartWidth && y >= chartY && y <= chartY + chartHeight;
}
getVisible() {
return !1 !== this._spec.visible;
}
}
Tooltip.type = ComponentTypeEnum.tooltip, Tooltip.transformerConstructor = TooltipSpecTransformer,
Tooltip.builtInTheme = {
tooltip: tooltip
}, Tooltip.specKey = "tooltip";
export const registerTooltip = () => {
Factory.registerComponent(Tooltip.type, Tooltip);
};
//# sourceMappingURL=tooltip.js.map