mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-17 06:31:32 +08:00
242 lines
6.0 KiB
TypeScript
242 lines
6.0 KiB
TypeScript
import { getToolTipValue } from 'components/Graph/yAxisConfig';
|
|
import { themeColors } from 'constants/theme';
|
|
import dayjs from 'dayjs';
|
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
import getLabelName from 'lib/getLabelName';
|
|
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
|
|
|
import { placement } from '../placement';
|
|
import { generateColor } from '../utils/generateColor';
|
|
|
|
dayjs.extend(customParseFormat);
|
|
|
|
interface UplotTooltipDataProps {
|
|
show: boolean;
|
|
color: string;
|
|
label: string;
|
|
focus: boolean;
|
|
value: number;
|
|
tooltipValue: string;
|
|
textContent: string;
|
|
queryName: string;
|
|
}
|
|
|
|
const generateTooltipContent = (
|
|
seriesList: any[],
|
|
data: any[],
|
|
idx: number,
|
|
yAxisUnit?: string,
|
|
series?: uPlot.Options['series'],
|
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
): HTMLElement => {
|
|
const container = document.createElement('div');
|
|
container.classList.add('tooltip-container');
|
|
const overlay = document.getElementById('overlay');
|
|
let tooltipCount = 0;
|
|
|
|
let tooltipTitle = '';
|
|
const formattedData: Record<string, UplotTooltipDataProps> = {};
|
|
const duplicatedLegendLabels: Record<string, true> = {};
|
|
|
|
function sortTooltipContentBasedOnValue(
|
|
tooltipDataObj: Record<string, UplotTooltipDataProps>,
|
|
): Record<string, UplotTooltipDataProps> {
|
|
const entries = Object.entries(tooltipDataObj);
|
|
entries.sort((a, b) => b[1].value - a[1].value);
|
|
return Object.fromEntries(entries);
|
|
}
|
|
|
|
if (Array.isArray(series) && series.length > 0) {
|
|
series.forEach((item, index) => {
|
|
if (index === 0) {
|
|
tooltipTitle = dayjs(data[0][idx] * 1000).format('MMM DD YYYY HH:mm:ss');
|
|
} else if (item.show) {
|
|
const { metric = {}, queryName = '', legend = '' } =
|
|
seriesList[index - 1] || {};
|
|
|
|
const value = data[index][idx];
|
|
const label = getLabelName(metric, queryName || '', legend || '');
|
|
|
|
const color = generateColor(label, themeColors.chartcolors);
|
|
|
|
let tooltipItemLabel = label;
|
|
|
|
if (Number.isFinite(value)) {
|
|
const tooltipValue = getToolTipValue(value, yAxisUnit);
|
|
if (
|
|
duplicatedLegendLabels[label] ||
|
|
Object.prototype.hasOwnProperty.call(formattedData, label)
|
|
) {
|
|
duplicatedLegendLabels[label] = true;
|
|
const tempDataObj = formattedData[label];
|
|
|
|
if (tempDataObj) {
|
|
const newLabel = `${tempDataObj.queryName}: ${tempDataObj.label}`;
|
|
|
|
tempDataObj.textContent = `${newLabel} : ${tempDataObj.tooltipValue}`;
|
|
|
|
formattedData[newLabel] = tempDataObj;
|
|
|
|
delete formattedData[label];
|
|
}
|
|
|
|
tooltipItemLabel = `${queryName}: ${label}`;
|
|
}
|
|
|
|
const dataObj = {
|
|
show: item.show || false,
|
|
color,
|
|
label,
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
focus: item?._focus || false,
|
|
value,
|
|
tooltipValue,
|
|
queryName,
|
|
textContent: `${tooltipItemLabel} : ${tooltipValue}`,
|
|
};
|
|
|
|
tooltipCount += 1;
|
|
|
|
formattedData[tooltipItemLabel] = dataObj;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Show tooltip only if atleast only series has a value at the hovered timestamp
|
|
if (tooltipCount <= 0) {
|
|
if (overlay && overlay.style.display === 'block') {
|
|
overlay.style.display = 'none';
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
const sortedData: Record<
|
|
string,
|
|
UplotTooltipDataProps
|
|
> = sortTooltipContentBasedOnValue(formattedData);
|
|
|
|
const div = document.createElement('div');
|
|
div.classList.add('tooltip-content-row');
|
|
div.textContent = tooltipTitle;
|
|
div.classList.add('tooltip-content-header');
|
|
container.appendChild(div);
|
|
|
|
const sortedKeys = Object.keys(sortedData);
|
|
|
|
if (Array.isArray(sortedKeys) && sortedKeys.length > 0) {
|
|
sortedKeys.forEach((key) => {
|
|
if (sortedData[key]) {
|
|
const { textContent, color, focus } = sortedData[key];
|
|
const div = document.createElement('div');
|
|
div.classList.add('tooltip-content-row');
|
|
div.classList.add('tooltip-content');
|
|
const squareBox = document.createElement('div');
|
|
squareBox.classList.add('pointSquare');
|
|
|
|
squareBox.style.borderColor = color;
|
|
|
|
const text = document.createElement('div');
|
|
text.classList.add('tooltip-data-point');
|
|
|
|
text.textContent = textContent;
|
|
text.style.color = color;
|
|
|
|
if (focus) {
|
|
text.classList.add('focus');
|
|
} else {
|
|
text.classList.remove('focus');
|
|
}
|
|
|
|
div.appendChild(squareBox);
|
|
div.appendChild(text);
|
|
|
|
container.appendChild(div);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (overlay && overlay.style.display === 'none') {
|
|
overlay.style.display = 'block';
|
|
}
|
|
|
|
return container;
|
|
};
|
|
|
|
const tooltipPlugin = (
|
|
apiResponse: MetricRangePayloadProps | undefined,
|
|
yAxisUnit?: string,
|
|
): any => {
|
|
let over: HTMLElement;
|
|
let bound: HTMLElement;
|
|
let bLeft: any;
|
|
let bTop: any;
|
|
|
|
const syncBounds = (): void => {
|
|
const bbox = over.getBoundingClientRect();
|
|
bLeft = bbox.left;
|
|
bTop = bbox.top;
|
|
};
|
|
|
|
let overlay = document.getElementById('overlay');
|
|
|
|
if (!overlay) {
|
|
overlay = document.createElement('div');
|
|
overlay.id = 'overlay';
|
|
overlay.style.display = 'none';
|
|
overlay.style.position = 'absolute';
|
|
document.body.appendChild(overlay);
|
|
}
|
|
|
|
const apiResult = apiResponse?.data?.result || [];
|
|
|
|
return {
|
|
hooks: {
|
|
init: (u: any): void => {
|
|
over = u?.over;
|
|
bound = over;
|
|
over.onmouseenter = (): void => {
|
|
if (overlay) {
|
|
overlay.style.display = 'block';
|
|
}
|
|
};
|
|
over.onmouseleave = (): void => {
|
|
if (overlay) {
|
|
overlay.style.display = 'none';
|
|
}
|
|
};
|
|
},
|
|
setSize: (): void => {
|
|
syncBounds();
|
|
},
|
|
setCursor: (u: {
|
|
cursor: { left: any; top: any; idx: any };
|
|
data: any[];
|
|
series: uPlot.Options['series'];
|
|
}): void => {
|
|
if (overlay) {
|
|
overlay.textContent = '';
|
|
const { left, top, idx } = u.cursor;
|
|
|
|
if (Number.isInteger(idx)) {
|
|
const anchor = { left: left + bLeft, top: top + bTop };
|
|
const content = generateTooltipContent(
|
|
apiResult,
|
|
u.data,
|
|
idx,
|
|
yAxisUnit,
|
|
u.series,
|
|
);
|
|
overlay.appendChild(content);
|
|
placement(overlay, anchor, 'right', 'start', { bound });
|
|
}
|
|
}
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
export default tooltipPlugin;
|