import React from 'react';
import { ColorValue } from 'react-native';
import Animated, {
withTiming,
useAnimatedProps,
} from 'react-native-reanimated';
import { Line, LineProps, NumberProp, Rect, RectProps } from 'react-native-svg';
import type { TCandle, TDomain } from './types';
import { getY, getHeight } from './utils';
const AnimatedRect = Animated.createAnimatedComponent(Rect);
const AnimatedLine = Animated.createAnimatedComponent(Line);
export type CandlestickChartCandleProps = {
candle: TCandle;
domain: TDomain;
maxHeight: number;
margin?: number;
positiveColor?: string;
negativeColor?: string;
index: number;
width: number;
rectProps?: RectProps;
lineProps?: LineProps;
useAnimations?: boolean;
renderRect?: ({
x,
y,
width,
height,
fill,
candle
}: {
x: NumberProp;
y: NumberProp;
width: NumberProp;
height: NumberProp;
fill: ColorValue;
useAnimations: boolean;
candle: TCandle;
}) => React.ReactNode;
renderLine?: ({
x1,
y1,
x2,
y2,
stroke,
strokeWidth,
candle,
}: {
x1: NumberProp;
y1: NumberProp;
x2: NumberProp;
y2: NumberProp;
stroke: ColorValue;
strokeWidth: NumberProp;
useAnimations: boolean;
candle: TCandle;
}) => React.ReactNode;
};
export const CandlestickChartCandle = ({
candle,
maxHeight,
domain,
margin = 2,
positiveColor = '#10b981',
negativeColor = '#ef4444',
rectProps: overrideRectProps,
lineProps: overrideLineProps,
index,
width,
useAnimations = true,
renderLine = (props) =>
props.useAnimations ? : ,
renderRect = (props) =>
props.useAnimations ? : ,
}: CandlestickChartCandleProps) => {
const { close, open, high, low } = candle;
const isPositive = close > open;
const fill = isPositive ? positiveColor : negativeColor;
const x = index * width;
const max = Math.max(open, close);
const min = Math.min(open, close);
const lineProps = React.useMemo(
() => ({
stroke: fill,
strokeWidth: 1,
direction: isPositive ? 'positive' : 'negative',
x1: x + width / 2,
y1: getY({ maxHeight, value: low, domain }),
x2: x + width / 2,
y2: getY({ maxHeight, value: high, domain }),
candle: candle,
...overrideLineProps,
}),
[
domain,
fill,
high,
isPositive,
low,
maxHeight,
overrideLineProps,
width,
x,
candle,
]
);
const animatedLineProps = useAnimatedProps(() => ({
x1: withTiming(x + width / 2),
y1: withTiming(getY({ maxHeight, value: low, domain })),
x2: withTiming(x + width / 2),
y2: withTiming(getY({ maxHeight, value: high, domain })),
}));
const rectProps = React.useMemo(
() => ({
width: width - margin * 2,
fill: fill,
direction: isPositive ? 'positive' : 'negative',
x: x + margin,
y: getY({ maxHeight, value: max, domain }),
height: getHeight({ maxHeight, value: max - min, domain }),
candle: candle,
...overrideRectProps,
}),
[
domain,
fill,
isPositive,
margin,
max,
maxHeight,
min,
overrideRectProps,
width,
x,
candle,
]
);
const animatedRectProps = useAnimatedProps(() => ({
x: withTiming(x + margin),
y: withTiming(getY({ maxHeight, value: max, domain })),
height: withTiming(getHeight({ maxHeight, value: max - min, domain })),
}));
return (
<>
{renderLine({
...lineProps,
useAnimations,
...(useAnimations ? { animatedProps: animatedLineProps } : {}),
})}
{renderRect({
...rectProps,
useAnimations,
...(useAnimations ? { animatedProps: animatedRectProps } : {}),
})}
>
);
};