@react-financial-charts/axes
Version:
Axes for react-financial-charts
244 lines • 9.81 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { first, GenericChartComponent, getAxisCanvas, getStrokeDasharrayCanvas, last, } from "@react-financial-charts/core";
import { range as d3Range, zip } from "d3-array";
import { forceCollide, forceSimulation, forceX } from "d3-force";
import * as React from "react";
import { AxisZoomCapture } from "./AxisZoomCapture";
export class Axis extends React.Component {
constructor() {
super(...arguments);
this.chartRef = React.createRef();
this.getMoreProps = () => {
return this.chartRef.current.getMoreProps();
};
this.drawOnCanvas = (ctx, moreProps) => {
const { showDomain, showGridLines, showTickLabel, showTicks, transform, range, getScale, tickLabelFill } = this.props;
ctx.save();
ctx.translate(transform[0], transform[1]);
const scale = getScale(moreProps);
const tickProps = tickHelper(this.props, scale);
if (showTicks) {
drawTicks(ctx, tickProps);
}
if (showGridLines) {
tickProps.ticks.forEach((tick) => {
drawGridLine(ctx, tick, tickProps, moreProps);
});
}
if (showTickLabel) {
const { fontFamily, fontSize, fontWeight, textAnchor } = tickProps;
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
if (tickLabelFill !== undefined) {
ctx.fillStyle = tickLabelFill;
}
ctx.textAlign = textAnchor === "middle" ? "center" : textAnchor;
tickProps.ticks.forEach((tick) => {
drawEachTickLabel(ctx, tick, tickProps);
});
}
if (showDomain) {
drawAxisLine(ctx, this.props, range);
}
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.chartRef, canvasToDraw: getAxisCanvas, clip: false, edgeClip: edgeClip, canvasDraw: this.drawOnCanvas, drawOn: ["pan"] })));
}
}
Axis.defaultProps = {
edgeClip: false,
zoomEnabled: false,
zoomCursorClassName: "",
};
const tickHelper = (props, scale) => {
const { orient, innerTickSize = 4, tickFormat, tickPadding = 4, tickLabelFill, tickStrokeWidth, tickStrokeDasharray, fontSize = 12, fontFamily, fontWeight, showTicks, showTickLabel, ticks: tickArguments, tickValues: tickValuesProp, tickStrokeStyle, tickInterval, tickIntervalFunction } = props, rest = __rest(props, ["orient", "innerTickSize", "tickFormat", "tickPadding", "tickLabelFill", "tickStrokeWidth", "tickStrokeDasharray", "fontSize", "fontFamily", "fontWeight", "showTicks", "showTickLabel", "ticks", "tickValues", "tickStrokeStyle", "tickInterval", "tickIntervalFunction"]);
let tickValues;
if (tickValuesProp !== undefined) {
if (typeof tickValuesProp === "function") {
tickValues = tickValuesProp(scale.domain());
}
else {
tickValues = tickValuesProp;
}
}
else if (tickInterval !== undefined) {
const [min, max] = scale.domain();
const baseTickValues = d3Range(min, max, (max - min) / tickInterval);
tickValues = tickIntervalFunction ? tickIntervalFunction(min, max, tickInterval) : baseTickValues;
}
else if (scale.ticks !== undefined) {
tickValues = scale.ticks(tickArguments);
}
else {
tickValues = scale.domain();
}
const format = tickFormat === undefined ? scale.tickFormat(tickArguments) : (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 * 0.71;
textAnchor = "middle";
const y2 = sign * innerTickSize;
const labelY = sign * tickSpacing;
ticks = tickValues.map((d) => {
const x = Math.round(scale(d));
return {
value: d,
x1: x,
y1: 0,
x2: x,
y2,
labelX: x,
labelY,
};
});
if (showTicks) {
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();
}
// @ts-ignore
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));
const x2 = sign * innerTickSize;
const labelX = sign * tickSpacing;
return {
value: d,
x1: 0,
y1: y,
x2,
y2: y,
labelX,
labelY: y,
};
});
dy = ".32em";
canvas_dy = fontSize * 0.32;
textAnchor = sign < 0 ? "end" : "start";
}
return Object.assign({ orient,
ticks,
scale,
tickStrokeStyle, tickLabelFill: tickLabelFill || tickStrokeStyle, tickStrokeWidth,
tickStrokeDasharray,
dy,
canvas_dy,
textAnchor,
fontSize,
fontFamily,
fontWeight,
format,
showTickLabel }, rest);
};
const drawAxisLine = (ctx, props, range) => {
const { orient, outerTickSize, strokeStyle, strokeWidth } = props;
const sign = orient === "top" || orient === "left" ? -1 : 1;
const xAxis = orient === "bottom" || orient === "top";
ctx.lineWidth = strokeWidth;
ctx.strokeStyle = strokeStyle;
ctx.beginPath();
const firstPoint = first(range);
const lastPoint = last(range);
const tickSize = sign * outerTickSize;
if (xAxis) {
ctx.moveTo(firstPoint, tickSize);
ctx.lineTo(firstPoint, 0);
ctx.lineTo(lastPoint, 0);
ctx.lineTo(lastPoint, tickSize);
}
else {
ctx.moveTo(tickSize, firstPoint);
ctx.lineTo(0, firstPoint);
ctx.lineTo(0, lastPoint);
ctx.lineTo(tickSize, lastPoint);
}
ctx.stroke();
};
const drawTicks = (ctx, result) => {
const { ticks, tickStrokeStyle } = result;
if (tickStrokeStyle !== undefined) {
ctx.strokeStyle = tickStrokeStyle;
ctx.fillStyle = tickStrokeStyle;
}
ticks.forEach((tick) => {
drawEachTick(ctx, tick, result);
});
};
const drawGridLine = (ctx, tick, result, moreProps) => {
const { orient, gridLinesStrokeWidth, gridLinesStrokeStyle, gridLinesStrokeDasharray } = result;
const { chartConfig } = moreProps;
const { height, width } = chartConfig;
if (gridLinesStrokeStyle !== undefined) {
ctx.strokeStyle = gridLinesStrokeStyle;
}
ctx.beginPath();
const sign = orient === "top" || orient === "left" ? 1 : -1;
switch (orient) {
case "top":
case "bottom":
ctx.moveTo(tick.x1, 0);
ctx.lineTo(tick.x2, sign * height);
break;
default:
ctx.moveTo(0, tick.y1);
ctx.lineTo(sign * width, tick.y2);
break;
}
ctx.lineWidth = gridLinesStrokeWidth;
const lineDash = getStrokeDasharrayCanvas(gridLinesStrokeDasharray);
ctx.setLineDash(lineDash);
ctx.stroke();
};
const 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();
};
const drawEachTickLabel = (ctx, tick, result) => {
const { canvas_dy, format } = result;
const text = format(tick.value);
ctx.beginPath();
ctx.fillText(text, tick.labelX, tick.labelY + canvas_dy);
};
//# sourceMappingURL=Axis.js.map