import { TooltipOptions, TooltipEventParams, TooltipDataItem } from '../../models'; import AtomComponent from '../../atomComponent'; import { EventName, SeriesCategory, TooltipDefaultStyle } from '../../constants'; import { datetime, merge } from '../../utils'; import { domToString } from '../../utils/htmlSnip'; import { hexToRgb } from '../../theme'; import { isNumber } from '@utils/type'; const name = 'tooltip'; export default abstract class Tooltip extends AtomComponent { protected static readonly defaultOptions: TooltipOptions = { show: true, formatMode: 'replace', width: 150, }; private static readonly defaultParams: TooltipEventParams = { coordinate: 'cartesian', }; protected seriesCategory = SeriesCategory.NONE; protected options: TooltipOptions; protected params: TooltipEventParams; /* todo: 后续优化 */ protected title = ''; protected transformedData = [] as any[]; /* todo: 后续优化 */ constructor(chart, options: TooltipOptions) { super({ name, chart }); this.options = merge(Tooltip.defaultOptions, options); this.initEvent(); } protected computeItemWidth(): number { const { ctx } = this.canvas; ctx.font = `${TooltipDefaultStyle.fontSize}px ${TooltipDefaultStyle.fontFamily}`; const itemTextWidths = this.transformedData.map(item => Math.ceil(ctx.measureText(item.text).width)); const maxItemTextWidth = Math.max(...itemTextWidths); return TooltipDefaultStyle.symbolWidth + TooltipDefaultStyle.symbolToTextGap + maxItemTextWidth; } protected computeTooltipX(width: number, needHandlePadding = false) { const { canvas, params } = this; const { padding, size } = canvas; const { centerX, coordinate, point } = params; if (coordinate === 'cartesian') { let x = 0; if (centerX - width / 2 <= 0) { x = 0; } else if (centerX + width / 2 > size.width) { x = size.width - width; } else { x = centerX - width / 2; } return x + (needHandlePadding ? padding.left : 0); } return point.x - TooltipDefaultStyle.padding - TooltipDefaultStyle.symbolWidth / 2 + (needHandlePadding ? padding.left : 0); // return point.x - TooltipDefaultStyle.padding + (needHandlePadding ? padding.left : 0); } protected getHtmlSinp() { const { formatter, formatMode } = this.options; const defaultHtmlSinp = this.getDefaultHtmlSinp(); const formattedHtmlSinp = this.getFormattedHtmlSinp(); if (!formatter) return defaultHtmlSinp; return formatMode === 'append' ? defaultHtmlSinp + formattedHtmlSinp : formattedHtmlSinp; } /** * 当legend居顶布局,需要将tooltip下移动padding.top距离来露出legend */ protected getPaddingTop() { const { legend } = this.chart.getParsedOptions(); if (!legend || !legend?.top) return 0; const { padding } = this.chart.canvas; return isNumber(padding?.top) ? padding?.top : 0; } private getDefaultHtmlSinp() { const { title, transformedData } = this; const { fontSize, fontFamily, color } = TooltipDefaultStyle; const itemWidth = this.computeItemWidth(); return `