function Component()

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>
  );
}