From 7870a36f1303e83a137eab7158fc62ac9337f8a6 Mon Sep 17 00:00:00 2001 From: Mark McDowell Date: Wed, 11 Sep 2019 17:40:40 +0100 Subject: [PATCH] fix(axis): adding strokeOpacity and missing props StrokeOpacity added and used instead of opacity. Lots of missing props from axis'. Closes #29 --- packages/charts/src/axes/Axis.tsx | 122 +++++++++++++++----------- packages/charts/src/axes/XAxis.tsx | 134 +++++++++++++++++------------ packages/charts/src/axes/YAxis.tsx | 124 +++++++++++++++----------- 3 files changed, 224 insertions(+), 156 deletions(-) diff --git a/packages/charts/src/axes/Axis.tsx b/packages/charts/src/axes/Axis.tsx index 914ab143b..e6f1c5b4f 100644 --- a/packages/charts/src/axes/Axis.tsx +++ b/packages/charts/src/axes/Axis.tsx @@ -9,13 +9,36 @@ import { AxisZoomCapture } from "./AxisZoomCapture"; import { colorToRGBA, first, getStrokeDasharray, identity, isDefined, isNotDefined, last, strokeDashTypes, zipper } from "../utils"; interface AxisProps { + readonly axisZoomCallback?: any; // func + readonly bg: { + h: number; + x: number; + w: number; + y: number; + }; + readonly className?: string; + readonly domainClassName?: string; + readonly edgeClip: boolean; + readonly fill?: string; readonly flexTicks?: boolean; readonly fontFamily?: string; readonly fontSize?: number; readonly fontWeight?: number; - readonly orient?: "top" | "left" | "right" | "bottom"; + readonly getMouseDelta: any; // func + readonly getScale: any; // func readonly innerTickSize?: number; - readonly outerTickSize?: number; + readonly inverted?: boolean; + readonly onContextMenu?: any; // func + readonly onDoubleClick?: any; // func + readonly orient?: "top" | "left" | "right" | "bottom"; + readonly outerTickSize: number; + readonly range: number[]; + readonly showDomain?: boolean; + readonly showTicks?: boolean; + readonly showTickLabel?: boolean; + readonly stroke: string; + readonly strokeOpacity?: number; + readonly strokeWidth: number; readonly tickFormat?: (data: any) => string; readonly tickPadding?: number; readonly tickSize?: number; @@ -28,43 +51,28 @@ interface AxisProps { readonly tickValues?: number[] | any; // func readonly tickInterval?: number; readonly tickIntervalFunction?: any; // func - readonly showDomain?: boolean; - readonly showTicks?: boolean; - readonly showTickLabel?: boolean; - readonly className?: string; - readonly axisZoomCallback?: any; // func + readonly transform: number[]; readonly zoomEnabled?: boolean; - readonly inverted?: boolean; readonly zoomCursorClassName?: string; - readonly transform: number[]; - readonly range: number[]; - readonly getMouseDelta: any; // func - readonly getScale: any; // func - readonly bg: { - h: number; - x: number; - w: number; - y: number; - }; - readonly edgeClip: boolean; - readonly onContextMenu?: any; // func - readonly onDoubleClick?: any; // func } class Axis extends React.Component { public static defaultProps = { + edgeClip: false, zoomEnabled: false, zoomCursorClassName: "", - edgeClip: false, }; - private node; + private node?: GenericChartComponent; public render() { - const { bg, axisZoomCallback, className, zoomCursorClassName, zoomEnabled, getScale, inverted } = this.props; - const { transform, getMouseDelta, edgeClip } = this.props; - const { onContextMenu, onDoubleClick } = this.props; + const { + bg, axisZoomCallback, className, + zoomCursorClassName, zoomEnabled, getScale, + inverted, transform, getMouseDelta, + edgeClip, onContextMenu, onDoubleClick, + } = this.props; const zoomCapture = zoomEnabled ? { ); } - private readonly saveNode = (node) => { + private readonly saveNode = (node: GenericChartComponent) => { this.node = node; } private readonly getMoreProps = () => { - return this.node.getMoreProps(); + return this.node!.getMoreProps(); } private readonly renderSVG = (moreProps) => { @@ -118,13 +126,16 @@ class Axis extends React.Component { ; } - private readonly drawOnCanvas = (ctx, moreProps) => { + private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps) => { const { showDomain, showTicks, transform, range, getScale } = this.props; ctx.save(); ctx.translate(transform[0], transform[1]); - if (showDomain) { drawAxisLine(ctx, this.props, range); } + if (showDomain) { + drawAxisLine(ctx, this.props, range); + } + if (showTicks) { const tickProps = tickHelper(this.props, getScale(moreProps)); drawTicks(ctx, tickProps); @@ -136,14 +147,25 @@ class Axis extends React.Component { function tickHelper(props, scale) { const { - orient, innerTickSize, tickFormat, tickPadding, - tickLabelFill, tickStrokeWidth, tickStrokeDasharray, - fontSize, fontFamily, fontWeight, showTicks, flexTicks, + orient, + innerTickSize, + tickFormat, + tickPadding, + tickLabelFill, + tickStrokeWidth, + tickStrokeDasharray, + fontSize, + fontFamily, + fontWeight, + showTicks, + flexTicks, showTickLabel, - } = props; - const { - ticks: tickArguments, tickValues: tickValuesProp, - tickStroke, tickStrokeOpacity, tickInterval, tickIntervalFunction, + ticks: tickArguments, + tickValues: tickValuesProp, + tickStroke, + tickStrokeOpacity, + tickInterval, + tickIntervalFunction, } = props; let tickValues; @@ -172,7 +194,7 @@ function tickHelper(props, scale) { const format = isNotDefined(tickFormat) ? baseFormat - : (d) => tickFormat(d) || ""; + : (d: any) => tickFormat(d) || ""; const sign = orient === "top" || orient === "left" ? -1 : 1; const tickSpacing = Math.max(innerTickSize, 0) + tickPadding; @@ -266,9 +288,8 @@ function tickHelper(props, scale) { }; } -function axisLineSVG(props, range) { - const { orient, outerTickSize } = props; - const { domainClassName, fill, stroke, strokeWidth, opacity } = props; +function axisLineSVG(props: AxisProps, range) { + const { domainClassName, orient, outerTickSize, fill, stroke, strokeWidth, strokeOpacity } = props; const sign = orient === "top" || orient === "left" ? -1 : 1; @@ -285,22 +306,22 @@ function axisLineSVG(props, range) { className={domainClassName} d={d} fill={fill} - opacity={opacity} + opacity={strokeOpacity} stroke={stroke} strokeWidth={strokeWidth} > ); } -function drawAxisLine(ctx, props, range) { +function drawAxisLine(ctx: CanvasRenderingContext2D, props: AxisProps, range) { - const { orient, outerTickSize, stroke, strokeWidth, opacity } = props; + const { orient, outerTickSize, stroke, strokeWidth, strokeOpacity } = props; const sign = orient === "top" || orient === "left" ? -1 : 1; const xAxis = (orient === "bottom" || orient === "top"); ctx.lineWidth = strokeWidth; - ctx.strokeStyle = colorToRGBA(stroke, opacity); + ctx.strokeStyle = colorToRGBA(stroke, strokeOpacity); ctx.beginPath(); @@ -409,7 +430,7 @@ function axisTicksSVG(props, scale) { ); } -function drawTicks(ctx, result) { +function drawTicks(ctx: CanvasRenderingContext2D, result) { const { tickStroke, tickStrokeOpacity, tickLabelFill } = result; const { textAnchor, fontSize, fontFamily, fontWeight, ticks, showTickLabel } = result; @@ -433,7 +454,7 @@ function drawTicks(ctx, result) { } } -function drawEachTick(ctx, tick, result) { +function drawEachTick(ctx: CanvasRenderingContext2D, tick, result) { const { tickStrokeWidth, tickStrokeDasharray } = result; ctx.beginPath(); @@ -441,11 +462,14 @@ function drawEachTick(ctx, tick, result) { ctx.moveTo(tick.x1, tick.y1); ctx.lineTo(tick.x2, tick.y2); ctx.lineWidth = tickStrokeWidth; - ctx.setLineDash(getStrokeDasharray(tickStrokeDasharray).split(",")); + + const lineDash = getStrokeDasharray(tickStrokeDasharray).split(",").map((dash) => Number(dash)); + + ctx.setLineDash(lineDash); ctx.stroke(); } -function drawEachTickLabel(ctx, tick, result) { +function drawEachTickLabel(ctx: CanvasRenderingContext2D, tick, result) { const { canvas_dy, format } = result; ctx.beginPath(); diff --git a/packages/charts/src/axes/XAxis.tsx b/packages/charts/src/axes/XAxis.tsx index c28e4cc71..3d29046d1 100644 --- a/packages/charts/src/axes/XAxis.tsx +++ b/packages/charts/src/axes/XAxis.tsx @@ -5,43 +5,56 @@ import Axis from "./Axis"; interface XAxisProps { readonly axisAt: number | "top" | "bottom" | "middle"; + readonly className?: string; + readonly domainClassName?: string; + readonly fill?: string; readonly flexTicks?: boolean; - readonly orient: "top" | "bottom"; + readonly fontFamily?: string; + readonly fontSize?: number; + readonly fontWeight?: number; + readonly getMouseDelta?: (startXY: [number, number], mouseXY: [number, number]) => number; readonly innerTickSize?: number; + readonly onContextMenu?: any; // func + readonly onDoubleClick?: any; // func + readonly orient: "top" | "bottom"; readonly outerTickSize?: number; + readonly showDomain?: boolean; + readonly showTicks?: boolean; + readonly showTickLabel?: boolean; + readonly stroke?: string; + readonly strokeOpacity?: number; + readonly strokeWidth?: number; readonly tickFormat?: (data: any) => string; readonly tickPadding?: number; readonly tickSize?: number; readonly tickLabelFill?: string; readonly ticks?: number; readonly tickStroke?: string; + readonly tickStrokeOpacity?: number; readonly tickStrokeWidth?: number; readonly tickStrokeDasharray?: strokeDashTypes; readonly tickValues?: number[]; - readonly showTicks?: boolean; - readonly className?: string; - readonly zoomEnabled?: boolean; - readonly onContextMenu?: any; // func - readonly onDoubleClick?: any; // func - readonly getMouseDelta?: (startXY: number[], mouseXY: number[]) => number; readonly xZoomHeight?: number; + readonly zoomEnabled?: boolean; + readonly zoomCursorClassName?: string; } export class XAxis extends React.Component { public static defaultProps = { + className: "react-financial-charts-x-axis", + domainClassName: "react-financial-charts-axis-domain", + fill: "none", showTicks: true, showTickLabel: true, showDomain: true, - className: "react-financial-charts-x-axis", - ticks: 10, - outerTickSize: 0, - fill: "none", stroke: "#000000", strokeWidth: 1, + strokeOpacity: 1, opacity: 1, - domainClassName: "react-financial-charts-axis-domain", + outerTickSize: 0, innerTickSize: 5, + ticks: 10, tickPadding: 6, tickLabelFill: "#000000", tickStroke: "#000000", @@ -51,7 +64,8 @@ export class XAxis extends React.Component { fontWeight: 400, xZoomHeight: 25, zoomEnabled: true, - getMouseDelta: (startXY, mouseXY) => startXY[0] - mouseXY[0], + zoomCursorClassName: "react-financial-charts-ew-resize-cursor", + getMouseDelta: (startXY: [number, number], mouseXY: [number, number]) => startXY[0] - mouseXY[0], }; public static contextTypes = { @@ -62,20 +76,25 @@ export class XAxis extends React.Component { public render() { const { getMouseDelta = XAxis.defaultProps.getMouseDelta, + outerTickSize = XAxis.defaultProps.outerTickSize, + stroke = XAxis.defaultProps.stroke, + strokeWidth = XAxis.defaultProps.strokeWidth, zoomEnabled, ...rest } = this.props; - const { ...moreProps } = helper(this.props, this.context); + const { ...moreProps } = this.helper(this.props, this.context); return ( + axisZoomCallback={this.axisZoomCallback} /> ); } @@ -84,51 +103,52 @@ export class XAxis extends React.Component { xAxisZoom(newXDomain); } -} -function helper(props: XAxisProps, context) { - const { - axisAt, - xZoomHeight = XAxis.defaultProps.xZoomHeight, - orient, - } = props; - const { chartConfig: { width, height } } = context; - - let axisLocation; - const x = 0; - 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; + private readonly helper = (props: XAxisProps, context) => { + const { + axisAt, + xZoomHeight = XAxis.defaultProps.xZoomHeight, + orient, + } = props; + const { chartConfig: { width, height } } = context; + + let axisLocation; + const x = 0; + 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; + } + + const y = (orient === "top") ? -xZoomHeight : 0; + + return { + transform: [0, axisLocation], + range: [0, width], + getScale: this.getXScale, + bg: { x, y, h, w }, + }; } - const y = (orient === "top") ? -xZoomHeight : 0; + private readonly getXScale = (moreProps) => { + const { xScale: scale, width } = moreProps; - return { - transform: [0, axisLocation], - range: [0, width], - getScale: getXScale, - bg: { x, y, h, w }, - }; -} - -function getXScale(moreProps) { - const { xScale: scale, width } = moreProps; + if (scale.invert) { + const trueRange = [0, width]; + const trueDomain = trueRange.map(scale.invert); + return scale + .copy() + .domain(trueDomain) + .range(trueRange); + } - if (scale.invert) { - const trueRange = [0, width]; - const trueDomain = trueRange.map(scale.invert); - return scale.copy() - .domain(trueDomain) - .range(trueRange); + return scale; } - - return scale; } diff --git a/packages/charts/src/axes/YAxis.tsx b/packages/charts/src/axes/YAxis.tsx index 0da1915b4..1a32a01c9 100644 --- a/packages/charts/src/axes/YAxis.tsx +++ b/packages/charts/src/axes/YAxis.tsx @@ -1,26 +1,42 @@ import * as PropTypes from "prop-types"; import * as React from "react"; +import { strokeDashTypes } from "../utils"; import Axis from "./Axis"; interface YAxisProps { readonly axisAt: number | "left" | "right" | "middle"; - readonly orient: "left" | "right"; + readonly className?: string; + readonly domainClassName?: string; + readonly fill?: string; readonly flexTicks?: boolean; + readonly fontFamily?: string; + readonly fontSize?: number; + readonly fontWeight?: number; + readonly getMouseDelta?: (startXY: number[], mouseXY: number[]) => number; readonly innerTickSize?: number; + readonly onContextMenu?: any; // func + readonly onDoubleClick?: any; // func + readonly orient: "left" | "right"; readonly outerTickSize?: number; + readonly showDomain?: boolean; + readonly showTicks?: boolean; + readonly showTickLabel?: boolean; + readonly stroke?: string; + readonly strokeOpacity?: number; + readonly strokeWidth?: number; readonly tickFormat?: (data: any) => string; readonly tickPadding?: number; readonly tickSize?: number; readonly tickLabelFill?: string; readonly ticks?: number; - readonly yZoomWidth?: number; + readonly tickStroke?: string; + readonly tickStrokeOpacity?: number; + readonly tickStrokeWidth?: number; + readonly tickStrokeDasharray?: strokeDashTypes; readonly tickValues?: number[]; - readonly showTicks?: boolean; - readonly className?: string; + readonly yZoomWidth?: number; readonly zoomEnabled?: boolean; - readonly onContextMenu?: any; // func - readonly onDoubleClick?: any; // func - readonly getMouseDelta?: (startXY: number[], mouseXY: number[]) => number; + readonly zoomCursorClassName?: string; } export class YAxis extends React.Component { @@ -36,6 +52,7 @@ export class YAxis extends React.Component { fill: "none", stroke: "#000000", strokeWidth: 1, + strokeOpacity: 1, opacity: 1, innerTickSize: 5, tickPadding: 6, @@ -47,6 +64,7 @@ export class YAxis extends React.Component { fontWeight: 400, yZoomWidth: 40, zoomEnabled: true, + zoomCursorClassName: "react-financial-charts-ns-resize-cursor", getMouseDelta: (startXY, mouseXY) => startXY[1] - mouseXY[1], }; @@ -60,20 +78,25 @@ export class YAxis extends React.Component { const { getMouseDelta = YAxis.defaultProps.getMouseDelta, + outerTickSize = YAxis.defaultProps.outerTickSize, + stroke = YAxis.defaultProps.stroke, + strokeWidth = YAxis.defaultProps.strokeWidth, ...rest } = this.props; - const { zoomEnabled, ...moreProps } = helper(this.props, this.context); + const { zoomEnabled, ...moreProps } = this.helper(this.props, this.context); return ( + axisZoomCallback={this.axisZoomCallback} /> ); } @@ -81,50 +104,51 @@ export class YAxis extends React.Component { const { chartId, yAxisZoom } = this.context; yAxisZoom(chartId, newYDomain); } -} -function helper(props: YAxisProps, context) { - const { - axisAt, - yZoomWidth = YAxis.defaultProps.yZoomWidth, - orient, - } = props; - const { chartConfig: { width, height } } = context; + private readonly helper = (props: YAxisProps, context) => { + const { + axisAt, + yZoomWidth = YAxis.defaultProps.yZoomWidth, + orient, + } = props; + const { chartConfig: { width, height } } = context; - let axisLocation; - const y = 0; - const w = yZoomWidth; - const h = height; + let axisLocation; + const y = 0; + 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; - } + if (axisAt === "left") { + axisLocation = 0; + } else if (axisAt === "right") { + axisLocation = width; + } else if (axisAt === "middle") { + axisLocation = (width) / 2; + } else { + axisLocation = axisAt; + } - const x = (orient === "left") ? -yZoomWidth : 0; + const x = (orient === "left") ? -yZoomWidth : 0; - return { - transform: [axisLocation, 0], - range: [0, height], - getScale: getYScale, - bg: { x, y, h, w }, - zoomEnabled: context.chartConfig.yPan, - }; -} + return { + transform: [axisLocation, 0], + range: [0, height], + getScale: this.getYScale, + bg: { x, y, h, w }, + zoomEnabled: context.chartConfig.yPan, + }; + } -function getYScale(moreProps) { - const { yScale: scale, flipYScale, height } = moreProps.chartConfig; - if (scale.invert) { - const trueRange = flipYScale ? [0, height] : [height, 0]; - const trueDomain = trueRange.map(scale.invert); - return scale.copy() - .domain(trueDomain) - .range(trueRange); + private readonly getYScale = (moreProps) => { + const { yScale: scale, flipYScale, height } = moreProps.chartConfig; + if (scale.invert) { + const trueRange = flipYScale ? [0, height] : [height, 0]; + const trueDomain = trueRange.map(scale.invert); + return scale + .copy() + .domain(trueDomain) + .range(trueRange); + } + return scale; } - return scale; }