diff --git a/packages/charts/src/axes/Axis.tsx b/packages/charts/src/axes/Axis.tsx index 1f76facb0..d0d32191b 100644 --- a/packages/charts/src/axes/Axis.tsx +++ b/packages/charts/src/axes/Axis.tsx @@ -63,7 +63,7 @@ class Axis extends React.Component { zoomCursorClassName: "", }; - private node?: GenericChartComponent; + private readonly chartRef = React.createRef(); public render() { const { @@ -92,7 +92,7 @@ class Axis extends React.Component { {zoomCapture} { ); } - private readonly saveNode = (node: GenericChartComponent) => { - this.node = node; - } - private readonly getMoreProps = () => { - return this.node!.getMoreProps(); + return this.chartRef.current!.getMoreProps(); } private readonly renderSVG = (moreProps) => { - const { className } = this.props; - const { showDomain, showTicks, range, getScale } = this.props; + const { className, showDomain, showTicks, range, getScale } = this.props; - const ticks = showTicks ? axisTicksSVG(this.props, getScale(moreProps)) : null; + const scale = getScale(moreProps); + const ticks = showTicks ? axisTicksSVG(this.props, scale) : null; const domain = showDomain ? axisLineSVG(this.props, range) : null; - return - {ticks} - {domain} - ; + return ( + + {ticks} + {domain} + + ); } private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps) => { @@ -131,13 +129,14 @@ class Axis extends React.Component { ctx.save(); ctx.translate(transform[0], transform[1]); - if (showDomain) { - drawAxisLine(ctx, this.props, range); + if (showTicks) { + const scale = getScale(moreProps); + const tickProps = tickHelper(this.props, scale); + drawTicks(ctx, tickProps, moreProps); } - if (showTicks) { - const tickProps = tickHelper(this.props, getScale(moreProps)); - drawTicks(ctx, tickProps); + if (showDomain) { + drawAxisLine(ctx, this.props, range); } ctx.restore(); @@ -164,6 +163,7 @@ function tickHelper(props, scale) { tickStrokeOpacity, tickInterval, tickIntervalFunction, + ...rest } = props; let tickValues; @@ -268,6 +268,7 @@ function tickHelper(props, scale) { } return { + orient, ticks, scale, tickStroke, @@ -283,6 +284,7 @@ function tickHelper(props, scale) { fontWeight, format, showTickLabel, + ...rest, }; } @@ -316,7 +318,7 @@ function drawAxisLine(ctx: CanvasRenderingContext2D, props: AxisProps, range) { const { orient, outerTickSize, stroke, strokeWidth, strokeOpacity } = props; const sign = orient === "top" || orient === "left" ? -1 : 1; - const xAxis = (orient === "bottom" || orient === "top"); + const xAxis = orient === "bottom" || orient === "top"; ctx.lineWidth = strokeWidth; ctx.strokeStyle = colorToRGBA(stroke, strokeOpacity); @@ -339,23 +341,23 @@ function drawAxisLine(ctx: CanvasRenderingContext2D, props: AxisProps, range) { } interface TickProps { - children: string; - x1: number; - y1: number; - x2: number; - y2: number; - labelX: number; - labelY: number; - dy: string; - tickStroke?: string; - tickLabelFill?: string; - tickStrokeWidth?: number; - tickStrokeOpacity?: number; - tickStrokeDasharray?: strokeDashTypes; - textAnchor?: string; - fontSize?: number; - fontFamily?: string; - fontWeight?: number | string; + readonly children: string; + readonly dy: string; + readonly fontSize?: number; + readonly fontFamily?: string; + readonly fontWeight?: number | string; + readonly labelX: number; + readonly labelY: number; + readonly textAnchor?: string; + readonly tickStroke?: string; + readonly tickLabelFill?: string; + readonly tickStrokeWidth?: number; + readonly tickStrokeOpacity?: number; + readonly tickStrokeDasharray?: strokeDashTypes; + readonly x1: number; + readonly y1: number; + readonly x2: number; + readonly y2: number; } function Tick(props: TickProps) { @@ -369,10 +371,15 @@ function Tick(props: TickProps) { fontSize, fontFamily, fontWeight, + x1, + y1, + x2, + y2, + labelX, + labelY, + dy, } = props; - const { x1, y1, x2, y2, labelX, labelY, dy } = props; - return ( + x1={x1} + y1={y1} + x2={x2} + y2={y2} /> @@ -428,9 +437,9 @@ function axisTicksSVG(props, scale) { ); } -function drawTicks(ctx: CanvasRenderingContext2D, result) { +function drawTicks(ctx: CanvasRenderingContext2D, result, moreProps) { - const { tickStroke, tickStrokeOpacity, tickLabelFill } = result; + const { showGridLines, tickStroke, tickStrokeOpacity, tickLabelFill } = result; const { textAnchor, fontSize, fontFamily, fontWeight, ticks, showTickLabel } = result; ctx.strokeStyle = colorToRGBA(tickStroke, tickStrokeOpacity); @@ -441,6 +450,12 @@ function drawTicks(ctx: CanvasRenderingContext2D, result) { drawEachTick(ctx, tick, result); }); + if (showGridLines) { + ticks.forEach((tick) => { + drawGridLine(ctx, tick, result, moreProps); + }); + } + ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`; ctx.fillStyle = tickLabelFill; ctx.textAlign = textAnchor === "middle" ? "center" : textAnchor; @@ -452,6 +467,35 @@ function drawTicks(ctx: CanvasRenderingContext2D, result) { } } +function drawGridLine(ctx: CanvasRenderingContext2D, tick, result, moreProps) { + const { orient, gridLinesStrokeWidth, gridLinesStroke, gridLinesStrokeDasharray } = result; + + const { chartConfig } = moreProps; + + const { height, width } = chartConfig; + + ctx.strokeStyle = colorToRGBA(gridLinesStroke); + ctx.beginPath(); + + switch (orient) { + case "top": + case "bottom": + ctx.moveTo(tick.x1, 0); + ctx.lineTo(tick.x2, -height); + break; + default: + ctx.moveTo(0, tick.y1); + ctx.lineTo(-width, tick.y2); + break; + } + ctx.lineWidth = gridLinesStrokeWidth; + + const lineDash = getStrokeDasharrayCanvas(gridLinesStrokeDasharray); + + ctx.setLineDash(lineDash); + ctx.stroke(); +} + function drawEachTick(ctx: CanvasRenderingContext2D, tick, result) { const { tickStrokeWidth, tickStrokeDasharray } = result; @@ -470,8 +514,11 @@ function drawEachTick(ctx: CanvasRenderingContext2D, tick, result) { function drawEachTickLabel(ctx: CanvasRenderingContext2D, tick, result) { const { canvas_dy, format } = result; + const text = format(tick.value); + ctx.beginPath(); - ctx.fillText(format(tick.value), tick.labelX, tick.labelY + canvas_dy); + + ctx.fillText(text, tick.labelX, tick.labelY + canvas_dy); } export default Axis; diff --git a/packages/charts/src/axes/XAxis.tsx b/packages/charts/src/axes/XAxis.tsx index 6c508b04d..4f2e5c547 100644 --- a/packages/charts/src/axes/XAxis.tsx +++ b/packages/charts/src/axes/XAxis.tsx @@ -12,12 +12,16 @@ interface XAxisProps { readonly fontSize?: number; readonly fontWeight?: number; readonly getMouseDelta?: (startXY: [number, number], mouseXY: [number, number]) => number; + readonly gridLinesStroke?: string; + readonly gridLinesStrokeWidth?: number; + readonly gridLinesStrokeDasharray?: strokeDashTypes; readonly innerTickSize?: number; readonly onContextMenu?: any; // func readonly onDoubleClick?: any; // func readonly orient?: "top" | "bottom"; readonly outerTickSize?: number; readonly showDomain?: boolean; + readonly showGridLines?: boolean; readonly showTicks?: boolean; readonly showTickLabel?: boolean; readonly stroke?: string; @@ -49,13 +53,16 @@ export class XAxis extends React.Component { fontSize: 12, fontWeight: 400, getMouseDelta: (startXY: [number, number], mouseXY: [number, number]) => startXY[0] - mouseXY[0], + gridLinesStroke: "#000000", + gridLinesStrokeWidth: 1, opacity: 1, orient: "bottom", outerTickSize: 0, innerTickSize: 5, + showDomain: true, + showGridLines: false, showTicks: true, showTickLabel: true, - showDomain: true, stroke: "#000000", strokeWidth: 1, strokeOpacity: 1, @@ -120,14 +127,18 @@ export class XAxis extends React.Component { const w = width; const h = xZoomHeight; - if (axisAt === "top") { - axisLocation = 0; - } else if (axisAt === "bottom") { - axisLocation = height; - } else if (axisAt === "middle") { - axisLocation = (height) / 2; - } else { - axisLocation = axisAt; + switch (axisAt) { + case "top": + axisLocation = 0; + break; + case "bottom": + axisLocation = height; + break; + case "middle": + axisLocation = (height) / 2; + break; + default: + axisLocation = axisAt; } const y = (orient === "top") ? -xZoomHeight : 0; diff --git a/packages/charts/src/axes/YAxis.tsx b/packages/charts/src/axes/YAxis.tsx index 84c666c9a..159fed28f 100644 --- a/packages/charts/src/axes/YAxis.tsx +++ b/packages/charts/src/axes/YAxis.tsx @@ -12,12 +12,16 @@ interface YAxisProps { readonly fontSize?: number; readonly fontWeight?: number; readonly getMouseDelta?: (startXY: number[], mouseXY: number[]) => number; + readonly gridLinesStroke?: string; + readonly gridLinesStrokeWidth?: number; + readonly gridLinesStrokeDasharray?: strokeDashTypes; readonly innerTickSize?: number; readonly onContextMenu?: any; // func readonly onDoubleClick?: any; // func readonly orient?: "left" | "right"; readonly outerTickSize?: number; readonly showDomain?: boolean; + readonly showGridLines?: boolean; readonly showTicks?: boolean; readonly showTickLabel?: boolean; readonly stroke?: string; @@ -49,13 +53,16 @@ export class YAxis extends React.Component { fontSize: 12, fontWeight: 400, getMouseDelta: (startXY, mouseXY) => startXY[1] - mouseXY[1], + gridLinesStroke: "#000000", + gridLinesStrokeWidth: 1, innerTickSize: 5, outerTickSize: 0, opacity: 1, orient: "right", + showDomain: true, + showGridLines: false, showTicks: true, showTickLabel: true, - showDomain: true, stroke: "#000000", strokeWidth: 1, strokeOpacity: 1, @@ -119,14 +126,18 @@ export class YAxis extends React.Component { const w = yZoomWidth; const h = height; - if (axisAt === "left") { - axisLocation = 0; - } else if (axisAt === "right") { - axisLocation = width; - } else if (axisAt === "middle") { - axisLocation = (width) / 2; - } else { - axisLocation = axisAt; + switch (axisAt) { + case "left": + axisLocation = 0; + break; + case "right": + axisLocation = width; + break; + case "middle": + axisLocation = (width) / 2; + break; + default: + axisLocation = axisAt; } const x = (orient === "left") ? -yZoomWidth : 0; diff --git a/packages/charts/src/tooltip/BollingerBandTooltip.tsx b/packages/charts/src/tooltip/BollingerBandTooltip.tsx index 6c0dc056f..ce930ef20 100644 --- a/packages/charts/src/tooltip/BollingerBandTooltip.tsx +++ b/packages/charts/src/tooltip/BollingerBandTooltip.tsx @@ -51,16 +51,15 @@ export class BollingerBandTooltip extends React.Component { const { onClick, displayFormat, yAccessor, options, textFill, labelFill } = this.props; - const { displayValuesFor, displayInit } = this.props; + const { className, displayValuesFor, displayInit, fontFamily, fontSize } = this.props; const { chartConfig: { width, height } } = moreProps; const currentItem = displayValuesFor(this.props, moreProps); - let top; - let middle; - let bottom; - top = middle = bottom = displayInit; + let top = displayInit; + let middle = displayInit; + let bottom = displayInit; if (isDefined(currentItem) && isDefined(yAccessor(currentItem))) { const item = yAccessor(currentItem); @@ -78,15 +77,14 @@ export class BollingerBandTooltip extends React.Component + fontFamily={fontFamily} + fontSize={fontSize}> {tooltipLabel} diff --git a/packages/stories/src/features/stockChart.tsx b/packages/stories/src/features/stockChart.tsx index 2a2860c5d..812bd919e 100644 --- a/packages/stories/src/features/stockChart.tsx +++ b/packages/stories/src/features/stockChart.tsx @@ -67,7 +67,6 @@ class StockChart extends React.Component { const end = xAccessor(data[Math.max(0, data.length - 100)]); const xExtents = [start, end]; - const gridWidth = width - margin.left - margin.right; const gridHeight = height - margin.top - margin.bottom; const elderRayHeight = 100; @@ -104,11 +103,14 @@ class StockChart extends React.Component { id={3} height={chartHeight} yExtents={this.candleChartExtents}> - + + showGridLines + gridLinesStroke="#e0e3eb" + tickFormat={this.pricesDisplayFormat} /> @@ -150,9 +152,11 @@ class StockChart extends React.Component { origin={elderRayOrigin} padding={{ top: 8, bottom: 8 }}> - + showGridLines + gridLinesStroke="#e0e3eb" /> +