react-financial-charts
Version:
React charts specific to finance.
181 lines • 7.22 kB
JavaScript
import { group, merge } from "d3-array";
import { stack as d3Stack } from "d3-shape";
import * as React from "react";
import GenericChartComponent from "../GenericChartComponent";
import { getAxisCanvas } from "../GenericComponent";
import { colorToRGBA, functor, head, identity, plotDataLengthBarWidth } from "../utils";
export class StackedBarSeries extends React.Component {
constructor() {
super(...arguments);
this.drawOnCanvas = (ctx, moreProps) => {
const { xAccessor } = moreProps;
drawOnCanvasHelper(ctx, this.props, moreProps, xAccessor, d3Stack);
};
this.renderSVG = (moreProps) => {
const { xAccessor } = moreProps;
return React.createElement("g", null, svgHelper(this.props, moreProps, xAccessor, d3Stack));
};
}
render() {
const { clip } = this.props;
return (React.createElement(GenericChartComponent, { clip: clip, svgDraw: this.renderSVG, canvasDraw: this.drawOnCanvas, canvasToDraw: getAxisCanvas, drawOn: ["pan"] }));
}
}
StackedBarSeries.defaultProps = {
baseAt: (xScale, yScale /* , d*/) => head(yScale.range()),
direction: "up",
className: "bar",
stroke: false,
fill: "#4682B4",
opacity: 0.5,
width: plotDataLengthBarWidth,
widthRatio: 0.8,
clip: true,
swapScales: false,
};
export function identityStack() {
let keys = [];
function stack(data) {
const response = keys.map((key, i) => {
const arrays = data.map((d) => {
const array = [0, d[key]];
// @ts-ignore
array.data = d;
return array;
});
arrays.key = key;
arrays.index = i;
return arrays;
});
return response;
}
stack.keys = function (x) {
if (!arguments.length) {
return keys;
}
keys = x;
return stack;
};
return stack;
}
export function drawOnCanvasHelper(ctx, props, moreProps, xAccessor, stackFn, defaultPostAction = identity, postRotateAction = rotateXY) {
const { xScale, chartConfig: { yScale }, plotData } = moreProps;
const bars = doStuff(props, xAccessor, plotData, xScale, yScale, stackFn, postRotateAction, defaultPostAction);
drawOnCanvas2(props, ctx, bars);
}
function convertToArray(item) {
return Array.isArray(item) ? item : [item];
}
export function svgHelper(props, moreProps, xAccessor, stackFn, defaultPostAction = identity, postRotateAction = rotateXY) {
const { xScale, chartConfig: { yScale }, plotData } = moreProps;
const bars = doStuff(props, xAccessor, plotData, xScale, yScale, stackFn, postRotateAction, defaultPostAction);
return getBarsSVG2(props, bars);
}
function doStuff(props, xAccessor, plotData, xScale, yScale, stackFn, postRotateAction, defaultPostAction) {
const { yAccessor, swapScales } = props;
const modifiedYAccessor = swapScales ? convertToArray(props.xAccessor) : convertToArray(yAccessor);
const modifiedXAccessor = swapScales ? yAccessor : xAccessor;
const modifiedXScale = swapScales ? yScale : xScale;
const modifiedYScale = swapScales ? xScale : yScale;
const postProcessor = swapScales ? postRotateAction : defaultPostAction;
const bars = getBars(props, modifiedXAccessor, modifiedYAccessor, modifiedXScale, modifiedYScale, plotData, stackFn, postProcessor);
return bars;
}
export const rotateXY = (array) => array.map((each) => {
return Object.assign(Object.assign({}, each), { x: each.y, y: each.x, height: each.width, width: each.height });
});
export function getBarsSVG2(props, bars) {
const { opacity } = props;
return bars.map((d, idx) => {
if (d.width <= 1) {
return React.createElement("line", { key: idx, className: d.className, stroke: d.fill, x1: d.x, y1: d.y, x2: d.x, y2: d.y + d.height });
}
return React.createElement("rect", { key: idx, className: d.className, stroke: d.stroke, fill: d.fill, x: d.x, y: d.y, width: d.width, fillOpacity: opacity, height: d.height });
});
}
export function drawOnCanvas2(props, ctx, bars) {
const { stroke } = props;
const nest = group(bars, (d) => d.fill);
nest.forEach((values, key) => {
if (head(values).width > 1) {
ctx.strokeStyle = key;
}
const fillStyle = head(values).width <= 1
? key
: colorToRGBA(key, props.opacity);
ctx.fillStyle = fillStyle;
values.forEach((d) => {
if (d.width <= 1) {
ctx.fillRect(d.x - 0.5, d.y, 1, d.height);
}
else {
ctx.fillRect(d.x + 0.5, d.y + 0.5, d.width, d.height);
if (stroke) {
ctx.strokeRect(d.x, d.y, d.width, d.height);
}
}
});
});
}
export function getBars(props, xAccessor, yAccessor, xScale, yScale, plotData, stack = identityStack, after = identity) {
const { baseAt, className, fill, stroke, spaceBetweenBar = 0 } = props;
const getClassName = functor(className);
const getFill = functor(fill);
const getBase = functor(baseAt);
const widthFunctor = functor(props.width);
const width = widthFunctor(props, {
xScale,
xAccessor,
plotData,
});
const barWidth = Math.round(width);
const eachBarWidth = (barWidth - spaceBetweenBar * (yAccessor.length - 1)) / yAccessor.length;
const offset = (barWidth === 1 ? 0 : 0.5 * width);
const ds = plotData
.map((each) => {
const d = {
appearance: {},
x: xAccessor(each),
};
yAccessor.forEach((eachYAccessor, i) => {
const key = `y${i}`;
d[key] = eachYAccessor(each);
const appearance = {
className: getClassName(each, i),
stroke: stroke ? getFill(each, i) : "none",
fill: getFill(each, i),
};
d.appearance[key] = appearance;
});
return d;
});
const keys = yAccessor.map((_, i) => `y${i}`);
// @ts-ignore
const data = stack().keys(keys)(ds);
const newData = data.map((each, i) => {
const key = each.key;
return each.map((d) => {
const array = [d[0], d[1]];
// @ts-ignore
array.data = {
x: d.data.x,
i,
appearance: d.data.appearance[key],
};
return array;
});
});
const bars = merge(newData)
.map((d) => {
let y = yScale(d[1]);
let h = getBase(xScale, yScale, d.data) - yScale(d[1] - d[0]);
if (h < 0) {
y = y + h;
h = -h;
}
return Object.assign(Object.assign({}, d.data.appearance), { x: Math.round(xScale(d.data.x) - width / 2), y, groupOffset: Math.round(offset - (d.data.i > 0 ? (eachBarWidth + spaceBetweenBar) * d.data.i : 0)), groupWidth: Math.round(eachBarWidth), offset: Math.round(offset), height: h, width: barWidth });
})
.filter((bar) => !isNaN(bar.y));
return after(bars);
}
//# sourceMappingURL=StackedBarSeries.js.map