react-native-wagmi-charts
Version:
A sweet candlestick chart for React Native
403 lines (395 loc) • 11.9 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.LineChartAxis = void 0;
var _react = _interopRequireDefault(require('react'));
var _reactNative = require('react-native');
var _reactNativeSvg = require('react-native-svg');
var _reactNativeReanimated = _interopRequireWildcard(
require('react-native-reanimated')
);
var _Chart = require('./Chart');
var _useLineChart = require('./useLineChart');
function _interopRequireWildcard(e, t) {
if ('function' == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ('object' != typeof e && 'function' != typeof e))
return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
'default' !== t &&
{}.hasOwnProperty.call(e, t) &&
((i =
(o = Object.defineProperty) &&
Object.getOwnPropertyDescriptor(e, t)) &&
(i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const LineChartAxis = ({
position,
orientation,
color = '#666',
strokeWidth,
tickCount = 5,
domain = [0, 100],
hideOnInteraction = false,
format = (value) => value,
textStyle,
labelPadding = 3,
containerStyle,
...props
}) => {
const { width, height } = _react.default.useContext(
_Chart.LineChartDimensionsContext
);
const { isActive } = (0, _useLineChart.useLineChart)();
// Reserve space at the bottom for x-axis cursor labels
const X_AXIS_LABEL_RESERVED_HEIGHT = 40;
// For vertical axes, don't extend into the reserved cursor label space
const effectiveHeight =
orientation === 'vertical' ? height - X_AXIS_LABEL_RESERVED_HEIGHT : height;
const padding = {
left: 5,
right: 5,
top: 20,
bottom: 20,
};
const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
return {
opacity: hideOnInteraction && isActive.value ? 0 : 1,
};
}, [hideOnInteraction, isActive]);
const renderTicks = () => {
const [min, max] = domain;
const ticks = [];
const labels = [];
const tickLength = 5;
const labelOffset = 8;
// Calculate maximum label width needed dynamically
let maxLabelWidth = 0;
const fontSize = textStyle?.fontSize || 10;
const charWidth = fontSize * 0.6; // Dynamic character width based on font size
for (let i = 0; i <= tickCount; i++) {
const value = min + (max - min) * (i / tickCount);
const formattedValue = String(format(value));
// Dynamic width calculation based on actual font size
const estimatedWidth = formattedValue.length * charWidth;
maxLabelWidth = Math.max(maxLabelWidth, estimatedWidth);
}
// Dynamic width allocation - ensure we don't exceed available space
const maxAllowableWidth =
position === 'left' || position === 'right'
? Math.max(width * 0.3, 60) // Allow up to 30% of chart width for y-axis labels, minimum 60px
: width * 0.9; // For horizontal labels, use most of the width
const dynamicLabelWidth = Math.min(
Math.max(maxLabelWidth + 16, 40),
// Minimum 40px, add 16px padding
maxAllowableWidth // Don't exceed the calculated max
);
for (let i = 0; i <= tickCount; i++) {
const value = min + (max - min) * (i / tickCount);
const tickPosition = i / tickCount;
if (orientation === 'vertical') {
// Add padding to prevent labels from going off-screen
const topPadding = labelPadding;
const bottomPadding = labelPadding;
// Calculate y position with padding to keep labels on screen
const availableHeight = effectiveHeight - topPadding - bottomPadding;
const y = topPadding + availableHeight * (1 - tickPosition);
const x = position === 'left' ? padding.left : width - padding.right;
if (strokeWidth) {
ticks.push(
/*#__PURE__*/ _react.default.createElement(_reactNativeSvg.Line, {
key: `tick-${i}`,
x1: x,
y1: y,
x2: position === 'left' ? x - tickLength : x + tickLength,
y2: y,
stroke: color,
strokeWidth: strokeWidth,
})
);
}
labels.push(
/*#__PURE__*/ _react.default.createElement(
_reactNativeReanimated.default.View,
{
key: `label-${i}`,
style: [
styles.verticalLabel,
{
width: dynamicLabelWidth,
// Use calculated width
left:
position === 'left'
? Math.max(0, x - labelOffset - dynamicLabelWidth) // Keep labels within left boundary
: Math.min(
width - dynamicLabelWidth - 5,
x + labelOffset
),
// Keep labels within right boundary with 5px margin
top: y - 10, // Center the label vertically on the tick mark
},
position === 'left' ? styles.alignEnd : styles.alignStart,
],
},
/*#__PURE__*/ _react.default.createElement(
_reactNative.Text,
{
numberOfLines: 1,
ellipsizeMode: 'tail',
style: [
styles.labelText,
{
color: color,
},
position === 'left' ? styles.textRight : styles.textLeft,
textStyle,
],
adjustsFontSizeToFit: true,
minimumFontScale: 0.7,
},
String(format(value))
)
)
);
} else {
const x = width * tickPosition;
const y = position === 'top' ? 15 : height - padding.bottom;
if (strokeWidth) {
ticks.push(
/*#__PURE__*/ _react.default.createElement(_reactNativeSvg.Line, {
key: `tick-${i}`,
x1: x,
y1: y,
x2: x,
y2: position === 'top' ? y - tickLength : height - 15,
stroke: color,
strokeWidth: strokeWidth,
})
);
}
const alignmentStyle =
i === 0
? styles.alignStart
: i === tickCount
? styles.alignEnd
: styles.alignCenter;
const textAlignStyle =
i === 0
? styles.textLeft
: i === tickCount
? styles.textRight
: styles.textCenter;
labels.push(
/*#__PURE__*/ _react.default.createElement(
_reactNativeReanimated.default.View,
{
key: `label-${i}`,
style: [
styles.horizontalLabel,
{
width: dynamicLabelWidth,
// Use dynamic width for horizontal labels too
left: Math.max(
0,
Math.min(
width - dynamicLabelWidth,
i === 0
? x + labelOffset
: i === tickCount
? x - dynamicLabelWidth - labelOffset
: x - dynamicLabelWidth / 2
)
),
top:
position === 'top'
? Math.max(0, y - labelOffset - 15)
: Math.max(0, height - 35),
},
alignmentStyle,
],
},
/*#__PURE__*/ _react.default.createElement(
_reactNative.Text,
{
numberOfLines: 1,
ellipsizeMode: 'tail',
style: [
styles.labelText,
{
color: color,
},
textAlignStyle,
textStyle,
],
adjustsFontSizeToFit: true,
minimumFontScale: 0.7,
},
String(format(value))
)
)
);
}
}
return {
ticks,
labels,
};
};
const renderAxis = () => {
const { ticks, labels } = renderTicks();
let axisLine = null;
if (strokeWidth) {
switch (position) {
case 'left':
axisLine = /*#__PURE__*/ _react.default.createElement(
_reactNativeSvg.Line,
{
x1: padding.left,
y1: 0,
x2: padding.left,
y2: effectiveHeight,
stroke: color,
strokeWidth: strokeWidth,
}
);
break;
case 'right':
axisLine = /*#__PURE__*/ _react.default.createElement(
_reactNativeSvg.Line,
{
x1: width - padding.right,
y1: 0,
x2: width - padding.right,
y2: effectiveHeight,
stroke: color,
strokeWidth: strokeWidth,
}
);
break;
case 'top':
axisLine = /*#__PURE__*/ _react.default.createElement(
_reactNativeSvg.Line,
{
x1: 0,
y1: 20,
x2: width,
y2: 20,
stroke: color,
strokeWidth: strokeWidth,
}
);
break;
case 'bottom':
axisLine = /*#__PURE__*/ _react.default.createElement(
_reactNativeSvg.Line,
{
x1: 0,
y1: height - 20,
x2: width,
y2: height - 20,
stroke: color,
strokeWidth: strokeWidth,
}
);
break;
}
}
return {
axisLine,
ticks,
labels,
};
};
const { axisLine, ticks, labels } = renderAxis();
return /*#__PURE__*/ _react.default.createElement(
_reactNativeReanimated.default.View,
{
style: [styles.container, containerStyle, props.style, animatedStyle],
},
/*#__PURE__*/ _react.default.createElement(
_reactNativeSvg.Svg,
{
width: width,
height: height,
style: _reactNative.StyleSheet.absoluteFill,
},
axisLine,
ticks
),
/*#__PURE__*/ _react.default.createElement(
_reactNativeReanimated.default.View,
{
style: [_reactNative.StyleSheet.absoluteFill, styles.labelsContainer],
},
labels
)
);
};
exports.LineChartAxis = LineChartAxis;
const styles = _reactNative.StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
pointerEvents: 'none',
},
verticalLabel: {
position: 'absolute',
minHeight: 20,
zIndex: 1000,
overflow: 'visible',
justifyContent: 'center',
},
horizontalLabel: {
position: 'absolute',
minHeight: 20,
zIndex: 1000,
overflow: 'hidden',
justifyContent: 'center',
},
labelText: {
fontSize: 10,
},
labelsContainer: {
zIndex: 1000,
},
alignStart: {
alignItems: 'flex-start',
},
alignEnd: {
alignItems: 'flex-end',
},
alignCenter: {
alignItems: 'center',
},
textLeft: {
textAlign: 'left',
},
textRight: {
textAlign: 'right',
},
textCenter: {
textAlign: 'center',
},
});
//# sourceMappingURL=Axis.js.map