import { get } from 'lodash'; import moment from 'moment-timezone'; import theme, { getTheme } from '../../utils/theme'; const CURSOR_TO_TOOLTIP_OFFSET = 32; const TOOLTIP_WIDTH = 256; const getOrCreateTooltip = (chart, updatedTheme, tooltipMinWidth) => { let tooltipEl = chart.canvas.parentNode.querySelector('div'); if (!tooltipEl) { tooltipEl = document.createElement('div'); tooltipEl.style.background = updatedTheme.colors.greys.lightest; tooltipEl.style.borderRadius = '4px'; tooltipEl.style.border = `1px solid ${updatedTheme.colors.greys.light}`; tooltipEl.style.color = updatedTheme.colors.greys.darkest; tooltipEl.style.pointerEvents = 'none'; tooltipEl.style.position = 'absolute'; tooltipEl.style.transition = 'all .1s ease'; tooltipEl.style.font = updatedTheme.fonts.textBigMicro; tooltipEl.style.padding = '16px'; tooltipEl.style.minWidth = tooltipMinWidth || '150px'; const table = document.createElement('table'); table.style.margin = '0px'; tooltipEl.appendChild(table); chart.canvas.parentNode.appendChild(tooltipEl); } return tooltipEl; }; /** * Depending on the graph period, will return the date at which the tooltip starts to appear on the left * * @param startDate * @param endDate * @returns a date */ const resolveTooltipLeftOffsetPeriod = (startDate, endDate, timezone) => { const diffInMonths = moment .tz(endDate, timezone) .diff(moment(startDate), 'month'); if (diffInMonths > 6) { return moment.tz(endDate, timezone).subtract(3, 'month'); } if (diffInMonths > 3) { return moment.tz(endDate, timezone).subtract(2, 'month'); } const diffInWeeks = moment .tz(endDate, timezone) .diff(moment.tz(startDate, timezone), 'week'); if (diffInWeeks > 7) { return moment.tz(endDate, timezone).subtract(3, 'week'); } if (diffInWeeks >= 3 && diffInWeeks <= 7) { return moment.tz(endDate, timezone).subtract(2, 'week'); } // By default for a short period the points over the last 3 days will have their tooltip on the left. return moment.tz(endDate, timezone).subtract(3, 'day'); }; export const externalTooltipHandler = ( // Not succeed to fix this type // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/explicit-module-boundary-types context: any, startDate: string, endDate: string, timezone: string, renderTooltipHeader?: Function, renderTooltipBody?: Function, tooltipTitlePropertyKey?: string, xAxisPropertyKey?: string, tooltipMinWidth?: string ): void => { if (!renderTooltipHeader || !renderTooltipBody) { return; } const updatedTheme = getTheme(theme); // Tooltip Element const { chart, tooltip } = context; const data = get(tooltip, 'dataPoints[0].raw'); const tooltipTitle = get(data, `${tooltipTitlePropertyKey}`, null); const tooltipEl = getOrCreateTooltip(chart, updatedTheme, tooltipMinWidth); if (tooltip.opacity === 0) { tooltipEl.style.opacity = 0; return; } if (!tooltipTitle) { tooltipEl.remove(); return; } if (tooltipTitlePropertyKey) { renderTooltipHeader(tooltipEl, data[tooltipTitlePropertyKey]); } renderTooltipBody(tooltipEl, data); const { offsetLeft: positionX, offsetTop: positionY } = chart.canvas; // Display, position, and set styles for font tooltipEl.style.opacity = 1; tooltipEl.style.font = tooltip.options.bodyFont.string; tooltipEl.style.top = `${positionY + tooltip.caretY - 40}px`; // Use to position tooltip on right if we are at the end of the chart if ( xAxisPropertyKey && moment .tz(data[xAxisPropertyKey], timezone) .isSameOrAfter( resolveTooltipLeftOffsetPeriod(startDate, endDate, timezone) ) ) { tooltipEl.style.left = `${ positionX + tooltip.caretX - (TOOLTIP_WIDTH + CURSOR_TO_TOOLTIP_OFFSET) }px`; return; } // tooltipEl.style.left = `${positionX + tooltip.caretX + 94}px`; tooltipEl.style.left = `${ positionX + tooltip.caretX + CURSOR_TO_TOOLTIP_OFFSET }px`; }; export default { externalTooltipHandler, };