billboard.js
Version:
Re-usable easy interface JavaScript chart library, based on D3 v4+
164 lines (163 loc) • 5.45 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 { diffDomain } from "../../module/util/type-checks.js";
import classes_default from "../../config/classes.js";
import { generateWait } from "../../module/generator.js";
//#region src/ChartInternal/interactions/flow.ts
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
var flow_default = {
/**
* Generate flow
* @param {object} args option object
* @returns {function}
* @private
*/
generateFlow(args) {
const $$ = this;
const { data, state, $el } = $$;
return function() {
const flowLength = args.flow.length;
state.flowing = true;
data.targets.forEach((d) => {
d.values.splice(0, flowLength);
});
state.dataGeneration++;
if ($$.updateXGrid) $$.updateXGrid(true);
const elements = {};
[
"axis.x",
"grid.x",
"gridLines.x",
"region.list",
"text",
"bar",
"line",
"area",
"circle"
].forEach((v) => {
const name = v.split(".");
let node = $el[name[0]];
if (node && name.length > 1) node = node[name[1]];
if (node?.size()) elements[v] = node;
});
$$.hideGridFocus?.();
$$.setFlowList(elements, args);
};
},
/**
* Set flow list
* @param {object} elements Target elements
* @param {object} args option object
* @private
*/
setFlowList(elements, args) {
const $$ = this;
const { flow, targets } = args;
const { duration = args.duration, index: flowIndex, length: flowLength, orgDataCount } = flow;
const transform = $$.getFlowTransform(targets, orgDataCount, flowIndex, flowLength);
const wait = generateWait();
let n;
wait.add(Object.keys(elements).map((v) => {
n = elements[v].transition().ease((t) => +t).duration(duration);
if (v === "axis.x") n = n.call((g) => {
$$.axis.x.setTransition(g).create(g);
});
else if (v === "region.list") n = n.filter($$.isRegionOnX).attr("transform", transform);
else n = n.attr("transform", transform);
return n;
}));
n.call(wait, () => {
$$.cleanUpFlow(elements, args);
});
},
/**
* Clean up flow
* @param {object} elements Target elements
* @param {object} args option object
* @private
*/
cleanUpFlow(elements, args) {
const $$ = this;
const { config, state, $el: { svg } } = $$;
const isRotated = config.axis_rotated;
const { flow, shape, xv } = args;
const { cx, cy, xForText, yForText } = shape.pos;
const { done = () => {}, length: flowLength } = flow;
if (flowLength) {
[
"circle",
"text",
"shape",
"eventRect"
].forEach((v) => {
const target = [];
for (let i = 0; i < flowLength; i++) target.push(`.${classes_default[v]}-${i}`);
svg.selectAll(`.${classes_default[`${v}s`]}`).selectAll(target).remove();
});
svg.select(`.${classes_default.xgrid}`).remove();
}
Object.keys(elements).forEach((v) => {
const n = elements[v];
if (v !== "axis.x") n.attr("transform", null);
if (v === "grid.x") n.attr(state.xgridAttr);
else if (v === "gridLines.x") {
n.attr("x1", isRotated ? 0 : xv).attr("x2", isRotated ? state.width : xv);
n.select("text").attr("x", isRotated ? state.width : 0).attr("y", xv);
} else if (/^(area|bar|line)$/.test(v)) n.attr("d", shape.type[v]);
else if (v === "text") n.attr("x", xForText).attr("y", yForText).style("fill-opacity", $$.opacityForText.bind($$));
else if (v === "circle") {
if ($$.isCirclePoint()) n.attr("cx", cx).attr("cy", cy);
else {
const xFunc = (d) => cx(d) - config.point_r;
const yFunc = (d) => cy(d) - config.point_r;
n.attr("x", xFunc).attr("y", yFunc);
}
} else if (v === "region.list") n.select("rect").filter($$.isRegionOnX).attr("x", $$.regionX.bind($$)).attr("width", $$.regionWidth.bind($$));
});
config.interaction_enabled && $$.redrawEventRect();
done.call($$.api);
state.flowing = false;
},
/**
* Get flow transform value
* @param {object} targets target
* @param {number} orgDataCount original data count
* @param {number} flowIndex flow index
* @param {number} flowLength flow length
* @returns {string}
* @private
*/
getFlowTransform(targets, orgDataCount, flowIndex, flowLength) {
const $$ = this;
const { data, scale: { x } } = $$;
const dataValues = data.targets[0].values;
let flowStart = $$.getValueOnIndex(dataValues, flowIndex);
let flowEnd = $$.getValueOnIndex(dataValues, flowIndex + flowLength);
let translateX;
const orgDomain = x.domain();
const domain = $$.updateXDomain(targets, true, true);
if (!orgDataCount) {
if (dataValues.length !== 1) translateX = x(orgDomain[0]) - x(domain[0]);
else if ($$.axis.isTimeSeries()) {
flowStart = $$.getValueOnIndex(dataValues, 0);
flowEnd = $$.getValueOnIndex(dataValues, dataValues.length - 1);
translateX = x(flowStart.x) - x(flowEnd.x);
} else translateX = diffDomain(domain) / 2;
} else if (orgDataCount === 1 || flowStart?.x === flowEnd?.x) translateX = x(orgDomain[0]) - x(domain[0]);
else translateX = $$.axis.isTimeSeries() ? x(orgDomain[0]) - x(domain[0]) : x(flowStart?.x || 0) - x(flowEnd.x);
const scaleX = diffDomain(orgDomain) / diffDomain(domain);
return `translate(${translateX},0) scale(${scaleX},1)`;
}
};
//#endregion
export { flow_default as default };