UNPKG

react-native-chart-kit

Version:

Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.

135 lines (134 loc) 10.5 kB
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 } });