import * as React from "react"; import { isDefined, isNotDefined } from "@react-financial-charts/core"; import { getValueFromOverride, isHoverForInteractiveType, saveNodeType, terminate } from "./utils"; import { HoverTextNearMouse, MouseLocationIndicator } from "./components"; import { EachLinearRegressionChannel } from "./wrapper"; export interface StandardDeviationChannelProps { readonly enabled: boolean; readonly snapTo?: (datum: any) => number; readonly onStart?: () => void; readonly onComplete?: (e: React.MouseEvent, newChannels: any, moreProps: any) => void; readonly onSelect?: (e: React.MouseEvent, interactives: any[], moreProps: any) => void; readonly currentPositionStroke?: string; readonly currentPositionStrokeWidth?: number; readonly currentPositionOpacity?: number; readonly currentPositionRadius?: number; readonly appearance: { readonly stroke?: string; readonly strokeOpacity?: number; readonly strokeWidth?: number; readonly fill?: string; readonly fillOpacity?: number; readonly edgeStrokeWidth?: number; readonly edgeStroke?: string; readonly edgeFill?: string; readonly r?: number; }; readonly hoverText: object; readonly channels: any[]; } interface StandardDeviationChannelState { current?: any; override?: any; } export class StandardDeviationChannel extends React.Component< StandardDeviationChannelProps, StandardDeviationChannelState > { public static defaultProps = { snapTo: (d: any) => d.close, appearance: { stroke: "#000000", fillOpacity: 0.2, strokeOpacity: 1, strokeWidth: 1, fill: "#8AAFE2", edgeStrokeWidth: 2, edgeStroke: "#000000", edgeFill: "#FFFFFF", r: 5, }, currentPositionStroke: "#000000", currentPositionOpacity: 1, currentPositionStrokeWidth: 3, currentPositionRadius: 4, hoverText: { ...HoverTextNearMouse.defaultProps, enable: true, bgHeight: "auto", bgWidth: "auto", text: "Click and drag the edge circles", selectedText: "", }, channels: [], }; // @ts-ignore private getSelectionState: any; private mouseMoved: any; private saveNodeType: any; // @ts-ignore private terminate: any; public constructor(props: StandardDeviationChannelProps) { super(props); this.terminate = terminate.bind(this); this.saveNodeType = saveNodeType.bind(this); this.getSelectionState = isHoverForInteractiveType("channels").bind(this); this.state = {}; } public render() { const { appearance, channels, currentPositionOpacity, currentPositionRadius = StandardDeviationChannel.defaultProps.currentPositionRadius, currentPositionStroke, currentPositionStrokeWidth, enabled, hoverText, snapTo, } = this.props; const { current, override } = this.state; const eachDefaultAppearance = { ...StandardDeviationChannel.defaultProps.appearance, ...appearance, }; const hoverTextDefault = { ...StandardDeviationChannel.defaultProps.hoverText, ...hoverText, }; const tempLine = isDefined(current) && isDefined(current.end) ? ( ) : null; return ( {channels.map((each, idx) => { const eachAppearance = isDefined(each.appearance) ? { ...eachDefaultAppearance, ...each.appearance } : eachDefaultAppearance; const eachHoverText = isDefined(each.hoverText) ? { ...hoverTextDefault, ...each.hoverText } : hoverTextDefault; return ( ); })} {tempLine} ); } private handleEnd = (e: React.MouseEvent, xyValue: any, moreProps: any) => { const { current } = this.state; const { appearance, channels } = this.props; if (this.mouseMoved && isDefined(current) && isDefined(current.start)) { const newChannels = [ ...channels.map((d) => ({ ...d, selected: false })), { start: current.start, end: xyValue, selected: true, appearance, }, ]; this.setState( { current: null, }, () => { const { onComplete } = this.props; if (onComplete !== undefined) { onComplete(e, newChannels, moreProps); } }, ); } }; private readonly handleStart = (_: React.MouseEvent, xyValue: any) => { const { current } = this.state; if (isNotDefined(current) || isNotDefined(current.start)) { this.mouseMoved = false; this.setState( { current: { start: xyValue, end: null, }, }, () => { const { onStart } = this.props; if (onStart !== undefined) { onStart(); } }, ); } }; private readonly handleDrawLine = (e: React.MouseEvent, xyValue: any) => { const { current } = this.state; if (isDefined(current) && isDefined(current.start)) { this.mouseMoved = true; this.setState({ current: { start: current.start, end: xyValue, }, }); } }; private readonly handleDragLineComplete = (e: React.MouseEvent, moreProps: any) => { const { override } = this.state; const { channels } = this.props; if (isDefined(override)) { const newChannels = channels.map((each, idx) => idx === override.index ? { ...each, start: [override.x1Value, override.y1Value], end: [override.x2Value, override.y2Value], selected: true, } : each, ); this.setState( { override: null, }, () => { const { onComplete } = this.props; if (onComplete !== undefined) { onComplete(e, newChannels, moreProps); } }, ); } }; private readonly handleDragLine = (e: React.MouseEvent, index: number | undefined, newXYValue: any) => { this.setState({ override: { index, ...newXYValue, }, }); }; }