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 } : {}), })} ); };