import * as React from "react"; import { first, getAxisCanvas, getMouseCanvas, GenericChartComponent, last } from "@fishbot/chart-core"; export interface SARSeriesProps { readonly fillStyle?: { falling: string; rising: string; }; readonly strokeStyle?: { falling: string; rising: string; }; readonly highlightOnHover?: boolean; readonly onClick?: (e: React.MouseEvent, moreProps: any) => void; readonly onDoubleClick?: (e: React.MouseEvent, moreProps: any) => void; readonly onContextMenu?: (e: React.MouseEvent, moreProps: any) => void; readonly yAccessor: (datum: any) => number | undefined; } /** * SAR stands for 'stop and reverse'. * The indicator is below prices as they're rising and above * prices as they're falling. In this regard, the indicator * stops and reverses when the price trend reverses and breaks above or below the indicator. */ export class SARSeries extends React.Component { public static defaultProps = { fillStyle: { falling: "#4682B4", rising: "#15EC2E", }, highlightOnHover: false, }; public render() { const { highlightOnHover, onClick, onDoubleClick, onContextMenu } = this.props; const hoverProps = highlightOnHover ? { isHover: this.isHover, drawOn: ["mousemove", "pan"], canvasToDraw: getMouseCanvas, } : { drawOn: ["pan"], canvasToDraw: getAxisCanvas, }; return ( ); } private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => { const { yAccessor, fillStyle = SARSeries.defaultProps.fillStyle, strokeStyle } = this.props; const { xAccessor, plotData, xScale, chartConfig: { yScale }, hovering, } = moreProps; const width = xScale(xAccessor(last(plotData))) - xScale(xAccessor(first(plotData))); const d = ((width / plotData.length) * 0.5) / 2; const radius = Math.min(2, Math.max(0.5, d)) + (hovering ? 2 : 0); (plotData as any[]).forEach((each) => { const yValue = yAccessor(each); if (yValue === undefined) { return; } const centerX = xScale(xAccessor(each)); const centerY = yScale(yValue); const color = yValue > each.close ? fillStyle.falling : fillStyle.rising; ctx.fillStyle = color; if (strokeStyle !== undefined) { ctx.strokeStyle = yValue > each.close ? strokeStyle.falling : strokeStyle.rising; } ctx.beginPath(); ctx.ellipse(centerX, centerY, radius, radius, 0, 0, 2 * Math.PI); ctx.closePath(); ctx.fill(); if (strokeStyle !== undefined) { ctx.stroke(); } }); }; private readonly isHover = (moreProps: any) => { const { mouseXY, currentItem, chartConfig: { yScale }, } = moreProps; const { yAccessor } = this.props; const y = mouseXY[1]; const currentY = yScale(yAccessor(currentItem)); return y < currentY + 5 && y > currentY - 5; }; }