billboard.js
Version:
Re-usable easy interface JavaScript chart library, based on D3 v4+
162 lines (161 loc) • 6.07 kB
JavaScript
/*!
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*
* billboard.js, JavaScript chart library
* https://naver.github.io/billboard.js/
*
* @version 4.1.1
*/
import { area, line } from "d3-shape";
//#region src/ChartInternal/shape/core/path.ts
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
/**
* Build the path y value accessor, with subchart candlestick projection.
* @param {object} $$ ChartInternal instance
* @param {boolean} isSub Whether to use subchart scales
* @returns {function} Accessor returning the path y value
* @private
*/
function getPathValueFn($$, isSub) {
if (!isSub) return (d) => $$.getBaseValue(d);
return (d) => {
const value = $$.getSubchartCandlestickShapeValue?.(d, true);
return value === void 0 ? $$.getBaseValue(d) : value;
};
}
/**
* Get drawable values with subchart candlestick projection.
* @param {object} $$ ChartInternal instance
* @param {Array} values Drawable values
* @param {boolean} isSub Whether to use subchart scales
* @returns {Array} Projected values
* @private
*/
function getProjectedValues($$, values, isSub) {
if (!isSub) return values;
return values.map((d) => {
const value = $$.getSubchartCandlestickShapeValue?.(d, isSub);
return value === void 0 ? d : {
...d,
value
};
});
}
/**
* Get values with step handling for line-like paths.
* @param {object} $$ ChartInternal instance
* @param {object} d Data target (needs `.id` for per-series step type detection)
* @param {Array} values Null-filtered values to draw
* @returns {Array} Drawable values
* @private
*/
function getLineValues($$, d, values) {
return $$.isStepType(d) ? $$.convertValuesToStep(values) : values;
}
/**
* Generate line path drawing function shared by SVG and canvas renderers.
* @param {object} $$ ChartInternal instance
* @param {object} lineIndices Shape indices
* @param {boolean} isSub Whether to use subchart scales
* @param {object} context Optional canvas path context
* @returns {function} Line path drawing function
* @private
*/
function generateDrawLinePath($$, lineIndices, isSub, context) {
const { config, scale } = $$;
const lineConnectNull = config.line_connectNull;
const isRotated = config.axis_rotated;
const getPoints = $$.generateGetLinePoints(lineIndices, isSub);
const yScale = $$.getYScaleById.bind($$);
const pathValue = getPathValueFn($$, isSub);
const xValue = (d) => (isSub ? $$.subxx : $$.xx).call($$, d);
const yValue = (d, i) => $$.isGrouped(d.id) ? getPoints(d, i)[0][1] : yScale(d.id, isSub)(pathValue(d));
let line$1 = line();
line$1 = isRotated ? line$1.x(yValue).y(xValue) : line$1.x(xValue).y(yValue);
context && (line$1 = line$1.context(context));
if (!lineConnectNull) line$1 = line$1.defined((d) => pathValue(d) !== null);
const x = isSub ? scale.subX : scale.x;
return (d) => {
const y = yScale(d.id, isSub);
let values = lineConnectNull ? $$.filterRemoveNull(d.values) : d.values;
let x0 = 0;
let y0 = 0;
let path;
if ($$.isLineType(d)) {
const regions = config.data_regions[d.id];
if (regions && !context && $$.lineWithRegions) {
values = getProjectedValues($$, values, isSub);
if ($$.isAreaRangeType(d)) values = values.map((dv) => ({
...dv,
value: $$.getRangedData(dv, "mid")
}));
if ($$.isStepType(d)) values = $$.convertValuesToStep(values);
path = $$.lineWithRegions(values, scale.zoom || x, y, regions);
} else path = line$1.curve($$.getCurve(d))(getLineValues($$, d, values));
} else {
if (values[0]) {
x0 = x(values[0].x);
y0 = y(pathValue(values[0]));
}
path = isRotated ? `M ${y0} ${x0}` : `M ${x0} ${y0}`;
}
return path || (context ? void 0 : "M 0 0");
};
}
/**
* Generate area path drawing function shared by SVG and canvas renderers.
* @param {object} $$ ChartInternal instance
* @param {object} areaIndices Shape indices
* @param {boolean} isSub Whether to use subchart scales
* @param {object} context Optional canvas path context
* @returns {function} Area path drawing function
* @private
*/
function generateDrawAreaPath($$, areaIndices, isSub, context) {
const { config } = $$;
const lineConnectNull = config.line_connectNull;
const isRotated = config.axis_rotated;
const getPoints = $$.generateGetAreaPoints(areaIndices, isSub);
const yScale = $$.getYScaleById.bind($$);
const pathValue = getPathValueFn($$, isSub);
const shapeYMin = /* @__PURE__ */ new Map();
const getShapeYMin = (id) => {
let min = shapeYMin.get(id);
if (min === void 0) {
min = $$.getShapeYMin(id, isSub);
shapeYMin.set(id, min);
}
return min;
};
const xValue = (d) => (isSub ? $$.subxx : $$.xx).call($$, d);
const value0 = (d, i) => $$.isGrouped(d.id) ? getPoints(d, i)[0][1] : yScale(d.id, isSub)($$.isAreaRangeType(d) ? $$.getRangedData(d, "high") : getShapeYMin(d.id));
const value1 = (d, i) => $$.isGrouped(d.id) ? getPoints(d, i)[1][1] : yScale(d.id, isSub)($$.isAreaRangeType(d) ? $$.getRangedData(d, "low") : pathValue(d));
return (d) => {
let values = lineConnectNull ? $$.filterRemoveNull(d.values) : d.values;
let x0 = 0;
let y0 = 0;
let path;
if ($$.isAreaType(d)) {
let area$1 = area();
area$1 = isRotated ? area$1.y(xValue).x0(value0).x1(value1) : area$1.x(xValue).y0(config.area_above ? 0 : config.area_below ? isSub ? $$.state.height2 : $$.state.height : value0).y1(value1);
context && (area$1 = area$1.context(context));
if (!lineConnectNull) area$1 = area$1.defined((d) => pathValue(d) !== null);
values = getProjectedValues($$, values, isSub);
if ($$.isStepType(d)) values = $$.convertValuesToStep(values);
path = area$1.curve($$.getCurve(d))(values);
} else {
if (values[0]) {
x0 = (isSub ? $$.scale.subX : $$.scale.x)(values[0].x);
y0 = $$.getYScaleById(d.id, isSub)(pathValue(values[0]));
}
path = isRotated ? `M ${y0} ${x0}` : `M ${x0} ${y0}`;
}
return path || (context ? void 0 : "M 0 0");
};
}
//#endregion
export { generateDrawAreaPath, generateDrawLinePath };