diff --git a/packages/axes/src/Axis.tsx b/packages/axes/src/Axis.tsx index 8ebed9dc2..4ba3550f1 100644 --- a/packages/axes/src/Axis.tsx +++ b/packages/axes/src/Axis.tsx @@ -1,5 +1,4 @@ import { - colorToRGBA, first, getAxisCanvas, GenericChartComponent, @@ -15,7 +14,7 @@ import * as React from "react"; import { AxisZoomCapture } from "./AxisZoomCapture"; interface AxisProps { - readonly axisZoomCallback?: (domain: any) => void; + readonly axisZoomCallback?: (domain: number[]) => void; readonly bg: { h: number; x: number; @@ -47,13 +46,12 @@ interface AxisProps { readonly tickSize?: number; readonly ticks?: number; readonly tickLabelFill?: string; - readonly tickStroke?: string; - readonly tickStrokeOpacity?: number; + readonly tickStrokeStyle?: string | CanvasGradient | CanvasPattern; readonly tickStrokeWidth?: number; readonly tickStrokeDasharray?: strokeDashTypes; - readonly tickValues?: number[] | any; // func + readonly tickValues?: number[] | ((domain: number[]) => number[]); readonly tickInterval?: number; - readonly tickIntervalFunction?: any; // func + readonly tickIntervalFunction?: (min: number, max: number, tickInterval: number) => number[]; readonly transform: number[]; readonly zoomEnabled?: boolean; readonly zoomCursorClassName?: string; @@ -118,7 +116,7 @@ export class Axis extends React.Component { return this.chartRef.current!.getMoreProps(); }; - private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps) => { + private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => { const { showDomain, showTicks, transform, range, getScale } = this.props; ctx.save(); @@ -154,14 +152,13 @@ const tickHelper = (props: AxisProps, scale: ScaleContinuousNumeric }; const drawTicks = (ctx: CanvasRenderingContext2D, result, moreProps) => { - const { showGridLines, tickStroke, tickStrokeOpacity, tickLabelFill } = result; + const { showGridLines, tickStrokeStyle, tickLabelFill } = result; const { textAnchor, fontSize, fontFamily, fontWeight, ticks, showTickLabel } = result; - ctx.strokeStyle = colorToRGBA(tickStroke, tickStrokeOpacity); - ctx.fillStyle = tickStroke; + if (tickStrokeStyle !== undefined) { + ctx.strokeStyle = tickStrokeStyle; + ctx.fillStyle = tickStrokeStyle; + } ticks.forEach((tick) => { drawEachTick(ctx, tick, result); @@ -339,13 +337,15 @@ const drawTicks = (ctx: CanvasRenderingContext2D, result, moreProps) => { }; const drawGridLine = (ctx: CanvasRenderingContext2D, tick, result, moreProps) => { - const { orient, gridLinesStrokeWidth, gridLinesStroke, gridLinesStrokeDasharray } = result; + const { orient, gridLinesStrokeWidth, gridLinesStrokeStyle, gridLinesStrokeDasharray } = result; const { chartConfig } = moreProps; const { height, width } = chartConfig; - ctx.strokeStyle = colorToRGBA(gridLinesStroke); + if (gridLinesStrokeStyle !== undefined) { + ctx.strokeStyle = gridLinesStrokeStyle; + } ctx.beginPath(); const sign = orient === "top" || orient === "left" ? 1 : -1; diff --git a/packages/axes/src/AxisZoomCapture.tsx b/packages/axes/src/AxisZoomCapture.tsx index 9b8133825..56e71598b 100644 --- a/packages/axes/src/AxisZoomCapture.tsx +++ b/packages/axes/src/AxisZoomCapture.tsx @@ -16,8 +16,8 @@ import { ScaleContinuousNumeric } from "d3-scale"; import { event as d3Event, mouse, select, touches } from "d3-selection"; import * as React from "react"; -interface AxisZoomCaptureProps { - readonly axisZoomCallback?: (domain: any) => void; +export interface AxisZoomCaptureProps { + readonly axisZoomCallback?: (domain: number[]) => void; readonly bg: { h: number; x: number; @@ -35,7 +35,7 @@ interface AxisZoomCaptureProps { readonly outerTickSize?: number; readonly showDomain?: boolean; readonly showTicks?: boolean; - readonly tickFormat?: any; // func + readonly tickFormat?: (datum: number) => string; readonly tickPadding?: number; readonly tickSize?: number; readonly ticks?: number; diff --git a/packages/axes/src/XAxis.tsx b/packages/axes/src/XAxis.tsx index f9f580c80..18f710457 100644 --- a/packages/axes/src/XAxis.tsx +++ b/packages/axes/src/XAxis.tsx @@ -11,7 +11,7 @@ export interface XAxisProps { readonly fontSize?: number; readonly fontWeight?: number; readonly getMouseDelta?: (startXY: [number, number], mouseXY: [number, number]) => number; - readonly gridLinesStroke?: string; + readonly gridLinesStrokeStyle?: string; readonly gridLinesStrokeWidth?: number; readonly gridLinesStrokeDasharray?: strokeDashTypes; readonly innerTickSize?: number; @@ -30,8 +30,7 @@ export interface XAxisProps { readonly tickSize?: number; readonly tickLabelFill?: string; readonly ticks?: number; - readonly tickStroke?: string; - readonly tickStrokeOpacity?: number; + readonly tickStrokeStyle?: string; readonly tickStrokeWidth?: number; readonly tickStrokeDasharray?: strokeDashTypes; readonly tickValues?: number[]; @@ -49,7 +48,7 @@ export class XAxis extends React.Component { fontSize: 12, fontWeight: 400, getMouseDelta: (startXY: [number, number], mouseXY: [number, number]) => startXY[0] - mouseXY[0], - gridLinesStroke: "#E2E4EC", + gridLinesStrokeStyle: "#E2E4EC", gridLinesStrokeWidth: 1, orient: "bottom", outerTickSize: 0, @@ -62,8 +61,7 @@ export class XAxis extends React.Component { strokeWidth: 1, tickPadding: 4, tickLabelFill: "#000000", - tickStroke: "#000000", - tickStrokeOpacity: 1, + tickStrokeStyle: "#000000", xZoomHeight: 25, zoomEnabled: true, zoomCursorClassName: "react-financial-charts-ew-resize-cursor", @@ -102,7 +100,7 @@ export class XAxis extends React.Component { ); } - private readonly axisZoomCallback = (newXDomain) => { + private readonly axisZoomCallback = (newXDomain: number[]) => { const { xAxisZoom } = this.context; xAxisZoom(newXDomain); diff --git a/packages/axes/src/YAxis.tsx b/packages/axes/src/YAxis.tsx index 1bb7e93b1..471da3cf6 100644 --- a/packages/axes/src/YAxis.tsx +++ b/packages/axes/src/YAxis.tsx @@ -11,7 +11,7 @@ export interface YAxisProps { readonly fontSize?: number; readonly fontWeight?: number; readonly getMouseDelta?: (startXY: [number, number], mouseXY: [number, number]) => number; - readonly gridLinesStroke?: string; + readonly gridLinesStrokeStyle?: string; readonly gridLinesStrokeWidth?: number; readonly gridLinesStrokeDasharray?: strokeDashTypes; readonly innerTickSize?: number; @@ -30,8 +30,7 @@ export interface YAxisProps { readonly tickSize?: number; readonly tickLabelFill?: string; readonly ticks?: number; - readonly tickStroke?: string; - readonly tickStrokeOpacity?: number; + readonly tickStrokeStyle?: string; readonly tickStrokeWidth?: number; readonly tickStrokeDasharray?: strokeDashTypes; readonly tickValues?: number[]; @@ -49,7 +48,7 @@ export class YAxis extends React.Component { fontSize: 12, fontWeight: 400, getMouseDelta: (startXY, mouseXY) => startXY[1] - mouseXY[1], - gridLinesStroke: "#E2E4EC", + gridLinesStrokeStyle: "#E2E4EC", gridLinesStrokeWidth: 1, innerTickSize: 4, outerTickSize: 0, @@ -62,8 +61,7 @@ export class YAxis extends React.Component { strokeWidth: 1, tickPadding: 4, tickLabelFill: "#000000", - tickStroke: "#000000", - tickStrokeOpacity: 1, + tickStrokeStyle: "#000000", yZoomWidth: 40, zoomEnabled: true, zoomCursorClassName: "react-financial-charts-ns-resize-cursor", @@ -101,8 +99,9 @@ export class YAxis extends React.Component { ); } - private readonly axisZoomCallback = (newYDomain) => { + private readonly axisZoomCallback = (newYDomain: number[]) => { const { chartId, yAxisZoom } = this.context; + yAxisZoom(chartId, newYDomain); }; diff --git a/packages/coordinates/src/CrossHairCursor.tsx b/packages/coordinates/src/CrossHairCursor.tsx index 43c3fac9a..8518ca5e0 100644 --- a/packages/coordinates/src/CrossHairCursor.tsx +++ b/packages/coordinates/src/CrossHairCursor.tsx @@ -54,6 +54,7 @@ export class CrossHairCursor extends React.Component { } const { margin, ratio } = this.context; + const originX = 0.5 * ratio + margin.left; const originY = 0.5 * ratio + margin.top; diff --git a/packages/coordinates/src/CurrentCoordinate.tsx b/packages/coordinates/src/CurrentCoordinate.tsx index 22d551e76..09daaaf29 100644 --- a/packages/coordinates/src/CurrentCoordinate.tsx +++ b/packages/coordinates/src/CurrentCoordinate.tsx @@ -2,17 +2,38 @@ import { getMouseCanvas, GenericChartComponent } from "@react-financial-charts/c import * as React from "react"; export interface CurrentCoordinateProps { + /** + * Fill style for the circle. + */ readonly fillStyle?: | string | CanvasGradient | CanvasPattern | ((datum: any) => string | CanvasGradient | CanvasPattern); + /** + * The radius to draw the circle + */ readonly r: number; + /** + * Stroke of the circle + */ + readonly strokeStyle?: + | string + | CanvasGradient + | CanvasPattern + | ((datum: any) => string | CanvasGradient | CanvasPattern); + /** + * Y accessor to use for the circle. + */ readonly yAccessor: (item: any) => number; } +/** + * Draws a circle at the current x location of radius `r`. + */ export class CurrentCoordinate extends React.Component { public static defaultProps = { + fillStyle: "#2196f3", r: 3, }; @@ -32,15 +53,24 @@ export class CurrentCoordinate extends React.Component { return; } - const { fillStyle, r } = this.props; + const { fillStyle, r, strokeStyle } = this.props; const fillColor = fillStyle instanceof Function ? fillStyle(moreProps.currentItem) : fillStyle; if (fillColor !== undefined) { ctx.fillStyle = fillColor; } + + const strokeColor = strokeStyle instanceof Function ? strokeStyle(moreProps.currentItem) : strokeStyle; + if (strokeColor !== undefined) { + ctx.strokeStyle = strokeColor; + } + ctx.beginPath(); ctx.arc(circle.x, circle.y, r, 0, 2 * Math.PI, false); ctx.fill(); + if (strokeColor !== undefined) { + ctx.stroke(); + } }; private readonly getCircle = (moreProps) => { diff --git a/packages/coordinates/src/Cursor.tsx b/packages/coordinates/src/Cursor.tsx index 0d43d979c..034088841 100644 --- a/packages/coordinates/src/Cursor.tsx +++ b/packages/coordinates/src/Cursor.tsx @@ -1,10 +1,8 @@ import { - colorToRGBA, first, getStrokeDasharrayCanvas, GenericComponent, getMouseCanvas, - isNotDefined, last, strokeDashTypes, } from "@react-financial-charts/core"; @@ -12,16 +10,23 @@ import * as PropTypes from "prop-types"; import * as React from "react"; export interface CursorProps { + readonly customX?: (props: CursorProps, moreProps: any) => number; readonly disableYCursor?: boolean; - readonly opacity?: number; readonly snapX?: boolean; - readonly stroke?: string; readonly strokeDasharray?: strokeDashTypes; + readonly strokeStyle?: string | CanvasGradient | CanvasPattern; readonly useXCursorShape?: boolean; - readonly xCursorShapeFill?: string | any; // func - readonly xCursorShapeStroke: string | any; // func + readonly xCursorShapeFillStyle?: + | string + | CanvasGradient + | CanvasPattern + | ((currentItem: any) => string | CanvasGradient | CanvasPattern); + readonly xCursorShapeStrokeStyle?: + | string + | CanvasGradient + | CanvasPattern + | ((currentItem: any) => string | CanvasGradient | CanvasPattern); readonly xCursorShapeStrokeDasharray?: strokeDashTypes; - readonly xCursorShapeOpacity?: number; } const defaultCustomSnapX = (props: CursorProps, moreProps) => { @@ -33,15 +38,13 @@ const defaultCustomSnapX = (props: CursorProps, moreProps) => { export class Cursor extends React.Component { public static defaultProps = { - stroke: "#000000", - opacity: 0.3, + strokeStyle: "rgba(55, 71, 79, 0.8)", strokeDasharray: "ShortDash", snapX: true, - customSnapX: defaultCustomSnapX, + customX: defaultCustomSnapX, disableYCursor: false, useXCursorShape: false, - xCursorShapeStroke: "#000000", - xCursorShapeOpacity: 0.5, + xCursorShapeStrokeStyle: "rgba(0, 0, 0, 0.5)", }; public static contextTypes = { @@ -60,16 +63,18 @@ export class Cursor extends React.Component { ); } - private getXCursorShapeStroke(moreProps) { - const { xCursorShapeStroke } = this.props; - const { currentItem } = moreProps; - return xCursorShapeStroke instanceof Function ? xCursorShapeStroke(currentItem) : xCursorShapeStroke; + private getXCursorShapeStroke({ currentItem }): string | CanvasGradient | CanvasPattern | undefined { + const { xCursorShapeStrokeStyle } = this.props; + + return xCursorShapeStrokeStyle instanceof Function + ? xCursorShapeStrokeStyle(currentItem) + : xCursorShapeStrokeStyle; } - private getXCursorShapeFill(moreProps) { - const { xCursorShapeFill } = this.props; - const { currentItem } = moreProps; - return xCursorShapeFill instanceof Function ? xCursorShapeFill(currentItem) : xCursorShapeFill; + private getXCursorShapeFill({ currentItem }): string | CanvasGradient | CanvasPattern | undefined { + const { xCursorShapeFillStyle } = this.props; + + return xCursorShapeFillStyle instanceof Function ? xCursorShapeFillStyle(currentItem) : xCursorShapeFillStyle; } private getXCursorShape(moreProps /* , ctx */) { @@ -84,35 +89,35 @@ export class Cursor extends React.Component { return { height, xPos, shapeWidth }; } - private getXYCursor(props, moreProps) { + private getXYCursor(props: CursorProps, moreProps) { const { mouseXY, currentItem, show, height, width } = moreProps; - const { customSnapX, stroke, opacity, strokeDasharray, disableYCursor } = props; - if (!show || isNotDefined(currentItem)) { + const { customX = Cursor.defaultProps.customX, strokeStyle, strokeDasharray, disableYCursor } = props; + + if (!show || currentItem === undefined) { return undefined; } const yCursor = { x1: 0, x2: width, - y1: mouseXY[1], - y2: mouseXY[1], - stroke, + y1: mouseXY[1] + 0.5, + y2: mouseXY[1] + 0.5, + strokeStyle, strokeDasharray, - opacity, - id: "yCursor", + isXCursor: false, }; - const x = customSnapX(props, moreProps); + + const x = customX(props, moreProps); const xCursor = { x1: x, x2: x, y1: 0, y2: height, - stroke, + strokeStyle, strokeDasharray, - opacity, - id: "xCursor", + isXCursor: true, }; return disableYCursor ? [xCursor] : [yCursor, xCursor]; @@ -120,55 +125,61 @@ export class Cursor extends React.Component { private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps) => { const cursors = this.getXYCursor(this.props, moreProps); + if (cursors === undefined) { + return; + } - if (cursors !== undefined) { - const { useXCursorShape } = this.props; + const { useXCursorShape } = this.props; - const { margin, ratio } = this.context; - const originX = 0.5 * ratio + margin.left; - const originY = 0.5 * ratio + margin.top; + const { margin, ratio } = this.context; - ctx.save(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.scale(ratio, ratio); + const originX = 0.5 * ratio + margin.left; + const originY = 0.5 * ratio + margin.top; - ctx.translate(originX, originY); + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(ratio, ratio); + ctx.translate(originX, originY); - cursors.forEach((line) => { - const dashArray = getStrokeDasharrayCanvas(line.strokeDasharray); - const xShapeFill = this.getXCursorShapeFill(moreProps); + cursors.forEach((line) => { + if (useXCursorShape && line.isXCursor) { + const { xCursorShapeStrokeDasharray } = this.props; + if (xCursorShapeStrokeDasharray !== undefined) { + const xShapeStrokeStyle = this.getXCursorShapeStroke(moreProps); + if (xShapeStrokeStyle !== undefined) { + ctx.strokeStyle = xShapeStrokeStyle; + } + ctx.setLineDash(getStrokeDasharrayCanvas(xCursorShapeStrokeDasharray)); + } - if (useXCursorShape && line.id === "xCursor") { - const { xCursorShapeOpacity, xCursorShapeStrokeDasharray } = this.props; - const xShape = this.getXCursorShape(moreProps); + ctx.beginPath(); + const xShapeFillStyle = this.getXCursorShapeFill(moreProps); + if (xShapeFillStyle !== undefined) { + ctx.fillStyle = xShapeFillStyle; + } - if (xCursorShapeStrokeDasharray != null) { - const xShapeStroke = this.getXCursorShapeStroke(moreProps); - ctx.strokeStyle = colorToRGBA(xShapeStroke, xCursorShapeOpacity); - ctx.setLineDash(getStrokeDasharrayCanvas(xCursorShapeStrokeDasharray)); - } + ctx.beginPath(); - ctx.beginPath(); - ctx.fillStyle = - xShapeFill != null ? colorToRGBA(xShapeFill, xCursorShapeOpacity) : "rgba(0, 0, 0, 0)"; // ="transparent" - - ctx.beginPath(); - xCursorShapeStrokeDasharray == null - ? ctx.fillRect(xShape.xPos, 0, xShape.shapeWidth, xShape.height) - : ctx.rect(xShape.xPos, 0, xShape.shapeWidth, xShape.height); - ctx.fill(); - } else { - ctx.strokeStyle = colorToRGBA(line.stroke, line.opacity); - ctx.setLineDash(dashArray); - ctx.beginPath(); - ctx.moveTo(line.x1, line.y1); - ctx.lineTo(line.x2, line.y2); + const xShape = this.getXCursorShape(moreProps); + xCursorShapeStrokeDasharray === undefined + ? ctx.fillRect(xShape.xPos, 0, xShape.shapeWidth, xShape.height) + : ctx.rect(xShape.xPos, 0, xShape.shapeWidth, xShape.height); + ctx.fill(); + } else { + if (line.strokeStyle !== undefined) { + ctx.strokeStyle = line.strokeStyle; } - ctx.stroke(); - }); + const dashArray = getStrokeDasharrayCanvas(line.strokeDasharray); + ctx.setLineDash(dashArray); + ctx.beginPath(); + ctx.moveTo(line.x1, line.y1); + ctx.lineTo(line.x2, line.y2); + } - ctx.restore(); - } + ctx.stroke(); + }); + + ctx.restore(); }; } diff --git a/packages/coordinates/src/PriceCoordinate.tsx b/packages/coordinates/src/PriceCoordinate.tsx index eb16910b3..cb6719dd4 100644 --- a/packages/coordinates/src/PriceCoordinate.tsx +++ b/packages/coordinates/src/PriceCoordinate.tsx @@ -1,32 +1,34 @@ +import { format } from "d3-format"; import * as React from "react"; import { getAxisCanvas, GenericChartComponent, functor, strokeDashTypes } from "@react-financial-charts/core"; import { drawOnCanvas } from "./EdgeCoordinateV3"; interface PriceCoordinateProps { - readonly displayFormat: any; // func - readonly yAxisPad?: number; - readonly rectWidth?: number; - readonly rectHeight?: number; - readonly orient?: "bottom" | "top" | "left" | "right"; + readonly arrowWidth?: number; readonly at?: "bottom" | "top" | "left" | "right"; - readonly price?: number; + readonly displayFormat: (n: number) => string; readonly dx?: number; - readonly arrowWidth?: number; - readonly opacity?: number; - readonly lineOpacity?: number; - readonly lineStroke?: string; readonly fontFamily?: string; readonly fontSize?: number; - readonly fill?: string | any; // func + readonly fill?: string | ((price: number) => string); + readonly lineOpacity?: number; + readonly lineStroke?: string; + readonly opacity?: number; + readonly orient?: "bottom" | "top" | "left" | "right"; + readonly price: number; + readonly rectWidth?: number; + readonly rectHeight?: number; readonly strokeDasharray?: strokeDashTypes; readonly stroke?: string; readonly strokeOpacity?: number; readonly strokeWidth?: number; - readonly textFill?: string | any; // func + readonly textFill?: string | ((price: number) => string); + readonly yAxisPad?: number; } export class PriceCoordinate extends React.Component { public static defaultProps = { + displayFormat: format(".2f"), yAxisPad: 0, rectWidth: 50, rectHeight: 20, @@ -63,7 +65,7 @@ export class PriceCoordinate extends React.Component { drawOnCanvas(ctx, props); }; - private readonly helper = (props, moreProps) => { + private readonly helper = (props: PriceCoordinateProps, moreProps) => { const { chartConfig: { yScale }, width, diff --git a/packages/core/src/Chart.tsx b/packages/core/src/Chart.tsx index 7c8d1e90c..c2102790e 100644 --- a/packages/core/src/Chart.tsx +++ b/packages/core/src/Chart.tsx @@ -8,8 +8,8 @@ export interface ChartProps { readonly flipYScale?: boolean; readonly height?: number; readonly id: number | string; - readonly onContextMenu?: (event: React.MouseEvent, props: any) => void; - readonly onDoubleClick?: (event: React.MouseEvent, props: any) => void; + readonly onContextMenu?: (event: React.MouseEvent, moreProps: any) => void; + readonly onDoubleClick?: (event: React.MouseEvent, moreProps: any) => void; readonly origin?: number[] | ((width: number, height: number) => number[]); readonly padding?: number | { top: number; bottom: number }; readonly yExtents?: number[] | ((data: any) => number) | ((data: any) => number[]); diff --git a/packages/core/src/ChartCanvas.tsx b/packages/core/src/ChartCanvas.tsx index 611c63431..a060368cf 100644 --- a/packages/core/src/ChartCanvas.tsx +++ b/packages/core/src/ChartCanvas.tsx @@ -192,19 +192,8 @@ function updateChart(newState, initialXScale, props, lastItemWasVisible, initial function calculateState(props) { const { xAccessor: inputXAccesor, xExtents: xExtentsProp, data, padding, flipXScale } = props; - if (process.env.NODE_ENV !== "production" && isDefined(props.xScale.invert)) { - for (let i = 1; i < data.length; i++) { - const prev = data[i - 1]; - const curr = data[i]; - if (inputXAccesor(prev) > inputXAccesor(curr)) { - throw new Error( - "'data' is not sorted on 'xAccessor', send 'data' sorted in ascending order of 'xAccessor'", - ); - } - } - } - const direction = getXScaleDirection(flipXScale); + const dimensions = getDimensions(props); const extent = @@ -213,6 +202,7 @@ function calculateState(props) { : d3Extent(xExtentsProp.map((d) => functor(d)).map((each) => each(data, inputXAccesor))); const { xAccessor, displayXAccessor, xScale, fullData, filterData } = calculateFullData(props); + const updatedXScale = setXRange(xScale, dimensions, padding, direction); const { plotData, domain } = filterData(fullData, extent, inputXAccesor, updatedXScale); @@ -273,6 +263,8 @@ export interface ChartCanvasProps { readonly data: any[]; readonly defaultFocus?: boolean; readonly disableInteraction?: boolean; + readonly disablePan?: boolean; + readonly disableZoom?: boolean; readonly displayXAccessor: any; // func readonly flipXScale?: boolean; readonly height: number; @@ -296,7 +288,6 @@ export interface ChartCanvasProps { right: number; top: number; }; - readonly panEvent?: boolean; readonly pointsPerPxThreshold?: number; readonly postCalculator?: (plotData: any[]) => any[]; readonly ratio: number; @@ -308,7 +299,6 @@ export interface ChartCanvasProps { readonly xScale: ScaleContinuousNumeric; readonly zIndex?: number; readonly zoomAnchor?: (options: IZoomAnchorOptions) => number; - readonly zoomEvent?: boolean; readonly zoomMultiplier?: number; } @@ -318,7 +308,7 @@ interface ChartCanvasState { filterData?: any; chartConfig?: any; plotData?: any; - xScale?: ScaleContinuousNumeric; + xScale: ScaleContinuousNumeric; } export class ChartCanvas extends React.Component { @@ -326,7 +316,9 @@ export class ChartCanvas extends React.Component(); private readonly eventCaptureRef = React.createRef(); private finalPinch?: boolean; - private fullData; + private fullData: any[]; private lastSubscriptionId = 0; private mutableState = {}; private panInProgress = false; - private prevMouseXY; + private prevMouseXY?: number[]; private subscriptions: any[] = []; private waitingForPinchZoomAnimationFrame?: boolean; private waitingForPanAnimationFrame?: boolean; @@ -458,13 +448,14 @@ export class ChartCanvas extends React.Component { + public subscribe = (id: string, rest) => { const { getPanConditions = functor({ draggable: false, panEnabled: true, }), } = rest; + this.subscriptions = this.subscriptions.concat({ id, ...rest, @@ -472,7 +463,7 @@ export class ChartCanvas extends React.Component { + public unsubscribe = (id: string) => { this.subscriptions = this.subscriptions.filter((each) => each.id !== id); }; @@ -797,18 +788,16 @@ export class ChartCanvas extends React.Component { + public panHelper = ( + mouseXY: number[], + initialXScale: ScaleContinuousNumeric, + { dx, dy }: { dx: number; dy: number }, + chartsToPan: string[], + ) => { const { xAccessor, displayXAccessor, chartConfig: initialChartConfig, filterData } = this.state; const { fullData } = this; const { postCalculator = ChartCanvas.defaultProps.postCalculator } = this.props; - if (isNotDefined(initialXScale.invert)) { - throw new Error( - "xScale provided does not have an invert() method." + - "You are likely using an ordinal scale. This scale does not support zoom, pan", - ); - } - const newDomain = initialXScale .range() .map((x) => x - dx) @@ -842,7 +831,13 @@ export class ChartCanvas extends React.Component { + public handlePan = ( + mousePosition: number[], + panStartXScale: ScaleContinuousNumeric, + dxdy: { dx: number; dy: number }, + chartsToPan: string[], + e: React.MouseEvent, + ) => { if (!this.waitingForPanAnimationFrame) { this.waitingForPanAnimationFrame = true; @@ -873,7 +868,13 @@ export class ChartCanvas extends React.Component { + public handlePanEnd = ( + mousePosition: number[], + panStartXScale: ScaleContinuousNumeric, + dxdy: { dx: number; dy: number }, + chartsToPan: string[], + e: React.MouseEvent | React.TouchEvent, + ) => { const state = this.panHelper(mousePosition, panStartXScale, dxdy, chartsToPan); this.hackyWayToStopPanBeyondBounds__plotData = null; this.hackyWayToStopPanBeyondBounds__domain = null; @@ -913,11 +914,11 @@ export class ChartCanvas extends React.Component { + public handleMouseDown = (_: number[], __: string[], e: React.MouseEvent) => { this.triggerEvent("mousedown", this.mutableState, e); }; - public handleMouseEnter = (e) => { + public handleMouseEnter = (e: React.MouseEvent) => { this.triggerEvent( "mouseenter", { @@ -927,7 +928,7 @@ export class ChartCanvas extends React.Component { + public handleMouseMove = (mouseXY: number[], _: string, e) => { if (!this.waitingForMouseMoveAnimationFrame) { this.waitingForMouseMoveAnimationFrame = true; @@ -972,7 +973,7 @@ export class ChartCanvas extends React.Component { + public handleDrag = ({ startPos, mouseXY }: { startPos: number[]; mouseXY: number[] }, e: React.MouseEvent) => { const { chartConfig, plotData, xScale, xAccessor } = this.state; const currentCharts = getCurrentCharts(chartConfig, mouseXY); const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); @@ -1000,7 +1001,7 @@ export class ChartCanvas extends React.Component { + public handleDragEnd = ({ mouseXY }: { mouseXY: number[] }, e: React.MouseEvent) => { this.triggerEvent("dragend", { mouseXY }, e); requestAnimationFrame(() => { @@ -1009,7 +1010,7 @@ export class ChartCanvas extends React.Component { + public handleClick = (_: number[], e: React.MouseEvent) => { this.triggerEvent("click", this.mutableState, e); requestAnimationFrame(() => { @@ -1018,7 +1019,7 @@ export class ChartCanvas extends React.Component { + public handleDoubleClick = (_: number[], e: React.MouseEvent) => { this.triggerEvent("dblclick", {}, e); }; @@ -1118,6 +1119,9 @@ export class ChartCanvas extends React.Component; readonly disableInteraction: boolean; readonly getAllPanConditions: () => { panEnabled: boolean; draggable: boolean }[]; - readonly onMouseMove?: (touchXY: number[], eventType: string, event: React.TouchEvent) => void; - readonly onMouseEnter?: (event: any) => void; + readonly onClick?: (mouseXY: number[], event: React.MouseEvent) => void; + readonly onContextMenu?: (mouseXY: number[], event: React.MouseEvent) => void; + readonly onDoubleClick?: (mouseXY: number[], event: React.MouseEvent) => void; + readonly onDragStart?: (details: { startPos: number[] }, event: React.MouseEvent) => void; + readonly onDrag?: (details: { startPos: number[]; mouseXY: number[] }, event: React.MouseEvent) => void; + readonly onDragComplete?: (details: { mouseXY: number[] }, event: React.MouseEvent) => void; + readonly onMouseDown?: (mouseXY: number[], currentCharts: string[], event: React.MouseEvent) => void; + readonly onMouseMove?: (touchXY: number[], eventType: string, event: React.MouseEvent | React.TouchEvent) => void; + readonly onMouseEnter?: (event: React.MouseEvent) => void; readonly onMouseLeave?: (event: React.MouseEvent) => void; - readonly onZoom?: (zoomDir: 1 | -1, mouseXY: number[], event: React.WheelEvent) => void; - readonly onPinchZoom?: (initialPinch, { touch1Pos, touch2Pos, xScale }, e) => void; - readonly onPinchZoomEnd?: (initialPinch, e) => void; + readonly onPinchZoom?: ( + initialPinch: { + xScale: ScaleContinuousNumeric; + touch1Pos: [number, number]; + touch2Pos: [number, number]; + range: number[]; + }, + currentPinch: { + xScale: ScaleContinuousNumeric; + touch1Pos: [number, number]; + touch2Pos: [number, number]; + }, + e: React.TouchEvent, + ) => void; + readonly onPinchZoomEnd?: ( + initialPinch: { + xScale: ScaleContinuousNumeric; + touch1Pos: [number, number]; + touch2Pos: [number, number]; + range: number[]; + }, + e: React.TouchEvent, + ) => void; readonly onPan?: ( mouseXY: number[], - panStartXScale, - dxdy: { dx: number; dy: number }, - chartsToPan: any[], + panStartXScale: ScaleContinuousNumeric, + panOrigin: { dx: number; dy: number }, + chartsToPan: string[], e: React.MouseEvent, ) => void; - readonly onPanEnd?: (mouseXY: number[], panStartXScale, panOrigin, chartsToPan: any[], e) => void; - readonly onDragStart?: (details: { startPos: number[] }, event: React.MouseEvent) => void; - readonly onDrag?: (details: { startPos: number[]; mouseXY: number[] }, event: React.MouseEvent) => void; - readonly onDragComplete?: (details: { mouseXY: number[] }, event: React.MouseEvent) => void; - readonly onClick?: (mouseXY: number[], event: React.MouseEvent) => void; - readonly onDoubleClick?: (mouseXY: number[], event: React.MouseEvent) => void; - readonly onContextMenu?: (mouseXY: number[], event: React.MouseEvent) => void; - readonly onMouseDown?: (mouseXY: number[], currentCharts: any, event: React.MouseEvent) => void; + readonly onPanEnd?: ( + mouseXY: number[], + panStartXScale: ScaleContinuousNumeric, + panOrigin: { dx: number; dy: number }, + chartsToPan: string[], + e: React.MouseEvent | React.TouchEvent, + ) => void; + readonly onZoom?: (zoomDir: 1 | -1, mouseXY: number[], event: React.WheelEvent) => void; } interface EventCaptureState { cursorOverrideClass?: string; dragInProgress?: boolean; - dragStartPosition?: any; + dragStartPosition?: number[]; panInProgress: boolean; - panStart?: any; - pinchZoomStart?: any; + panStart?: { + panStartXScale: ScaleContinuousNumeric; + panOrigin: number[]; + chartsToPan: string[]; + }; + pinchZoomStart?: { + xScale: ScaleContinuousNumeric; + touch1Pos: [number, number]; + touch2Pos: [number, number]; + range: number[]; + chartsToPan: string[]; + }; } export class EventCapture extends React.Component { @@ -81,7 +117,7 @@ export class EventCapture extends React.Component(); @@ -176,7 +212,7 @@ export class EventCapture extends React.Component { + this.panEndTimeout = window.setTimeout(() => { this.handlePanEnd(); }, 100); } @@ -265,17 +301,22 @@ export class EventCapture extends React.Component { - const e = d3Event; - if (this.props.onDrag) { - this.dragHappened = true; - const mouseXY = mouse(this.ref.current!); - this.props.onDrag( - { - startPos: this.state.dragStartPosition, - mouseXY, - }, - e, - ); + if (this.props.onDrag === undefined) { + return; } + + this.dragHappened = true; + + const { dragStartPosition } = this.state; + if (dragStartPosition === undefined) { + return; + } + + const e = d3Event; + const mouseXY = mouse(this.ref.current!); + + this.props.onDrag( + { + startPos: dragStartPosition, + mouseXY, + }, + e, + ); }; public cancelDrag() { @@ -390,7 +440,7 @@ export class EventCapture extends React.Component { const { pan: panEnabled, onPan } = this.props; - return panEnabled && onPan && isDefined(this.state.panStart); + return panEnabled && onPan && this.state.panStart !== undefined; }; public handlePan = () => { const e = d3Event; - if (this.shouldPan()) { + if (this.shouldPan() && this.state.panStart !== undefined) { this.panHappened = true; const { panStartXScale, panOrigin, chartsToPan } = this.state.panStart; @@ -452,7 +502,7 @@ export class EventCapture extends React.Component { return contexts.axes; -} +}; -export function getMouseCanvas(contexts) { +export const getMouseCanvas = (contexts: ICanvasContexts) => { return contexts.mouseCoord; -} +}; diff --git a/packages/core/src/utils/ChartDataUtil.ts b/packages/core/src/utils/ChartDataUtil.ts index 29201d907..b218a6636 100644 --- a/packages/core/src/utils/ChartDataUtil.ts +++ b/packages/core/src/utils/ChartDataUtil.ts @@ -1,4 +1,5 @@ import { extent } from "d3-array"; +import { ScaleContinuousNumeric } from "d3-scale"; import flattenDeep from "lodash.flattendeep"; import * as React from "react"; @@ -120,7 +121,7 @@ export function getNewChartConfig(innerDimension, children, existingChartConfig: return undefined; }).filter((each) => isDefined(each)); } -export function getCurrentCharts(chartConfig, mouseXY) { +export function getCurrentCharts(chartConfig, mouseXY: number[]) { const currentCharts = chartConfig .filter((eachConfig) => { const top = eachConfig.origin[1]; @@ -166,7 +167,7 @@ export function getChartConfigWithUpdatedYScales( { plotData, xAccessor, displayXAccessor, fullData }, xDomain, dy?: number, - chartsToPan?: any, + chartsToPan?: string[], ) { const yDomains = chartConfig.map(({ yExtentsCalculator, yExtents, yScale }) => { const realYDomain = isDefined(yExtentsCalculator) @@ -190,7 +191,7 @@ export function getChartConfigWithUpdatedYScales( const combine = zipper().combine((config, { realYDomain, yDomainDY, prevYDomain }) => { const { id, padding, height, yScale, yPan, flipYScale, yPanEnabled = false } = config; - const another = isDefined(chartsToPan) ? chartsToPan.indexOf(id) > -1 : true; + const another = chartsToPan !== undefined ? chartsToPan.indexOf(id) > -1 : true; const domain = yPan && yPanEnabled ? (another ? yDomainDY : prevYDomain) : realYDomain; const newYScale = setRange(yScale.copy().domain(domain), height, padding, flipYScale); @@ -208,7 +209,7 @@ export function getChartConfigWithUpdatedYScales( return updatedChartConfig; } -export function getCurrentItem(xScale, xAccessor, mouseXY, plotData) { +export function getCurrentItem(xScale: ScaleContinuousNumeric, xAccessor, mouseXY, plotData) { let xValue; let item; if (xScale.invert) { diff --git a/packages/scales/src/discontinuousTimeScaleProvider.ts b/packages/scales/src/discontinuousTimeScaleProvider.ts index 8d296fb4c..a55eeb61a 100644 --- a/packages/scales/src/discontinuousTimeScaleProvider.ts +++ b/packages/scales/src/discontinuousTimeScaleProvider.ts @@ -1,16 +1,16 @@ -import { identity, isNotDefined, slidingWindow, zipper } from "@react-financial-charts/core"; +import { identity, slidingWindow, zipper } from "@react-financial-charts/core"; import { timeFormat, timeFormatDefaultLocale } from "d3-time-format"; import financeDiscontinuousScale from "./financeDiscontinuousScale"; import { defaultFormatters, levelDefinition } from "./levels"; -const evaluateLevel = (d, date, i, formatters) => { +const evaluateLevel = (row, date: Date, i, formatters) => { return levelDefinition .map((eachLevel, idx) => { return { level: levelDefinition.length - idx - 1, // @ts-ignore - format: formatters[eachLevel(d, date, i)], + format: formatters[eachLevel(row, date, i)], }; }) .find((level) => !!level.format); @@ -18,7 +18,7 @@ const evaluateLevel = (d, date, i, formatters) => { const discontinuousIndexCalculator = slidingWindow() .windowSize(2) - .undefinedValue((d, idx, { initialIndex, formatters }) => { + .undefinedValue((d: Date, idx, { initialIndex, formatters }) => { const i = initialIndex; const row = { date: d.getTime(), @@ -44,7 +44,7 @@ const discontinuousIndexCalculator = slidingWindow() }); const discontinuousIndexCalculatorLocalTime = discontinuousIndexCalculator.accumulator( - ([prevDate, nowDate], i, idx, { initialIndex, formatters }) => { + ([prevDate, nowDate], i: number, idx, { initialIndex, formatters }) => { const startOf30Seconds = nowDate.getSeconds() % 30 === 0; const startOfMinute = nowDate.getMinutes() !== prevDate.getMinutes(); @@ -87,6 +87,7 @@ const discontinuousIndexCalculatorLocalTime = discontinuousIndexCalculator.accum startOfQuarter, startOfYear, }; + const level = evaluateLevel(row, nowDate, i, formatters); return { ...row, index: i + initialIndex, ...level }; @@ -126,7 +127,7 @@ export function discontinuousTimeScaleProviderBuilder() { const discontinuousTimeScaleProvider = function (data) { let index = withIndex; - if (isNotDefined(index)) { + if (index === undefined) { const response = doStuff(realDateAccessor, inputDateAccessor, initialIndex, currentFormatters)(data); index = response.index; diff --git a/packages/scales/src/financeDiscontinuousScale.ts b/packages/scales/src/financeDiscontinuousScale.ts index 7456e56e1..c2b2ddd3a 100644 --- a/packages/scales/src/financeDiscontinuousScale.ts +++ b/packages/scales/src/financeDiscontinuousScale.ts @@ -1,13 +1,12 @@ -import { head, isDefined, isNotDefined, last } from "@react-financial-charts/core"; +import { head, isDefined, last } from "@react-financial-charts/core"; import { ascending } from "d3-array"; import { scaleLinear } from "d3-scale"; - import { levelDefinition } from "./levels"; const MAX_LEVEL = levelDefinition.length - 1; export default function financeDiscontinuousScale(index, backingLinearScale = scaleLinear()) { - if (isNotDefined(index)) { + if (index === undefined) { throw new Error("Use the discontinuousTimeScaleProvider to create financeDiscontinuousScale"); } @@ -51,7 +50,7 @@ export default function financeDiscontinuousScale(index, backingLinearScale = sc }; scale.ticks = function (m) { const backingTicks = backingLinearScale.ticks(m); - const ticksMap = new Map(); + const ticksMap = new Map(); const [domainStart, domainEnd] = backingLinearScale.domain(); @@ -64,7 +63,7 @@ export default function financeDiscontinuousScale(index, backingLinearScale = sc for (let i = MAX_LEVEL; i >= 0; i--) { const ticksAtLevel = ticksMap.get(i); - const temp = isNotDefined(ticksAtLevel) ? [] : ticksAtLevel.slice(); + const temp = ticksAtLevel === undefined ? [] : ticksAtLevel.slice(); for (let j = start; j <= end; j++) { if (index[j].level === i) { @@ -75,12 +74,13 @@ export default function financeDiscontinuousScale(index, backingLinearScale = sc ticksMap.set(i, temp); } - let unsortedTicks = []; + let unsortedTicks: number[] = []; for (let i = MAX_LEVEL; i >= 0; i--) { - if (ticksMap.get(i).length + unsortedTicks.length > desiredTickCount * 1.5) { + const ticksAtLevel = ticksMap.get(i) ?? []; + if (ticksAtLevel.length + unsortedTicks.length > desiredTickCount * 1.5) { break; } - unsortedTicks = unsortedTicks.concat(ticksMap.get(i).map((d) => d.index)); + unsortedTicks = unsortedTicks.concat(ticksAtLevel.map((d) => d.index)); } const ticks = unsortedTicks.sort(ascending); @@ -104,6 +104,7 @@ export default function financeDiscontinuousScale(index, backingLinearScale = sc } } + // @ts-ignore const tickValues = [...ticksSet.values()].map((i) => parseInt(i, 10)); return tickValues; diff --git a/packages/scales/src/index.ts b/packages/scales/src/index.ts index d4cc8c24e..12bbb9c54 100644 --- a/packages/scales/src/index.ts +++ b/packages/scales/src/index.ts @@ -1,9 +1,10 @@ +import { ScaleContinuousNumeric } from "d3-scale"; export { default as discontinuousTimeScaleProvider, discontinuousTimeScaleProviderBuilder, } from "./discontinuousTimeScaleProvider"; export { default as financeDiscontinuousScale } from "./financeDiscontinuousScale"; -export const defaultScaleProvider = (xScale) => { +export const defaultScaleProvider = (xScale: ScaleContinuousNumeric) => { return (data, xAccessor) => ({ data, xScale, xAccessor, displayXAccessor: xAccessor }); }; diff --git a/packages/scales/src/levels.ts b/packages/scales/src/levels.ts index 13021f7d7..287eebabd 100644 --- a/packages/scales/src/levels.ts +++ b/packages/scales/src/levels.ts @@ -12,24 +12,24 @@ export const defaultFormatters = { }; export const levelDefinition = [ - /* 19 */ (d, date, i) => d.startOfYear && date.getFullYear() % 12 === 0 && "yearFormat", - /* 18 */ (d, date, i) => d.startOfYear && date.getFullYear() % 4 === 0 && "yearFormat", - /* 17 */ (d, date, i) => d.startOfYear && date.getFullYear() % 2 === 0 && "yearFormat", - /* 16 */ (d, date, i) => d.startOfYear && "yearFormat", - /* 15 */ (d, date, i) => d.startOfQuarter && "quarterFormat", - /* 14 */ (d, date, i) => d.startOfMonth && "monthFormat", - /* 13 */ (d, date, i) => d.startOfWeek && "weekFormat", - /* 12 */ (d, date, i) => d.startOfDay && i % 2 === 0 && "dayFormat", - /* 11 */ (d, date, i) => d.startOfDay && "dayFormat", - /* 10 */ (d, date, i) => d.startOfHalfDay && "hourFormat", // 12h - /* 9 */ (d, date, i) => d.startOfQuarterDay && "hourFormat", // 6h - /* 8 */ (d, date, i) => d.startOfEighthOfADay && "hourFormat", // 3h - /* 7 */ (d, date, i) => d.startOfHour && date.getHours() % 2 === 0 && "hourFormat", // 2h -- REMOVE THIS - /* 6 */ (d, date, i) => d.startOfHour && "hourFormat", // 1h - /* 5 */ (d, date, i) => d.startOf30Minutes && "minuteFormat", - /* 4 */ (d, date, i) => d.startOf15Minutes && "minuteFormat", - /* 3 */ (d, date, i) => d.startOf5Minutes && "minuteFormat", - /* 2 */ (d, date, i) => d.startOfMinute && "minuteFormat", - /* 1 */ (d, date, i) => d.startOf30Seconds && "secondFormat", - /* 0 */ (d, date, i) => "secondFormat", + /* 19 */ (d, date: Date, i: number) => d.startOfYear && date.getFullYear() % 12 === 0 && "yearFormat", + /* 18 */ (d, date: Date, i: number) => d.startOfYear && date.getFullYear() % 4 === 0 && "yearFormat", + /* 17 */ (d, date: Date, i: number) => d.startOfYear && date.getFullYear() % 2 === 0 && "yearFormat", + /* 16 */ (d, date: Date, i: number) => d.startOfYear && "yearFormat", + /* 15 */ (d, date: Date, i: number) => d.startOfQuarter && "quarterFormat", + /* 14 */ (d, date: Date, i: number) => d.startOfMonth && "monthFormat", + /* 13 */ (d, date: Date, i: number) => d.startOfWeek && "weekFormat", + /* 12 */ (d, date: Date, i: number) => d.startOfDay && i % 2 === 0 && "dayFormat", + /* 11 */ (d, date: Date, i: number) => d.startOfDay && "dayFormat", + /* 10 */ (d, date: Date, i: number) => d.startOfHalfDay && "hourFormat", // 12h + /* 9 */ (d, date: Date, i: number) => d.startOfQuarterDay && "hourFormat", // 6h + /* 8 */ (d, date: Date, i: number) => d.startOfEighthOfADay && "hourFormat", // 3h + /* 7 */ (d, date: Date, i: number) => d.startOfHour && date.getHours() % 2 === 0 && "hourFormat", // 2h -- REMOVE THIS + /* 6 */ (d, date: Date, i: number) => d.startOfHour && "hourFormat", // 1h + /* 5 */ (d, date: Date, i: number) => d.startOf30Minutes && "minuteFormat", + /* 4 */ (d, date: Date, i: number) => d.startOf15Minutes && "minuteFormat", + /* 3 */ (d, date: Date, i: number) => d.startOf5Minutes && "minuteFormat", + /* 2 */ (d, date: Date, i: number) => d.startOfMinute && "minuteFormat", + /* 1 */ (d, date: Date, i: number) => d.startOf30Seconds && "secondFormat", + /* 0 */ (d, date: Date, i: number) => "secondFormat", ]; diff --git a/packages/stories/src/features/cursors/Cursors.tsx b/packages/stories/src/features/cursors/Cursors.tsx new file mode 100644 index 000000000..e3f2b9053 --- /dev/null +++ b/packages/stories/src/features/cursors/Cursors.tsx @@ -0,0 +1,79 @@ +import * as React from "react"; +import { + discontinuousTimeScaleProviderBuilder, + LineSeries, + Chart, + ChartCanvas, + CrossHairCursor, + XAxis, + YAxis, + withDeviceRatio, + withSize, + Cursor, + CursorProps, + CurrentCoordinate, +} from "react-financial-charts"; +import { IOHLCData, withOHLCData } from "../../data"; + +interface ChartProps extends CursorProps { + readonly crosshair?: boolean; + readonly data: IOHLCData[]; + readonly height: number; + readonly ratio: number; + readonly width: number; +} + +class Cursors extends React.Component { + private readonly margin = { left: 0, right: 40, top: 0, bottom: 24 }; + private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( + (d: IOHLCData) => d.date, + ); + + public render() { + const { crosshair, data: initialData, height, ratio, width, ...rest } = this.props; + + const { margin, xScaleProvider } = this; + + const { data, xScale, xAccessor, displayXAccessor } = xScaleProvider(initialData); + + const start = xAccessor(data[data.length - 1]); + const end = xAccessor(data[Math.max(0, data.length - 100)]); + const xExtents = [start, end]; + + const { customX, ...cursorProps } = rest; + + return ( + + + + + + + + {crosshair && } + {!crosshair && } + + ); + } + + private readonly yAccessor = (data: IOHLCData) => { + return data.close; + }; + + private readonly yExtents = (data: IOHLCData) => { + return [data.high, data.low]; + }; +} + +export default withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(Cursors))); diff --git a/packages/stories/src/features/cursors/index.stories.tsx b/packages/stories/src/features/cursors/index.stories.tsx new file mode 100644 index 000000000..afdc0e920 --- /dev/null +++ b/packages/stories/src/features/cursors/index.stories.tsx @@ -0,0 +1,20 @@ +import { Story } from "@storybook/react"; +import * as React from "react"; +import { Cursor, CursorProps } from "../../../../coordinates/src/Cursor"; +import Cursors from "./Cursors"; + +export default { + component: Cursor, + title: "Features/Cursors", + argTypes: { + strokeStyle: { control: "color" }, + xCursorShapeFillStyle: { control: "color" }, + xCursorShapeStrokeStyle: { control: "color" }, + }, +}; + +const Template: Story = (args) => ; + +export const cursor = Template.bind({}); + +export const crosshair = () => ; diff --git a/packages/stories/src/features/interaction/Interaction.tsx b/packages/stories/src/features/interaction/Interaction.tsx index 5465694e6..0defd6954 100644 --- a/packages/stories/src/features/interaction/Interaction.tsx +++ b/packages/stories/src/features/interaction/Interaction.tsx @@ -59,13 +59,13 @@ class Interaction extends React.Component { margin={margin} data={data} disableInteraction={disableInteraction} + disablePan={disablePan} + disableZoom={disableZoom} displayXAccessor={displayXAccessor} - panEvent={!disablePan} seriesName="Data" xScale={xScale} xAccessor={xAccessor} xExtents={xExtents} - zoomEvent={!disableZoom} zoomAnchor={zoomAnchor} > diff --git a/packages/stories/src/features/interaction/index.stories.tsx b/packages/stories/src/features/interaction/index.stories.tsx index 8b7f30814..9a094a7fc 100644 --- a/packages/stories/src/features/interaction/index.stories.tsx +++ b/packages/stories/src/features/interaction/index.stories.tsx @@ -1,10 +1,10 @@ import * as React from "react"; import { - ChartCanvas, lastVisibleItemBasedZoomAnchor, mouseBasedZoomAnchor, rightDomainBasedZoomAnchor, } from "@react-financial-charts/core"; +import { ChartCanvas } from "../../../../core/src/ChartCanvas"; import Interaction from "./Interaction"; export default {