react-native-chart-kit
Version:
Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.
104 lines (103 loc) • 4.41 kB
JavaScript
import { useMemo } from "react";
import { resolveLineChartSeriesInput } from "./seriesInput";
import { defaultFormatXLabel, defaultFormatYLabel } from "./utils";
const pluralize = (count, singular, plural = `${singular}s`) => `${count} ${count === 1 ? singular : plural}`;
const normalizeXValue = (value, fallback) => {
return typeof value === "string" ||
(typeof value === "number" && Number.isFinite(value)) ||
value instanceof Date
? value
: fallback;
};
const normalizeYValue = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
const getDefinedSeriesPoints = (series, rows) => rows.flatMap((row) => {
const columnKey = String(series.key ?? series.yKey);
const value = row.values[columnKey];
return typeof value === "number"
? [
{
formattedValue: row.formattedValues[columnKey] ?? String(value),
raw: row.raw,
value,
xLabel: row.xLabel
}
]
: [];
});
const getMinPoint = (points) => points.reduce((min, point) => (!min || point.value < min.value ? point : min), undefined);
const getMaxPoint = (points) => points.reduce((max, point) => (!max || point.value > max.value ? point : max), undefined);
export const getLineChartDataTable = ({ data, formatXLabel = defaultFormatXLabel, formatYLabel = defaultFormatYLabel, series, xKey, yKey, yKeys }) => {
const seriesInputs = resolveLineChartSeriesInput(yKey, yKeys, series);
const columns = seriesInputs.map((item, index) => ({
key: String(item.key ?? item.yKey ?? `series-${index}`),
label: String(item.label ?? item.key ?? item.yKey ?? `Series ${index + 1}`)
}));
return {
columns,
rows: data.map((row, index) => {
const x = normalizeXValue(row[xKey], index);
const values = {};
const formattedValues = {};
seriesInputs.forEach((item, seriesIndex) => {
const key = String(item.key ?? item.yKey ?? `series-${seriesIndex}`);
const value = normalizeYValue(row[item.yKey]);
values[key] = value;
formattedValues[key] =
value === null ? "No value" : formatYLabel(value);
});
return {
index,
x,
xLabel: formatXLabel(x, index),
values,
formattedValues,
raw: row
};
})
};
};
export const getLineChartAccessibilitySummary = (input) => {
const table = getLineChartDataTable(input);
if (table.columns.length === 0 || table.rows.length === 0) {
return "Line chart with no data.";
}
const seriesInputs = resolveLineChartSeriesInput(input.yKey, input.yKeys, input.series);
const intro = table.columns.length === 1
? `${table.columns[0].label} line chart with ${pluralize(table.rows.length, "point")}.`
: `Line chart with ${pluralize(table.columns.length, "series", "series")} and ${pluralize(table.rows.length, "point")}.`;
const seriesSummaries = seriesInputs.flatMap((series) => {
const columnKey = String(series.key ?? series.yKey);
const label = table.columns.find((column) => column.key === columnKey)?.label ??
columnKey;
const points = getDefinedSeriesPoints(series, table.rows);
const minPoint = getMinPoint(points);
const maxPoint = getMaxPoint(points);
const currentPoint = points[points.length - 1];
if (!minPoint || !maxPoint || !currentPoint) {
return [`${label} has no defined values.`];
}
return [
`${label} ranges from ${minPoint.formattedValue} in ${minPoint.xLabel} to ${maxPoint.formattedValue} in ${maxPoint.xLabel}. Current value is ${currentPoint.formattedValue}.`
];
});
return [intro, ...seriesSummaries].join(" ");
};
export const useLineChartAccessibilityLabel = ({ accessibilityLabel, data, formatXLabel, formatYLabel, series, xKey, yKey, yKeys }) => useMemo(() => accessibilityLabel ??
getLineChartAccessibilitySummary({
data,
formatXLabel,
formatYLabel,
series,
xKey,
yKey,
yKeys
}), [
accessibilityLabel,
data,
formatXLabel,
formatYLabel,
series,
xKey,
yKey,
yKeys
]);