react-native-chart-kit
Version:
Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.
152 lines (151 loc) • 12.4 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { View } from "react-native";
import { renderDefaultTooltip } from "./defaultTooltip";
import { renderLineChartDebugLayout } from "./debugOverlay";
import { renderConfiguredLegend } from "./legend";
import { renderDefaultDot } from "./markers";
import { getLineChartRenderer } from "./renderer";
import { getFontFamilyProps, measureLineChartText } from "./text";
import { getLineChartAreaGradientId, LineChartAreaPaths, LineChartLinePaths, LineChartThresholdClipDefs } from "./thresholdRendering";
import { useLineChartDebugLayout } from "./useDebugLayout";
export const LineChartSurface = ({ animatedTooltip, chartId, chartWidth, isScrollable, mainHeight, model, props, renderer: rendererProp, responderProps, yAxisLabels }) => {
const { boxes, geometries, legendModel, referenceBandModels, referenceLineModels, resolvedTheme, showHorizontalGridLines, showVerticalGridLines, crosshairConfig, selectionModel, xLabelLayout, yScale, yTicks } = model;
const { debugLayout, onLayoutDebug } = props;
const debugLayoutModel = useLineChartDebugLayout({
enabled: Boolean(debugLayout || onLayoutDebug),
model,
onLayoutDebug,
tooltip: animatedTooltip,
yAxisLabels
});
const renderer = getLineChartRenderer(rendererProp);
const { Defs, Group, Line, Rect, Surface, Text } = renderer;
const Layer = renderer.Layer ?? Group;
const LinearGradient = renderer.LinearGradient;
const canRenderText = renderer.capabilities?.text !== false;
const supportsGradientDefs = renderer.capabilities?.gradients !== false &&
renderer.capabilities?.pathGradients !== true &&
Boolean(LinearGradient);
const renderReferenceLabel = (key, label) => {
const fontFamilyProps = getFontFamilyProps(resolvedTheme.typography.fontFamily);
if (!label.container) {
return (_jsx(Text, { x: label.x, y: label.y, fill: label.color, fontSize: label.fontSize, textAnchor: label.textAnchor, ...fontFamilyProps, children: label.text }, key));
}
const labelSize = measureLineChartText(label.text, {
fontFamily: resolvedTheme.typography.fontFamily,
fontSize: label.fontSize
});
const backgroundWidth = labelSize.width + label.container.paddingX * 2;
const backgroundHeight = labelSize.height + label.container.paddingY * 2;
const backgroundX = label.textAnchor === "end"
? label.x - backgroundWidth
: label.textAnchor === "middle"
? label.x - backgroundWidth / 2
: label.x;
const backgroundY = label.y - labelSize.height + 2 - label.container.paddingY;
const textX = backgroundX + backgroundWidth / 2;
return (_jsxs(Group, { children: [_jsx(Rect, { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight, rx: label.container.borderRadius, fill: label.container.backgroundColor, opacity: label.container.opacity }), _jsx(Text, { x: textX, y: label.y, fill: label.color, fontSize: label.fontSize, textAnchor: "middle", ...fontFamilyProps, children: label.text })] }, key));
};
return (_jsx(View, { collapsable: false, style: { width: chartWidth, height: mainHeight }, ...responderProps, children: _jsxs(Surface, { width: chartWidth, height: mainHeight, children: [_jsx(Layer, { name: "background", children: _jsx(Rect, { x: 0, y: 0, width: chartWidth, height: mainHeight, rx: 8, fill: resolvedTheme.background }) }), _jsx(Layer, { name: "plot", children: _jsx(Rect, { x: boxes.plot.x, y: boxes.plot.y, width: boxes.plot.width, height: boxes.plot.height, rx: 6, fill: resolvedTheme.plotBackground }) }), _jsxs(Defs, { children: [supportsGradientDefs && LinearGradient
? geometries.map(({ style }, index) => (_jsx(LinearGradient, { id: getLineChartAreaGradientId(chartId, index), x1: "0%", x2: "0%", y1: "0%", y2: "100%", stops: [
{
offset: "0%",
color: style.areaFill.fromColor,
opacity: style.areaFill.fromOpacity
},
{
offset: "100%",
color: style.areaFill.toColor,
opacity: style.areaFill.toOpacity
}
] }, `area-gradient-${index}`)))
: null, _jsx(LineChartThresholdClipDefs, { chartId: chartId, geometries: geometries, plot: boxes.plot, renderer: renderer, yScale: yScale })] }), _jsxs(Layer, { name: "grid", children: [showVerticalGridLines
? xLabelLayout.items.map((label) => (_jsx(Line, { x1: label.gridX, x2: label.gridX, y1: boxes.plot.y, y2: boxes.plot.y + boxes.plot.height, stroke: resolvedTheme.grid, strokeOpacity: 0.72, strokeWidth: 1 }, `grid-x-${label.index}`)))
: null, showHorizontalGridLines
? yTicks.map((tick) => {
const y = yScale.scale(tick);
return (_jsx(Line, { x1: boxes.plot.x, x2: boxes.plot.x + boxes.plot.width, y1: y, y2: y, stroke: resolvedTheme.grid, strokeOpacity: 0.78, strokeWidth: 1 }, `grid-y-${tick}`));
})
: null] }), _jsxs(Layer, { name: "axes", children: [yAxisLabels.map((label) => {
if (isScrollable || !canRenderText) {
return null;
}
return (_jsx(Text, { x: boxes.plot.x - 8, y: label.y, fill: resolvedTheme.mutedText, fontSize: resolvedTheme.typography.axisLabelSize, opacity: label.opacity, textAnchor: "end", ...getFontFamilyProps(resolvedTheme.typography.fontFamily), children: label.text }, `label-y-${label.key}`));
}), canRenderText
? xLabelLayout.items.map((label) => (_jsx(Group, { transform: label.rotation !== 0
? `rotate(${label.rotation} ${label.x} ${label.y})`
: undefined, children: _jsx(Text, { x: label.x, y: label.y, fill: resolvedTheme.mutedText, fontSize: resolvedTheme.typography.axisLabelSize, textAnchor: label.textAnchor, ...getFontFamilyProps(resolvedTheme.typography.fontFamily), children: label.text }) }, `label-x-${label.index}`)))
: null] }), _jsx(Layer, { name: "referenceBands", children: referenceBandModels.map((band) => (_jsx(Rect, { x: band.x, y: band.y, width: band.width, height: band.height, fill: band.color, opacity: band.opacity }, band.key))) }), _jsx(Layer, { name: "referenceLines", children: referenceLineModels.map((line) => (_jsx(Line, { x1: line.x1, x2: line.x2, y1: line.y, y2: line.y, stroke: line.color, strokeOpacity: line.opacity, strokeWidth: line.strokeWidth, ...(line.strokeDasharray
? { strokeDasharray: line.strokeDasharray }
: {}) }, line.key))) }), _jsx(Layer, { name: "dataArea", children: _jsx(LineChartAreaPaths, { chartId: chartId, geometries: geometries, plot: boxes.plot, renderer: renderer, yScale: yScale }) }), _jsx(Layer, { name: "data", children: _jsx(LineChartLinePaths, { chartId: chartId, geometries: geometries, plot: boxes.plot, renderer: renderer, yScale: yScale }) }), _jsx(Layer, { name: "markers", children: geometries.flatMap(({ geometry, style }) => geometry.points
.filter((point) => point.defined && style.dot.visible)
.map((point) => {
const dotProps = {
point,
seriesKey: geometry.key,
seriesLabel: geometry.label,
color: style.color,
x: point.x,
y: point.y,
value: point.value,
dataIndex: point.dataIndex,
config: style.dot,
theme: resolvedTheme
};
const renderedDot = props.renderDot
? props.renderDot(dotProps)
: renderDefaultDot(dotProps, renderer);
return renderedDot ? (_jsx(Group, { children: renderedDot }, `dot-${geometry.key}-${point.index}`)) : null;
})) }), _jsxs(Layer, { name: "referenceLabels", children: [referenceBandModels.map((band) => band.label && canRenderText
? renderReferenceLabel(`${band.key}-label`, band.label)
: null), referenceLineModels.map((line) => line.label && canRenderText
? renderReferenceLabel(`${line.key}-label`, line.label)
: null)] }), _jsxs(Layer, { name: "overlays", children: [selectionModel && crosshairConfig.visible ? (props.renderCrosshair ? (props.renderCrosshair({
chartHeight: mainHeight,
chartWidth,
config: crosshairConfig,
plot: boxes.plot,
series: selectionModel.series,
theme: resolvedTheme,
x: selectionModel.x,
xLabel: selectionModel.xLabel,
y: selectionModel.y
})) : (_jsx(Line, { x1: selectionModel.x, x2: selectionModel.x, y1: boxes.plot.y, y2: boxes.plot.y + boxes.plot.height, stroke: crosshairConfig.color, strokeOpacity: crosshairConfig.opacity, strokeWidth: crosshairConfig.strokeWidth, ...(crosshairConfig.strokeDasharray
? { strokeDasharray: crosshairConfig.strokeDasharray }
: {}) }, "selection-crosshair"))) : null, legendModel
? renderConfiguredLegend({
legend: legendModel.renderProps,
config: legendModel.config,
renderer
})
: null] }), _jsxs(Layer, { name: "interaction", children: [selectionModel
? selectionModel.series
.filter((item) => item.activeDot.visible)
.map((item) => {
const dotProps = {
point: item.point,
seriesKey: item.key,
seriesLabel: item.label,
color: item.color,
x: item.point.x,
y: item.point.y,
value: item.value,
dataIndex: item.point.dataIndex,
config: item.activeDot,
theme: resolvedTheme
};
const renderedDot = props.renderActiveDot
? props.renderActiveDot(dotProps)
: renderDefaultDot(dotProps, renderer);
return renderedDot ? (_jsx(Group, { children: renderedDot }, `active-dot-${item.key}`)) : null;
})
: null, animatedTooltip
? props.renderTooltip
? props.renderTooltip(animatedTooltip)
: renderDefaultTooltip(animatedTooltip, renderer)
: null] }), debugLayout && debugLayoutModel ? (_jsx(Layer, { name: "debug", children: renderLineChartDebugLayout({
fontFamily: resolvedTheme.typography.fontFamily,
model: debugLayoutModel,
renderer
}) })) : null] }) }));
};