react-native-chart-kit
Version:
Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.
135 lines (134 loc) • 10.5 kB
JavaScript
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { Fragment } from "react";
import { StyleSheet, View } from "react-native";
import { createSvgTestId } from "../../../svg-renderer/index";
import { getLineChartRenderer as getBarChartRenderer } from "../line/renderer";
import { getFontFamilyProps } from "../line/text";
import { getAnimatedBarSelectionFill, getAnimatedBarSelectionStrokeOpacity, getBarChartSelectionGridOpacity, shouldRenderBarChartGridLines, useBarChartSelectionAnimation } from "./selectionAnimation";
import { renderDefaultBarChartTooltip } from "./tooltip";
import { offsetBarChartTooltipForViewport } from "./tooltipPlacement";
import { useAnimatedBarChartTooltipModel } from "./useAnimatedTooltipModel";
const RendererLayer = ({ children }) => _jsx(_Fragment, { children: children });
export const BarChartSurface = ({ barRadius, height, model, responderProps, renderBar, selectedBarKey, selectionAnimation, showYAxis, width, renderer: rendererProp }) => {
const { bars, boxes, legendItems, orientation, resolvedTheme, showHorizontalGridLines, showXAxisLabels, showYAxisLabels, valueLabels, xLabels, yLabels, yTicks } = model;
const fontProps = getFontFamilyProps(resolvedTheme.typography.fontFamily);
const selectionAnimationState = useBarChartSelectionAnimation({
animation: selectionAnimation,
selectedBarKey
});
const gridStrokeOpacity = getBarChartSelectionGridOpacity({
selectedBarKey,
state: selectionAnimationState
});
const shouldRenderGridLines = shouldRenderBarChartGridLines({
selectedBarKey,
state: selectionAnimationState
});
const shouldCoverSelectionGrid = !shouldRenderGridLines;
const renderer = getBarChartRenderer(rendererProp);
const Layer = renderer.Layer ?? RendererLayer;
const Line = renderer.Line;
const Rect = renderer.Rect;
const Surface = renderer.Surface;
const Text = renderer.Text;
const isInteractive = Object.keys(responderProps).length > 0;
return (_jsxs(View, { collapsable: false, style: { width, height, position: "relative" }, children: [_jsxs(Surface, { width: width, height: height, children: [_jsxs(Layer, { name: "background", children: [_jsx(Rect, { x: 0, y: 0, width: width, height: height, rx: 8, fill: resolvedTheme.background }), _jsx(Rect, { x: boxes.plot.x, y: boxes.plot.y, width: boxes.plot.width, height: boxes.plot.height, rx: 6, fill: resolvedTheme.plotBackground })] }), _jsxs(Layer, { name: "grid", children: [shouldRenderGridLines &&
showHorizontalGridLines &&
orientation === "horizontal"
? xLabels.map((label) => (_jsx(Line, { x1: label.x, x2: label.x, y1: boxes.plot.y, y2: boxes.plot.y + boxes.plot.height, stroke: resolvedTheme.grid, strokeOpacity: gridStrokeOpacity, strokeWidth: 1 }, `grid-x-${label.index}`)))
: null, shouldRenderGridLines &&
showHorizontalGridLines &&
orientation === "vertical"
? yTicks.map((tick) => {
const label = yLabels.find((item) => item.key === `tick-${tick}`);
return label ? (_jsx(Line, { x1: boxes.plot.x, x2: boxes.plot.x + boxes.plot.width, y1: label.y - resolvedTheme.typography.axisLabelSize / 2 + 2, y2: label.y - resolvedTheme.typography.axisLabelSize / 2 + 2, stroke: resolvedTheme.grid, strokeOpacity: gridStrokeOpacity, strokeWidth: 1 }, `grid-y-${tick}`)) : null;
})
: null] }), _jsxs(Layer, { name: "data", children: [shouldCoverSelectionGrid ? (_jsx(Rect, { x: boxes.plot.x, y: boxes.plot.y, width: boxes.plot.width, height: boxes.plot.height, rx: 6, fill: resolvedTheme.plotBackground, testID: createSvgTestId("bar-chart-selection-grid-cover") })) : null, bars.map((bar) => {
const fill = getAnimatedBarSelectionFill({
backgroundColor: resolvedTheme.plotBackground,
barKey: bar.key,
color: bar.color,
state: selectionAnimationState
});
const strokeOpacity = getAnimatedBarSelectionStrokeOpacity({
barKey: bar.key,
state: selectionAnimationState
});
const radius = Math.min(barRadius, bar.width / 2, bar.height / 2);
const barNode = renderBar?.({
bar,
fill,
radius,
selected: selectedBarKey === bar.key,
strokeColor: resolvedTheme.text,
strokeOpacity,
strokeWidth: 1.5,
theme: resolvedTheme
});
return (_jsxs(Fragment, { children: [_jsx(Rect, { x: bar.x, y: bar.y, width: bar.width, height: bar.height, rx: 0, fill: resolvedTheme.plotBackground }), barNode ?? (_jsx(Rect, { x: bar.x, y: bar.y, width: bar.width, height: bar.height, rx: radius, fill: fill, ...(strokeOpacity > 0
? {
stroke: resolvedTheme.text,
strokeOpacity,
strokeWidth: 1.5
}
: {}), testID: createSvgTestId("bar-chart-bar", bar.seriesKey, bar.dataIndex) }))] }, bar.key));
})] }), _jsxs(Layer, { name: "axes", children: [showYAxis && showYAxisLabels
? yLabels.map((label) => (_jsx(Text, { x: label.x, y: label.y, fill: resolvedTheme.mutedText, fontSize: resolvedTheme.typography.axisLabelSize, textAnchor: "end", ...fontProps, children: label.text }, `label-y-${label.key}`)))
: null, showXAxisLabels
? xLabels.map((label) => (_jsx(Text, { x: label.x, y: label.y, fill: resolvedTheme.mutedText, fontSize: resolvedTheme.typography.axisLabelSize, textAnchor: label.textAnchor, ...fontProps, children: label.text }, `label-x-${label.index}`)))
: null, valueLabels.map((label) => (_jsx(Text, { x: label.x, y: label.y, fill: label.color, fontSize: resolvedTheme.typography.axisLabelSize, textAnchor: label.textAnchor ?? "middle", ...fontProps, children: label.text }, label.key))), legendItems.map((item) => (_jsx(Rect, { x: item.markerX, y: item.markerY, width: 8, height: 8, rx: 2, fill: item.color }, `legend-marker-${item.key}`))), legendItems.map((item) => (_jsx(Text, { x: item.labelX, y: item.labelY, fill: resolvedTheme.mutedText, fontSize: resolvedTheme.typography.legendLabelSize, textAnchor: "start", ...fontProps, children: item.label }, `legend-label-${item.key}`)))] })] }), isInteractive ? (_jsx(View, { accessible: false, collapsable: false, importantForAccessibility: "no", pointerEvents: "auto", style: styles.interactionOverlay, ...responderProps })) : null] }));
};
export const BarChartTooltipOverlay = ({ height, model, tooltipConfig, tooltipModel, viewportOffsetX, width, renderer: rendererProp }) => {
const animatedTooltipModel = useAnimatedBarChartTooltipModel(tooltipModel, tooltipConfig.positionAnimationDuration);
const viewportTooltipModel = animatedTooltipModel
? offsetBarChartTooltipForViewport({
leftInset: model.boxes.plot.x + tooltipConfig.edgePadding,
rightInset: tooltipConfig.edgePadding,
tooltip: animatedTooltipModel,
viewportOffsetX,
viewportWidth: width
})
: undefined;
const renderer = getBarChartRenderer(rendererProp);
const Layer = renderer.Layer ?? RendererLayer;
const Surface = renderer.Surface;
return (_jsx(View, { pointerEvents: "none", style: [styles.tooltipOverlay, { width, height }], children: _jsx(Surface, { width: width, height: height, children: _jsx(Layer, { name: "interaction", children: viewportTooltipModel
? renderDefaultBarChartTooltip({
...viewportTooltipModel,
config: tooltipConfig
}, renderer)
: null }) }) }));
};
export const StickyBarChartYAxis = ({ height, model, width, renderer: rendererProp }) => {
const { boxes, resolvedTheme, showYAxisLabels, yLabels } = model;
const fontProps = getFontFamilyProps(resolvedTheme.typography.fontFamily);
const renderer = getBarChartRenderer(rendererProp);
const Layer = renderer.Layer ?? RendererLayer;
const Rect = renderer.Rect;
const Surface = renderer.Surface;
const Text = renderer.Text;
return (_jsx(View, { pointerEvents: "none", style: [styles.stickyYAxis, { width, height }], children: _jsx(Surface, { width: width, height: height, children: _jsxs(Layer, { name: "axes", children: [_jsx(Rect, { x: 0, y: 0, width: Math.max(0, boxes.plot.x), height: boxes.plot.y + boxes.plot.height, fill: resolvedTheme.background }), _jsx(Rect, { x: 0, y: boxes.plot.y + boxes.plot.height, width: Math.max(0, boxes.plot.x), height: Math.max(0, height - (boxes.plot.y + boxes.plot.height)), fill: resolvedTheme.background }), showYAxisLabels
? yLabels.map((label) => (_jsx(Text, { x: label.x, y: label.y, fill: resolvedTheme.mutedText, fontSize: resolvedTheme.typography.axisLabelSize, textAnchor: "end", ...fontProps, children: label.text }, `sticky-label-y-${label.key}`)))
: null] }) }) }));
};
const styles = StyleSheet.create({
stickyYAxis: {
left: 0,
position: "absolute",
top: 0,
zIndex: 1
},
tooltipOverlay: {
left: 0,
position: "absolute",
top: 0,
zIndex: 2
},
interactionOverlay: {
bottom: 0,
left: 0,
position: "absolute",
right: 0,
top: 0
}
});