UNPKG

@alicloud/cloud-charts

Version:

![](https://img.shields.io/npm/v/@alicloud/cloud-charts?color=%23ff8200)

332 lines (316 loc) 18.8 kB
'use strict'; import _extends from "@babel/runtime/helpers/extends"; import { customFormatter, getRawData, merge, pxToNumber } from './common'; import { render } from 'react-dom'; import themes from '../themes'; import { debounce } from '@antv/util'; // import TooltipController from '@antv/g2/esm/chart/controller/tooltip'; // import { registerComponentController } from '@antv/g2/esm/chart/controller'; // // 自定义 TooltipController 来处理 titleFormatter 的问题 // class WidgetsTooltipController extends TooltipController { // getTooltipItems(point: Types.Point) { // const rawItems = super.getTooltipItems(point); // const view = this.view; // const option = view.getOptions().tooltip; // // @ts-ignore // if (rawItems.length > 0 && typeof option !== 'boolean' && option.customTitle) { // // @ts-ignore // rawItems[0].title = option.customTitle(rawItems[0].title || rawItems[0].name, rawItems); // } // return rawItems; // } // } // registerComponentController('tooltip', WidgetsTooltipController); // 排序函数 var sortFun = { // 升序 asce: function asce(a, b) { return a.value - b.value; }, // 降序 desc: function desc(a, b) { return b.value - a.value; } }; /** * rectTooltip 直角坐标系的tooltip配置 * * @param {this} ctx 组件实例 this 指针 * @param {Chart} chart 图表实例 * @param {Object} config 配置项 * @param {Object} defaultConfig 组件的自定义配置 * @param {Function} onTooltipChange 自定义 tooltip:change 事件 * @param {Object} componentConfig * */ export default function (ctx, chart, config, defaultConfig, onTooltipChange, componentConfig) { if (config.tooltip === false || config.tooltip && typeof config.tooltip !== 'boolean' && config.tooltip.visible === false) { chart.tooltip(false); } else { var _config$xAxis; var _ref = config.tooltip === true ? {} : config.tooltip || {}, sort = _ref.sort, _ref$showTitle = _ref.showTitle, showTitle = _ref$showTitle === void 0 ? true : _ref$showTitle, _ref$showColon = _ref.showColon, showColon = _ref$showColon === void 0 ? false : _ref$showColon, position = _ref.position, offset = _ref.offset, titleFormatter = _ref.titleFormatter, nameFormatter = _ref.nameFormatter, valueFormatter = _ref.valueFormatter, customContent = _ref.customContent, reactContent = _ref.reactContent, customConfig = _ref.customConfig, columns = _ref.columns, lockable = _ref.lockable, dodge = _ref.dodge, customTooltip = _ref.customTooltip; var tooltipConfig = _extends({}, defaultConfig, { showTitle: showTitle, // title: '_customTitle_', showCrosshairs: true, // crosshairs 空对象不可省略,否则在混合图表中会没有crosshairs line crosshairs: { type: 'x' }, position: position, offset: offset, shared: true, // inPlot, itemTpl: "<li class=\"g2-tooltip-list-item\" data-index={index}><span class=\"g2-tooltip-marker\" style=\"background:{color}\"></span><span class=\"g2-tooltip-name\">{name}</span>" + (showColon ? ': ' : ' ') + "<span class=\"g2-tooltip-value\">{value}</span></li>", // 尝试自定义title,可以达到效果,但是重绘次数过多,性能差 // customContent(title, data) { // console.log(title, data); // return `<div class="g2-tooltip-title">${title}</div> // <ul class="g2-tooltip-list"> // ${ // data.map((d, i) => { // return `<li class="g2-tooltip-list-item" data-index="${i}"> // <span class="g2-tooltip-marker" style="background:${d.color}"></span> // <span class="g2-tooltip-name">${d.name}</span>:<span class="g2-tooltip-value">${d.value}</span> // </li>`; // }).join('') // } // </ul> // `; // }, customContent: customContent, // @ts-ignore customTooltip: customTooltip, lockable: lockable }); if (titleFormatter) { // 下面这段是 TooltipCfg.title 不为 function 的逻辑 // tooltipConfig.title = '_customTitle_'; // 下面这段是 TooltipCfg.title 为 function 的逻辑 tooltipConfig.title = function (title, item) { return titleFormatter(title, item); }; // 下面这行是配合自定义 TooltipController // // @ts-ignore // tooltipConfig.customTitle = titleFormatter; } // react tooltip 渲染模式 if (reactContent) { var reactContentDom = document.createElement('div'); reactContentDom.classList.add('g2-tooltip'); reactContentDom.style.width = 'auto'; reactContentDom.style.height = 'auto'; tooltipConfig.customContent = function (title, data) { render(reactContent(title, data), reactContentDom); return reactContentDom; }; } // 分组不和自定义内容同时使用 if (dodge && !reactContent && !customContent) { var _reactContentDom = document.createElement('div'); _reactContentDom.classList.add('g2-tooltip'); _reactContentDom.style.width = 'auto'; _reactContentDom.style.height = 'auto'; tooltipConfig.customContent = function (name, data) { var title = "<div class=\"g2-tooltip-title\" style=\"margin-top: 12px;margin-bottom: 12px;\">" + name + "</div>"; var listItem = ''; var dodgeGroups = []; data.forEach(function (item) { var _item$mappingData; var rawData = item.data; listItem += "<li class=\"g2-tooltip-list-item\" data-index={index} style=\"margin-bottom:4px;display:flex;align-items: center;\">\n " + ((rawData !== null && rawData !== void 0 && rawData.dodge || rawData !== null && rawData !== void 0 && rawData.facet) && !dodgeGroups.includes((rawData === null || rawData === void 0 ? void 0 : rawData.dodge) || (rawData === null || rawData === void 0 ? void 0 : rawData.facet)) ? "<span style=\"margin-right: 10px;\">" + ((rawData === null || rawData === void 0 ? void 0 : rawData.dodge) || (rawData === null || rawData === void 0 ? void 0 : rawData.facet)) + ": </span>" : "<span style=\"color: rgba(0,0,0,0);margin-right: 10px;\">" + ((rawData === null || rawData === void 0 ? void 0 : rawData.dodge) || (rawData === null || rawData === void 0 ? void 0 : rawData.facet)) + ": </span>") + "\n <span style=\"background-color:" + ((item === null || item === void 0 ? void 0 : (_item$mappingData = item.mappingData) === null || _item$mappingData === void 0 ? void 0 : _item$mappingData.color) || (item === null || item === void 0 ? void 0 : item.color)) + ";\" class=\"g2-tooltip-marker\"></span>\n <span style=\"display:inline-flex;flex:1;justify-content:space-between\">\n <span style=\"margin-right: 16px;\">" + (item === null || item === void 0 ? void 0 : item.name) + ":</span><span>" + (item === null || item === void 0 ? void 0 : item.value) + "</span>\n </span>\n </li>"; dodgeGroups.push((rawData === null || rawData === void 0 ? void 0 : rawData.dodge) || (rawData === null || rawData === void 0 ? void 0 : rawData.facet)); }); _reactContentDom.innerHTML = title + listItem; return _reactContentDom; }; } // 多列设置无法和自定义内容同时使用 if (columns !== false && !reactContent && !customContent) { var tooltipListStyle = {}; if (columns > 1) { tooltipListStyle['column-count'] = columns; } merge(tooltipConfig, { domStyles: { 'g2-tooltip-list': tooltipListStyle } }); var fontSize1 = themes['widgets-font-size-1']; var baseFontSizeNum = pxToNumber(themes['widgets-font-size-1']); // 在 tooltip 展示前,根据 items 个数更新 chart.on('tooltip:show', function (ev) { if (ev.view) { var chartHeight = ev.view.height; // 图表高度最多能容纳的 tooltip 项个数 var maxLen = Math.floor((chartHeight - 3 * baseFontSizeNum) / (2 * baseFontSizeNum)); var _items = ev.data.items; // 计算最终分列数,自动计算最多分 3 列,最少 1 列 var computeColumns = columns || Math.min(3, Math.max(1, Math.ceil(_items.length / maxLen))); var tooltipOptions = ev.view.getOptions().tooltip; tooltipOptions.domStyles['g2-tooltip-list']['column-count'] = computeColumns; if (_items.length % computeColumns !== 0) { tooltipOptions.domStyles['g2-tooltip-list'].margin = "0 0 " + fontSize1 + " 0"; } else { tooltipOptions.domStyles['g2-tooltip-list'].margin = 0; } } }); } // 当 x 轴为 time 或 timeCat 类型时,添加 customItems 过滤逻辑 // 避免 shared tooltip 将不同 x 值的数据点合并显示 var xAxisType = typeof config.xAxis === 'object' && ((_config$xAxis = config.xAxis) === null || _config$xAxis === void 0 ? void 0 : _config$xAxis.type); if (xAxisType && (xAxisType === 'time' || xAxisType === 'timeCat')) { tooltipConfig.customItems = function (items) { var _items$, _items$$data; if (items.length <= 1) return items; // 获取第一个 item(离鼠标最近的点)的 x 值 var baseX = (_items$ = items[0]) === null || _items$ === void 0 ? void 0 : (_items$$data = _items$.data) === null || _items$$data === void 0 ? void 0 : _items$$data.x; if (baseX === undefined || baseX === null) return items; // 只保留 x 值完全相同的 items return items.filter(function (item) { var _item$data; return ((_item$data = item.data) === null || _item$data === void 0 ? void 0 : _item$data.x) === baseX; }); }; } if (componentConfig) { Object.assign(tooltipConfig, componentConfig); } if (customConfig) { merge(tooltipConfig, customConfig); } chart.tooltip(tooltipConfig); if (onTooltipChange) { chart.on('tooltip:change', onTooltipChange); } else { var _config$yAxis, _config$tooltip, _config$tooltip2, _config$tooltip3, _config$tooltip4, _config$tooltip5; // 默认tooltip的单位转换沿用Y轴配置 if (typeof config.yAxis === 'object' && !Array.isArray(config.yAxis) && config !== null && config !== void 0 && (_config$yAxis = config.yAxis) !== null && _config$yAxis !== void 0 && _config$yAxis.needUnitTransform && typeof config.tooltip !== 'boolean' && ((_config$tooltip = config.tooltip) === null || _config$tooltip === void 0 ? void 0 : _config$tooltip.needUnitTransform) === undefined) { var _config$yAxis2, _config$tooltip$unit, _config$yAxis3, _config$tooltip$unitT, _config$yAxis4, _config$tooltip$value, _config$yAxis5, _config$tooltip$custo, _config$yAxis6, _config$tooltip$custo2, _config$yAxis7, _config$tooltip$addon, _config$yAxis8; config.tooltip.needUnitTransform = config === null || config === void 0 ? void 0 : (_config$yAxis2 = config.yAxis) === null || _config$yAxis2 === void 0 ? void 0 : _config$yAxis2.needUnitTransform; config.tooltip.unit = (_config$tooltip$unit = config.tooltip.unit) !== null && _config$tooltip$unit !== void 0 ? _config$tooltip$unit : config === null || config === void 0 ? void 0 : (_config$yAxis3 = config.yAxis) === null || _config$yAxis3 === void 0 ? void 0 : _config$yAxis3.unit; config.tooltip.unitTransformTo = (_config$tooltip$unitT = config.tooltip.unitTransformTo) !== null && _config$tooltip$unitT !== void 0 ? _config$tooltip$unitT : config === null || config === void 0 ? void 0 : (_config$yAxis4 = config.yAxis) === null || _config$yAxis4 === void 0 ? void 0 : _config$yAxis4.unitTransformTo; config.tooltip.valueType = (_config$tooltip$value = config.tooltip.valueType) !== null && _config$tooltip$value !== void 0 ? _config$tooltip$value : config === null || config === void 0 ? void 0 : (_config$yAxis5 = config.yAxis) === null || _config$yAxis5 === void 0 ? void 0 : _config$yAxis5.valueType; config.tooltip.customCarryUnits = (_config$tooltip$custo = config.tooltip.customCarryUnits) !== null && _config$tooltip$custo !== void 0 ? _config$tooltip$custo : config === null || config === void 0 ? void 0 : (_config$yAxis6 = config.yAxis) === null || _config$yAxis6 === void 0 ? void 0 : _config$yAxis6.customCarryUnits; config.tooltip.customCarryThreshold = (_config$tooltip$custo2 = config.tooltip.customCarryThreshold) !== null && _config$tooltip$custo2 !== void 0 ? _config$tooltip$custo2 : config === null || config === void 0 ? void 0 : (_config$yAxis7 = config.yAxis) === null || _config$yAxis7 === void 0 ? void 0 : _config$yAxis7.customCarryThreshold; config.tooltip.addonTextAfter = (_config$tooltip$addon = config.tooltip.addonTextAfter) !== null && _config$tooltip$addon !== void 0 ? _config$tooltip$addon : config === null || config === void 0 ? void 0 : (_config$yAxis8 = config.yAxis) === null || _config$yAxis8 === void 0 ? void 0 : _config$yAxis8.addonTextAfter; } if (typeof config.tooltip === 'object') { var _config$tooltip$group, _config$yAxis9, _config$tooltip$decim, _config$yAxis10; config.tooltip.grouping = (_config$tooltip$group = config.tooltip.grouping) !== null && _config$tooltip$group !== void 0 ? _config$tooltip$group : config === null || config === void 0 ? void 0 : (_config$yAxis9 = config.yAxis) === null || _config$yAxis9 === void 0 ? void 0 : _config$yAxis9.grouping; config.tooltip.decimal = (_config$tooltip$decim = config.tooltip.decimal) !== null && _config$tooltip$decim !== void 0 ? _config$tooltip$decim : config === null || config === void 0 ? void 0 : (_config$yAxis10 = config.yAxis) === null || _config$yAxis10 === void 0 ? void 0 : _config$yAxis10.decimal; } // 进位相关配置项 var formatConfig; // 当tooltip中配置了单位相关信息时,直接使用tooltip的配置项,否则使用y轴配置项 if (typeof (config === null || config === void 0 ? void 0 : config.tooltip) === 'object' && (config !== null && config !== void 0 && (_config$tooltip2 = config.tooltip) !== null && _config$tooltip2 !== void 0 && _config$tooltip2.valueType || config !== null && config !== void 0 && (_config$tooltip3 = config.tooltip) !== null && _config$tooltip3 !== void 0 && _config$tooltip3.unit || config !== null && config !== void 0 && (_config$tooltip4 = config.tooltip) !== null && _config$tooltip4 !== void 0 && _config$tooltip4.needUnitTransform || config !== null && config !== void 0 && (_config$tooltip5 = config.tooltip) !== null && _config$tooltip5 !== void 0 && _config$tooltip5.unitTransformTo)) { formatConfig = config.tooltip; } else if (Array.isArray(config.yAxis) && config.yAxis.length >= 2) { // 双轴 formatConfig = config.yAxis; } else if (Array.isArray(config.yAxis)) { var _config$yAxis$, _config$yAxis11; formatConfig = (_config$yAxis$ = config === null || config === void 0 ? void 0 : (_config$yAxis11 = config.yAxis) === null || _config$yAxis11 === void 0 ? void 0 : _config$yAxis11[0]) !== null && _config$yAxis$ !== void 0 ? _config$yAxis$ : {}; } else { var _config$yAxis12; formatConfig = (_config$yAxis12 = config === null || config === void 0 ? void 0 : config.yAxis) !== null && _config$yAxis12 !== void 0 ? _config$yAxis12 : {}; } // const customValueFormatter = customFormatter(formatConfig); chart.on('tooltip:change', function (ev) { // x: 当前鼠标的 x 坐标, // y: 当前鼠标的 y 坐标, // items: 数组对象,当前 tooltip 显示的每条内容 // title: tooltip 标题 var items = ev.data.items; // console.log(ev); // 如果设置了合法的排序关键字,则开始排序 if (typeof sort === 'function') { items.sort(sort); } else if (sortFun[sort]) { items.sort(sortFun[sort]); } // 格式化标题,下面这段是 TooltipCfg.title 不为 function 的逻辑 // if (titleFormatter && !items[0].data.hasCustomTitle) { // // ev.title = titleFormatter(ev.title, ev.items); // // items[0].title = titleFormatter(items[0].title, items); // items[0].data._customTitle_ = titleFormatter(items[0].data.x, items); // items[0].data.hasCustomTitle = true; // } // console.log(ev); // 对每一项格式化 名字 和 值 items.forEach(function (item, index) { // @ts-ignore var raw = getRawData(config, ctx.rawData, item); if (valueFormatter) { item.value = valueFormatter(item.value, raw, index, items); } else { var customValueFormatter = null; if (Array.isArray(formatConfig)) { // 双轴 customValueFormatter = 'y1' in (item === null || item === void 0 ? void 0 : item.data) ? customFormatter(formatConfig[1]) : customFormatter(formatConfig[0]); } else { // 单轴 customValueFormatter = customFormatter(formatConfig); } if (customValueFormatter) { item.value = customValueFormatter(item.value); } } if (item.name.startsWith('undefined-name-')) { item.name = ''; } else if (nameFormatter) { item.name = nameFormatter(item.name, raw, index, items); } }); }); } // 支持鼠标单击锁定 if (lockable) { chart.on('plot:click', function () { var isLocked = chart.isTooltipLocked(); if (isLocked) { chart.unlockTooltip(); } else { chart.lockTooltip(); } }); var reflag = false; // 锁定状态下,tooltip 需要额外多一次渲染,避免切换图例显示状态/改变图表尺寸时显示不正确 var rerenderTooltip = debounce(function () { if (chart.isTooltipLocked()) { if (reflag) { reflag = false; return; } var tooltipController = chart.getController('tooltip'); tooltipController.update(); reflag = true; } }, 100); chart.on('tooltip:show', rerenderTooltip); // 锁定 tooltip 时坐标固定,如果 changeSize,会导致 tooltip 内容发生变化,这里直接解锁 chart.on('beforechangesize', function () { if (chart.isTooltipLocked()) { chart.unlockTooltip().hideTooltip(); } }); } } }