UNPKG

billboard.js

Version:

Re-usable easy interface JavaScript chart library, based on D3 v4+

166 lines (165 loc) • 5.54 kB
/*! * 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 };