billboard.js
Version:
Re-usable easy interface JavaScript chart library, based on D3 v4+
158 lines (155 loc) • 5.52 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.0.1
*/
import { isNumber } from '../../../module/util/type-checks.js';
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
/**
* Get the configured bar corner radius resolver.
* @param {object} $$ ChartInternal instance
* @returns {function|null} Radius resolver
* @private
*/
function getBarRadiusResolver($$) {
const { bar_radius: radius, bar_radius_ratio: ratio } = $$.config;
return isNumber(radius) && radius > 0 ? () => radius : (isNumber(ratio) ? width => width * ratio : null);
}
/**
* Get stacked bar rows that should receive rounded bar-end corners.
* @param {object} $$ ChartInternal instance
* @returns {Set} Radius row keys
* @private
*/
function getStackingBarRadiusSet($$) {
const { config, data } = $$;
const set = new Set();
if (!config.data_groups.length) {
return set;
}
const orderedTargets = $$.orderTargets($$.filterTargetsToShow(data.targets.filter($$.isBarType, $$)));
for (const group of config.data_groups) {
const ids = new Set(group);
const lastPosByIndex = new Map();
const lastNegByIndex = new Map();
for (const target of orderedTargets) {
if (!ids.has(target.id)) {
continue;
}
for (const d of target.values) {
if (d.value === null || d.value === 0) {
continue;
}
(d.value > 0 ? lastPosByIndex : lastNegByIndex).set(d.index, target.id);
}
}
for (const [index, id] of lastPosByIndex) {
set.add(`${id}:${index}`);
}
for (const [index, id] of lastNegByIndex) {
set.add(`${id}:${index}`);
}
}
return set;
}
/**
* Get canvas/SVG corner flags for the rounded bar edge.
* @param {boolean} isRotated Whether axis is rotated
* @param {boolean} isNegative Whether the bar direction is negative
* @param {number} radius Radius value
* @returns {number|object} Corner radius map
* @private
*/
function getBarRadiusCorners(isRotated, isNegative, radius) {
if (!radius) {
return 0;
}
return isRotated ?
(isNegative ? { tl: radius, bl: radius } : { tr: radius, br: radius }) :
(isNegative ? { br: radius, bl: radius } : { tl: radius, tr: radius });
}
/**
* Get SVG clip-path inset for radius arcs that exceed the bar shape.
* @param {Array} init Initial render coordinate
* @param {number} pos Rounded edge position
* @param {boolean} isRotated Whether axis is rotated
* @param {boolean} isNegative Whether the bar direction is negative
* @returns {string|null} Clip-path value
* @private
*/
function getBarRadiusClipPath(init, pos, isRotated, isNegative) {
let clipPath = "";
if (isRotated) {
if (isNegative && init[0] < pos) {
clipPath = `0 ${pos - init[0]}px 0 0`;
}
else if (!isNegative && init[0] > pos) {
clipPath = `0 0 0 ${init[0] - pos}px`;
}
}
else {
if (isNegative && init[1] > pos) {
clipPath = `${init[1] - pos}px 0 0 0`;
}
else if (!isNegative && init[1] < pos) {
clipPath = `0 0 ${pos - init[1]}px 0`;
}
}
return clipPath ? `inset(${clipPath})` : null;
}
/**
* Get shared rounded bar geometry that renderers can translate to their own primitives.
* @param {object} $$ ChartInternal instance
* @param {object} d Data row
* @param {Array} points Shared bar points
* @param {function|null} getRadius Radius resolver
* @param {Set} stackingRadiusSet Stacked radius row keys
* @param {function} isStackingRadiusData Optional fallback for hidden SVG bars
* @returns {object} Rounded bar geometry
* @private
*/
function getBarRadiusInfo($$, d, points, getRadius, stackingRadiusSet, isStackingRadiusData) {
const { config, state } = $$;
const isRotated = config.axis_rotated;
const indexX = +isRotated;
const indexY = +!indexX;
const isUnderZero = d.value < 0;
const isInverted = config[`axis_${$$.axis.getId(d.id)}_inverted`];
const isNegative = (!isInverted && isUnderZero) || (isInverted && !isUnderZero);
const isGrouped = $$.isGrouped(d.id);
const isRadiusData = getRadius && isGrouped && d.value !== 0 ?
(state.hiddenTargetIds.has(d.id) && isStackingRadiusData ?
isStackingRadiusData(d) :
stackingRadiusSet.has(`${d.id}:${d.index}`)) :
false;
const init = [
points[0][indexX],
points[0][indexY]
];
let radius = 0;
if (getRadius) {
const index = isRotated ? indexY : indexX;
const barW = Math.abs(points[2][index] - points[0][index]);
radius = Math.max(0, !isGrouped || isRadiusData ? getRadius(barW) : 0);
}
const pos = isRotated ?
points[1][indexX] + (isNegative ? radius : -radius) :
points[1][indexY] + (isNegative ? -radius : radius);
return {
radius,
corners: getBarRadiusCorners(isRotated, isNegative, radius),
clipPath: radius ? getBarRadiusClipPath(init, pos, isRotated, isNegative) : null,
indexX,
indexY,
isNegative,
pos
};
}
export { getBarRadiusInfo, getBarRadiusResolver, getStackingBarRadiusSet };