in packages/charts/src/chart_types/metric/renderer/dom/index.tsx [70:237]
function Component({
chartId,
hasTitles,
initialized,
size: { width, height },
a11y,
specs: [spec],
style,
backgroundColor,
onElementClick,
onElementOut,
onElementOver,
locale,
onChartRendered,
}: StateProps & DispatchProps) {
useEffect(() => {
onChartRendered();
});
if (!initialized || !spec || width === 0 || height === 0) {
return null;
}
const { data } = spec;
const totalRows = data.length;
const maxColumns = data.reduce((acc, row) => Math.max(acc, row.length), 0);
const panel = { width: width / maxColumns, height: height / totalRows };
const contrastOptions: ColorContrastOptions = {
lightColor: colorToRgba(style.textLightColor),
darkColor: colorToRgba(style.textDarkColor),
};
const emptyBackgroundRGBA = combineColors(colorToRgba(style.emptyBackground), colorToRgba(backgroundColor));
const emptyBackground = RGBATupleToString(emptyBackgroundRGBA);
const emptyForegroundColor = highContrastColor(emptyBackgroundRGBA, undefined, contrastOptions).color;
const metricsConfigs = data.reduce<{
fittedValueFontSize: number;
configs: Array<
| { key: string; className: string; type: 'left-empty' | 'right-empty' }
| {
key: string;
rowIndex: number;
type: 'metric';
columnIndex: number;
textDimensions: MetricTextDimensions;
datum: MetricDatum;
}
>;
}>(
(acc, columns, rowIndex) => {
acc.configs = acc.configs.concat(
columns.map((datum, columnIndex) => {
const key = `${columnIndex}-${rowIndex}`;
if (!datum) {
// fill with empty panels at the beginning of the row
return {
key,
type: 'left-empty',
className: classNames('echMetric', {
'echMetric--rightBorder': columnIndex < maxColumns - 1,
'echMetric--bottomBorder': rowIndex < totalRows - 1,
'echMetric--topBorder': hasTitles && rowIndex === 0,
}),
};
}
const textDimensions = getMetricTextPartDimensions(datum, panel, style, locale);
const fontSize = getFitValueFontSize(
textDimensions.heightBasedSizes.valueFontSize,
panel.width - textDimensions.progressBarWidth,
textDimensions.visibility.gapHeight,
textDimensions.textParts,
style.minValueFontSize,
datum.valueIcon !== undefined,
);
acc.fittedValueFontSize = Math.min(acc.fittedValueFontSize, fontSize);
return {
type: 'metric',
key,
datum,
columnIndex,
rowIndex,
textDimensions,
};
}),
// adding all missing panels to fill up the row
Array.from({ length: maxColumns - columns.length }, (_, zeroBasedColumnIndex) => {
const columnIndex = zeroBasedColumnIndex + columns.length;
return {
key: `missing-${columnIndex}-${rowIndex}`,
type: 'right-empty',
className: classNames('echMetric', {
'echMetric--bottomBorder': rowIndex < totalRows - 1,
'echMetric--topBorder': hasTitles && rowIndex === 0,
}),
};
}),
);
return acc;
},
{ configs: [], fittedValueFontSize: Number.MAX_SAFE_INTEGER },
);
// update the configs with the globally aligned valueFontSize
const { valueFontSize, valuePartFontSize } =
typeof style.valueFontSize === 'number'
? getFixedFontSizes(style.valueFontSize)
: style.valueFontSize === 'default'
? getSnappedFontSizes(metricsConfigs.fittedValueFontSize, panel.height, style)
: getFittedFontSizes(metricsConfigs.fittedValueFontSize);
metricsConfigs.configs.forEach((config) => {
if (config.type === 'metric') {
config.textDimensions.heightBasedSizes.valueFontSize = valueFontSize;
config.textDimensions.heightBasedSizes.valuePartFontSize = valuePartFontSize;
}
});
return (
// eslint-disable-next-line jsx-a11y/no-redundant-roles
<ul
role="list"
className="echMetricContainer"
aria-labelledby={a11y.labelId}
aria-describedby={a11y.descriptionId}
style={{
gridTemplateColumns: `repeat(${maxColumns}, minmax(0, 1fr)`,
gridTemplateRows: `repeat(${totalRows}, minmax(${style.minHeight}px, 1fr)`,
}}
>
{metricsConfigs.configs.map((config) => {
return config.type !== 'metric' ? (
<li key={config.key} role="presentation">
<div className={config.className} style={{ borderColor: style.border, backgroundColor: emptyBackground }}>
{config.type === 'left-empty' && (
<div className="echMetricEmpty" style={{ borderColor: emptyForegroundColor.keyword }}></div>
)}
</div>
</li>
) : (
<li key={config.key}>
<MetricComponent
chartId={chartId}
hasTitles={hasTitles}
datum={config.datum}
totalRows={totalRows}
totalColumns={maxColumns}
rowIndex={config.rowIndex}
columnIndex={config.columnIndex}
style={style}
backgroundColor={backgroundColor}
contrastOptions={contrastOptions}
onElementClick={onElementClick}
onElementOut={onElementOut}
onElementOver={onElementOver}
textDimensions={config.textDimensions}
/>
</li>
);
})}
</ul>
);
}