billboard.js
Version:
Re-usable easy interface JavaScript chart library, based on D3 v4+
166 lines (165 loc) • 5.54 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 { isFunction } from "../../module/util/type-checks.js";
import { getRandom } from "../../module/util/object.js";
import { $COMMON, $TREEMAP } from "../../config/classes.js";
import { meetsLabelThreshold } from "../internals/text.util.js";
import { getTreemapNodeRect } from "./core/geometry.js";
import treemap_default$1 from "./core/treemap.js";
import { select } from "d3-selection";
//#region src/ChartInternal/shape/treemap.ts
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
/**
* Get treemap elements' position
* @param {d3Selection} group Root selection
* @param {object} root Root data
* @private
*/
function position(group, root) {
const $$ = this;
group.selectAll("g").attr("transform", (d) => {
const rect = getTreemapNodeRect($$, d, root);
return `translate(${rect.x},${rect.y})`;
}).select("rect").attr("width", (d) => getTreemapNodeRect($$, d, root).w).attr("height", (d) => getTreemapNodeRect($$, d, root).h);
}
var treemap_default = {
...treemap_default$1,
initTreemap() {
const $$ = this;
const { $el, state: { current: { width, height }, clip, datetimeId } } = $$;
clip.id = `${datetimeId}-clip`;
$$.initTreemapLayout();
$el.defs.append("clipPath").attr("id", clip.id).append("rect").attr("width", width).attr("height", height);
$el.treemap = $el.main.select(`.${$COMMON.chart}`).attr("clip-path", `url(#${clip.id})`).append("g").classed($TREEMAP.chartTreemaps, true);
$$.bindTreemapEvent();
},
/**
* Bind events
* @private
*/
bindTreemapEvent() {
const $$ = this;
const { $el, config, state } = $$;
const getTarget = (event) => {
const target = event.isTrusted ? event.target : state.eventReceiver.rect?.node();
let data;
if (target && /^rect$/i.test(target.tagName)) {
state.event = event;
data = select(target).datum();
}
return data?.data;
};
if (config.interaction_enabled) {
const isTouch = state.inputType === "touch";
$el.treemap.on(isTouch ? "touchstart" : "mouseover mousemove", (event) => {
const data = getTarget(event);
if (data) {
$$.showTooltip([data], event.currentTarget);
/^(touchstart|mouseover)$/.test(event.type) && $$.setOverOut(true, data);
}
}, isTouch ? { passive: true } : void 0).on(isTouch ? "touchend" : "mouseout", (event) => {
const data = getTarget(event);
if (config.interaction_onout) {
$$.hideTooltip();
$$.setOverOut(false, data);
}
});
}
},
/**
* Update treemap data
* @param {Array} targets Data targets
* @private
*/
updateTargetsForTreemap(targets) {
const $$ = this;
const { $el: { treemap } } = $$;
const treemapData = [$$.getTreemapRoot(targets ?? $$.data.targets)];
treemap.data($$.filterNullish(treemapData));
},
/**
* Render treemap
* @param {number} durationForExit Duration for exit transition
* @private
*/
updateTreemap(durationForExit) {
const $$ = this;
const { $el, $T } = $$;
const data = $el.treemap.datum();
const classChartTreemap = $$.getChartClass("Treemap");
const classTreemap = $$.getClass("treemap", true);
const treemap = $el.treemap.selectAll("g").data(data.children);
$T(treemap.exit(), durationForExit).style("opacity", "0").remove();
treemap.enter().append("g").append("rect");
$el.treemap.selectAll("g").attr("class", classChartTreemap).select("rect").attr("class", classTreemap).attr("fill", (d) => $$.color(d.data.name));
},
/**
* Generate treemap coordinate points data
* @returns {Array} Array of coordinate points
* @private
*/
generateGetTreemapPoints() {
const $$ = this;
const { $el } = $$;
const points = {};
$el.treemap.selectAll("g").each((d) => {
const rect = getTreemapNodeRect($$, d);
points[d.data.name] = [[rect.x, rect.y], [rect.x + rect.w, rect.y + rect.h]];
});
return (d) => points[d.id];
},
/**
* Redraw treemap
* @param {boolean} withTransition With or without transition
* @returns {Array} Selections
* @private
*/
redrawTreemap(withTransition) {
const $$ = this;
const { $el, state: { current: { width, height } } } = $$;
$el.defs.select("rect").attr("width", width).attr("height", height);
return [$$.$T($el.treemap, withTransition, getRandom()).call(position.bind($$), $el.treemap.datum())];
},
/**
* Get treemap data label format function
* @param {object} d Data object
* @returns {function} Label formatter function
* @private
*/
treemapDataLabelFormat(d) {
const $$ = this;
const { $el: { treemap }, config, scale: { x, y } } = $$;
const { id, value } = d;
const format = config.treemap_label_format;
const ratio = $$.getRatio("treemap", d);
const percentValue = (ratio * 100).toFixed(2);
const meetLabelThreshold = config.treemap_label_show && meetsLabelThreshold.call($$, ratio, "treemap") ? null : "0";
const treemapNode = treemap.selectAll("g").filter((node) => node.data.id === id).datum();
let width = 0;
let height = 0;
if (treemapNode) {
const { x0, x1, y0, y1 } = treemapNode;
width = x(x1) - x(x0);
height = y(y1) - y(y0);
}
return function(node) {
node.style("opacity", meetLabelThreshold);
return isFunction(format) ? format.bind($$.api)(value, ratio, id, {
width,
height
}) : `${id}\n${percentValue}%`;
};
}
};
//#endregion
export { treemap_default as default };