react-native-chart-kit
Version:
Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.
157 lines (156 loc) • 6.34 kB
JavaScript
import { resolveReferenceLineLabelY } from "./referenceLabelPlacement";
const defaultReferenceLineOpacity = 0.86;
const defaultReferenceBandOpacity = 0.1;
const defaultReferenceLabelContainerBorderRadius = 4;
const defaultReferenceLabelContainerOpacity = 0.9;
const defaultReferenceLabelContainerPaddingX = 3;
const defaultReferenceLabelContainerPaddingY = 2;
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const resolveOpacity = (value, fallback) => typeof value === "number" && Number.isFinite(value)
? clamp(value, 0, 1)
: fallback;
const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
const resolveFiniteNumber = (value, fallback) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
const resolveReferenceLabelContainer = ({ container, theme }) => {
if (!container) {
return undefined;
}
const config = typeof container === "object" ? container : {};
return {
backgroundColor: config.backgroundColor ?? theme.plotBackground,
borderRadius: Math.max(0, resolveFiniteNumber(config.borderRadius, defaultReferenceLabelContainerBorderRadius)),
opacity: resolveOpacity(config.opacity, defaultReferenceLabelContainerOpacity),
paddingX: Math.max(0, resolveFiniteNumber(config.paddingX, defaultReferenceLabelContainerPaddingX)),
paddingY: Math.max(0, resolveFiniteNumber(config.paddingY, defaultReferenceLabelContainerPaddingY))
};
};
const getTextAnchor = (position) => {
if (position === "start") {
return "start";
}
if (position === "center") {
return "middle";
}
return "end";
};
const getReferenceLabelX = ({ plot, position }) => {
if (position === "start") {
return plot.x + 8;
}
if (position === "center") {
return plot.x + plot.width / 2;
}
return plot.x + plot.width - 8;
};
export const buildLineChartReferenceLineModels = ({ geometries, lines, plot, theme, yScale }) => {
if (!lines || lines.length === 0) {
return [];
}
const plotBottom = plot.y + plot.height;
return lines.flatMap((line, index) => {
if (!isFiniteNumber(line.y)) {
return [];
}
const y = yScale.scale(line.y);
if (y < plot.y || y > plotBottom) {
return [];
}
const labelPosition = line.labelPosition ?? "end";
const labelFontSize = typeof line.labelFontSize === "number" && line.labelFontSize > 0
? line.labelFontSize
: theme.typography.axisLabelSize;
const labelOffset = typeof line.labelOffset === "number" && Number.isFinite(line.labelOffset)
? line.labelOffset
: 6;
const model = {
key: `reference-line-${index}-${line.y}`,
x1: plot.x,
x2: plot.x + plot.width,
y,
color: line.color ?? theme.mutedText,
opacity: resolveOpacity(line.opacity, defaultReferenceLineOpacity),
strokeWidth: line.strokeWidth ?? 1
};
if (line.strokeDasharray !== undefined) {
model.strokeDasharray = line.strokeDasharray;
}
if (line.label) {
const textAnchor = getTextAnchor(labelPosition);
const labelX = getReferenceLabelX({ plot, position: labelPosition });
const labelContainer = resolveReferenceLabelContainer({
container: line.labelContainer,
theme
});
model.label = {
text: line.label,
x: labelX,
y: resolveReferenceLineLabelY({
geometries,
labelOffset,
labelPlacement: line.labelPlacement,
lineY: y,
plot,
text: line.label,
textAnchor,
fontSize: labelFontSize,
x: labelX
}),
color: line.labelColor ?? line.color ?? theme.mutedText,
...(labelContainer !== undefined ? { container: labelContainer } : {}),
fontSize: labelFontSize,
textAnchor
};
}
return [model];
});
};
export const buildLineChartReferenceBandModels = ({ bands, plot, theme, yScale }) => {
if (!bands || bands.length === 0) {
return [];
}
const plotBottom = plot.y + plot.height;
return bands.flatMap((band, index) => {
if (!isFiniteNumber(band.y1) || !isFiniteNumber(band.y2)) {
return [];
}
const yA = yScale.scale(band.y1);
const yB = yScale.scale(band.y2);
const top = Math.min(yA, yB);
const bottom = Math.max(yA, yB);
const clampedTop = clamp(top, plot.y, plotBottom);
const clampedBottom = clamp(bottom, plot.y, plotBottom);
const height = clampedBottom - clampedTop;
if (height <= 0.5) {
return [];
}
const labelPosition = band.labelPosition ?? "start";
const labelFontSize = typeof band.labelFontSize === "number" && band.labelFontSize > 0
? band.labelFontSize
: theme.typography.axisLabelSize;
const model = {
key: `reference-band-${index}-${band.y1}-${band.y2}`,
x: plot.x,
y: clampedTop,
width: plot.width,
height,
color: band.color ?? theme.series[0] ?? theme.grid,
opacity: resolveOpacity(band.opacity, defaultReferenceBandOpacity)
};
if (band.label) {
const labelContainer = resolveReferenceLabelContainer({
container: band.labelContainer,
theme
});
model.label = {
text: band.label,
x: getReferenceLabelX({ plot, position: labelPosition }),
y: clamp(clampedTop + labelFontSize + 6, plot.y + labelFontSize, plotBottom - 4),
color: band.labelColor ?? band.color ?? theme.mutedText,
...(labelContainer !== undefined ? { container: labelContainer } : {}),
fontSize: labelFontSize,
textAnchor: getTextAnchor(labelPosition)
};
}
return [model];
});
};