react-native-chart-kit
Version:
Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.
112 lines (111 loc) • 6.16 kB
JavaScript
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { StyleSheet, Text, View } from "react-native";
import { useChartKitTheme } from "../../theme";
import { getLineChartRenderer as getProgressChartRenderer } from "../line/renderer";
import { getProgressChartAccessibilitySummary } from "./accessibility";
import { getAnimatedProgressRings, getAverageProgress, getProgressChartAnimationTargetKey, useProgressChartAnimation } from "./animation";
import { buildProgressChartModel } from "./model";
export { getProgressChartAccessibilitySummary, getProgressChartDataTable } from "./accessibility";
const defaultStrokeLinecap = "round";
const RendererLayer = ({ children }) => _jsx(_Fragment, { children: children });
export const ProgressChart = (props) => {
const chartKitTheme = useChartKitTheme();
const model = useMemo(() => buildProgressChartModel({ chartKitTheme, props }), [chartKitTheme, props]);
const { average, centerX, centerY, chartHeight, legendItems, legendVisible, resolvedTheme, rings } = model;
const animationTargetKey = useMemo(() => getProgressChartAnimationTargetKey(rings), [rings]);
const progressAnimation = useProgressChartAnimation({
animation: props.animation,
targetKey: animationTargetKey
});
const { progress: animationProgress, stagger: animationStagger } = progressAnimation;
const animatedRings = useMemo(() => getAnimatedProgressRings({
centerX,
centerY,
progress: animationProgress,
rings,
stagger: animationStagger
}), [animationProgress, animationStagger, centerX, centerY, rings]);
const displayRings = props.animation ? animatedRings : rings;
const animatedAverage = useMemo(() => getAverageProgress(displayRings), [displayRings]);
const strokeLinecap = props.strokeLinecap ?? defaultStrokeLinecap;
const centerLabel = typeof props.centerLabel === "function"
? props.centerLabel({
average: props.animation ? animatedAverage : average,
rings: displayRings,
theme: resolvedTheme
})
: props.centerLabel;
const accessibilityLabel = props.accessibilityLabel ??
getProgressChartAccessibilitySummary({
colorKey: props.colorKey,
colors: props.colors,
data: props.data,
formatPercentage: props.formatPercentage,
labelKey: props.labelKey,
labels: props.labels,
valueKey: props.valueKey
});
const renderer = getProgressChartRenderer(props.renderer ?? chartKitTheme.renderer);
const Layer = renderer.Layer ?? RendererLayer;
const Path = renderer.Path;
const Surface = renderer.Surface;
const SvgText = renderer.Text;
const canRenderText = renderer.capabilities?.text !== false;
return (_jsxs(View, { accessible: true, accessibilityLabel: accessibilityLabel, accessibilityRole: "image", style: [
styles.container,
{
backgroundColor: resolvedTheme.background,
height: props.height,
width: props.width
}
], testID: props.testID, children: [_jsxs(Surface, { width: props.width, height: chartHeight, children: [_jsx(Layer, { name: "background", children: rings.map((ring) => ring.backgroundPath.length > 0 ? (_jsx(Path, { d: ring.backgroundPath, fill: "none", stroke: resolvedTheme.grid, strokeLinecap: strokeLinecap, strokeWidth: ring.strokeWidth, testID: `${props.testID ?? "progress-chart"}-background.${ring.index}` }, `progress-background-${ring.index}`)) : null) }), _jsx(Layer, { name: "data", children: displayRings.map((ring) => ring.defined ? (_jsx(Path, { d: ring.path, fill: "none", stroke: ring.color ?? resolvedTheme.text, strokeLinecap: strokeLinecap, strokeLinejoin: "round", strokeWidth: ring.strokeWidth, testID: `${props.testID ?? "progress-chart"}-ring.${ring.index}` }, `progress-ring-${ring.index}`)) : null) }), typeof centerLabel === "string" &&
centerLabel.length > 0 &&
canRenderText ? (_jsx(Layer, { name: "overlays", children: _jsx(SvgText, { fill: resolvedTheme.text, fontSize: 18, fontWeight: "800", text: centerLabel, textAnchor: "middle", x: centerX, y: centerY + 6, ...(resolvedTheme.typography.fontFamily
? { fontFamily: resolvedTheme.typography.fontFamily }
: {}), children: centerLabel }) })) : null] }), legendVisible && legendItems.length > 0 ? (_jsx(View, { style: styles.legend, children: legendItems.map((item) => (_jsxs(View, { style: styles.legendItem, children: [_jsx(View, { style: [styles.legendSwatch, { backgroundColor: item.color }] }), _jsx(Text, { numberOfLines: 1, style: [styles.legendText, { color: resolvedTheme.text }], children: item.label }), _jsx(Text, { numberOfLines: 1, style: [styles.legendValue, { color: resolvedTheme.mutedText }], children: item.percentageLabel })] }, item.key))) })) : null] }));
};
export const ProgressRing = ({ color, label, value, ...props }) => {
const row = {
value,
...(label !== undefined ? { label } : {}),
...(color !== undefined ? { color } : {})
};
return (_jsx(ProgressChart, { ...props, data: [row], labelKey: "label", valueKey: "value", colorKey: "color", legend: props.legend ?? false }));
};
const styles = StyleSheet.create({
container: {
borderRadius: 8,
overflow: "hidden"
},
legend: {
alignContent: "center",
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
justifyContent: "center",
paddingHorizontal: 8,
paddingTop: 4
},
legendItem: {
alignItems: "center",
flexDirection: "row",
gap: 5,
maxWidth: "48%",
minHeight: 18
},
legendSwatch: {
borderRadius: 4,
height: 8,
width: 8
},
legendText: {
flexShrink: 1,
fontSize: 11,
fontWeight: "700"
},
legendValue: {
fontSize: 10,
fontWeight: "700"
}
});