victory-native
Version:
A charting library for React Native with a focus on performance and customization.
198 lines (197 loc) • 9.74 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.XAxis = exports.XAxisDefaults = void 0;
const react_1 = __importDefault(require("react"));
const react_native_1 = require("react-native");
const react_native_skia_1 = require("@shopify/react-native-skia");
const getOffsetFromAngle_1 = require("../../utils/getOffsetFromAngle");
const boundsToClip_1 = require("../../utils/boundsToClip");
const tickHelpers_1 = require("../../utils/tickHelpers");
const getXAxisLabelPosition_1 = require("../utils/getXAxisLabelPosition");
const getXAxisTicks_1 = require("../utils/getXAxisTicks");
const getAxisTitleLayout_1 = require("../utils/getAxisTitleLayout");
const getAxisLabelLayout_1 = require("../utils/getAxisLabelLayout");
var axisDefaults_1 = require("../utils/axisDefaults");
Object.defineProperty(exports, "XAxisDefaults", { enumerable: true, get: function () { return axisDefaults_1.XAxisDefaults; } });
const XAxis = ({ xScale: xScaleProp, yScale, axisSide = "bottom", labelPosition = "outset", labelRotate, tickCount = tickHelpers_1.DEFAULT_TICK_COUNT, tickValues, labelOffset = 2, labelColor = "#000000", lineWidth = react_native_1.StyleSheet.hairlineWidth, lineColor = "hsla(0, 0%, 0%, 0.25)", font, title, labelRenderer, formatXLabel = (label) => String(label), ix = [], isNumericalData, linePathEffect, chartBounds, orientation, enableRescaling, zoom, }) => {
var _a;
const xScale = zoom ? zoom.rescaleX(xScaleProp) : xScaleProp;
const [y1 = 0, y2 = 0] = yScale.domain();
const fontSize = (_a = font === null || font === void 0 ? void 0 : font.getSize()) !== null && _a !== void 0 ? _a : 0;
const xTicksNormalized = (0, getXAxisTicks_1.getXAxisTicks)({
isNumericalData,
ix,
tickCount,
tickValues,
xScale: enableRescaling ? xScale : xScaleProp,
});
const xTickLabels = xTicksNormalized.map((tick, index) => {
const tickPosition = xScale(tick);
const val = (isNumericalData ? tick : ix[tick]);
const contentX = String(formatXLabel(val));
const labelLayout = (0, getAxisLabelLayout_1.getAxisLabelLayout)({
axis: "x",
orientation,
value: val,
text: contentX,
index,
font,
labelRenderer,
});
return {
tick,
tickPosition,
labelLayout,
};
});
const maxXLabelLayout = xTickLabels.reduce((max, { labelLayout }) => ({
width: Math.max(max.width, labelLayout.width),
height: Math.max(max.height, labelLayout.height),
}), { width: 0, height: 0 });
const titleLayout = (0, getAxisTitleLayout_1.getAxisTitleLayout)({ title, font });
const labelOutset = labelPosition === "outset" &&
xTickLabels.length > 0 &&
maxXLabelLayout.width > 0
? maxXLabelLayout.height + labelOffset * 2
: 0;
const titleX = (() => {
if (titleLayout.position === "start") {
return chartBounds.left;
}
if (titleLayout.position === "end") {
return chartBounds.right - titleLayout.width;
}
return (chartBounds.left +
(chartBounds.right - chartBounds.left) / 2 -
titleLayout.width / 2);
})();
const titleY = (() => {
if (axisSide === "bottom") {
return (chartBounds.bottom +
labelOutset +
titleLayout.offset +
titleLayout.fontSize);
}
return (chartBounds.top -
labelOutset -
titleLayout.offset -
Math.max(0, titleLayout.height - titleLayout.fontSize));
})();
const titleFont = titleLayout.font;
const titleNodes = titleLayout.hasContent && titleFont
? titleLayout.lines.map((line, index) => {
var _a;
return (<react_native_skia_1.Text key={`x-axis-title-line-${index}`} color={(_a = titleLayout.color) !== null && _a !== void 0 ? _a : labelColor} text={line} font={titleFont} y={titleY + index * titleLayout.lineHeight} x={titleX}/>);
})
: null;
const xAxisNodes = xTickLabels.map(({ tick, tickPosition, labelLayout }) => {
const p1 = (0, react_native_skia_1.vec)(tickPosition, yScale(y2));
const p2 = (0, react_native_skia_1.vec)(tickPosition, yScale(y1));
const labelWidth = labelLayout.width;
const { canRenderLabel, labelX, labelCenterX } = (0, getXAxisLabelPosition_1.getXAxisLabelPosition)({
tickPosition,
labelWidth,
chartBounds,
});
const labelY = (() => {
const multilineOffset = Math.max(0, labelLayout.height - fontSize);
// bottom, outset
if (axisSide === "bottom" && labelPosition === "outset") {
return chartBounds.bottom + labelOffset + fontSize;
}
// bottom, inset
if (axisSide === "bottom" && labelPosition === "inset") {
return yScale(y2) - labelOffset - multilineOffset;
}
// top, outset
if (axisSide === "top" && labelPosition === "outset") {
return yScale(y1) - labelOffset - multilineOffset;
}
// top, inset
return yScale(y1) + fontSize + labelOffset;
})();
const labelTopY = (() => {
if (axisSide === "bottom" && labelPosition === "outset") {
return chartBounds.bottom + labelOffset;
}
if (axisSide === "bottom" && labelPosition === "inset") {
return yScale(y2) - labelOffset - labelLayout.height;
}
if (axisSide === "top" && labelPosition === "outset") {
return yScale(y1) - labelOffset - labelLayout.height;
}
return yScale(y1) + labelOffset;
})();
// Calculate origin and translate for label rotation
const { origin, rotateOffset } = (() => {
let rotateOffset = 0;
let origin;
// return defaults if no labelRotate is provided
if (!labelRotate)
return { origin, rotateOffset };
if (axisSide === "bottom" && labelPosition === "outset") {
// bottom, outset
origin = (0, react_native_skia_1.vec)(labelCenterX, labelY);
rotateOffset = Math.abs((labelWidth / 2) * (0, getOffsetFromAngle_1.getOffsetFromAngle)(labelRotate));
}
else if (axisSide === "bottom" && labelPosition === "inset") {
// bottom, inset
origin = (0, react_native_skia_1.vec)(labelCenterX, labelY);
rotateOffset = -Math.abs((labelWidth / 2) * (0, getOffsetFromAngle_1.getOffsetFromAngle)(labelRotate));
}
else if (axisSide === "top" && labelPosition === "inset") {
// top, inset
origin = (0, react_native_skia_1.vec)(labelCenterX, labelY - fontSize / 4);
rotateOffset = Math.abs((labelWidth / 2) * (0, getOffsetFromAngle_1.getOffsetFromAngle)(labelRotate));
}
else {
// top, outset
origin = (0, react_native_skia_1.vec)(labelCenterX, labelY - fontSize / 4);
rotateOffset = -Math.abs((labelWidth / 2) * (0, getOffsetFromAngle_1.getOffsetFromAngle)(labelRotate));
}
return { origin, rotateOffset };
})();
const customOrigin = (0, react_native_skia_1.vec)(labelX + labelLayout.width / 2, labelTopY + labelLayout.height / 2);
const canRenderCustomLabel = labelWidth > 0 && labelLayout.height > 0 && canRenderLabel;
return (<react_1.default.Fragment key={`x-tick-${tick}`}>
{lineWidth > 0 ? (<react_native_skia_1.Group clip={(0, boundsToClip_1.boundsToClip)(chartBounds)}>
<react_native_skia_1.Line p1={p1} p2={p2} color={lineColor} strokeWidth={lineWidth}>
{linePathEffect ? linePathEffect : null}
</react_native_skia_1.Line>
</react_native_skia_1.Group>) : null}
{labelRenderer ? (canRenderCustomLabel ? (labelRenderer.render({
axis: "x",
orientation,
value: labelLayout.value,
text: labelLayout.text,
index: labelLayout.index,
x: labelX,
y: labelTopY,
width: labelLayout.width,
height: labelLayout.height,
fontSize: labelLayout.fontSize,
lineHeight: labelLayout.lineHeight,
color: labelColor,
canFitContent: canRenderLabel,
chartBounds,
rotation: labelRotate,
origin: customOrigin,
})) : null) : font && labelWidth && canRenderLabel ? (<react_native_skia_1.Group transform={[{ translateY: rotateOffset }]}>
{labelLayout.lines.map((line, index) => (<react_native_skia_1.Text key={`x-tick-${tick}-label-line-${index}`} transform={[
{
rotate: (Math.PI / 180) * (labelRotate !== null && labelRotate !== void 0 ? labelRotate : 0),
},
]} origin={origin} color={labelColor} text={line} font={font} y={labelY + index * labelLayout.lineHeight} x={labelX}/>))}
</react_native_skia_1.Group>) : null}
<></>
</react_1.default.Fragment>);
});
return (<>
{xAxisNodes}
{titleNodes}
</>);
};
exports.XAxis = XAxis;