in src/platform/plugins/shared/chart_expressions/expression_xy/public/components/xy_chart.tsx [197:1065]
export function XYChart({
args,
data,
formatFactory,
timeZone,
chartsThemeService,
chartsActiveCursorService,
paletteService,
minInterval,
onClickValue,
onClickMultiValue,
onCreateAlertRule,
layerCellValueActions,
onSelectRange,
setChartSize,
interactive = true,
syncColors,
syncTooltips,
syncCursor,
useLegacyTimeAxis,
renderComplete,
uiState,
timeFormat,
overrides,
}: XYChartRenderProps) {
const {
legend,
layers,
fittingFunction,
endValue,
emphasizeFitting,
valueLabels,
hideEndzones,
yAxisConfigs,
xAxisConfig,
splitColumnAccessor,
splitRowAccessor,
singleTable,
annotations,
} = args;
const chartRef = useRef<Chart>(null);
const chartBaseTheme = chartsThemeService.useChartsBaseTheme();
const darkMode = chartsThemeService.useDarkMode();
const palettes = useKbnPalettes();
const appFixedViewport = useAppFixedViewport();
const filteredLayers = getFilteredLayers(layers);
const layersById = filteredLayers.reduce<Record<string, CommonXYLayerConfig>>(
(hashMap, layer) => ({ ...hashMap, [layer.layerId]: layer }),
{}
);
const chartHasMoreThanOneSeries =
filteredLayers.length > 1 ||
filteredLayers.some((layer) => layer.accessors.length > 1) ||
filteredLayers.some(
(layer) => isDataLayer(layer) && layer.splitAccessors && layer.splitAccessors.length
);
const getShowLegendDefault = useCallback(() => {
const legendStateDefault =
legend.isVisible && !legend.showSingleSeries ? chartHasMoreThanOneSeries : legend.isVisible;
return uiState?.get('vis.legendOpen', legendStateDefault) ?? legendStateDefault;
}, [chartHasMoreThanOneSeries, legend.isVisible, legend.showSingleSeries, uiState]);
const [showLegend, setShowLegend] = useState<boolean>(() => getShowLegendDefault());
useEffect(() => {
const legendShow = getShowLegendDefault();
setShowLegend(legendShow);
}, [getShowLegendDefault]);
const toggleLegend = useCallback(() => {
setShowLegend((value) => {
const newValue = !value;
uiState?.set?.('vis.legendOpen', newValue);
return newValue;
});
}, [uiState]);
const setColor = useCallback(
(newColor: string | null, seriesLabel: string | number) => {
const colors = uiState?.get('vis.colors') || {};
if (colors[seriesLabel] === newColor || !newColor) {
delete colors[seriesLabel];
} else {
colors[seriesLabel] = newColor;
}
uiState?.setSilent('vis.colors', null);
uiState?.set('vis.colors', colors);
uiState?.emit('reload');
uiState?.emit('colorChanged');
},
[uiState]
);
// Exclude the reference layers from the cursor update
const cursorSyncLayers = filteredLayers.filter(isDataLayer);
const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, {
datatables: cursorSyncLayers.map(({ table }) => table),
});
const onRenderChange = useCallback(
(isRendered: boolean = true) => {
if (isRendered) {
renderComplete();
}
},
[renderComplete]
);
const dataLayers: CommonXYDataLayerConfig[] = filteredLayers.filter(isDataLayer);
const isTimeViz = isTimeChart(dataLayers);
useEffect(() => {
const chartSizeSpec: ChartSizeSpec =
isTimeViz && !isHorizontalChart(dataLayers)
? {
aspectRatio: {
x: 16,
y: 9,
},
minDimensions: {
y: { value: 300, unit: 'pixels' },
x: { value: 100, unit: 'percentage' },
},
}
: {
maxDimensions: {
x: { value: 100, unit: 'percentage' },
y: { value: 100, unit: 'percentage' },
},
};
setChartSize(chartSizeSpec);
}, [dataLayers, isTimeViz, setChartSize]);
const formattedDatatables = useMemo(
() =>
getFormattedTablesByLayers(dataLayers, formatFactory, splitColumnAccessor, splitRowAccessor),
[dataLayers, formatFactory, splitColumnAccessor, splitRowAccessor]
);
const fieldFormats = useMemo(
() => getLayersFormats(dataLayers, { splitColumnAccessor, splitRowAccessor }, formatFactory),
[dataLayers, splitColumnAccessor, splitRowAccessor, formatFactory]
);
const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]);
if (dataLayers.length === 0) {
return (
<EmptyPlaceholder icon={icon} renderComplete={onRenderChange} css={xyChartEmptyStyles} />
);
}
// use formatting hint of first x axis column to format ticks
const xAxisColumn = dataLayers[0].xAccessor
? getColumnByAccessor(dataLayers[0].xAccessor, dataLayers[0]?.table.columns)
: undefined;
const xAxisFormatter = formatFactory(
xAxisColumn?.id ? fieldFormats[dataLayers[0].layerId].xAccessors[xAxisColumn?.id] : undefined
);
// This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers
const safeXAccessorLabelRenderer = (value: unknown): string =>
xAxisColumn && formattedDatatables[dataLayers[0]?.layerId]?.formattedColumns[xAxisColumn.id]
? String(value)
: String(xAxisFormatter.convert(value));
const shouldRotate = isHorizontalChart(dataLayers);
const yAxesConfiguration = getAxesConfiguration(
dataLayers,
shouldRotate,
formatFactory,
fieldFormats,
yAxisConfigs
);
const axesConfiguration = getAxesConfiguration(
dataLayers,
shouldRotate,
formatFactory,
fieldFormats,
[...(yAxisConfigs ?? []), ...(xAxisConfig ? [xAxisConfig] : [])]
);
const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name) || undefined;
const yAxesMap = {
left: yAxesConfiguration.find(
({ position }) => position === getAxisPosition(Position.Left, shouldRotate)
),
right: yAxesConfiguration.find(
({ position }) => position === getAxisPosition(Position.Right, shouldRotate)
),
};
const titles = getLayersTitles(
dataLayers,
{ splitColumnAccessor, splitRowAccessor },
{ xTitle },
yAxesConfiguration
);
const filteredBarLayers = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.BAR);
const chartHasMoreThanOneBarSeries =
filteredBarLayers.length > 1 ||
filteredBarLayers.some((layer) => layer.accessors.length > 1) ||
filteredBarLayers.some(
(layer) => isDataLayer(layer) && layer.splitAccessors && layer.splitAccessors.length
);
const defaultXScaleType = isTimeViz ? XScaleTypes.TIME : XScaleTypes.ORDINAL;
const isHistogramViz = dataLayers.every((l) => l.isHistogram);
const isEsqlMode = dataLayers.some((l) => l.table?.meta?.type === ESQL_TABLE_TYPE);
const hasBars = dataLayers.some((l) => l.seriesType === SeriesTypes.BAR);
const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain(
data.datatableUtilities,
dataLayers,
minInterval,
isTimeViz,
isHistogramViz,
hasBars,
timeZone,
xAxisConfig?.extent
);
const axisTitlesVisibilitySettings = {
yLeft: yAxesMap?.left?.showTitle ?? true,
yRight: yAxesMap?.right?.showTitle ?? true,
};
const tickLabelsVisibilitySettings = {
yLeft: yAxesMap?.left?.showLabels ?? true,
yRight: yAxesMap?.right?.showLabels ?? true,
};
const getYAxesTitles = (axisSeries: Series[]) => {
return axisSeries
.map(({ layer, accessor }) => titles?.[layer]?.yTitles?.[accessor])
.find((name) => Boolean(name));
};
const referenceLineLayers = getReferenceLayers(layers);
const [rangeAnnotations, lineAnnotations] = isTimeViz
? partition(annotations?.datatable.rows, isRangeAnnotation)
: [[], []];
const groupedLineAnnotations = getAnnotationsGroupedByInterval(
lineAnnotations as PointEventAnnotationRow[],
annotations?.layers.flatMap((l) => l.annotations),
annotations?.datatable.columns,
formatFactory,
timeFormat
);
const visualConfigs = [
...referenceLineLayers
.flatMap<ReferenceLineDecorationConfig | ExtendedReferenceLineDecorationConfig | undefined>(
({ decorations }) => decorations
)
.map((config) => ({
...config,
position: config
? getAxisGroupForReferenceLine(axesConfiguration, config, shouldRotate)?.position ??
Position.Left
: Position.Bottom,
})),
...groupedLineAnnotations,
].filter(nonNullable);
const shouldHideDetails =
annotations?.layers && annotations.layers.length > 0
? annotations?.layers[0].simpleView
: false;
const linesPaddings = !shouldHideDetails
? getLinesCausedPaddings(visualConfigs, yAxesMap, shouldRotate)
: {};
const getYAxesStyle = (axis: AxisConfiguration) => {
const tickVisible = axis.showLabels;
const position = getOriginalAxisPosition(axis.position, shouldRotate);
const style = {
tickLabel: {
fill: axis.labelColor,
visible: tickVisible,
rotation: axis.labelsOrientation,
padding:
linesPaddings[position] != null
? {
inner: linesPaddings[position],
}
: undefined,
},
axisTitle: {
visible: axis.showTitle,
// if labels are not visible add the padding to the title
padding:
!tickVisible && linesPaddings[position] != null
? {
inner: linesPaddings[position],
}
: undefined,
},
};
return style;
};
const getYAxisDomain = (axis: GroupsConfiguration[number]) => {
const extent: AxisExtentConfigResult = axis.extent || {
type: 'axisExtentConfig',
mode: 'full',
};
const hasBarOrArea = Boolean(
axis.series.some((series) => {
const layer = layersById[series.layer];
if (!(layer && isDataLayer(layer))) {
return false;
}
return layer.seriesType === SeriesTypes.BAR || layer.seriesType === SeriesTypes.AREA;
})
);
const fit = Boolean(
(!hasBarOrArea || axis.extent?.enforce) && extent.mode === AxisExtentModes.DATA_BOUNDS
);
const padding = axis.boundsMargin || undefined;
let min: number = NaN;
let max: number = NaN;
if (extent.mode === 'custom') {
const validExtent = validateExtent(hasBarOrArea, extent);
if (validExtent || extent.enforce) {
min = extent.lowerBound ?? NaN;
max = extent.upperBound ?? NaN;
}
}
return {
fit,
min,
max,
padding,
includeDataFromIds: referenceLineLayers
.flatMap(
(l) => l.decorations?.map((decoration) => ({ layerId: l.layerId, decoration })) || []
)
.filter(({ decoration }) => {
if (decoration.axisId) {
return axis.groupId.includes(decoration.axisId);
}
return (
axis.position === getAxisPosition(decoration.position ?? Position.Left, shouldRotate)
);
})
.map(({ layerId, decoration }) =>
isReferenceLineDecorationConfig(decoration)
? `${layerId}-${decoration.value}-${decoration.fill !== 'none' ? 'rect' : 'line'}`
: `${layerId}-${decoration.forAccessor}-${decoration.fill !== 'none' ? 'rect' : 'line'}`
),
};
};
const shouldShowValueLabels = !uiState || valueLabels !== ValueLabelModes.HIDE;
const valueLabelsStyling =
shouldShowValueLabels &&
valueLabels !== ValueLabelModes.HIDE &&
getValueLabelsStyling(shouldRotate);
const clickHandler: ElementClickListener = ([elementEvent]) => {
// this cast is safe because we are rendering a cartesian chart
const [xyGeometry, xySeries] = elementEvent as XYChartElementEvent;
const layer = dataLayers.find((l) =>
xySeries.seriesKeys.some((key: string | number) =>
l.accessors.some(
(accessor) => getAccessorByDimension(accessor, l.table.columns) === key.toString()
)
)
);
if (!layer) {
return;
}
const { table } = layer;
const xSeriesPoint = getXSeriesPoint(
layer,
xyGeometry.x,
fieldFormats,
formattedDatatables,
xAxisFormatter,
formatFactory
);
const splitPoints: FilterEvent['data']['data'] = [];
if (xySeries.seriesKeys.length > 1) {
xySeries.splitAccessors.forEach((value, accessor) => {
const point = createSplitPoint(
accessor,
value,
formattedDatatables[layer.layerId].table.rows,
table
);
if (point) {
splitPoints.push(point);
}
});
}
if (xySeries.smHorizontalAccessorValue && splitColumnAccessor) {
const accessor = getAccessorByDimension(splitColumnAccessor, table.columns);
const point = createSplitPoint(
accessor,
xySeries.smHorizontalAccessorValue,
formattedDatatables[layer.layerId].table.rows,
table
);
if (point) {
splitPoints.push(point);
}
}
if (xySeries.smVerticalAccessorValue && splitRowAccessor) {
const accessor = getAccessorByDimension(splitRowAccessor, table.columns);
const point = createSplitPoint(
accessor,
xySeries.smVerticalAccessorValue,
formattedDatatables[layer.layerId].table.rows,
table
);
if (point) {
splitPoints.push(point);
}
}
const context: FilterEvent['data'] = {
data: [xSeriesPoint, ...splitPoints],
};
onClickValue(context);
};
const brushHandler = ({ x }: XYBrushEvent) => {
if (!x) {
return;
}
const [min, max] = x;
if (!xAxisColumn || !isHistogramViz) {
return;
}
const { table } = dataLayers[0];
const xAccessor =
dataLayers[0].xAccessor !== undefined
? getAccessorByDimension(dataLayers[0].xAccessor, table.columns)
: undefined;
const xAxisColumnIndex = table.columns.findIndex((el) => el.id === xAccessor);
const context: BrushEvent['data'] = {
range: [min, max],
table,
column: xAxisColumnIndex,
...(isEsqlMode ? { timeFieldName: table.columns[xAxisColumnIndex].name } : {}),
};
onSelectRange(context);
};
const legendInsideParams: LegendPositionConfig = {
vAlign: legend.verticalAlignment ?? VerticalAlignment.Top,
hAlign: legend?.horizontalAlignment ?? HorizontalAlignment.Right,
direction: LayoutDirection.Vertical,
floating: true,
floatingColumns: legend?.floatingColumns ?? 1,
};
const isHistogramModeEnabled = dataLayers.some(
({ isHistogram, seriesType, isStacked }) =>
isHistogram && (isStacked || seriesType !== SeriesTypes.BAR || !chartHasMoreThanOneBarSeries)
);
const shouldUseNewTimeAxis =
isTimeViz && isHistogramModeEnabled && !useLegacyTimeAxis && !shouldRotate;
const defaultXAxisPosition = shouldRotate ? Position.Left : Position.Bottom;
const gridLineStyle = {
visible: xAxisConfig?.showGridLines,
strokeWidth: 1,
};
const xAxisStyle: RecursivePartial<AxisStyle> = shouldUseNewTimeAxis
? {
...MULTILAYER_TIME_AXIS_STYLE,
tickLabel: {
...MULTILAYER_TIME_AXIS_STYLE.tickLabel,
visible: Boolean(xAxisConfig?.showLabels),
fill: xAxisConfig?.labelColor,
},
tickLine: {
...MULTILAYER_TIME_AXIS_STYLE.tickLine,
visible: Boolean(xAxisConfig?.showLabels),
},
axisTitle: {
visible: xAxisConfig?.showTitle,
},
}
: {
tickLabel: {
visible: xAxisConfig?.showLabels,
rotation: xAxisConfig?.labelsOrientation,
padding: linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined,
fill: xAxisConfig?.labelColor,
},
axisTitle: {
visible: xAxisConfig?.showTitle,
padding:
!xAxisConfig?.showLabels && linesPaddings.bottom != null
? { inner: linesPaddings.bottom }
: undefined,
},
};
const isSplitChart = splitColumnAccessor || splitRowAccessor;
const splitTable = isSplitChart ? dataLayers[0].table : undefined;
const splitColumnId =
splitColumnAccessor && splitTable
? getAccessorByDimension(splitColumnAccessor, splitTable?.columns)
: undefined;
const splitRowId =
splitRowAccessor && splitTable
? getAccessorByDimension(splitRowAccessor, splitTable?.columns)
: undefined;
const chartContainerStyle = css({
width: '100%',
height: '100%',
overflowX: 'hidden',
position: uiState ? 'absolute' : 'relative',
});
const { theme: settingsThemeOverrides = {}, ...settingsOverrides } = getOverridesFor(
overrides,
'settings'
) as Partial<SettingsProps>;
const referenceLinesFormatters = getReferenceLinesFormattersMap(
referenceLineLayers,
formatFactory
);
// ES|QL charts are allowed to create filters only when the unified search bar query is ES|QL (e.g. in Discover)
const applicationQuery = data.query.queryString.getQuery();
const canCreateFilters =
!isEsqlMode || (isEsqlMode && applicationQuery && isOfAggregateQueryType(applicationQuery));
// ES|QL charts are allowed to create alert rules only in dashboards
const canCreateAlerts =
isEsqlMode && applicationQuery && !isOfAggregateQueryType(applicationQuery);
return (
<>
<GlobalXYChartStyles />
<div css={chartContainerStyle}>
{showLegend !== undefined && uiState && (
<LegendToggle
onClick={toggleLegend}
showLegend={showLegend}
legendPosition={legend.position}
/>
)}
<LegendColorPickerWrapperContext.Provider
value={{
uiState,
setColor,
legendPosition: legend.position,
dataLayers,
formattedDatatables,
titles,
fieldFormats,
singleTable,
}}
>
<Chart ref={chartRef} {...getOverridesFor(overrides, 'chart')}>
<Tooltip<Record<string, string | number>, XYChartSeriesIdentifier>
boundary={appFixedViewport}
headerFormatter={
!args.detailedTooltip && xAxisColumn
? ({ value }) => (
<TooltipHeader
value={value}
formatter={safeXAccessorLabelRenderer}
xDomain={rawXDomain}
/>
)
: undefined
}
actions={getTooltipActions(
dataLayers,
onClickMultiValue,
onCreateAlertRule,
fieldFormats,
formattedDatatables,
xAxisFormatter,
formatFactory,
isEsqlMode,
canCreateAlerts,
interactive && !args.detailedTooltip
)}
customTooltip={
args.detailedTooltip
? ({ header, values }) => (
<CustomTooltip
header={header}
values={values}
titles={titles}
fieldFormats={fieldFormats}
formatFactory={formatFactory}
formattedDatatables={formattedDatatables}
splitAccessors={{
splitColumnAccessor: splitColumnId,
splitRowAccessor: splitRowId,
}}
layers={dataLayers}
xDomain={isTimeViz ? rawXDomain : undefined}
/>
)
: undefined
}
type={args.showTooltip ? TooltipType.VerticalCursor : TooltipType.None}
/>
<Settings
noResults={
<EmptyPlaceholder
icon={icon}
renderComplete={onRenderChange}
css={xyChartEmptyStyles}
/>
}
onRenderChange={onRenderChange}
pointerUpdateDebounce={0} // use the `handleCursorUpdate` debounce time
onPointerUpdate={syncCursor ? handleCursorUpdate : undefined}
externalPointerEvents={{
tooltip: { visible: syncTooltips, placement: Placement.Right },
}}
legendColorPicker={uiState ? LegendColorPickerWrapper : undefined}
debugState={window._echDebugStateFlag ?? false}
showLegend={showLegend}
legendPosition={legend?.isInside ? legendInsideParams : legend.position}
legendSize={LegendSizeToPixels[legend.legendSize ?? DEFAULT_LEGEND_SIZE]}
legendValues={isHistogramViz ? legend.legendStats : []}
legendTitle={getLegendTitle(legend.title, dataLayers[0], legend.isTitleVisible)}
theme={[
{
barSeriesStyle: {
...valueLabelsStyling,
},
background: {
color: undefined, // removes background for embeddables
},
legend: {
labelOptions: { maxLines: legend.shouldTruncate ? legend?.maxLines ?? 1 : 0 },
},
// if not title or labels are shown for axes, add some padding if required by reference line markers
chartMargins: {
// Temporary margin defaults
...LEGACY_LIGHT_THEME.chartMargins,
...computeChartMargins(
linesPaddings,
{ ...tickLabelsVisibilitySettings, x: xAxisConfig?.showLabels },
{ ...axisTitlesVisibilitySettings, x: xAxisConfig?.showTitle },
yAxesMap,
shouldRotate
),
},
markSizeRatio: args.markSizeRatio,
},
...(Array.isArray(settingsThemeOverrides)
? settingsThemeOverrides
: [settingsThemeOverrides]),
]}
baseTheme={chartBaseTheme}
allowBrushingLastHistogramBin={isTimeViz}
rotation={shouldRotate ? 90 : 0}
xDomain={xDomain}
// enable brushing only for time charts, for both ES|QL and DSL queries
onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined}
onElementClick={interactive ? clickHandler : undefined}
legendAction={
interactive && canCreateFilters
? getLegendAction(
dataLayers,
onClickValue,
layerCellValueActions,
fieldFormats,
formattedDatatables,
titles,
singleTable
)
: undefined
}
ariaLabel={args.ariaLabel}
ariaUseDefaultSummary={!args.ariaLabel}
orderOrdinalBinsBy={
args.orderBucketsBySum
? {
direction: Direction.Descending,
}
: undefined
}
locale={i18n.getLocale()}
{...settingsOverrides}
/>
<XYCurrentTime
enabled={Boolean(args.addTimeMarker && isTimeViz)}
isDarkMode={darkMode}
domain={rawXDomain}
/>
<Axis
id="x"
position={
xAxisConfig?.position
? getOriginalAxisPosition(xAxisConfig?.position, shouldRotate)
: defaultXAxisPosition
}
title={xTitle}
gridLine={gridLineStyle}
hide={xAxisConfig?.hide || dataLayers[0]?.simpleView || !dataLayers[0]?.xAccessor}
tickFormat={(d) => {
let value = safeXAccessorLabelRenderer(d) || '';
if (xAxisConfig?.truncate && value.length > xAxisConfig.truncate) {
value = `${value.slice(0, xAxisConfig.truncate)}...`;
}
return value;
}}
style={xAxisStyle}
showOverlappingLabels={xAxisConfig?.showOverlappingLabels}
showDuplicatedTicks={xAxisConfig?.showDuplicates}
timeAxisLayerCount={shouldUseNewTimeAxis ? 2 : 0}
{...getOverridesFor(overrides, 'axisX')}
/>
{isSplitChart && splitTable && (
<SplitChart
splitColumnAccessor={splitColumnAccessor}
splitRowAccessor={splitRowAccessor}
columns={splitTable.columns}
/>
)}
{yAxesConfiguration.map((axis) => {
return (
<Axis
key={axis.groupId}
id={axis.groupId}
groupId={axis.groupId}
position={axis.position}
title={axis.title || getYAxesTitles(axis.series)}
gridLine={{
visible: axis.showGridLines,
}}
hide={axis.hide || dataLayers[0]?.simpleView}
tickFormat={(d) => {
let value = axis.formatter?.convert(d) || '';
if (axis.truncate && value.length > axis.truncate) {
value = `${value.slice(0, axis.truncate)}...`;
}
return value;
}}
style={getYAxesStyle(axis)}
domain={getYAxisDomain(axis)}
showOverlappingLabels={axis.showOverlappingLabels}
showDuplicatedTicks={axis.showDuplicates}
{...getOverridesFor(
overrides,
/left/i.test(axis.groupId) ? 'axisLeft' : 'axisRight'
)}
/>
);
})}
{!hideEndzones && (
<XyEndzones
baseDomain={rawXDomain}
extendedDomain={xDomain}
darkMode={darkMode}
histogramMode={dataLayers.every(
(layer) =>
layer.isHistogram &&
(layer.isStacked || !layer.splitAccessors || !layer.splitAccessors.length) &&
(layer.isStacked ||
layer.seriesType !== SeriesTypes.BAR ||
!chartHasMoreThanOneBarSeries)
)}
/>
)}
{dataLayers.length && (
<DataLayers
titles={titles}
layers={dataLayers}
endValue={endValue}
timeZone={timeZone}
syncColors={syncColors}
valueLabels={valueLabels}
fillOpacity={args.fillOpacity}
minBarHeight={args.minBarHeight}
formatFactory={formatFactory}
paletteService={paletteService}
palettes={palettes}
fittingFunction={fittingFunction}
emphasizeFitting={emphasizeFitting}
yAxesConfiguration={yAxesConfiguration}
xAxisConfiguration={
xAxisConfig ? axesConfiguration[axesConfiguration.length - 1] : undefined
}
shouldShowValueLabels={shouldShowValueLabels}
formattedDatatables={formattedDatatables}
chartHasMoreThanOneBarSeries={chartHasMoreThanOneBarSeries}
defaultXScaleType={defaultXScaleType}
fieldFormats={fieldFormats}
uiState={uiState}
singleTable={singleTable}
isDarkMode={darkMode}
/>
)}
{referenceLineLayers.length ? (
<ReferenceLines
layers={referenceLineLayers}
xAxisFormatter={xAxisFormatter}
axesConfiguration={axesConfiguration}
isHorizontal={shouldRotate}
paddingMap={linesPaddings}
titles={titles}
yAxesMap={yAxesMap}
formatters={referenceLinesFormatters}
/>
) : null}
{(rangeAnnotations.length || lineAnnotations.length) && isTimeViz ? (
<Annotations
rangeAnnotations={rangeAnnotations}
groupedLineAnnotations={groupedLineAnnotations}
timeFormat={timeFormat}
isHorizontal={shouldRotate}
paddingMap={linesPaddings}
isBarChart={filteredBarLayers.length > 0}
minInterval={minInterval}
simpleView={shouldHideDetails}
outsideDimension={
rangeAnnotations.length && shouldHideDetails
? OUTSIDE_RECT_ANNOTATION_WIDTH_SUGGESTION
: shouldUseNewTimeAxis
? Number(MULTILAYER_TIME_AXIS_STYLE.tickLine?.padding ?? 0) +
chartBaseTheme.axes.tickLabel.fontSize
: Math.max(chartBaseTheme.axes.tickLine.size, OUTSIDE_RECT_ANNOTATION_WIDTH)
}
/>
) : null}
</Chart>
</LegendColorPickerWrapperContext.Provider>
</div>
</>
);
}