in superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx [80:359]
export default function PopKPI(props: PopKPIProps) {
const {
height,
width,
bigNumber,
prevNumber,
valueDifference,
percentDifferenceFormattedString,
metricName,
metricNameFontSize,
headerFontSize,
subheaderFontSize,
comparisonColorEnabled,
comparisonColorScheme,
percentDifferenceNumber,
currentTimeRangeFilter,
startDateOffset,
shift,
subtitle,
subtitleFontSize,
dashboardTimeRange,
showMetricName,
} = props;
const [comparisonRange, setComparisonRange] = useState<string>('');
useEffect(() => {
if (!currentTimeRangeFilter || (!shift && !startDateOffset)) {
setComparisonRange('');
} else if (!isEmpty(shift) || startDateOffset) {
const promise: any = fetchTimeRange(
dashboardTimeRange ?? (currentTimeRangeFilter as any).comparator,
currentTimeRangeFilter.subject,
);
Promise.resolve(promise).then((res: any) => {
const dates = res?.value?.match(DEFAULT_DATE_PATTERN);
const [parsedStartDate, parsedEndDate] = dates ?? [];
const newShift = getTimeOffset({
timeRangeFilter: {
...currentTimeRangeFilter,
comparator: `${parsedStartDate} : ${parsedEndDate}`,
},
shifts: ensureIsArray(shift),
startDate: startDateOffset || '',
});
fetchTimeRange(
dashboardTimeRange ?? (currentTimeRangeFilter as any).comparator,
currentTimeRangeFilter.subject,
ensureIsArray(newShift),
).then(res => {
const response: string[] = ensureIsArray(res.value);
const firstRange: string = response.flat()[0];
const rangeText = firstRange.split('vs\n');
setComparisonRange(
rangeText.length > 1 ? rangeText[1].trim() : rangeText[0],
);
});
});
}
}, [currentTimeRangeFilter, shift, startDateOffset, dashboardTimeRange]);
const theme = useTheme();
const flexGap = theme.gridUnit * 5;
const wrapperDivStyles = css`
font-family: ${theme.typography.families.sansSerif};
display: flex;
justify-content: center;
align-items: center;
height: ${height}px;
width: ${width}px;
overflow: auto;
`;
const bigValueContainerStyles = css`
font-size: ${headerFontSize || 60}px;
font-weight: ${theme.typography.weights.normal};
text-align: center;
margin-bottom: ${theme.gridUnit * 4}px;
`;
const SubtitleText = styled.div`
${({ theme }) => `
font-family: ${theme.typography.families.sansSerif};
font-weight: ${theme.typography.weights.medium};
text-align: center;
margin-top: -10px;
margin-bottom: ${theme.gridUnit * 4}px;
`}
`;
const getArrowIndicatorColor = () => {
if (!comparisonColorEnabled || percentDifferenceNumber === 0) {
return theme.colors.grayscale.base;
}
if (percentDifferenceNumber > 0) {
// Positive difference
return comparisonColorScheme === ColorSchemeEnum.Green
? theme.colors.success.base
: theme.colors.error.base;
}
// Negative difference
return comparisonColorScheme === ColorSchemeEnum.Red
? theme.colors.success.base
: theme.colors.error.base;
};
const arrowIndicatorStyle = css`
color: ${getArrowIndicatorColor()};
margin-left: ${theme.gridUnit}px;
`;
const defaultBackgroundColor = theme.colors.grayscale.light4;
const defaultTextColor = theme.colors.grayscale.base;
const { backgroundColor, textColor } = useMemo(() => {
let bgColor = defaultBackgroundColor;
let txtColor = defaultTextColor;
if (comparisonColorEnabled && percentDifferenceNumber !== 0) {
const useSuccess =
(percentDifferenceNumber > 0 &&
comparisonColorScheme === ColorSchemeEnum.Green) ||
(percentDifferenceNumber < 0 &&
comparisonColorScheme === ColorSchemeEnum.Red);
// Set background and text colors based on the conditions
bgColor = useSuccess
? theme.colors.success.light2
: theme.colors.error.light2;
txtColor = useSuccess
? theme.colors.success.base
: theme.colors.error.base;
}
return {
backgroundColor: bgColor,
textColor: txtColor,
};
}, [
theme,
comparisonColorScheme,
comparisonColorEnabled,
percentDifferenceNumber,
]);
const SYMBOLS_WITH_VALUES = useMemo(
() =>
[
{
defaultSymbol: '#',
value: prevNumber,
tooltipText: t('Data for %s', comparisonRange || 'previous range'),
columnKey: 'Previous value',
},
{
defaultSymbol: '△',
value: valueDifference,
tooltipText: t('Value difference between the time periods'),
columnKey: 'Delta',
},
{
defaultSymbol: '%',
value: percentDifferenceFormattedString,
tooltipText: t('Percentage difference between the time periods'),
columnKey: 'Percent change',
},
].map(item => {
const config = props.columnConfig?.[item.columnKey];
return {
...item,
symbol: config?.displayTypeIcon === false ? '' : item.defaultSymbol,
label: config?.customColumnName || item.columnKey,
};
}),
[
comparisonRange,
prevNumber,
valueDifference,
percentDifferenceFormattedString,
props.columnConfig,
],
);
const visibleSymbols = SYMBOLS_WITH_VALUES.filter(
symbol => props.columnConfig?.[symbol.columnKey]?.visible !== false,
);
const { isOverflowing, symbolContainerRef, wrapperRef } =
useOverflowDetection(flexGap);
return (
<div css={wrapperDivStyles} ref={wrapperRef}>
<NumbersContainer
css={
isOverflowing &&
css`
width: fit-content;
margin: auto;
align-items: flex-start;
overflow: auto;
`
}
>
{showMetricName && metricName && (
<MetricNameText metricNameFontSize={metricNameFontSize}>
{metricName}
</MetricNameText>
)}
<div css={bigValueContainerStyles}>
{bigNumber}
{percentDifferenceNumber !== 0 && (
<span css={arrowIndicatorStyle}>
{percentDifferenceNumber > 0 ? '↑' : '↓'}
</span>
)}
</div>
{subtitle && (
<SubtitleText
style={{
fontSize: `${subtitleFontSize * height * 0.4}px`,
}}
>
{subtitle}
</SubtitleText>
)}
{visibleSymbols.length > 0 && (
<div
css={[
css`
display: flex;
justify-content: space-around;
gap: ${flexGap}px;
min-width: 0;
flex-shrink: 1;
`,
isOverflowing
? css`
flex-direction: column;
align-items: flex-start;
width: fit-content;
`
: css`
align-items: center;
width: 100%;
`,
]}
ref={symbolContainerRef}
>
{visibleSymbols.map((symbol_with_value, index) => (
<ComparisonValue
key={`comparison-symbol-${symbol_with_value.columnKey}`}
subheaderFontSize={subheaderFontSize}
>
<Tooltip
id="tooltip"
placement="top"
title={symbol_with_value.tooltipText}
>
{symbol_with_value.symbol && (
<SymbolWrapper
backgroundColor={
index > 0 ? backgroundColor : defaultBackgroundColor
}
textColor={index > 0 ? textColor : defaultTextColor}
>
{symbol_with_value.symbol}
</SymbolWrapper>
)}
{symbol_with_value.value}{' '}
{props.columnConfig?.[symbol_with_value.columnKey]
?.customColumnName || ''}
</Tooltip>
</ComparisonValue>
))}
</div>
)}
</NumbersContainer>
</div>
);
}