UNPKG

react-financial-charts

Version:
245 lines 10.6 kB
import { range as d3Range, zip } from "d3-array"; import { forceCollide, forceSimulation, forceX } from "d3-force"; import * as React from "react"; import GenericChartComponent from "../GenericChartComponent"; import { getAxisCanvas } from "../GenericComponent"; import { AxisZoomCapture } from "./AxisZoomCapture"; import { colorToRGBA, first, getStrokeDasharray, getStrokeDasharrayCanvas, identity, isDefined, isNotDefined, last } from "../utils"; class Axis extends React.Component { constructor() { super(...arguments); this.saveNode = (node) => { this.node = node; }; this.getMoreProps = () => { return this.node.getMoreProps(); }; this.renderSVG = (moreProps) => { const { className } = this.props; const { showDomain, showTicks, range, getScale } = this.props; const ticks = showTicks ? axisTicksSVG(this.props, getScale(moreProps)) : null; const domain = showDomain ? axisLineSVG(this.props, range) : null; return React.createElement("g", { className: className }, ticks, domain); }; this.drawOnCanvas = (ctx, 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 (showTicks) { const tickProps = tickHelper(this.props, getScale(moreProps)); drawTicks(ctx, tickProps); } ctx.restore(); }; } render() { const { bg, axisZoomCallback, className, zoomCursorClassName, zoomEnabled, getScale, inverted, transform, getMouseDelta, edgeClip, onContextMenu, onDoubleClick, } = this.props; const zoomCapture = zoomEnabled ? React.createElement(AxisZoomCapture, { bg: bg, getScale: getScale, getMoreProps: this.getMoreProps, getMouseDelta: getMouseDelta, axisZoomCallback: axisZoomCallback, className: className, zoomCursorClassName: zoomCursorClassName, inverted: inverted, onContextMenu: onContextMenu, onDoubleClick: onDoubleClick }) : null; return (React.createElement("g", { transform: `translate(${transform[0]}, ${transform[1]})` }, zoomCapture, React.createElement(GenericChartComponent, { ref: this.saveNode, canvasToDraw: getAxisCanvas, clip: false, edgeClip: edgeClip, svgDraw: this.renderSVG, canvasDraw: this.drawOnCanvas, drawOn: ["pan"] }))); } } Axis.defaultProps = { edgeClip: false, zoomEnabled: false, zoomCursorClassName: "", }; function tickHelper(props, scale) { const { orient, innerTickSize, tickFormat, tickPadding, tickLabelFill, tickStrokeWidth, tickStrokeDasharray, fontSize, fontFamily, fontWeight, showTicks, flexTicks, showTickLabel, ticks: tickArguments, tickValues: tickValuesProp, tickStroke, tickStrokeOpacity, tickInterval, tickIntervalFunction, } = props; let tickValues; if (isDefined(tickValuesProp)) { if (typeof tickValuesProp === "function") { tickValues = tickValuesProp(scale.domain()); } else { tickValues = tickValuesProp; } } else if (isDefined(tickInterval)) { const [min, max] = scale.domain(); const baseTickValues = d3Range(min, max, (max - min) / tickInterval); tickValues = tickIntervalFunction ? tickIntervalFunction(min, max, tickInterval) : baseTickValues; } else if (isDefined(scale.ticks)) { tickValues = scale.ticks(tickArguments, flexTicks); } else { tickValues = scale.domain(); } const baseFormat = scale.tickFormat ? scale.tickFormat(tickArguments) : identity; const format = isNotDefined(tickFormat) ? baseFormat : (d) => tickFormat(d) || ""; const sign = orient === "top" || orient === "left" ? -1 : 1; const tickSpacing = Math.max(innerTickSize, 0) + tickPadding; let ticks; let dy; // tslint:disable-next-line: variable-name let canvas_dy; let textAnchor; if (orient === "bottom" || orient === "top") { dy = sign < 0 ? "0em" : ".71em"; canvas_dy = sign < 0 ? 0 : (fontSize * .71); textAnchor = "middle"; ticks = tickValues.map((d) => { const x = Math.round(scale(d)); return { value: d, x1: x, y1: 0, x2: x, y2: sign * innerTickSize, labelX: x, labelY: sign * tickSpacing, }; }); if (showTicks && flexTicks) { const nodes = ticks.map((d) => ({ id: d.value, value: d.value, fy: d.y2, origX: d.x1 })); const simulation = forceSimulation(nodes) .force("x", forceX((d) => d.origX).strength(1)) .force("collide", forceCollide(22)) .stop(); for (let i = 0; i < 100; ++i) { simulation.tick(); } ticks = zip(ticks, nodes) .map((d) => { const a = d[0]; const b = d[1]; if (Math.abs(b.x - b.origX) > 0.01) { return Object.assign(Object.assign({}, a), { x2: b.x, labelX: b.x }); } return a; }); } } else { ticks = tickValues.map((d) => { const y = Math.round(scale(d)); return { value: d, x1: 0, y1: y, x2: sign * innerTickSize, y2: y, labelX: sign * tickSpacing, labelY: y, }; }); dy = ".32em"; canvas_dy = (fontSize * .32); textAnchor = sign < 0 ? "end" : "start"; } return { ticks, scale, tickStroke, tickLabelFill: (tickLabelFill || tickStroke), tickStrokeOpacity, tickStrokeWidth, tickStrokeDasharray, dy, canvas_dy, textAnchor, fontSize, fontFamily, fontWeight, format, showTickLabel, }; } function axisLineSVG(props, range) { const { domainClassName, orient, outerTickSize, fill, stroke, strokeWidth, strokeOpacity } = props; const sign = orient === "top" || orient === "left" ? -1 : 1; let d; if (orient === "bottom" || orient === "top") { d = "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize; } else { d = "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize; } return (React.createElement("path", { className: domainClassName, d: d, fill: fill, opacity: strokeOpacity, stroke: stroke, strokeWidth: strokeWidth })); } function drawAxisLine(ctx, props, range) { 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, strokeOpacity); ctx.beginPath(); if (xAxis) { ctx.moveTo(first(range), sign * outerTickSize); ctx.lineTo(first(range), 0); ctx.lineTo(last(range), 0); ctx.lineTo(last(range), sign * outerTickSize); } else { ctx.moveTo(sign * outerTickSize, first(range)); ctx.lineTo(0, first(range)); ctx.lineTo(0, last(range)); ctx.lineTo(sign * outerTickSize, last(range)); } ctx.stroke(); } function Tick(props) { const { tickLabelFill, tickStroke, tickStrokeOpacity, tickStrokeDasharray, tickStrokeWidth, textAnchor, fontSize, fontFamily, fontWeight, } = props; const { x1, y1, x2, y2, labelX, labelY, dy } = props; return (React.createElement("g", { className: "tick" }, React.createElement("line", { shapeRendering: "crispEdges", opacity: tickStrokeOpacity, stroke: tickStroke, strokeWidth: tickStrokeWidth, strokeDasharray: getStrokeDasharray(tickStrokeDasharray), x1: x1, y1: y1, x2: x2, y2: y2 }), React.createElement("text", { dy: dy, x: labelX, y: labelY, fill: tickLabelFill, fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily, textAnchor: textAnchor }, props.children))); } function axisTicksSVG(props, scale) { const result = tickHelper(props, scale); const { tickLabelFill, tickStroke, tickStrokeOpacity, tickStrokeWidth, tickStrokeDasharray, textAnchor } = result; const { fontSize, fontFamily, fontWeight, ticks, format } = result; const { dy } = result; return (React.createElement("g", null, ticks.map((tick, idx) => { return (React.createElement(Tick, { key: idx, tickStroke: tickStroke, tickLabelFill: tickLabelFill, tickStrokeWidth: tickStrokeWidth, tickStrokeOpacity: tickStrokeOpacity, tickStrokeDasharray: tickStrokeDasharray, dy: dy, x1: tick.x1, y1: tick.y1, x2: tick.x2, y2: tick.y2, labelX: tick.labelX, labelY: tick.labelY, textAnchor: textAnchor, fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily }, format(tick.value))); }))); } function drawTicks(ctx, result) { const { tickStroke, tickStrokeOpacity, tickLabelFill } = result; const { textAnchor, fontSize, fontFamily, fontWeight, ticks, showTickLabel } = result; ctx.strokeStyle = colorToRGBA(tickStroke, tickStrokeOpacity); ctx.fillStyle = tickStroke; ticks.forEach((tick) => { drawEachTick(ctx, tick, result); }); ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`; ctx.fillStyle = tickLabelFill; ctx.textAlign = textAnchor === "middle" ? "center" : textAnchor; if (showTickLabel) { ticks.forEach((tick) => { drawEachTickLabel(ctx, tick, result); }); } } function drawEachTick(ctx, tick, result) { const { tickStrokeWidth, tickStrokeDasharray } = result; ctx.beginPath(); ctx.moveTo(tick.x1, tick.y1); ctx.lineTo(tick.x2, tick.y2); ctx.lineWidth = tickStrokeWidth; const lineDash = getStrokeDasharrayCanvas(tickStrokeDasharray); ctx.setLineDash(lineDash); ctx.stroke(); } function drawEachTickLabel(ctx, tick, result) { const { canvas_dy, format } = result; ctx.beginPath(); ctx.fillText(format(tick.value), tick.labelX, tick.labelY + canvas_dy); } export default Axis; //# sourceMappingURL=Axis.js.map