import * as React from "react"; import { ascending as d3Ascending } from "d3-array"; import { noop, strokeDashTypes } from "@react-financial-charts/core"; import { getXValue } from "@react-financial-charts/core/lib/utils/ChartDataUtil"; import { isHover, saveNodeType } from "../utils"; import { ClickableCircle, HoverTextNearMouse, InteractiveStraightLine } from "../components"; export interface EachTrendLineProps { readonly x1Value: any; readonly x2Value: any; readonly y1Value: any; readonly y2Value: any; readonly index?: number; readonly type: | "XLINE" // extends from -Infinity to +Infinity | "RAY" // extends to +/-Infinity in one direction | "LINE"; // extends between the set bounds readonly onDrag: (e: React.MouseEvent, index: number | undefined, moreProps: any) => void; readonly onEdge1Drag: any; // func readonly onEdge2Drag: any; // func readonly onDragComplete?: (e: React.MouseEvent, moreProps: any) => void; readonly onSelect: (e: React.MouseEvent, interactives: any[], moreProps: any) => void; readonly r: number; readonly strokeOpacity: number; readonly defaultClassName?: string; readonly selected?: boolean; readonly strokeStyle: string; readonly strokeWidth: number; readonly strokeDasharray: strokeDashTypes; readonly edgeStrokeWidth: number; readonly edgeStroke: string; readonly edgeInteractiveCursor: string; readonly lineInteractiveCursor: string; readonly edgeFill: string; readonly hoverText: { readonly enable: boolean; readonly fontFamily: string; readonly fontSize: number; readonly fill: string; readonly text: string; readonly selectedText: string; readonly bgFill: string; readonly bgOpacity: number; readonly bgWidth: number | string; readonly bgHeight: number | string; }; } interface EachTrendLineState { anchor?: string; hover?: any; } export class EachTrendLine extends React.Component { public static defaultProps = { onDrag: noop, onEdge1Drag: noop, onEdge2Drag: noop, onSelect: noop, selected: false, edgeStroke: "#000000", edgeFill: "#FFFFFF", edgeStrokeWidth: 2, r: 5, strokeWidth: 1, strokeDasharray: "Solid", hoverText: { enable: false, }, }; private dragStart: any; // @ts-ignore private isHover: any; private saveNodeType: any; public constructor(props: EachTrendLineProps) { super(props); this.isHover = isHover.bind(this); this.saveNodeType = saveNodeType.bind(this); this.state = { hover: false, }; } public render() { const { x1Value, y1Value, x2Value, y2Value, type, strokeStyle, strokeWidth, strokeDasharray, r, edgeStrokeWidth, edgeFill, edgeStroke, edgeInteractiveCursor, lineInteractiveCursor, hoverText, selected, onDragComplete, } = this.props; const { enable: hoverTextEnabled, selectedText: hoverTextSelected, text: hoverTextUnselected, ...restHoverTextProps } = hoverText; const { hover, anchor } = this.state; return ( ); } private readonly handleHover = (_: React.MouseEvent, moreProps: any) => { if (this.state.hover !== moreProps.hovering) { this.setState({ hover: moreProps.hovering, }); } }; private readonly handleEdge2Drag = (e: React.MouseEvent, moreProps: any) => { const { index, onDrag, x1Value, y1Value } = this.props; const [x2Value, y2Value] = getNewXY(moreProps); onDrag(e, index, { x1Value, y1Value, x2Value, y2Value, }); }; private readonly handleEdge1Drag = (e: React.MouseEvent, moreProps: any) => { const { index, onDrag, x2Value, y2Value } = this.props; const [x1Value, y1Value] = getNewXY(moreProps); onDrag(e, index, { x1Value, y1Value, x2Value, y2Value, }); }; private readonly handleDragComplete = (e: React.MouseEvent, moreProps: any) => { this.setState({ anchor: undefined, }); const { onDragComplete } = this.props; if (onDragComplete === undefined) { return; } onDragComplete(e, moreProps); }; private readonly handleEdge2DragStart = () => { this.setState({ anchor: "edge1", }); }; private readonly handleEdge1DragStart = () => { this.setState({ anchor: "edge2", }); }; private readonly handleLineDrag = (e: React.MouseEvent, moreProps: any) => { const { index, onDrag } = this.props; const { x1Value, y1Value, x2Value, y2Value } = this.dragStart; const { xScale, chartConfig: { yScale }, xAccessor, fullData, } = moreProps; const { startPos, mouseXY } = moreProps; const x1 = xScale(x1Value); const y1 = yScale(y1Value); const x2 = xScale(x2Value); const y2 = yScale(y2Value); const dx = startPos[0] - mouseXY[0]; const dy = startPos[1] - mouseXY[1]; const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); const newY1Value = yScale.invert(y1 - dy); const newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData); const newY2Value = yScale.invert(y2 - dy); onDrag(e, index, { x1Value: newX1Value, y1Value: newY1Value, x2Value: newX2Value, y2Value: newY2Value, }); }; private readonly handleLineDragStart = () => { const { x1Value, y1Value, x2Value, y2Value } = this.props; this.dragStart = { x1Value, y1Value, x2Value, y2Value, }; }; } export function getNewXY(moreProps: any) { const { xScale, chartConfig: { yScale }, xAccessor, plotData, mouseXY, } = moreProps; const mouseY = mouseXY[1]; const x = getXValue(xScale, xAccessor, mouseXY, plotData); const [small, big] = yScale.domain().slice().sort(d3Ascending); const y = yScale.invert(mouseY); const newY = Math.min(Math.max(y, small), big); return [x, newY]; }