mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
274 lines
6.6 KiB
TypeScript
274 lines
6.6 KiB
TypeScript
import type {Theme} from '@emotion/react';
|
|
import Color from 'color';
|
|
|
|
import type {DurationDisplay} from 'sentry/components/performance/waterfall/types';
|
|
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
|
|
import {space} from 'sentry/styles/space';
|
|
|
|
import type {SpanBarType} from './constants';
|
|
import {getSpanBarColours} from './constants';
|
|
|
|
export const getBackgroundColor = ({
|
|
showStriping,
|
|
showDetail,
|
|
theme,
|
|
}: {
|
|
theme: Theme;
|
|
showDetail?: boolean;
|
|
showStriping?: boolean;
|
|
}) => {
|
|
if (showDetail) {
|
|
return theme.textColor;
|
|
}
|
|
|
|
if (showStriping) {
|
|
return theme.backgroundSecondary;
|
|
}
|
|
|
|
return theme.background;
|
|
};
|
|
|
|
export function getHatchPattern(spanBarType: SpanBarType | undefined, theme: Theme) {
|
|
if (spanBarType) {
|
|
const {primary, alternate} = getSpanBarColours(spanBarType, theme);
|
|
|
|
return `
|
|
background-image: linear-gradient(135deg,
|
|
${alternate},
|
|
${alternate} 2.5px,
|
|
${primary} 2.5px,
|
|
${primary} 5px,
|
|
${alternate} 6px,
|
|
${alternate} 8px,
|
|
${primary} 8px,
|
|
${primary} 11px,
|
|
${alternate} 11px,
|
|
${alternate} 14px,
|
|
${primary} 14px,
|
|
${primary} 16.5px,
|
|
${alternate} 16.5px,
|
|
${alternate} 19px,
|
|
${primary} 20px
|
|
);
|
|
background-size: 16px 16px;
|
|
`;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export const getDurationPillAlignment = ({
|
|
durationDisplay,
|
|
}: {
|
|
durationDisplay: DurationDisplay;
|
|
theme: Theme;
|
|
spanBarType?: SpanBarType;
|
|
}) => {
|
|
switch (durationDisplay) {
|
|
case 'left':
|
|
return `right: calc(100% + ${space(0.5)});`;
|
|
case 'right':
|
|
return `left: calc(100% + ${space(0.75)});`;
|
|
default:
|
|
return `
|
|
right: ${space(0.75)};
|
|
`;
|
|
}
|
|
};
|
|
|
|
export const getDurationPillColours = ({
|
|
durationDisplay,
|
|
theme,
|
|
showDetail,
|
|
spanBarType,
|
|
}: {
|
|
durationDisplay: DurationDisplay;
|
|
showDetail: boolean;
|
|
theme: Theme;
|
|
spanBarType?: SpanBarType;
|
|
}) => {
|
|
if (durationDisplay === 'inset') {
|
|
const {alternate, insetTextColour} = getSpanBarColours(spanBarType, theme);
|
|
return `background: ${alternate}; color: ${insetTextColour};`;
|
|
}
|
|
|
|
return `color: ${showDetail ? theme.gray200 : theme.gray300};`;
|
|
};
|
|
|
|
export const getToggleTheme = ({
|
|
theme,
|
|
isExpanded,
|
|
disabled,
|
|
errored,
|
|
isSpanGroupToggler,
|
|
spanBarType,
|
|
}: {
|
|
disabled: boolean;
|
|
errored: boolean;
|
|
isExpanded: boolean;
|
|
theme: Theme;
|
|
isSpanGroupToggler?: boolean;
|
|
spanBarType?: SpanBarType;
|
|
}) => {
|
|
if (spanBarType) {
|
|
const {primary} = getSpanBarColours(spanBarType, theme);
|
|
return `
|
|
background: ${primary};
|
|
border: 2px solid ${theme.button.default.border};
|
|
color: ${theme.button.primary.color};
|
|
cursor: pointer;
|
|
`;
|
|
}
|
|
|
|
const buttonTheme = isExpanded ? theme.button.default : theme.button.primary;
|
|
const errorTheme = theme.button.danger;
|
|
|
|
const background = errored
|
|
? isExpanded
|
|
? buttonTheme.background
|
|
: errorTheme.background
|
|
: buttonTheme.background;
|
|
const border = errored ? errorTheme.background : buttonTheme.border;
|
|
const color = errored
|
|
? isExpanded
|
|
? errorTheme.background
|
|
: buttonTheme.color
|
|
: buttonTheme.color;
|
|
|
|
if (isSpanGroupToggler) {
|
|
return `
|
|
background: ${theme.blue300};
|
|
border: 2px solid ${theme.button.default.border};
|
|
color: ${color};
|
|
cursor: pointer;
|
|
`;
|
|
}
|
|
|
|
if (disabled) {
|
|
return `
|
|
background: ${background};
|
|
border: 2px solid ${border};
|
|
color: ${color};
|
|
cursor: default;
|
|
`;
|
|
}
|
|
|
|
return `
|
|
background: ${background};
|
|
border: 2px solid ${border};
|
|
color: ${color};
|
|
`;
|
|
};
|
|
|
|
export const getDurationDisplay = ({
|
|
width,
|
|
left,
|
|
}: {
|
|
left: undefined | number;
|
|
width: undefined | number;
|
|
}): DurationDisplay => {
|
|
const spaceNeeded = 0.3;
|
|
|
|
if (left === undefined || width === undefined) {
|
|
return 'inset';
|
|
}
|
|
if (left + width < 1 - spaceNeeded) {
|
|
return 'right';
|
|
}
|
|
if (left > spaceNeeded) {
|
|
return 'left';
|
|
}
|
|
return 'inset';
|
|
};
|
|
|
|
export const getHumanDuration = (duration: number): string => {
|
|
// note: duration is assumed to be in seconds
|
|
const durationMs = duration * 1000;
|
|
return `${durationMs.toLocaleString(undefined, {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
})}ms`;
|
|
};
|
|
|
|
type Rect = {
|
|
height: number;
|
|
width: number;
|
|
// x and y are left/top coords respectively
|
|
x: number;
|
|
y: number;
|
|
};
|
|
|
|
// get position of element relative to top/left of document
|
|
export const getOffsetOfElement = (element: Element) => {
|
|
// left and top are relative to viewport
|
|
const {left, top} = element.getBoundingClientRect();
|
|
|
|
// get values that the document is currently scrolled by
|
|
const scrollLeft = window.pageXOffset;
|
|
const scrollTop = window.pageYOffset;
|
|
|
|
return {x: left + scrollLeft, y: top + scrollTop};
|
|
};
|
|
|
|
export const rectOfContent = (element: Element): Rect => {
|
|
const {x, y} = getOffsetOfElement(element);
|
|
|
|
// offsets for the border and any scrollbars (clientLeft and clientTop),
|
|
// and if the element was scrolled (scrollLeft and scrollTop)
|
|
//
|
|
// NOTE: clientLeft and clientTop does not account for any margins nor padding
|
|
const contentOffsetLeft = element.clientLeft - element.scrollLeft;
|
|
const contentOffsetTop = element.clientTop - element.scrollTop;
|
|
|
|
return {
|
|
x: x + contentOffsetLeft,
|
|
y: y + contentOffsetTop,
|
|
width: element.scrollWidth,
|
|
height: element.scrollHeight,
|
|
};
|
|
};
|
|
|
|
const getLetterIndex = (letter: string): number => {
|
|
const index = 'abcdefghijklmnopqrstuvwxyz'.indexOf(letter) || 0;
|
|
return index === -1 ? 0 : index;
|
|
};
|
|
|
|
const colorsAsArray = Object.keys(CHART_PALETTE).map(key => CHART_PALETTE[17][key]);
|
|
|
|
export const barColors = {
|
|
default: CHART_PALETTE[17][4],
|
|
transaction: CHART_PALETTE[17][8],
|
|
http: CHART_PALETTE[17][10],
|
|
db: CHART_PALETTE[17][17],
|
|
};
|
|
|
|
export const pickBarColor = (input: string | undefined): string => {
|
|
// We pick the color for span bars using the first three letters of the op name.
|
|
// That way colors stay consistent between transactions.
|
|
|
|
if (!input || input.length < 3) {
|
|
return CHART_PALETTE[17][4];
|
|
}
|
|
|
|
if (barColors[input]) {
|
|
return barColors[input];
|
|
}
|
|
|
|
const letterIndex1 = getLetterIndex(input[0]);
|
|
const letterIndex2 = getLetterIndex(input[1]);
|
|
const letterIndex3 = getLetterIndex(input[2]);
|
|
const letterIndex4 = getLetterIndex(input[3]);
|
|
|
|
return colorsAsArray[
|
|
(letterIndex1 + letterIndex2 + letterIndex3 + letterIndex4) % colorsAsArray.length
|
|
];
|
|
};
|
|
|
|
export const lightenBarColor = (
|
|
input: string | undefined,
|
|
lightenRatio: number
|
|
): string => {
|
|
const barColor = pickBarColor(input);
|
|
return Color(barColor).lighten(lightenRatio).string();
|
|
};
|